From 538b0f52f157a980ebe4f7e5ddc3a3d12d81fa8c Mon Sep 17 00:00:00 2001 From: Testmerge Worker Date: Wed, 23 Oct 2024 13:40:07 +0000 Subject: [PATCH] add: Experimentor relics rework (#5683) [testmerge][8c55b01] --- code/game/objects/effects/anomalies.dm | 16 + code/game/objects/items/anomaly_beacon.dm | 30 ++ code/game/objects/items/devices/paicard.dm | 4 +- .../items/weapons/experimental_syringe_gun.dm | 68 +++ .../items/weapons/grenades/fauna_bomb.dm | 64 +++ .../objects/items/weapons/grenades/grenade.dm | 2 +- .../weapons/tuned_anomalous_teleporter.dm | 74 +++ .../living/simple_animal/hostile/hostile.dm | 10 + .../simple_animal/hostile/mining/hivelord.dm | 2 + .../mob/living/simple_animal/simple_animal.dm | 14 + code/modules/research/experimentor.dm | 459 +++++++++++------- .../mob/inhands/relics_production/inhandl.dmi | Bin 0 -> 740 bytes .../mob/inhands/relics_production/inhandr.dmi | Bin 0 -> 740 bytes icons/obj/weapons/techrelic.dmi | Bin 0 -> 1601 bytes paradise.dme | 4 + 15 files changed, 563 insertions(+), 184 deletions(-) create mode 100644 code/game/objects/items/anomaly_beacon.dm create mode 100644 code/game/objects/items/weapons/experimental_syringe_gun.dm create mode 100644 code/game/objects/items/weapons/grenades/fauna_bomb.dm create mode 100644 code/game/objects/items/weapons/tuned_anomalous_teleporter.dm create mode 100644 icons/mob/inhands/relics_production/inhandl.dmi create mode 100644 icons/mob/inhands/relics_production/inhandr.dmi create mode 100644 icons/obj/weapons/techrelic.dmi diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index b7adbe7f282..5f15b415f03 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -59,6 +59,22 @@ return ..() /obj/effect/anomaly/process() + for(var/obj/item/I in get_turf(src)) + if(!I.origin_tech) + continue + if (istype(I, /obj/item/relict_production/rapid_dupe)) + var/amount = rand(1, 3) + for (var/i; i <= amount; i++) + new /obj/item/relic(get_turf(I)) + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(5, get_turf(I)) + smoke.start() + qdel(I) + continue + if (prob(2)) + new /obj/item/relic(get_turf(I)) + qdel(I) + anomalyEffect() if(death_time < world.time) if(loc) diff --git a/code/game/objects/items/anomaly_beacon.dm b/code/game/objects/items/anomaly_beacon.dm new file mode 100644 index 00000000000..113136a524a --- /dev/null +++ b/code/game/objects/items/anomaly_beacon.dm @@ -0,0 +1,30 @@ +/obj/item/assembly/anomaly_beacon + icon = 'icons/obj/weapons/techrelic.dmi' + icon_state = "beacon" + item_state = "beacon" + lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' + righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' + name = "anomaly beacon" + desc = "A device that draws power from bluespace and creates a permanent tracking beacon." + origin_tech = "bluespace=6" + +/obj/item/assembly/anomaly_beacon/activate() + var/obj/effect/anomaly/anomaly_path = pick(subtypesof(/obj/effect/anomaly/)) + var/newAnomaly = new anomaly_path(get_turf(src)) + notify_ghosts("[name] has an object of interest: [newAnomaly]!", title = "Something's Interesting!", source = newAnomaly, action = NOTIFY_FOLLOW) + qdel(src) + +/obj/item/assembly/anomaly_beacon/attack_self(mob/user) + activate() + +/datum/crafting_recipe/anomaly_beacon + name = "Anomaly beacon" + result = /obj/item/assembly/anomaly_beacon + tools = list(TOOL_SCREWDRIVER) + reqs = list(/obj/item/assembly/signaler/anomaly = 1, + /obj/item/relict_production/rapid_dupe = 1, + /obj/item/radio/beacon = 1, + /obj/item/stack/cable_coil = 5) + time = 300 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index 6914b59cc95..b291e1b0067 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -5,7 +5,7 @@ item_state = "pai" w_class = WEIGHT_CLASS_SMALL slot_flags = ITEM_SLOT_BELT - origin_tech = "programming=2" + origin_tech = "programming=3;powerstorage=2" // Or it will be cloned in the experimentor var/request_cooldown = 5 // five seconds var/last_request var/obj/item/radio/headset/radio @@ -531,7 +531,7 @@ icon = 'icons/obj/pda.dmi' icon_state = "pai-spai" w_class = WEIGHT_CLASS_TINY - origin_tech = "programming=2;syndicate=2" + origin_tech = "programming=3;syndicate=2" // Or it will be cloned in the experimentor var/extra_memory = 50 var/used = TRUE diff --git a/code/game/objects/items/weapons/experimental_syringe_gun.dm b/code/game/objects/items/weapons/experimental_syringe_gun.dm new file mode 100644 index 00000000000..e6095af7b03 --- /dev/null +++ b/code/game/objects/items/weapons/experimental_syringe_gun.dm @@ -0,0 +1,68 @@ +/obj/item/gun/syringe/rapidsyringe/experimental + name = "experimental syringe gun" + desc = "Эксперементальный шприцемет с 6 слотами для шприцев, встроенным, самовосполняющимся хранилищем химикатов и новейшей системой автозаправки шприцев." + origin_tech = "combat=3;biotech=4;bluespace=5" + icon = 'icons/obj/weapons/techrelic.dmi' + item_state = "strynggun" + lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' + righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' + icon_state = "strynggun" + materials = list(MAT_METAL=2000, MAT_GLASS=2000, MAT_BLUESPACE=400) + var/obj/item/reagent_containers/glass/beaker/large/ready_reagents = new + var/obj/item/reagent_containers/glass/beaker/large/processed_reagents = new + var/synth_speed = 5 + var/bank_size = 100 + origin_tech = "bluespace=4;biotech=5" + +/obj/item/gun/syringe/rapidsyringe/experimental/Initialize() + . = ..() + START_PROCESSING(SSobj, src) + +/obj/item/gun/syringe/rapidsyringe/experimental/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/gun/syringe/rapidsyringe/experimental/attackby(obj/item/A, mob/user) + if(istype(A, /obj/item/reagent_containers/syringe)) + var/in_clip = length(syringes) + (chambered.BB ? 1 : 0) + if(in_clip < max_syringes) + if(!user.drop_transfer_item_to_loc(A, src)) + return ..() + balloon_alert(user, "заряжено!") + syringes.Add(A) + process_chamber() // Chamber the syringe if none is already + return ATTACK_CHAIN_BLOCKED_ALL + else + balloon_alert(user, "недостаточно места!") + return ATTACK_CHAIN_PROCEED + else if(istype(A, /obj/item/reagent_containers/glass)) + var/obj/item/reagent_containers/glass/RC = A + if (!RC.reagents.reagent_list) + return ..() + ready_reagents.reagents.clear_reagents() + processed_reagents.reagents.clear_reagents() + RC.reagents.trans_to(ready_reagents, bank_size) + ready_reagents.reagents.trans_to(processed_reagents, synth_speed) + balloon_alert(user, "синтезируемый набор веществ изменен!") + return ATTACK_CHAIN_BLOCKED_ALL + else + return ..() + +/obj/item/gun/syringe/rapidsyringe/experimental/process() + for (var/obj/item/reagent_containers/syringe/S in syringes) + ready_reagents.reagents.trans_to(S, ready_reagents.reagents.total_volume) + for (var/datum/reagent/R in processed_reagents.reagents.reagent_list) + if (R.can_synth) + ready_reagents.reagents.add_reagent(R.id, R.volume) + +/datum/crafting_recipe/rapidsyringe_experimental + name = "Experemintal syringe gun" + result = /obj/item/gun/syringe/rapidsyringe/experimental + tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH) + reqs = list(/obj/item/relict_production/perfect_mix = 1, + /obj/item/assembly/signaler/anomaly/vortex = 1, + /obj/item/gun/syringe/rapidsyringe = 1, + /obj/item/stock_parts/matter_bin = 1) + time = 300 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON diff --git a/code/game/objects/items/weapons/grenades/fauna_bomb.dm b/code/game/objects/items/weapons/grenades/fauna_bomb.dm new file mode 100644 index 00000000000..6ca2c4314be --- /dev/null +++ b/code/game/objects/items/weapons/grenades/fauna_bomb.dm @@ -0,0 +1,64 @@ +/obj/item/grenade/fauna_bomb + name = "fauna bomb" + desc = "Эксперементальная, многоразовая граната, создающая фауну агрессивную ко всем, кроме активировавшего гранату." + w_class = WEIGHT_CLASS_SMALL + icon = 'icons/obj/weapons/techrelic.dmi' + icon_state = "bomb" + item_state = "bomb" + lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' + righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' + var/deliveryamt = 8 + var/amount = 3 + COOLDOWN_DECLARE(fauna_bomb_cooldown) + var/mob/activator + origin_tech = "bluespace=4;biotech=5" + +/obj/item/grenade/fauna_bomb/attack_self(mob/user) + if(!COOLDOWN_FINISHED(src, fauna_bomb_cooldown)) + to_chat(user, span_warning("[src] is still recharging!")) + return + + COOLDOWN_START(src, fauna_bomb_cooldown, 60 SECONDS) + activator = user + return ..(user, FALSE) + +/obj/item/grenade/fauna_bomb/prime() + active = FALSE + playsound(get_turf(src), 'sound/items/rawr.ogg', 100, TRUE) + var/faction = activator.name + "_fauna_bomb" + activator.faction |= faction + var/list/mob/living/simple_animal/mobs = list() + + var/mob/living/simple_animal/spawn_mob_type = pick(/mob/living/simple_animal/hostile/asteroid/hivelord/legion, /mob/living/simple_animal/hostile/asteroid/goliath, /mob/living/simple_animal/hostile/asteroid/marrowweaver) + + for(var/i in 1 to amount) + var/mob/living/simple_animal/new_mob = new spawn_mob_type(get_turf(src)) + mobs.Add(new_mob) + new_mob.set_leash(activator, 10) + new_mob.faction |= faction + if(prob(50)) + for(var/j = 1, j <= rand(1, 3), j++) + step(new_mob, pick(NORTH, SOUTH, EAST, WEST)) + + if(prob(40)) + to_chat(activator, span_warning("[src] falls apart!")) + qdel(src) + + sleep(600) + for (var/mob/mob in mobs) + mob.dust() + +/obj/item/grenade/fauna_bomb/update_icon_state() + return + +/datum/crafting_recipe/fauna_bomb + name = "Fauna bomb" + result = /obj/item/grenade/fauna_bomb + tools = list(TOOL_SCREWDRIVER) + reqs = list(/obj/item/relict_production/pet_spray = 1, + /obj/item/assembly/signaler/anomaly/pyro = 1, + /obj/item/grenade/chem_grenade/adv_release = 1, + /obj/item/stack/cable_coil = 5) + time = 300 + category = CAT_WEAPONRY + subcategory = CAT_WEAPON diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index 8e27a202b85..96ebb79d2f1 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -56,7 +56,7 @@ /obj/item/grenade/attack_self(mob/user) if(!active && clown_check(user)) - to_chat(user, "You prime the [name]! [det_time/10] seconds!") + to_chat(user, span_warning("You prime the [name]! [det_time/10] seconds!")) active = TRUE update_icon(UPDATE_ICON_STATE) add_fingerprint(user) diff --git a/code/game/objects/items/weapons/tuned_anomalous_teleporter.dm b/code/game/objects/items/weapons/tuned_anomalous_teleporter.dm new file mode 100644 index 00000000000..620ea5f9222 --- /dev/null +++ b/code/game/objects/items/weapons/tuned_anomalous_teleporter.dm @@ -0,0 +1,74 @@ +/obj/item/tuned_anomalous_teleporter + name = "tuned anomalous teleporter" + desc = "A portable item using blue-space technology." + icon = 'icons/obj/weapons/techrelic.dmi' + icon_state = "teleport" + lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' + righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' + item_state = "teleport" + throwforce = 0 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + throw_range = 5 + materials = list(MAT_METAL=10000) + origin_tech = "magnets=3;bluespace=4" + armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 30, BIO = 0, RAD = 0, FIRE = 100, ACID = 100) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + /// Variable contains next time hand tele can be used to make it not EMP proof + var/emp_timer = 0 + COOLDOWN_DECLARE(tuned_anomalous_teleporter_cooldown) // declare cooldown for teleportations + COOLDOWN_DECLARE(emp_cooldown) // declare cooldown for EMP + var/base_cooldown = 20 SECONDS // cooldown for teleportations + var/emp_cooldown_min = 10 SECONDS // min cooldown for emp + var/emp_cooldown_max = 15 SECONDS // max cooldown for emp + var/tp_range = 5 // range of teleportations + origin_tech = "bluespace=5" + +/obj/item/tuned_anomalous_teleporter/attack_self(mob/user) + if(!COOLDOWN_FINISHED(src, emp_cooldown)) + do_sparks(5, FALSE, loc) + to_chat(user, span_warning("[src] attempts to teleport you, but abruptly shuts off.")) + return FALSE + if(!COOLDOWN_FINISHED(src, tuned_anomalous_teleporter_cooldown)) + to_chat(user, span_warning("[src] is still recharging.")) + return FALSE + + COOLDOWN_START(src, tuned_anomalous_teleporter_cooldown, base_cooldown) + + var/datum/teleport/TP = new /datum/teleport() + var/crossdir = angle2dir((dir2angle(user.dir)) % 360) + var/turf/T1 = get_turf(user) + for(var/i in 1 to tp_range) + T1 = get_step(T1, crossdir) + var/datum/effect_system/smoke_spread/s1 = new + var/datum/effect_system/smoke_spread/s2 = new + s1.set_up(5, FALSE, user) + s2.set_up(5, FALSE, user) + TP.start(user, T1, FALSE, TRUE, s1, s2, 'sound/effects/phasein.ogg', ) + TP.doTeleport() + +/obj/item/tuned_anomalous_teleporter/emp_act(severity) + make_inactive(severity) + return ..() + +/obj/item/tuned_anomalous_teleporter/proc/make_inactive(severity) + var/time = rand(emp_cooldown_min, emp_cooldown_max) * (severity == EMP_HEAVY ? 2 : 1) + COOLDOWN_START(src, emp_cooldown, time) + +/obj/item/tuned_anomalous_teleporter/examine(mob/user) + . = ..() + if(emp_timer > world.time) + . += span_warning("It looks inactive.") + +/datum/crafting_recipe/tuned_anomalous_teleporter + name = "Tuned anomalous teleporter" + result = /obj/item/tuned_anomalous_teleporter + tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) + reqs = list(/obj/item/relict_production/strange_teleporter = 1, + /obj/item/assembly/signaler/anomaly/bluespace = 1, + /obj/item/gps = 1, + /obj/item/stack/ore/bluespace_crystal, + /obj/item/stack/sheet/metal = 2, + /obj/item/stack/cable_coil = 5) + time = 300 + category = CAT_MISC diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index ebf48eaaa54..32908854ec8 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -272,8 +272,18 @@ var/possible_target_distance = get_dist(targets_from, A) if(target_dist < possible_target_distance) Targets -= A + + var/list/mob/high_priority_targets = list() + for(var/mob/T in Targets) + if (!(T.UID() in low_priority_targets)) + high_priority_targets.Add(T) + + if (high_priority_targets.len) + Targets = high_priority_targets + if(!Targets.len)//We didnt find nothin! return + var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random return chosen_target diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm index ec04e0c4281..860a9c43c4a 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm @@ -209,6 +209,8 @@ /mob/living/simple_animal/hostile/asteroid/hivelord/legion/death(gibbed) visible_message("The skulls on [src] wail in anger as they flee from their dying host!") var/turf/T = get_turf(src) + if (!T) // When legion dusts T = null. Maybe not onli this way. + return for(var/i in 1 to 2) new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/weaken(T) if(T) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 0538a1d6465..49f45d9499b 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -133,6 +133,10 @@ var/Discipline = 0 // if a slime has been hit with a freeze gun, or wrestled/attacked off a human, they become disciplined and don't attack anymore for a while var/SStun = 0 // stun variable + var/list/low_priority_targets = list() + + var/atom/leash // autodust on a big distance + var/leash_radius = 10 /mob/living/simple_animal/Initialize(mapload) . = ..() @@ -301,6 +305,12 @@ /mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) + if (leash) + var/dist = get_dist(src, leash) + if (dist > leash_radius) + src.dust() + return + var/atmos_suitable = TRUE if(!HAS_TRAIT(src, TRAIT_NO_BREATH)) @@ -789,3 +799,7 @@ if(!can_collar) return AddElement(/datum/element/strippable, create_strippable_list(list(/datum/strippable_item/pet_collar))) + +/mob/living/simple_animal/proc/set_leash(atom/A, radius) + leash = A + leash_radius = radius diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm index b0deac1cb8d..78594494b1d 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -29,6 +29,7 @@ var/resetTime = 15 var/cloneMode = FALSE var/cloneCount = 0 + var/clone_next = FALSE // Clones the next inserted technological item. /// The distance to your rnd console. Useful for creative mapping. var/console_dist = 3 var/list/item_reactions = list() @@ -69,10 +70,9 @@ /obj/machinery/r_n_d/experimentor/proc/SetTypeReactions() var/probWeight = 0 for(var/I in typesof(/obj/item)) - if(istype(I,/obj/item/relic)) //does istype even work here - item_reactions["[I]"] = SCANTYPE_DISCOVER - else - item_reactions["[I]"] = pick(SCANTYPE_POKE,SCANTYPE_IRRADIATE,SCANTYPE_GAS,SCANTYPE_HEAT,SCANTYPE_COLD,SCANTYPE_OBLITERATE) + if(istype(I,/obj/item/relic)) + continue + item_reactions["[I]"] = pick(SCANTYPE_POKE,SCANTYPE_IRRADIATE,SCANTYPE_GAS,SCANTYPE_HEAT,SCANTYPE_COLD,SCANTYPE_OBLITERATE) if(ispath(I,/obj/item/stock_parts) || ispath(I,/obj/item/grenade/chem_grenade) || ispath(I,/obj/item/kitchen)) var/obj/item/tempCheck = I if(initial(tempCheck.icon_state) != null) //check it's an actual usable item, in a hacky way @@ -145,24 +145,52 @@ if(disabled) to_chat(user, span_warning("The [name] is offline.")) return ATTACK_CHAIN_PROCEED + if(!linked_console) to_chat(user, span_warning("The [name] should be linked to an R&D console first.")) return ATTACK_CHAIN_PROCEED + if(loaded_item) to_chat(user, span_warning("The [name] is already loaded.")) return ATTACK_CHAIN_PROCEED + if(!checkCircumstances(I)) to_chat(user, span_warning("The [I.name] is not yet valid for [src] and must be completed.")) return ATTACK_CHAIN_PROCEED + if(!I.origin_tech) to_chat(user, span_warning("The [I.name] has no technological origin.")) return ATTACK_CHAIN_PROCEED - var/list/temp_tech = ConvertReqString2List(I.origin_tech) - if(!length(temp_tech)) - to_chat(user, span_warning("The [I.name] has no technological origin.")) + + if(clone_next) + var/list/temp_tech = ConvertReqString2List(I.origin_tech) + var/techs_sum = 0 + for(var/T in temp_tech) + techs_sum += temp_tech[T] + + if(istype(I, /obj/item/relic) || (techs_sum > 4 || isstorage(I)) && !istype(I, /obj/item/storage/backpack/holding)) + to_chat(user, span_warning("Этот предмет слишком сложен для копирования. Попробуйте вставить что-то попроще.")) + return ATTACK_CHAIN_PROCEED + + if (I.type in subtypesof(/obj/item/stack)) + var/obj/item/stack/stack = I + if (stack.amount > 1) + to_chat(user, span_warning("Предмет должен быть цельным.")) + return ATTACK_CHAIN_PROCEED + + investigate_log("Experimentor has made a clone of [I]", INVESTIGATE_EXPERIMENTOR) + throwSmoke(get_turf(pick(oview(1,src)))) + for (var/i = 1; i <= badThingCoeff; i++) + visible_message(span_notice("A duplicate [I] pops out!")) + var/type_to_make = I.type + new type_to_make(get_turf(pick(oview(1,src)))) + + clone_next = FALSE return ATTACK_CHAIN_PROCEED + if(!user.drop_transfer_item_to_loc(I, src)) - return ..() + return ATTACK_CHAIN_PROCEED + loaded_item = I to_chat(user, span_notice("You have added [I] to [src].")) flick("h_lathe_load", src) @@ -214,8 +242,6 @@ dat += "
Burn" dat += "
Freeze" dat += "
Destroy
" - if(istype(loaded_item,/obj/item/relic)) - dat += "
Discover
" dat += "
Eject" else dat += "Nothing loaded." @@ -229,10 +255,10 @@ /obj/machinery/r_n_d/experimentor/proc/matchReaction(matching,reaction) var/obj/item/D = matching if(D) - if(item_reactions.Find("[D.type]")) + if(istype(D, /obj/item/relic) || item_reactions.Find("[D.type]")) var/tor = item_reactions["[D.type]"] - if(tor == text2num(reaction)) - return tor + if(istype(D, /obj/item/relic) || tor == text2num(reaction)) + return text2num(reaction) else return FAIL else @@ -276,14 +302,9 @@ else counter = 1 -/obj/machinery/r_n_d/experimentor/proc/experiment(exp,obj/item/exp_on) - recentlyExperimented = TRUE - update_icon(UPDATE_ICON_STATE) - var/chosenchem - var/criticalReaction = (exp_on.type in critical_items) ? TRUE : FALSE - //////////////////////////////////////////////////////////////////////////////////////////////// - if(exp == SCANTYPE_POKE) - visible_message("[src] prods at [exp_on] with mechanical arms.") +/obj/machinery/r_n_d/experimentor/proc/scan_poke(exp, obj/item/exp_on, chosenchem, criticalReaction, isRelict) + visible_message("[src] prods at [exp_on] with mechanical arms.") + if(!isRelict) if(prob(EFFECT_PROB_LOW) && criticalReaction) visible_message("[exp_on] is gripped in just the right way, enhancing its focus.") badThingCoeff++ @@ -305,9 +326,21 @@ ejectItem() if(throwing) throwing.throw_at(target, 10, 1) - //////////////////////////////////////////////////////////////////////////////////////////////// - if(exp == SCANTYPE_IRRADIATE) - visible_message("[src] reflects radioactive rays at [exp_on]!") + else if(prob(EFFECT_PROB_VERYLOW)) + visible_message("The [exp_on] begins to vibrate!") + playsound(src.loc, 'sound/effects/supermatter.ogg', 50, 3, -1) + ejectItem() + throwSmoke(get_turf(exp_on)) + var/obj/item/relict_production/strange_teleporter/teleporter = new /obj/item/relict_production/strange_teleporter(get_turf(exp_on)) + teleporter.icon_state = exp_on.icon_state + qdel(exp_on) + else + exp = FAIL + + +/obj/machinery/r_n_d/experimentor/proc/scan_irradiate(exp, obj/item/exp_on, chosenchem, criticalReaction, isRelict) + visible_message("[src] reflects radioactive rays at [exp_on]!") + if(!isRelict) if(prob(EFFECT_PROB_LOW) && criticalReaction) visible_message("[exp_on] has activated an unknown subroutine!") cloneMode = TRUE @@ -337,36 +370,52 @@ var/obj/item/grenade/chem_grenade/CG = loaded_item CG.prime() ejectItem() - //////////////////////////////////////////////////////////////////////////////////////////////// - if(exp == SCANTYPE_GAS) - visible_message("[src] fills its chamber with gas, [exp_on] included.") + else if(prob(EFFECT_PROB_VERYLOW)) + visible_message("The [exp_on] has activated an unknown subroutine!") + clone_next = TRUE + ejectItem() + qdel(exp_on) + + var/T = rand(1, linked_console.files.known_tech.len) + var/datum/tech/KT = linked_console.files.known_tech[linked_console.files.known_tech[T]] + var/new_level = linked_console.files.UpdateTech(linked_console.files.known_tech[T], KT.level + 1) + var/tech_log = "[T] [new_level], " + if(tech_log) + investigate_log("[usr] increased tech experimentoring [loaded_item]: [tech_log]. ", INVESTIGATE_RESEARCH) + else + exp = FAIL + + +/obj/machinery/r_n_d/experimentor/proc/scan_gas(exp, obj/item/exp_on, chosenchem, criticalReaction, isRelict) + visible_message("[src] fills its chamber with gas, [exp_on] included.") + if(!isRelict) if(prob(EFFECT_PROB_LOW) && criticalReaction) visible_message("[exp_on] achieves the perfect mix!") new /obj/item/stack/sheet/mineral/plasma(get_turf(pick(oview(1,src)))) if(prob(EFFECT_PROB_VERYLOW-badThingCoeff)) visible_message("[src] destroys [exp_on], leaking dangerous gas!") chosenchem = pick("carbon","radium","toxin","condensedcapsaicin","psilocybin","space_drugs","ethanol","beepskysmash") - var/datum/reagents/R = new/datum/reagents(400) - R.my_atom = src - R.add_reagent(chosenchem , 375) + var/datum/reagents/inner_reagent = new/datum/reagents(400) + inner_reagent.my_atom = src + inner_reagent.add_reagent(chosenchem , 375) investigate_log("Experimentor has released [chosenchem] smoke.", INVESTIGATE_EXPERIMENTOR) var/datum/effect_system/smoke_spread/chem/smoke = new - smoke.set_up(R, src, TRUE) + smoke.set_up(inner_reagent, src, TRUE) playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3) smoke.start() - qdel(R) + qdel(inner_reagent) ejectItem(TRUE) if(prob(EFFECT_PROB_VERYLOW-badThingCoeff)) visible_message("[src]'s chemical chamber has sprung a leak!") chosenchem = pick("mutationtoxin","nanomachines","sacid") - var/datum/reagents/R = new/datum/reagents(400) - R.my_atom = src - R.add_reagent(chosenchem , 375) + var/datum/reagents/inner_reagent = new/datum/reagents(400) + inner_reagent.my_atom = src + inner_reagent.add_reagent(chosenchem , 375) var/datum/effect_system/smoke_spread/chem/smoke = new - smoke.set_up(R, src, TRUE) + smoke.set_up(inner_reagent, src, TRUE) playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3) smoke.start() - qdel(R) + qdel(inner_reagent) ejectItem(TRUE) warn_admins(usr, "[chosenchem] smoke") investigate_log("Experimentor has released [chosenchem] smoke!", INVESTIGATE_EXPERIMENTOR) @@ -378,9 +427,20 @@ empulse(src.loc, 4, 0) //change this to 4,6 once the EXPERI-Mentor is moved. investigate_log("Experimentor has generated an Electromagnetic Pulse.", INVESTIGATE_EXPERIMENTOR) ejectItem(TRUE) - //////////////////////////////////////////////////////////////////////////////////////////////// - if(exp == SCANTYPE_HEAT) - visible_message("[src] raises [exp_on]'s temperature.") + else if(prob(EFFECT_PROB_LOW)) + visible_message("[exp_on] achieves the perfect mix!") + playsound(src.loc, 'sound/effects/supermatter.ogg', 50, 3, -1) + ejectItem() + throwSmoke(get_turf(exp_on)) + new /obj/item/relict_production/perfect_mix(get_turf(exp_on)) + qdel(exp_on) + else + exp = FAIL + + +/obj/machinery/r_n_d/experimentor/proc/scan_heat(exp, obj/item/exp_on, chosenchem, criticalReaction, isRelict) + visible_message("[src] raises [exp_on]'s temperature.") + if(!isRelict) if(prob(EFFECT_PROB_LOW) && criticalReaction) visible_message("[src]'s emergency coolant system gives off a small ding!") playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) @@ -430,9 +490,21 @@ m.apply_damage(5,BURN,pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_PRECISE_GROIN)) investigate_log("Experimentor has dealt minor burn damage to [key_name_log(m)]", INVESTIGATE_EXPERIMENTOR) ejectItem() - //////////////////////////////////////////////////////////////////////////////////////////////// - if(exp == SCANTYPE_COLD) - visible_message("[src] lowers [exp_on]'s temperature.") + else if(prob(EFFECT_PROB_LOW)) + visible_message("[exp_on] begins to shake, and in the distance the sound of rampaging animals arises!") + playsound(src.loc, 'sound/effects/supermatter.ogg', 50, 3, -1) + ejectItem() + throwSmoke(get_turf(exp_on)) + var/obj/item/relict_production/pet_spray/R = new /obj/item/relict_production/pet_spray(get_turf(exp_on)) + R.icon_state = exp_on.icon_state + qdel(exp_on) + else + exp = FAIL + + +/obj/machinery/r_n_d/experimentor/proc/scan_cold(exp, obj/item/exp_on, chosenchem, criticalReaction, isRelict) + visible_message("[src] lowers [exp_on]'s temperature.") + if(!isRelict) if(prob(EFFECT_PROB_LOW) && criticalReaction) visible_message("[src]'s emergency coolant system gives off a small ding!") var/obj/item/reagent_containers/food/drinks/coffee/C = new /obj/item/reagent_containers/food/drinks/coffee(get_turf(pick(oview(1,src)))) @@ -445,15 +517,15 @@ investigate_log("Experimentor has made a cup of [chosenchem] coffee.", INVESTIGATE_EXPERIMENTOR) 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(400) - R.my_atom = src - R.add_reagent("frostoil" , 375) + var/datum/reagents/inner_reagent = new/datum/reagents(400) + inner_reagent.my_atom = src + inner_reagent.add_reagent("frostoil" , 375) investigate_log("Experimentor has released frostoil gas.", INVESTIGATE_EXPERIMENTOR) var/datum/effect_system/smoke_spread/chem/smoke = new - smoke.set_up(R, src, TRUE) + smoke.set_up(inner_reagent, src, TRUE) playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3) smoke.start() - qdel(R) + qdel(inner_reagent) ejectItem(TRUE) if(prob(EFFECT_PROB_LOW-badThingCoeff)) visible_message("[src] malfunctions, shattering [exp_on] and leaking cold air!") @@ -475,9 +547,21 @@ 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!") + else if(prob(EFFECT_PROB_LOW)) + visible_message("[exp_on] emits a loud pop!") + playsound(src.loc, 'sound/effects/supermatter.ogg', 50, 3, -1) + ejectItem() + throwSmoke(get_turf(exp_on)) + var/obj/item/relict_production/R = new /obj/item/relict_production/rapid_dupe(get_turf(exp_on)) + R.icon_state = exp_on.icon_state + qdel(exp_on) + else + exp = FAIL + + +/obj/machinery/r_n_d/experimentor/proc/scan_obliterate(exp, obj/item/exp_on, chosenchem, criticalReaction, isRelict) + visible_message("[exp_on] activates the crushing mechanism.") + if(!isRelict) if(prob(EFFECT_PROB_LOW) && criticalReaction) visible_message("[src]'s crushing mechanism slowly and smoothly descends, flattening the [exp_on]!") new /obj/item/stack/sheet/plasteel(get_turf(pick(oview(1,src)))) @@ -507,22 +591,46 @@ spawn(0) cast.throw_at(pick(throwAt),10,1) ejectItem(TRUE) - //////////////////////////////////////////////////////////////////////////////////////////////// + else if(prob(EFFECT_PROB_LOW)) + visible_message("[src]'s crushing mechanism slowly and smoothly descends, flattening the [exp_on]!") + badThingCoeff++ + var/list/obj/item/stack/sheet/mineral/minreals = list(/obj/item/stack/sheet/mineral/diamond, /obj/item/stack/sheet/mineral/gold, /obj/item/stack/sheet/glass,/obj/item/stack/sheet/metal,/obj/item/stack/sheet/mineral/plasma,/obj/item/stack/sheet/mineral/silver,/obj/item/stack/sheet/mineral/titanium,/obj/item/stack/sheet/mineral/uranium,/obj/item/stack/sheet/mineral/tranquillite,/obj/item/stack/sheet/mineral/bananium) + // Plastinium and abductor alloy are alloys, not processed ores. + for (var/i = 1; i <= 3; ++i) + var/obj/item/stack/sheet/mineral/m0 = pick(minreals) + var/obj/item/stack/sheet/mineral/M = new m0(get_turf(exp_on)) + M.amount = 10 + qdel(exp_on) + ejectItem(TRUE) + else + exp = FAIL + + +/obj/machinery/r_n_d/experimentor/proc/experiment(exp, obj/item/exp_on) + recentlyExperimented = TRUE + update_icon(UPDATE_ICON_STATE) + var/chosenchem + var/criticalReaction = (exp_on.type in critical_items) ? TRUE : FALSE + var/isRelict = istype(exp_on, /obj/item/relic) + + if(exp == SCANTYPE_POKE) + scan_poke(exp, exp_on, chosenchem, criticalReaction, isRelict) + if(exp == SCANTYPE_IRRADIATE) + scan_irradiate(exp, exp_on, chosenchem, criticalReaction, isRelict) + if(exp == SCANTYPE_GAS) + scan_gas(exp, exp_on, chosenchem, criticalReaction, isRelict) + if(exp == SCANTYPE_HEAT) + scan_heat(exp, exp_on, chosenchem, criticalReaction, isRelict) + if(exp == SCANTYPE_COLD) + scan_cold(exp, exp_on, chosenchem, criticalReaction, isRelict) + if(exp == SCANTYPE_OBLITERATE) + scan_obliterate(exp, exp_on, chosenchem, criticalReaction, isRelict) + 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 failure.") - if(exp == SCANTYPE_DISCOVER) - visible_message("[src] scans the [exp_on], revealing its true nature!") - playsound(src.loc, 'sound/effects/supermatter.ogg', 50, 3, -1) - var/obj/item/relic/R = loaded_item - R.reveal() - investigate_log("Experimentor has revealed a relic with [R.realProc] effect.", INVESTIGATE_EXPERIMENTOR) - ejectItem() - - //Global reactions - if(prob(EFFECT_PROB_VERYLOW) && prob(13)) visible_message("Experimentor draws the life essence of those nearby!") for(var/mob/living/m in view(4,src)) @@ -647,6 +755,114 @@ #undef FAIL +/obj/item/relict_production + name = "perfect mix" + desc = "Странный объект без эффекта и иконки. Щитспавн онли." + icon_state = "" + icon = 'icons/obj/assemblies.dmi' + origin_tech = "bluespace=3;materials=3" + var/cooldown = 5 SECONDS + COOLDOWN_DECLARE(relict_production_cooldown) + +/obj/item/relict_production/attack_self(mob/user) + if(!COOLDOWN_FINISHED(src, relict_production_cooldown)) + to_chat(user, "[src] is not ready yet.") + return FALSE + COOLDOWN_START(src, relict_production_cooldown, cooldown) + return TRUE + +/obj/item/relict_production/perfect_mix + name = "perfect mix" + desc = "Странный объект из которого можно бесконечно заполнять емкости какой-то жидкостью." + icon_state = "beaker" + item_state = "beaker" + icon = 'icons/obj/weapons/techrelic.dmi' + lefthand_file = 'icons/mob/inhands/relics_production/inhandl.dmi' + righthand_file = 'icons/mob/inhands/relics_production/inhandr.dmi' + origin_tech = "materials=4;bluespace=3" + var/datum/reagent/inner_reagent + var/transfer = 10 + +/obj/item/relict_production/perfect_mix/New() + . = ..() + inner_reagent = pick(/datum/reagent/uranium, /datum/reagent/plasma, /datum/reagent/consumable/capsaicin, /datum/reagent/consumable/frostoil, /datum/reagent/space_cleaner, /datum/reagent/consumable/drink/coffee, pick(/datum/reagent/consumable/drink/non_alcoholic_beer, /datum/reagent/consumable/ethanol/beer, /datum/reagent/beer2)) + +/obj/item/relict_production/perfect_mix/afterattack(atom/target, mob/user, proximity) + if(istype(target, /obj/item/reagent_containers/glass)) + var/obj/item/reagent_containers/glass/beaker = target + beaker.reagents.add_reagent(inner_reagent.id, transfer) + to_chat(user, "You have poured 10 units of content into this.") + else + to_chat(user, "You can't pour [src]'s content into this.") + +/obj/item/relict_production/strange_teleporter + name = "strange teleporter" + desc = "Странный объект телепортирующий вас при активации." + icon_state = "prox-multitool2" + icon = 'icons/obj/assemblies.dmi' + origin_tech = "materials=4;bluespace=4" + cooldown = 10 SECONDS + +/obj/item/relict_production/strange_teleporter/attack_self(mob/user) + if(!..()) + return + to_chat(user, "[src] begins to vibrate!") + spawn(rand(10,30)) + var/turf/userturf = get_turf(user) + if(src.loc == user && is_teleport_allowed(userturf.z)) + visible_message("The [src] twists and bends, relocating itself!") + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(5, get_turf(user)) + smoke.start() + do_teleport(user, userturf, 8, asoundin = 'sound/effects/phasein.ogg') + smoke = new + smoke.set_up(5, get_turf(user)) + smoke.start() + +/obj/item/relict_production/pet_spray + name = "pet spray" + desc = "Странный объект создающий враждебных существ." + icon_state = "armor-igniter-analyzer" + icon = 'icons/obj/assemblies.dmi' + origin_tech = "biotech=5" + cooldown = 60 SECONDS + +/obj/item/relict_production/pet_spray/attack_self(mob/user) + if(!..()) + return + var/message = "[src] begins to shake, and in the distance the sound of rampaging animals arises!" + visible_message(message) + to_chat(user, message) + var/amount = rand(1,3) + var/list/possible_mobs = list(/mob/living/simple_animal/hostile/bear, + /mob/living/simple_animal/hostile/poison/bees, + /mob/living/simple_animal/hostile/carp, + /mob/living/simple_animal/hostile/alien, + /mob/living/simple_animal/butterfly, + /mob/living/simple_animal/pet/dog/corgi + ) + var/mob/to_spawn = pick(possible_mobs) + + for(var/i in 1 to amount) + var/mob/living/simple_animal/S + S = new to_spawn(get_turf(src)) + S.faction |= "petSpraySummon" + S.gold_core_spawnable = HOSTILE_SPAWN + S.low_priority_targets += user.UID() + if(prob(50)) + for(var/j = 1, j <= rand(1, 3), j++) + step(S, pick(NORTH, SOUTH, EAST, WEST)) + + if(prob(60)) + to_chat(user, "[src] falls apart!") + qdel(src) + +/obj/item/relict_production/rapid_dupe + name = "rapid dupe" + desc = "Странный объект создающий другие странные объекты при контакте с аномалиями." + icon_state = "shock_kit" + icon = 'icons/obj/assemblies.dmi' + origin_tech = "materials=5" //////////////////////////////////SPECIAL ITEMS//////////////////////////////////////// @@ -659,127 +875,8 @@ var/realName = "defined object" var/revealed = FALSE var/realProc - var/cooldownMax = 60 - var/cooldown - var/floof /obj/item/relic/New() ..() icon_state = pick("shock_kit","armor-igniter-analyzer","infra-igniter0","infra-igniter1","radio-multitool","prox-radio1","radio-radio","timer-multitool0","radio-igniter-tank") realName = "[pick("broken","twisted","spun","improved","silly","regular","badly made")] [pick("device","object","toy","suspicious tech","gear")]" - floof = pick(/mob/living/simple_animal/pet/dog/corgi, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/dog/fox, /mob/living/simple_animal/mouse, /mob/living/simple_animal/pet/dog/pug, /mob/living/simple_animal/lizard, /mob/living/simple_animal/diona, /mob/living/simple_animal/butterfly, /mob/living/carbon/human/lesser/monkey) - - -/obj/item/relic/proc/reveal() - if(revealed) //Re-rolling your relics seems a bit overpowered, yes? - return - revealed = TRUE - name = realName - cooldownMax = rand(60,300) - realProc = pick("teleport","explode","rapidDupe","petSpray","flash","clean","floofcannon") - origin_tech = pick("engineering=[rand(2,5)]","magnets=[rand(2,5)]","plasmatech=[rand(2,5)]","programming=[rand(2,5)]","powerstorage=[rand(2,5)]") - -/obj/item/relic/attack_self(mob/user) - if(revealed) - if(cooldown) - to_chat(user, "[src] does not react!") - return - else if(src.loc == user) - cooldown = TRUE - call(src,realProc)(user) - spawn(cooldownMax) - cooldown = FALSE - else - to_chat(user, "You aren't quite sure what to do with this, yet.") - -//////////////// RELIC PROCS ///////////////////////////// - -/obj/item/relic/proc/throwSmoke(turf/where) - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(1,0, where, 0) - smoke.start() - -/obj/item/relic/proc/floofcannon(mob/user) - playsound(src.loc, "sparks", rand(25, 50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - var/mob/living/C = new floof(get_turf(user)) - C.throw_at(pick(oview(10,user)),10,rand(3,8)) - throwSmoke(get_turf(C)) - warn_admins(user, "Floof Cannon", 0) - -/obj/item/relic/proc/clean(mob/user) - playsound(src.loc, "sparks", rand(25, 50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - var/obj/item/grenade/chem_grenade/cleaner/CL = new/obj/item/grenade/chem_grenade/cleaner(get_turf(user)) - CL.prime() - warn_admins(user, "Smoke", 0) - -/obj/item/relic/proc/flash(mob/user) - playsound(src.loc, "sparks", rand(25, 50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - var/obj/item/grenade/flashbang/CB = new/obj/item/grenade/flashbang(get_turf(user)) - CB.prime() - warn_admins(user, "Flash") - -/obj/item/relic/proc/petSpray(mob/user) - var/message = "[src] begins to shake, and in the distance the sound of rampaging animals arises!" - visible_message(message) - to_chat(user, message) - var/animals = rand(1,25) - var/counter - var/list/valid_animals = list(/mob/living/simple_animal/parrot,/mob/living/simple_animal/butterfly,/mob/living/simple_animal/pet/cat,/mob/living/simple_animal/pet/dog/corgi,/mob/living/simple_animal/crab,/mob/living/simple_animal/pet/dog/fox,/mob/living/simple_animal/lizard,/mob/living/simple_animal/mouse,/mob/living/simple_animal/pet/dog/pug,/mob/living/simple_animal/hostile/bear,/mob/living/simple_animal/hostile/poison/bees,/mob/living/simple_animal/hostile/carp) - for(counter = 1; counter < animals; counter++) - var/mobType = pick(valid_animals) - new mobType(get_turf(src)) - warn_admins(user, "Mass Mob Spawn") - if(prob(60)) - to_chat(user, "[src] falls apart!") - qdel(src) - -/obj/item/relic/proc/rapidDupe(mob/user) - audible_message("[src] emits a loud pop!") - var/list/dupes = list() - var/counter - var/max = rand(5,10) - for(counter = 1; counter < max; counter++) - var/obj/item/relic/R = new src.type(get_turf(src)) - R.name = name - R.desc = desc - R.realName = realName - R.realProc = realProc - R.revealed = TRUE - dupes |= R - spawn() - R.throw_at(pick(oview(7,get_turf(src))),10,1) - counter = 0 - spawn(rand(10,100)) - for(counter = 1; counter <= dupes.len; counter++) - var/obj/item/relic/R = dupes[counter] - qdel(R) - warn_admins(user, "Rapid duplicator", 0) - -/obj/item/relic/proc/explode(mob/user) - to_chat(user, "[src] begins to heat up!") - spawn(rand(35,100)) - if(src.loc == user) - visible_message("The [src]'s top opens, releasing a powerful blast!") - explosion(user.loc, -1, rand(1,5), rand(1,5), rand(1,5), rand(1,5), flame_range = 2, cause = src) - warn_admins(user, "Explosion") - qdel(src) //Comment this line to produce a light grenade (the bomb that keeps on exploding when used)!! - -/obj/item/relic/proc/teleport(mob/user) - to_chat(user, "The [src] begins to vibrate!") - spawn(rand(10,30)) - var/turf/userturf = get_turf(user) - if(src.loc == user && is_teleport_allowed(userturf.z)) //Because Nuke Ops bringing this back on their shuttle, then looting the ERT area is 2fun4you! - visible_message("The [src] twists and bends, relocating itself!") - throwSmoke(userturf) - do_teleport(user, userturf, 8, asoundin = 'sound/effects/phasein.ogg') - throwSmoke(get_turf(user)) - warn_admins(user, "Teleport", 0) - -//Admin Warning proc for relics -/obj/item/relic/proc/warn_admins(mob/user, RelicType, priority = 1) - var/turf/T = get_turf(src) - var/log_msg = "[RelicType] relic used by [key_name_log(user)] in [COORD(T)]" - if(priority) //For truly dangerous relics that may need an admin's attention. BWOINK! - message_admins("[RelicType] relic activated by [key_name_admin(user)] in [ADMIN_COORDJMP(T)]") - add_game_logs(log_msg) - investigate_log(log_msg, INVESTIGATE_EXPERIMENTOR) diff --git a/icons/mob/inhands/relics_production/inhandl.dmi b/icons/mob/inhands/relics_production/inhandl.dmi new file mode 100644 index 0000000000000000000000000000000000000000..9a95b0f2c2ed8b638ea4cc05ae936e4b1b0862c3 GIT binary patch literal 740 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