diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 7008f050337..1be91011a17 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -1788,8 +1788,8 @@ area/security/podbay icon_state = "toxlab" /area/toxins/xenobiology - name = "\improper Xenobiology Lab" - icon_state = "toxlab" + name = "Xenobiology Lab" + icon_state = "toxmix" /area/toxins/xenobiology/xenoflora_storage name = "\improper Xenoflora Storage" diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm new file mode 100644 index 00000000000..0e6e77ec9ec --- /dev/null +++ b/code/game/machinery/computer/camera_advanced.dm @@ -0,0 +1,161 @@ +/obj/machinery/computer/camera_advanced + name = "advanced camera console" + desc = "Used to access the various cameras on the station." + icon_screen = "cameras" + icon_keyboard = "security_key" + var/mob/camera/aiEye/remote/eyeobj + var/mob/living/carbon/human/current_user = null + var/list/networks = list("SS13") + var/datum/action/camera_off/off_action = new + var/datum/action/camera_jump/jump_action = new + +/obj/machinery/computer/camera_advanced/proc/CreateEye() + eyeobj = new() + eyeobj.origin = src + +/obj/machinery/computer/camera_advanced/proc/GrantActions(mob/living/carbon/user) + off_action.target = user + off_action.Grant(user) + jump_action.target = user + jump_action.Grant(user) + +/obj/machinery/computer/camera_advanced/check_eye(mob/user) + if (get_dist(user, src) > 1 || user.eye_blind) + if(user == current_user) + off_action.Activate() + return 0 + return 1 + +/obj/machinery/computer/camera_advanced/attack_hand(mob/user) + if(..()) + return + if(!iscarbon(user)) + return + var/mob/living/carbon/L = user + + if(!current_user) + if(!eyeobj) + CreateEye() + GrantActions(user) + current_user = user + eyeobj.user = user + eyeobj.name = "Camera Eye ([user.name])" + L.remote_view = 1 + L.remote_control = eyeobj + L.client.perspective = EYE_PERSPECTIVE + if(!eyeobj.initialized) + for(var/obj/machinery/camera/C in cameranet.cameras) + if(!C.can_use()) + continue + if(C.network&networks) + eyeobj.setLoc(get_turf(C)) + break + eyeobj.initialized = 1 + else + eyeobj.setLoc(eyeobj.loc) + else + user << "The console is already in use!" + +/mob/camera/aiEye/remote + name = "Inactive Camera Eye" + var/sprint = 10 + var/cooldown = 0 + var/acceleration = 1 + var/mob/living/carbon/human/user = null + var/obj/machinery/computer/camera_advanced/origin + var/initialized = 0 + var/visible_icon = 0 + var/image/user_image = null + +/mob/camera/aiEye/remote/GetViewerClient() + if(user) + return user.client + return null + +/mob/camera/aiEye/remote/setLoc(T) + if(user) + if(!isturf(user.loc)) + return + T = get_turf(T) + loc = T + cameranet.visibility(src) + if(user.client) + if(visible_icon) + user.client.images -= user_image + user_image = image(icon,loc,icon_state,FLY_LAYER) + user.client.images += user_image + user.client.eye = src + +/mob/camera/aiEye/remote/relaymove(mob/user,direct) + var/initial = initial(sprint) + var/max_sprint = 50 + + if(cooldown && cooldown < world.timeofday) // 3 seconds + sprint = initial + + for(var/i = 0; i < max(sprint, initial); i += 20) + var/turf/step = get_turf(get_step(src, direct)) + if(step) + src.setLoc(step) + + cooldown = world.timeofday + 5 + if(acceleration) + sprint = min(sprint + 0.5, max_sprint) + else + sprint = initial + +/datum/action/camera_off + name = "End Camera View" + action_type = AB_INNATE + button_icon_state = "camera_off" + +/datum/action/camera_off/Activate() + if(!target || !iscarbon(target)) + return + var/mob/living/carbon/C = target + var/mob/camera/aiEye/remote/remote_eye = C.remote_control + C.remote_view = 0 + remote_eye.origin.current_user = null + remote_eye.origin.jump_action.Remove(C) + remote_eye.user = null + if(C.client) + C.client.perspective = MOB_PERSPECTIVE + C.client.eye = src + C.client.images -= remote_eye.user_image + for(var/datum/camerachunk/chunk in remote_eye.visibleCameraChunks) + C.client.images -= chunk.obscured + C.remote_control = null + C.unset_machine() + src.Remove(C) + +/datum/action/camera_jump + name = "Jump To Camera" + action_type = AB_INNATE + button_icon_state = "camera_jump" + +/datum/action/camera_jump/Activate() + if(!target || !iscarbon(target)) + return + var/mob/living/carbon/C = target + var/mob/camera/aiEye/remote/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/origin = remote_eye.origin + + var/list/L = list() + + for (var/obj/machinery/camera/cam in cameranet.cameras) + L.Add(cam) + + camera_sort(L) + + var/list/T = list() + + for (var/obj/machinery/camera/netcam in L) + var/list/tempnetwork = netcam.network&origin.networks + if (tempnetwork.len) + T[text("[][]", netcam.c_tag, (netcam.can_use() ? null : " (Deactivated)"))] = netcam + + + var/camera = input("Choose which camera you want to view", "Cameras") as null|anything in T + var/obj/machinery/camera/final = T[camera] + if(final) + remote_eye.setLoc(get_turf(final)) \ No newline at end of file diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index 71c6be4d154..99dbe5b366b 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -179,7 +179,7 @@ A.name = str usr << "You rename the '[prevname]' to '[str]'." interact() - return + return 1 /obj/item/areaeditor/proc/set_area_machinery_title(var/area/A,var/title,var/oldtitle) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index a3a9a2721e4..6fe1cae458d 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -939,6 +939,9 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc src << "Your psy-connection grows too faint to maintain!" isRemoteObserve = 0 + if(remote_view) + isRemoteObserve = 1 + if(!isRemoteObserve && client && !client.adminobs) remoteview_target = null reset_view(null) diff --git a/code/modules/mob/living/carbon/slime/slime.dm b/code/modules/mob/living/carbon/slime/slime.dm index 37c2e112c24..e2a7da1ae5e 100644 --- a/code/modules/mob/living/carbon/slime/slime.dm +++ b/code/modules/mob/living/carbon/slime/slime.dm @@ -66,6 +66,12 @@ coretype = text2path("/obj/item/slime_extract/[sanitizedcolour]") ..() +/mob/living/carbon/slime/Destroy() + for(var/obj/machinery/computer/camera_advanced/xenobio/X in machines) + if(src in X.stored_slimes) + X.stored_slimes -= src + return ..() + /mob/living/carbon/slime/regenerate_icons() icon_state = "[colour] [is_adult ? "adult" : "baby"] slime" overlays.len = 0 diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 7a325ca2385..f8dcf7a5d0d 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -212,7 +212,7 @@ if (!( machine.check_eye(src) )) reset_view(null) else - if(!client.adminobs) + if(!remote_view && !client.adminobs) reset_view(null) /mob/living/proc/update_sight() diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 4f7327180c6..a4142b41d49 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -211,6 +211,8 @@ var/atom/movable/remote_control //Calls relaymove() to whatever it is + var/remote_view = 0 // Set to 1 to prevent view resets on Life + var/obj/control_object //Used by admins to possess objects. All mobs should have this var var/datum/visibility_interface/visibility_interface = null // used by the visibility system to provide an interface for the visibility networks diff --git a/code/modules/reagents/oldchem/chemical_reaction/chemical_reaction_slime.dm b/code/modules/reagents/oldchem/chemical_reaction/chemical_reaction_slime.dm index d6e1ed34ce4..bb069890392 100644 --- a/code/modules/reagents/oldchem/chemical_reaction/chemical_reaction_slime.dm +++ b/code/modules/reagents/oldchem/chemical_reaction/chemical_reaction_slime.dm @@ -492,6 +492,20 @@ feedback_add_details("slime_cores_used","[replacetext(name," ","_")]") var/obj/item/weapon/slimesteroid2/P = new /obj/item/weapon/slimesteroid2 P.loc = get_turf(holder.my_atom) + + slime_territory + name = "Slime Territory" + id = "s_territory" + result = null + required_reagents = list("blood" = 1) + result_amount = 1 + required_container = /obj/item/slime_extract/cerulean + required_other = 1 + on_reaction(datum/reagents/holder) + feedback_add_details("slime_cores_used","[type]") + var/obj/item/areaeditor/blueprints/slime/P = new /obj/item/areaeditor/blueprints/slime + P.loc = get_turf(holder.my_atom) + //Sepia slimestop name = "Slime Stop" diff --git a/code/modules/reagents/reagent_containers/food/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm index eee8c78f9b0..df6edbaa03f 100644 --- a/code/modules/reagents/reagent_containers/food/snacks.dm +++ b/code/modules/reagents/reagent_containers/food/snacks.dm @@ -1597,6 +1597,12 @@ user.drop_item() forceMove(get_turf(O)) return Expand() + if(istype(O, /obj/machinery/computer/camera_advanced/xenobio)) + var/obj/machinery/computer/camera_advanced/xenobio/X = O + X.monkeys++ + user << "You feed [src] to the [X]. It now has [X.monkeys] monkey cubes stored." + qdel(src) + return ..() /obj/item/weapon/reagent_containers/food/snacks/monkeycube/attack_self(mob/user) diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm new file mode 100644 index 00000000000..129c97761d1 --- /dev/null +++ b/code/modules/research/xenobiology/xenobio_camera.dm @@ -0,0 +1,171 @@ +//Xenobio control console +/mob/camera/aiEye/remote/xenobio + visible_icon = 1 + icon = 'icons/obj/abductor.dmi' + icon_state = "camera_target" + + +/mob/camera/aiEye/remote/xenobio/setLoc(var/t) + var/area/new_area = get_area(t) + if(new_area && new_area.name == "Xenobiology Lab" || istype(new_area, /area/toxins/xenobiology )) + return ..() + else + return + +/obj/machinery/computer/camera_advanced/xenobio + name = "Slime management console" + desc = "A computer used for remotely handling slimes." + networks = list("SS13") + off_action = new/datum/action/camera_off/xenobio + var/datum/action/slime_place/slime_place_action = new + var/datum/action/slime_pick_up/slime_up_action = new + var/datum/action/feed_slime/feed_slime_action = new + var/datum/action/monkey_recycle/monkey_recycle_action = new + + var/list/stored_slimes = list() + var/max_slimes = 5 + var/monkeys = 0 + + icon_screen = "slime_comp" + icon_keyboard = "rd_key" + +/obj/machinery/computer/camera_advanced/xenobio/CreateEye() + eyeobj = new /mob/camera/aiEye/remote/xenobio() + eyeobj.loc = get_turf(src) + eyeobj.origin = src + eyeobj.visible_icon = 1 + eyeobj.icon = 'icons/obj/abductor.dmi' + eyeobj.icon_state = "camera_target" + +/obj/machinery/computer/camera_advanced/xenobio/GrantActions(mob/living/carbon/user) + off_action.target = user + off_action.Grant(user) + + jump_action.target = user + jump_action.Grant(user) + + slime_up_action.target = src + slime_up_action.Grant(user) + + slime_place_action.target = src + slime_place_action.Grant(user) + + feed_slime_action.target = src + feed_slime_action.Grant(user) + + monkey_recycle_action.target = src + monkey_recycle_action.Grant(user) + + +/obj/machinery/computer/camera_advanced/xenobio/attack_hand(mob/user) + if(!ishuman(user)) //AIs using it might be weird + return + return ..() + +/datum/action/camera_off/xenobio/Activate() + if(!target || !ishuman(target)) + return + var/mob/living/carbon/C = target + var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/origin = remote_eye.origin + C.remote_view = 0 + origin.current_user = null + origin.jump_action.Remove(C) + origin.slime_place_action.Remove(C) + origin.slime_up_action.Remove(C) + origin.feed_slime_action.Remove(C) + origin.monkey_recycle_action.Remove(C) + //All of this stuff below could probably be a proc for all advanced cameras, only the action removal needs to be camera specific + remote_eye.user = null + if(C.client) + C.client.perspective = MOB_PERSPECTIVE + C.client.eye = src + C.client.images -= remote_eye.user_image + for(var/datum/camerachunk/chunk in remote_eye.visibleCameraChunks) + C.client.images -= chunk.obscured + C.remote_control = null + C.unset_machine() + src.Remove(C) + + +/datum/action/slime_place + name = "Place Slimes" + action_type = AB_INNATE + button_icon_state = "slime_down" + +/datum/action/slime_place/Activate() + if(!target || !ishuman(owner)) + return + var/mob/living/carbon/human/C = owner + var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = target + + if(cameranet.checkTurfVis(remote_eye.loc)) + for(var/mob/living/carbon/slime/S in X.stored_slimes) + S.loc = remote_eye.loc + S.visible_message("[S] warps in!") + X.stored_slimes -= S + +/datum/action/slime_pick_up + name = "Pick up Slime" + action_type = AB_INNATE + button_icon_state = "slime_up" + +/datum/action/slime_pick_up/Activate() + if(!target || !ishuman(owner)) + return + var/mob/living/carbon/human/C = owner + var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = target + + if(cameranet.checkTurfVis(remote_eye.loc)) + for(var/mob/living/carbon/slime/S in remote_eye.loc) + if(X.stored_slimes.len >= X.max_slimes) + break + if(!S.ckey) + if(S.buckled) + S.buckled.unbuckle_mob() + S.Feedstop() + S.visible_message("[S] vanishes in a flash of light!") + S.loc = X + X.stored_slimes += S + + +/datum/action/feed_slime + name = "Feed Slimes" + action_type = AB_INNATE + button_icon_state = "monkey_down" + +/datum/action/feed_slime/Activate() + if(!target || !ishuman(owner)) + return + var/mob/living/carbon/human/C = owner + var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = target + + if(cameranet.checkTurfVis(remote_eye.loc)) + if(X.monkeys >= 1) + var/mob/living/carbon/human/monkey/food = new /mob/living/carbon/human/monkey(remote_eye.loc) + food.LAssailant = C + X.monkeys -- + owner << "[X] now has [X.monkeys] monkeys left." + + +/datum/action/monkey_recycle + name = "Recycle Monkeys" + action_type = AB_INNATE + button_icon_state = "monkey_up" + +/datum/action/monkey_recycle/Activate() + if(!target || !ishuman(owner)) + return + var/mob/living/carbon/human/C = owner + var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/X = target + + if(cameranet.checkTurfVis(remote_eye.loc)) + for(var/mob/living/carbon/human/M in remote_eye.loc) + if(issmall(M) && M.stat) + M.visible_message("[M] vanishes as they are reclaimed for recycling!") + X.monkeys += 0.2 + qdel(M) \ No newline at end of file diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index a126db30def..154792c7b12 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -438,6 +438,18 @@ max_amount = 60 turf_type = /turf/simulated/floor/sepia +/obj/item/areaeditor/blueprints/slime + name = "cerulean prints" + desc = "A one use set of blueprints made of jelly like organic material. Renaming an area to 'Xenobiology Lab' will extend the reach of the management console." + color = "#2956B2" + +/obj/item/areaeditor/blueprints/slime/edit_area() + var/success = ..() + var/area/A = get_area(src) + if(success) + for(var/turf/T in A) + T.color = "#2956B2" + qdel(src) /turf/simulated/floor/sepia slowdown = 2 diff --git a/icons/mob/actions.dmi b/icons/mob/actions.dmi index 75bbde4e4a3..a2847325875 100644 Binary files a/icons/mob/actions.dmi and b/icons/mob/actions.dmi differ diff --git a/icons/obj/abductor.dmi b/icons/obj/abductor.dmi new file mode 100644 index 00000000000..0896e9a500a Binary files /dev/null and b/icons/obj/abductor.dmi differ diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi index 958c28dcaa5..09380373db8 100644 Binary files a/icons/obj/computer.dmi and b/icons/obj/computer.dmi differ diff --git a/paradise.dme b/paradise.dme index 4a673c9da4a..f2e230118ac 100644 --- a/paradise.dme +++ b/paradise.dme @@ -502,6 +502,7 @@ #include "code\game\machinery\computer\atmos_control.dm" #include "code\game\machinery\computer\buildandrepair.dm" #include "code\game\machinery\computer\camera.dm" +#include "code\game\machinery\computer\camera_advanced.dm" #include "code\game\machinery\computer\card.dm" #include "code\game\machinery\computer\cloning.dm" #include "code\game\machinery\computer\communications.dm" @@ -1835,6 +1836,7 @@ #include "code\modules\research\xenoarchaeology\tools\tools_depthscanner.dm" #include "code\modules\research\xenoarchaeology\tools\tools_locater.dm" #include "code\modules\research\xenoarchaeology\tools\tools_pickaxe.dm" +#include "code\modules\research\xenobiology\xenobio_camera.dm" #include "code\modules\research\xenobiology\xenobiology.dm" #include "code\modules\scripting\Errors.dm" #include "code\modules\scripting\Options.dm"