diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index 8441647f791..2259b631491 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -382,6 +382,17 @@ to destroy them and players will be able to make replacements. /obj/item/weapon/stock_parts/console_screen = 1, /obj/item/weapon/stock_parts/cell = 1) +//Almost the same recipe as destructive analyzer to give people choices. +/obj/item/weapon/circuitboard/experimentor + name = "circuit board (E.X.P.E.R.I-MENTOR)" + build_path = /obj/machinery/r_n_d/experimentor + board_type = "machine" + origin_tech = "magnets=1;engineering=1;programming=1;biotech=1;bluespace=2" + req_components = list( + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/weapon/stock_parts/manipulator = 2, + /obj/item/weapon/stock_parts/micro_laser = 2) + /obj/item/weapon/circuitboard/destructive_analyzer name = "circuit board (Destructive Analyzer)" diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index cf6f48f456c..3c347b9dae7 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -76,7 +76,6 @@ new /obj/item/clothing/under/hosparademale(src) new /obj/item/clothing/suit/armor/hos(src) new /obj/item/clothing/head/HoS(src) - new /obj/item/clothing/head/HoS/formal(src) new /obj/item/clothing/glasses/hud/security/eyepatch(src) new /obj/item/weapon/storage/lockbox/loyalty(src) new /obj/item/weapon/storage/box/flashbangs(src) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index bbe55e693c4..7f2d0ee9a1e 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -578,10 +578,8 @@ var/global/floorIsLava = 0 set category = "Server" set desc="Start the round RIGHT NOW" set name="Start Now" - if(!ticker) - alert("Unable to start the game as it is not set up.") - return if(ticker.current_state == GAME_STATE_PREGAME) + ticker.can_fire = 1 ticker.current_state = GAME_STATE_SETTING_UP log_admin("[usr.key] has started the game.") message_admins("[usr.key] has started the game.") diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index 408e205c679..256138f8beb 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -66,11 +66,6 @@ desc = "A robust beret for the Head of Security, for looking stylish while not sacrificing protection." icon_state = "hosberetblack" -/obj/item/clothing/head/HoS/formal - name = "formal head of security cap" - desc = "A robust formal cap for special occasions." - icon_state = "hosformal" - /obj/item/clothing/head/warden name = "warden's police hat" desc = "It's a special armored hat issued to the Warden of a security force. Protects the head from impacts." diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index aed63e5e5f9..88cc52f77bd 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -40,7 +40,7 @@ /obj/item/clothing/under/rank/prisoner name = "prison jumpsuit" - desc = "It's standardised Nanotrasen prisoner wear. Its suit sensors are stuck in the \"Fully On\" position." + desc = "It's standardised Nanotrasen prisoner-wear. Its suit sensors are stuck in the \"Fully On\" position." icon_state = "prisoner" item_state = "o_suit" item_color = "prisoner" @@ -336,7 +336,7 @@ /obj/item/clothing/under/captainparade name = "captain's parade uniform" - desc = "A captain's luxury wear, for special occasions." + desc = "A captain's luxury-wear, for special occasions." icon_state = "captain_parade" item_state = "by_suit" item_color = "captain_parade" @@ -344,20 +344,15 @@ /obj/item/clothing/under/hosparademale name = "head of security's parade uniform" - desc = "A male head of security's luxury wear, for special occasions." + desc = "A male head of security's luxury-wear, for special occasions." icon_state = "hos_parade_male" item_state = "r_suit" item_color = "hos_parade_male" can_adjust = 0 - fitted = 0 - -/obj/item/clothing/under/hosparademale/New() - ..() - attachTie(new /obj/item/clothing/tie/red(src)) /obj/item/clothing/under/hosparadefem name = "head of security's parade uniform" - desc = "A female head of security's luxury wear, for special occasions." + desc = "A female head of security's luxury-wear, for special occasions." icon_state = "hos_parade_fem" item_state = "r_suit" item_color = "hos_parade_fem" @@ -366,7 +361,7 @@ /obj/item/clothing/under/assistantformal name = "assistant's formal uniform" - desc = "An assistant's formal wear. Why an assistant needs formal wear is still unknown." + desc = "An assistant's formal-wear. Why an assistant needs formal-wear is still unknown." icon_state = "assistant_formal" item_state = "gy_suit" item_color = "assistant_formal" diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index bcd39b203e3..f984e47c32c 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -142,6 +142,16 @@ build_path = /obj/item/weapon/circuitboard/destructive_analyzer category = list("Research Machinery") +/datum/design/experimentor + name = "Machine Design (E.X.P.E.R.I-MENTOR Board)" + desc = "The circuit board for an E.X.P.E.R.I-MENTOR." + id = "experimentor" + req_tech = list("programming" = 2, "magnets" = 2, "engineering" = 2, "bluespace" = 2) + build_type = IMPRINTER + materials = list("$glass" = 1000, "sacid" = 20) + build_path = /obj/item/weapon/circuitboard/experimentor + category = list("Research Machinery") + /datum/design/protolathe name = "Machine Design (Protolathe Board)" desc = "The circuit board for a protolathe." diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm new file mode 100644 index 00000000000..2bafbeb8472 --- /dev/null +++ b/code/modules/research/experimentor.dm @@ -0,0 +1,392 @@ +//this is designed to replace the destructive analyzer + +#define SCANTYPE_POKE 1 +#define SCANTYPE_IRRADIATE 2 +#define SCANTYPE_GAS 3 +#define SCANTYPE_HEAT 4 +#define SCANTYPE_COLD 5 +#define SCANTYPE_OBLITERATE 6 + +#define EFFECT_PROB_VERYLOW 10 +#define EFFECT_PROB_LOW 20 +#define EFFECT_PROB_MEDIUM 45 +#define EFFECT_PROB_HIGH 65 +#define EFFECT_PROB_VERYHIGH 85 + +#define FAIL 7 +/obj/machinery/r_n_d/experimentor + name = "E.X.P.E.R.I-MENTOR" + icon = 'icons/obj/machines/heavy_lathe.dmi' + icon_state = "h_lathe" + density = 1 + anchored = 1 + use_power = 1 + var/obj/item/loaded_item = null + /// + var/badThingCoeff = 0 + var/resetTime = 15 + var/list/item_reactions = list() + var/list/valid_items = list() + +/obj/machinery/r_n_d/experimentor/proc/ConvertReqString2List(var/list/source_list) + var/list/temp_list = params2list(source_list) + for(var/O in temp_list) + temp_list[O] = text2num(temp_list[O]) + return temp_list + +/obj/machinery/r_n_d/experimentor/proc/SetTypeReactions() + for(var/I in typesof(/obj/item)) + item_reactions["[I]"] = pick(SCANTYPE_POKE,SCANTYPE_IRRADIATE,SCANTYPE_GAS,SCANTYPE_HEAT,SCANTYPE_COLD,SCANTYPE_OBLITERATE) + if(ispath(I,/obj/item/weapon/reagent_containers/food) || ispath(I,/obj/item/weapon/stock_parts) || ispath(I,/obj/item/weapon/grenade/chem_grenade) || ispath(I,/obj/item/weapon/kitchen)) + var/obj/item/tempCheck = new I() + if(tempCheck.icon_state != null) //check it's an actual usable item, in a hacky way + valid_items += I + qdel(tempCheck) + + +/obj/machinery/r_n_d/experimentor/New() + ..() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/experimentor(src) + component_parts += new /obj/item/weapon/stock_parts/scanning_module(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(src) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(src) + SetTypeReactions() + RefreshParts() + +/obj/machinery/r_n_d/experimentor/RefreshParts() + for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) + if(resetTime > 0 && (resetTime - M.rating) >= 1) + resetTime -= M.rating + for(var/obj/item/weapon/stock_parts/scanning_module/M in component_parts) + badThingCoeff += M.rating*2 + for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) + badThingCoeff += M.rating + +/obj/machinery/r_n_d/experimentor/attackby(var/obj/item/O as obj, var/mob/user as mob) + if (shocked) + shock(user,50) + if (istype(O, /obj/item/weapon/screwdriver)) + if (!panel_open) + panel_open = 1 + if(linked_console) + linked_console.linked_destroy = null + linked_console = null + icon_state = "h_lathe_maint" + user << "You open the maintenance hatch of [src]." + else + panel_open = 0 + icon_state = "h_lathe" + user << "You close the maintenance hatch of [src]." + return + if (panel_open) + if(istype(O, /obj/item/weapon/crowbar)) + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(src.loc) + M.state = 2 + M.icon_state = "box_1" + for(var/obj/I in component_parts) + I.loc = src.loc + del(src) + return 1 + else + user << "You can't load the [src.name] while it's opened." + return 1 + if (disabled) + return + if (!linked_console) + user << "The [src] must be linked to an R&D console first!" + return + if (busy) + user << "The [src] is busy right now." + return + if (istype(O, /obj/item) && !loaded_item) + if(!O.origin_tech) + user << "This doesn't seem to have a tech origin!" + return + var/list/temp_tech = ConvertReqString2List(O.origin_tech) + if (temp_tech.len == 0) + user << "You cannot experiment on this item!" + return + if(O.reliability < 90 && O.crit_fail == 0) + usr << "Item is neither reliable enough or broken enough to learn from." + return + busy = 1 + loaded_item = O + user.drop_item() + O.loc = src + user << "You add the [O.name] to the machine!" + flick("h_lathe_load", src) + spawn(15) + icon_state = "h_lathe_wloop" + return + + +/obj/machinery/r_n_d/experimentor/attack_hand(mob/user as mob) + user.set_machine(src) + var/dat = "
" + if(!linked_console) + dat += "Scan for R&D Console
" + if(loaded_item) + dat += "Loaded Item: [loaded_item]
" + dat += "Technology:
" + var/list/D = ConvertReqString2List(loaded_item.origin_tech) + for(var/T in D) + dat += "[T]
" + dat += "

Available tests:" + dat += "
Poke" + dat += "
Irradiate" + dat += "
Gas" + dat += "
Burn" + dat += "
Freeze" + dat += "
Destroy
" + dat += "
Eject" + else + dat += "Nothing loaded." + dat += "
Close
" + var/datum/browser/popup = new(user, "experimentor","Experimentor", 700, 400, src) + popup.set_content(dat) + popup.open() + onclose(user, "experimentor") + + +/obj/machinery/r_n_d/experimentor/proc/matchReaction(var/matching,var/reaction) + var/obj/item/D = matching + if(D) + if(item_reactions.Find("[D.type]")) + var/tor = item_reactions["[D.type]"] + if(tor == text2num(reaction)) + return tor + else + return FAIL + else + return FAIL + else + return FAIL + +/obj/machinery/r_n_d/experimentor/proc/ejectItem(var/delete=FALSE) + loaded_item.loc = src.loc + if(delete) + qdel(loaded_item) + loaded_item = null + +/* +#define EFFECT_PROB_VERYLOW 10 +#define EFFECT_PROB_LOW 20 +#define EFFECT_PROB_MEDIUM 45 +#define EFFECT_PROB_HIGH 65 +#define EFFECT_PROB_VERYHIGH 85 +*/ +/obj/machinery/r_n_d/experimentor/proc/experiment(var/exp,var/obj/item/exp_on) + //////////////////////////////////////////////////////////////////////////////////////////////// + if(exp == SCANTYPE_POKE) + visible_message("[src] prods at [exp_on] with mechanical arms.") + if(prob(EFFECT_PROB_VERYLOW-badThingCoeff)) + visible_message("[src] malfunctions and destroys [exp_on], lashing it's arms out at nearby people!.") + for(var/mob/living/m in oview(1)) + m.apply_damage(15,"brute",pick("head","chest","groin")) + ejectItem(TRUE) + else if(prob(EFFECT_PROB_LOW-badThingCoeff)) + visible_message("[src] malfunctions!.") + exp = SCANTYPE_OBLITERATE + else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff)) + visible_message("[src] malfunctions, throwing the [exp_on]!.") + var/mob/living/target = locate(/mob/living) in oview(7,src) + if(target) + var/obj/item/throwing = loaded_item + ejectItem() + throwing.throw_at(target, 10, 1) + //////////////////////////////////////////////////////////////////////////////////////////////// + if(exp == SCANTYPE_IRRADIATE) + visible_message("[src] reflects radioactive rays at [exp_on]!") + if(prob(EFFECT_PROB_VERYLOW-badThingCoeff)) + visible_message("[src] malfunctions, melting [exp_on] and leaking radiation!.") + for(var/mob/living/m in oview(1)) + m.apply_effect(25,IRRADIATE) + ejectItem(TRUE) + else if(prob(EFFECT_PROB_LOW-badThingCoeff)) + visible_message("[src] malfunctions, spewing toxic waste!.") + for(var/turf/T in oview(1)) + if(!T.density) + if(prob(EFFECT_PROB_VERYHIGH)) + new /obj/effect/decal/cleanable/greenglow(T) + else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff)) + visible_message("[src] malfunctions, transforming the [exp_on]!.") + ejectItem(TRUE) + var/newPath = pick(valid_items) + loaded_item = new newPath(src) + if(istype(loaded_item,/obj/item/weapon/grenade/chem_grenade)) + var/obj/item/weapon/grenade/chem_grenade/CG = loaded_item + CG.prime() + ejectItem() + //////////////////////////////////////////////////////////////////////////////////////////////// + if(exp == SCANTYPE_GAS) + visible_message("[src] fills it's chamber with gas, [exp_on] included.") + if(prob(EFFECT_PROB_VERYLOW-badThingCoeff)) + visible_message("[src] destroys [exp_on], leaking dangerous gas!.") + var/list/chems = list("carbon","radium","toxin","condensedcapsaicin","mushroomhallucinogen","space_drugs","ethanol","beepskysmash") + var/datum/reagents/R = new/datum/reagents(50) + R.my_atom = src + R.add_reagent(pick(chems), 50) + var/datum/effect/effect/system/chem_smoke_spread/smoke = new + smoke.set_up(R, 1, 0, src, 0, silent = 1) + playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3) + smoke.start() + R.delete() + ejectItem(TRUE) + else if(prob(EFFECT_PROB_LOW-badThingCoeff)) + visible_message("[src] malfunctions, spewing harmless gas!.") + var/datum/effect/effect/system/harmless_smoke_spread/smoke = new + smoke.set_up(1,0, src.loc, 0) + smoke.start() + else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff)) + visible_message("[src] melts [exp_on], ionizing the air around it!.") + empulse(src.loc, 8, 10) + ejectItem(TRUE) + //////////////////////////////////////////////////////////////////////////////////////////////// + if(exp == SCANTYPE_HEAT) + visible_message("[src] raises [exp_on]'s temperature.") + if(prob(EFFECT_PROB_VERYLOW-badThingCoeff)) + visible_message("[src] malfunctions, melting [exp_on] and releasing a burst of flame!.") + explosion(src.loc, -1, 0, 0, 0, 0, flame_range = 2) + ejectItem(TRUE) + else if(prob(EFFECT_PROB_LOW-badThingCoeff)) + visible_message("[src] malfunctions, melting [exp_on] and leaking hot air!.") + var/datum/gas_mixture/env = src.loc.return_air() + var/transfer_moles = 0.25 * env.total_moles() + var/datum/gas_mixture/removed = env.remove(transfer_moles) + if(removed) + var/heat_capacity = removed.heat_capacity() + if(heat_capacity == 0 || heat_capacity == null) + heat_capacity = 1 + removed.temperature = min((removed.temperature*heat_capacity + 100000)/heat_capacity, 1000) + env.merge(removed) + air_update_turf() + ejectItem(TRUE) + else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff)) + visible_message("[src] malfunctions, activating it's emergency coolant systems!.") + var/datum/effect/effect/system/harmless_smoke_spread/smoke = new + smoke.set_up(1,0, src.loc, 0) + smoke.start() + for(var/mob/living/m in oview(1)) + m.apply_damage(5,"burn",pick("head","chest","groin")) + ejectItem() + //////////////////////////////////////////////////////////////////////////////////////////////// + if(exp == SCANTYPE_COLD) + visible_message("[src] lowers [exp_on]'s temperature.") + if(prob(EFFECT_PROB_VERYLOW-badThingCoeff)) + visible_message("[src] malfunctions, shattering [exp_on] and releasing a dangerous cloud of coolant!") + var/datum/reagents/R = new/datum/reagents(50) + R.my_atom = src + R.add_reagent("frostoil", 50) + var/datum/effect/effect/system/chem_smoke_spread/smoke = new + smoke.set_up(R, 1, 0, src, 0, silent = 1) + playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3) + smoke.start() + R.delete() + ejectItem(TRUE) + else if(prob(EFFECT_PROB_LOW-badThingCoeff)) + visible_message("[src] malfunctions, shattering [exp_on] and leaking cold air!.") + var/datum/gas_mixture/env = src.loc.return_air() + var/transfer_moles = 0.25 * env.total_moles() + var/datum/gas_mixture/removed = env.remove(transfer_moles) + if(removed) + var/heat_capacity = removed.heat_capacity() + if(heat_capacity == 0 || heat_capacity == null) + heat_capacity = 1 + removed.temperature = (removed.temperature*heat_capacity - 75000)/heat_capacity + env.merge(removed) + air_update_turf() + ejectItem(TRUE) + else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff)) + visible_message("[src] malfunctions, releasing a flurry of chilly air as [exp_on] pops out!.") + var/datum/effect/effect/system/harmless_smoke_spread/smoke = new + smoke.set_up(1,0, src.loc, 0) + smoke.start() + ejectItem() + //////////////////////////////////////////////////////////////////////////////////////////////// + if(exp == SCANTYPE_OBLITERATE) + visible_message("[exp_on] activates the crushing mechanism, [exp_on] is destroyed!") + if(linked_console.linked_lathe) + linked_console.linked_lathe.m_amount += min((linked_console.linked_lathe.max_material_storage - linked_console.linked_lathe.TotalMaterials()), (exp_on.m_amt)) + linked_console.linked_lathe.g_amount += min((linked_console.linked_lathe.max_material_storage - linked_console.linked_lathe.TotalMaterials()), (exp_on.g_amt)) + if(prob(EFFECT_PROB_VERYLOW-badThingCoeff)) + visible_message("[src]'s crusher goes way too many levels too high, crushing right through space-time!") + playsound(src.loc, 'sound/effects/supermatter.ogg', 50, 1, -3) + var/list/throwAt = list() + for(var/i in oview(7,src)) + if(istype(i,/obj/item) || istype(i,/mob/living)) + throwAt.Add(i) + var/counter + for(counter = 1, counter < throwAt.len, ++counter) + var/cast = throwAt[counter] + cast:throw_at(src,10,1) + else if(prob(EFFECT_PROB_LOW-badThingCoeff)) + visible_message("[src]'s crusher goes one level too high, crushing right into space-time!.") + playsound(src.loc, 'sound/effects/supermatter.ogg', 50, 1, -3) + var/list/oViewStuff = oview(7,src) + var/list/throwAt = list() + for(var/i in oViewStuff) + if(istype(i,/obj/item) || istype(i,/mob/living)) + throwAt.Add(i) + var/counter + for(counter = 1, counter < throwAt.len, ++counter) + var/cast = throwAt[counter] + cast:throw_at(pick(throwAt),10,1) + ejectItem(TRUE) + //////////////////////////////////////////////////////////////////////////////////////////////// + if(exp == FAIL) + var/a = pick("rumbles","shakes","vibrates","shudders") + var/b = pick("crushes","spins","viscerates","smashes","insults") + visible_message("[exp_on] [a], and [b], the experiment was a failiure!") + + spawn(resetTime) + icon_state = "h_lathe" + busy = 0 + +/obj/machinery/r_n_d/experimentor/Topic(href, href_list) + if(..()) + return + usr.set_machine(src) + + var/scantype = href_list["function"] + var/obj/item/process = locate(href_list["item"]) in src + + if(scantype == "close") + usr << browse(null, "window=experimentor") + else if(scantype == "search") + var/obj/machinery/computer/rdconsole/D = locate(/obj/machinery/computer/rdconsole) in oview(3,src) + if(D) + linked_console = D + else if(scantype == "eject") + ejectItem() + else + var/dotype = matchReaction(process,scantype) + experiment(dotype,process) + use_power(750) + if(dotype != FAIL) + if(process.origin_tech) + var/list/temp_tech = ConvertReqString2List(process.origin_tech) + for(var/T in temp_tech) + linked_console.files.UpdateTech(T, temp_tech[T]) + linked_console.files.UpdateDesigns(process,process.type) + if(scantype != "close") + src.updateUsrDialog() + return + +#undef SCANTYPE_POKE +#undef SCANTYPE_IRRADIATE +#undef SCANTYPE_GAS +#undef SCANTYPE_HEAT +#undef SCANTYPE_COLD +#undef SCANTYPE_OBLITERATE + +#undef EFFECT_PROB_VERYLOW +#undef EFFECT_PROB_LOW +#undef EFFECT_PROB_MEDIUM +#undef EFFECT_PROB_HIGH +#undef EFFECT_PROB_VERYHIGH + +#undef FAIL diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index 550d1eaa80a..b10bcfce92b 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/uniform.dmi b/icons/mob/uniform.dmi index fc3d36fd310..43dcda058a5 100644 Binary files a/icons/mob/uniform.dmi and b/icons/mob/uniform.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index cc946cfab32..24f9048fc45 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/clothing/uniforms.dmi b/icons/obj/clothing/uniforms.dmi index cbec4b7a7e5..9cee4e8a3bb 100644 Binary files a/icons/obj/clothing/uniforms.dmi and b/icons/obj/clothing/uniforms.dmi differ diff --git a/icons/obj/machines/heavy_lathe.dmi b/icons/obj/machines/heavy_lathe.dmi index 1189687d861..68557159eb4 100644 Binary files a/icons/obj/machines/heavy_lathe.dmi and b/icons/obj/machines/heavy_lathe.dmi differ diff --git a/tgstation.dme b/tgstation.dme index b104e9f60f2..c08bebbd9c9 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1295,6 +1295,7 @@ #include "code\modules\research\circuitprinter.dm" #include "code\modules\research\designs.dm" #include "code\modules\research\destructive_analyzer.dm" +#include "code\modules\research\experimentor.dm" #include "code\modules\research\message_server.dm" #include "code\modules\research\protolathe.dm" #include "code\modules\research\rd-readme.dm"