diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 8ec06dbcbd4..58e2be31b01 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -7,6 +7,7 @@ #define PLANE_SPACE_PARALLAX -90 #define GAME_PLANE 0 +#define SPACE_LAYER 1.8 //#define TURF_LAYER 2 //For easy recordkeeping; this is a byond define #define MID_TURF_LAYER 2.02 #define HIGH_TURF_LAYER 2.03 diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 3e631b64f18..bf12f4f987d 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -479,3 +479,16 @@ This is always put in the attack log. /proc/update_all_mob_security_hud() for(var/mob/living/carbon/human/H in mob_list) H.sec_hud_set_security_status() + +/proc/getviewsize(view) + var/viewX + var/viewY + if(isnum(view)) + var/totalviewrange = 1 + 2 * view + viewX = totalviewrange + viewY = totalviewrange + else + var/list/viewrangelist = splittext(view,"x") + viewX = text2num(viewrangelist[1]) + viewY = text2num(viewrangelist[2]) + return list(viewX, viewY) \ No newline at end of file diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 8fe3c3ba7fa..35d271461df 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1055,6 +1055,8 @@ proc/get_mob_with_client_list() //Find coordinates var/turf/T = get_turf(AM) //use AM's turfs, as it's coords are the same as AM's AND AM's coords are lost if it is inside another atom + if(!T) + return null var/final_x = T.x + rough_x var/final_y = T.y + rough_y diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index d1a1a219eef..b2521527a98 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -35,6 +35,17 @@ return var/turf/pixel_turf = get_turf_pixel(A) + if(isnull(pixel_turf)) + return + if(!can_see(A)) + if(isturf(A)) //On unmodified clients clicking the static overlay clicks the turf underneath + return // So there's no point messaging admins + message_admins("[key_name_admin(src)] might be running a modified client! (failed can_see on AI click of [A]([ADMIN_COORDJMP(pixel_turf)]))") + var/message = "[key_name(src)] might be running a modified client! (failed can_see on AI click of [A]([COORD(pixel_turf)]))" + log_admin(message) + send2irc_adminless_only("NOCHEAT", "[key_name(src)] might be running a modified client! (failed checkTurfVis on AI click of [A]([COORD(pixel_turf)]))") + + var/turf_visible if(pixel_turf) turf_visible = cameranet.checkTurfVis(pixel_turf) diff --git a/code/_onclick/hud/movable_screen_objects.dm b/code/_onclick/hud/movable_screen_objects.dm index 315fce5bf65..668e0e67fc7 100644 --- a/code/_onclick/hud/movable_screen_objects.dm +++ b/code/_onclick/hud/movable_screen_objects.dm @@ -11,6 +11,8 @@ /obj/screen/movable var/snap2grid = FALSE var/moved = FALSE + var/x_off = -16 + var/y_off = -16 //Snap Screen Object //Tied to the grid, snaps to the nearest turf @@ -39,8 +41,8 @@ screen_loc = "[screen_loc_X[1]],[screen_loc_Y[1]]" else //Normalise Pixel Values (So the object drops at the center of the mouse, not 16 pixels off) - var/pix_X = text2num(screen_loc_X[2]) - 16 - var/pix_Y = text2num(screen_loc_Y[2]) - 16 + var/pix_X = text2num(screen_loc_X[2]) + x_off + var/pix_Y = text2num(screen_loc_Y[2]) + y_off screen_loc = "[screen_loc_X[1]]:[pix_X],[screen_loc_Y[1]]:[pix_Y]" moved = screen_loc diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm index 2195ee25522..00f6a98f2bc 100644 --- a/code/game/machinery/computer/camera_advanced.dm +++ b/code/game/machinery/computer/camera_advanced.dm @@ -8,16 +8,39 @@ var/list/networks = list("SS13") var/datum/action/innate/camera_off/off_action = new var/datum/action/innate/camera_jump/jump_action = new + var/list/actions = list() /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/proc/GrantActions(mob/living/user) + if(off_action) + off_action.target = user + off_action.Grant(user) + actions += off_action + + if(jump_action) + jump_action.target = user + jump_action.Grant(user) + actions += jump_action + +/obj/machinery/computer/camera_advanced/proc/remove_eye_control(mob/living/user) + if(!user) + return + for(var/V in actions) + var/datum/action/A = V + A.Remove(user) + if(user.client) + user.reset_perspective(null) + eyeobj.RemoveImages() + eyeobj.eye_user = null + user.remote_control = null + user.remote_view = FALSE + + current_user = null + user.unset_machine() + playsound(src, 'sound/machines/terminal_off.ogg', 25, 0) /obj/machinery/computer/camera_advanced/check_eye(mob/user) if((stat & (NOPOWER|BROKEN)) || !Adjacent(user) || !user.has_vision() || user.incapacitated()) @@ -33,7 +56,7 @@ /obj/machinery/computer/camera_advanced/on_unset_machine(mob/M) if(M == current_user) - off_action.Activate() + remove_eye_control(M) /obj/machinery/computer/camera_advanced/attack_hand(mob/user) if(current_user) @@ -95,6 +118,13 @@ origin = null return ..() +/mob/camera/aiEye/remote/RemoveImages() + ..() + if(visible_icon) + var/client/C = GetViewerClient() + if(C) + C.images -= user_image + /mob/camera/aiEye/remote/GetViewerClient() if(eye_user) return eye_user.client @@ -140,20 +170,8 @@ 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.eye_user = null - if(C.client) - C.reset_perspective(null) - if(remote_eye.visible_icon) - 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) - playsound(remote_eye.origin, 'sound/machines/terminal_off.ogg', 25, 0) + var/obj/machinery/computer/camera_advanced/console = remote_eye.origin + console.remove_eye_control(target) /datum/action/innate/camera_jump name = "Jump To Camera" diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 09b62f1c89c..bf11356b3a5 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -87,11 +87,12 @@ var/list/ai_verbs_default = list( var/mob/living/simple_animal/bot/Bot var/turf/waypoint //Holds the turf of the currently selected waypoint. var/waypoint_mode = 0 //Waypoint mode is for selecting a turf via clicking. + var/apc_override = FALSE //hack for letting the AI use its APC even when visionless var/nuking = 0 var/obj/machinery/doomsday_device/doomsday_device var/obj/machinery/hologram/holopad/holo = null - var/mob/camera/aiEye/eyeobj = new() + var/mob/camera/aiEye/eyeobj var/sprint = 10 var/cooldown = 0 var/acceleration = 1 @@ -191,9 +192,7 @@ var/list/ai_verbs_default = list( spawn(5) new /obj/machinery/ai_powersupply(src) - eyeobj.ai = src - eyeobj.name = "[src.name] (AI Eye)" // Give it a name - eyeobj.loc = src.loc + create_eye() builtInCamera = new /obj/machinery/camera/portable(src) builtInCamera.c_tag = name @@ -1163,6 +1162,17 @@ var/list/ai_verbs_default = list( client.eye = eyeobj return TRUE + +/mob/living/silicon/ai/proc/can_see(atom/A) + if(isturf(loc)) //AI in core, check if on cameras + //get_turf_pixel() is because APCs in maint aren't actually in view of the inner camera + //apc_override is needed here because AIs use their own APC when depowered + return (cameranet && cameranet.checkTurfVis(get_turf_pixel(A))) || apc_override + //AI is carded/shunted + //view(src) returns nothing for carded/shunted AIs and they have x-ray vision so just use get_dist + var/list/viewscale = getviewsize(client.view) + return get_dist(src, A) <= max(viewscale[1]*0.5,viewscale[2]*0.5) + /mob/living/silicon/ai/proc/relay_speech(mob/living/M, text, verb, datum/language/speaking) if(!say_understands(M, speaking))//The AI will be able to understand most mobs talking through the holopad. if(speaking) diff --git a/code/modules/mob/living/silicon/ai/freelook/chunk.dm b/code/modules/mob/living/silicon/ai/freelook/chunk.dm index eeeeed8e8bb..6737be463d6 100644 --- a/code/modules/mob/living/silicon/ai/freelook/chunk.dm +++ b/code/modules/mob/living/silicon/ai/freelook/chunk.dm @@ -28,7 +28,7 @@ eye.visibleCameraChunks += src visible++ seenby += eye - if(changed && !updating) + if(changed) update() // Remove an AI eye from the chunk, then update if changed. diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index 3f105fa8d4c..2728a1af55e 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -20,7 +20,6 @@ // It will also stream the chunk that the new loc is in. /mob/camera/aiEye/setLoc(T) - if(ai) if(!isturf(ai.loc)) return @@ -42,8 +41,22 @@ return ai.client return null + +/mob/camera/aiEye/proc/RemoveImages() + var/client/C = GetViewerClient() + if(C) + for(var/V in visibleCameraChunks) + var/datum/camerachunk/chunk = V + C.images -= chunk.obscured + + /mob/camera/aiEye/Destroy() - ai = null + if(ai) + //ai.all_eyes -= src + ai = null + for(var/V in visibleCameraChunks) + var/datum/camerachunk/chunk = V + chunk.remove(src) return ..() /atom/proc/move_camera_by_click() @@ -103,12 +116,18 @@ src.eyeobj.loc = src.loc else to_chat(src, "ERROR: Eyeobj not found. Creating new eye...") - src.eyeobj = new(src.loc) - src.eyeobj.ai = src - src.eyeobj.name = "[src.name] (AI Eye)" // Give it a name + create_eye() eyeobj.setLoc(loc) +/mob/living/silicon/ai/proc/create_eye() + if(eyeobj) + return + eyeobj = new /mob/camera/aiEye() + eyeobj.ai = src + eyeobj.setLoc(loc) + eyeobj.name = "[name] (AI Eye)" + /mob/living/silicon/ai/proc/toggle_acceleration() set category = "AI Commands" set name = "Toggle Camera Acceleration" diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 1755674b4f6..42f132e39d4 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -129,8 +129,10 @@ to_chat(src, "Receiving control information from APC.") sleep(2) //bring up APC dialog - aiRestorePowerRoutine = 3 + apc_override = 1 theAPC.attack_ai(src) + apc_override = 0 + aiRestorePowerRoutine = 3 to_chat(src, "Here are your current laws:") src.show_laws() //WHY THE FUCK IS THIS HERE sleep(50) diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm index f7976645bc4..dcae187b028 100644 --- a/code/modules/research/xenobiology/xenobio_camera.dm +++ b/code/modules/research/xenobiology/xenobio_camera.dm @@ -22,7 +22,6 @@ desc = "A computer used for remotely handling slimes." networks = list("SS13") circuit = /obj/item/circuitboard/xenobiology - off_action = new /datum/action/innate/camera_off/xenobio var/datum/action/innate/slime_place/slime_place_action = new var/datum/action/innate/slime_pick_up/slime_up_action = new var/datum/action/innate/feed_slime/feed_slime_action = new @@ -43,23 +42,27 @@ eyeobj.icon_state = "camera_target" /obj/machinery/computer/camera_advanced/xenobio/GrantActions(mob/living/carbon/user) - off_action.target = user - off_action.Grant(user) + ..() + if(slime_up_action) + slime_up_action.target = src + slime_up_action.Grant(user) + actions += slime_up_action + + if(slime_place_action) + slime_place_action.target = src + slime_place_action.Grant(user) + actions += slime_place_action + + if(feed_slime_action) + feed_slime_action.target = src + feed_slime_action.Grant(user) + actions += feed_slime_action + + if(monkey_recycle_action) + monkey_recycle_action.target = src + monkey_recycle_action.Grant(user) + actions += monkey_recycle_action - 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) @@ -87,31 +90,6 @@ return ..() -/datum/action/innate/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.eye_user = null - C.reset_perspective(null) - if(C.client) - 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/innate/slime_place name = "Place Slimes" button_icon_state = "slime_down"