diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 9a32e23950..793454869f 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -89,7 +89,7 @@ #define COMSIG_EXIT_AREA "exit_area" //from base of area/Exited(): (/area) #define COMSIG_CLICK "atom_click" //from base of atom/Click(): (location, control, params, mob/user) -#define COMSIG_CLICK_SHIFT "shift_click" //from base of atom/ShiftClick(): (/mob) +#define COMSIG_CLICK_SHIFT "shift_click" //from base of atom/ShiftClick(): (/mob), return flags also used by other signals. #define COMPONENT_ALLOW_EXAMINATE 1 #define COMPONENT_DENY_EXAMINATE 2 //Higher priority compared to the above one @@ -141,13 +141,17 @@ #define HEARING_SOURCE 8*/ #define COMSIG_MOVABLE_DISPOSING "movable_disposing" //called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source) #define COMSIG_MOVABLE_TELEPORTED "movable_teleported" //from base of do_teleport(): (channel, turf/origin, turf/destination) + // /mind signals #define COMSIG_PRE_MIND_TRANSFER "pre_mind_transfer" //from base of mind/transfer_to() before it's done: (new_character, old_character) #define COMPONENT_STOP_MIND_TRANSFER 1 //stops the mind transfer from happening. #define COMSIG_MIND_TRANSFER "mind_transfer" //from base of mind/transfer_to() when it's done: (new_character, old_character) // /mob signals -#define COMSIG_MOB_EXAMINATE "mob_examinate" //from base of /mob/verb/examinate(): (atom/A) +#define COMSIG_MOB_CLICKED_SHIFT_ON "mob_shift_click_on" //from base of /atom/ShiftClick(): (atom/A), for return values, see COMSIG_CLICK_SHIFT +#define COMSIG_MOB_VISIBLE_ATOMS "mob_visible_atoms" //from base of mob/visible_atoms(): (list/visible_atoms) +#define COMSIG_MOB_EXAMINATE "mob_examinate" //from base of /mob/verb/examinate(): (atom/A), for return values, see COMSIG_CLICK_SHIFT + #define COMPONENT_EXAMINATE_BLIND 3 //outputs the "something is there but you can't see it" message. #define COMSIG_MOB_DEATH "mob_death" //from base of mob/death(): (gibbed) #define COMPONENT_BLOCK_DEATH_BROADCAST 1 //stops the death from being broadcasted in deadchat. #define COMSIG_MOB_CLICKON "mob_clickon" //from base of mob/clickon(): (atom/A, params) @@ -181,10 +185,15 @@ #define SPEECH_LANGUAGE 5 // #define SPEECH_IGNORE_SPAM 6 // #define SPEECH_FORCED 7 +#define COMSIG_MOB_IS_VIEWER "mob_is_viewer" //from base of /get_actual_viewers(): (atom/center, depth, viewers_list) +#define COMSIG_MOB_GET_VISIBLE_MESSAGE "mob_get_visible_message" //from base of atom/visible_message(): (atom/A, msg, range, ignored_mobs) + #define COMPONENT_NO_VISIBLE_MESSAGE 1 //exactly what's said on the tin. #define COMSIG_MOB_ANTAG_ON_GAIN "mob_antag_on_gain" //from base of /datum/antagonist/on_gain(): (antag_datum) #define COMSIG_MOB_SPELL_CAN_CAST "mob_spell_can_cast" //from base of /obj/effect/proc_holder/spell/can_cast(): (spell) +#define COMSIG_ROBOT_UPDATE_ICONS "robot_update_icons" //from base of robot/update_icons(): () + // /mob/living signals #define COMSIG_LIVING_REGENERATE_LIMBS "living_regenerate_limbs" //from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs) #define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living) @@ -193,9 +202,13 @@ #define COMSIG_LIVING_ELECTROCUTE_ACT "living_electrocute_act" //from base of mob/living/electrocute_act(): (shock_damage, source, siemens_coeff, flags) #define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock" //sent by stuff like stunbatons and tasers: () #define COMSIG_LIVING_REVIVE "living_revive" //from base of mob/living/revive() (full_heal, admin_revive) -#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login" //sent when a mob/login() finishes: (client) -#define COMSIG_MOB_CLIENT_LOGOUT "comsig_mob_client_logout" //sent when a mob/logout() starts: (client) -#define COMSIG_MOB_CLIENT_MOVE "comsig_mob_client_move" //sent when client/Move() finishes with no early returns: (client, direction, n, oldloc) + +#define COMSIG_MOB_CLIENT_LOGIN "mob_client_login" //sent when a mob/login() finishes: (client) +#define COMSIG_MOB_CLIENT_LOGOUT "mob_client_logout" //sent when a mob/logout() starts: (client) +#define COMSIG_MOB_CLIENT_MOVE "mob_client_move" //sent when client/Move() finishes with no early returns: (client, direction, n, oldloc) +#define COMSIG_MOB_CLIENT_CHANGE_VIEW "mob_client_change_view" //from base of /client/change_view(): (client, old_view, view) + +#define COMSIG_MOB_RESET_PERSPECTIVE "mob_reset_perspective" //from base of /mob/reset_perspective(): (atom/target) #define COMSIG_LIVING_GUN_PROCESS_FIRE "living_gun_process_fire" //from base of /obj/item/gun/proc/process_fire(): (atom/target, params, zone_override) // This returns flags as defined for block in __DEFINES/combat.dm! #define COMSIG_LIVING_RUN_BLOCK "living_do_run_block" //from base of mob/living/do_run_block(): (real_attack, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone) diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 396cf25be1..9d34bc14d2 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -31,8 +31,9 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define HOLOGRAM_1 (1<<12) #define TESLA_IGNORE_1 (1<<13) // TESLA_IGNORE grants immunity from being targeted by tesla-style electricity #define INITIALIZED_1 (1<<14) //Whether /atom/Initialize() has already run for the object -#define ADMIN_SPAWNED_1 (1<<15) //was this spawned by an admin? used for stat tracking stuff. +#define ADMIN_SPAWNED_1 (1<<15) //was this spawned by an admin? used for stat tracking stuff. #define PREVENT_CONTENTS_EXPLOSION_1 (1<<16) /// should not get harmed if this gets caught by an explosion? +#define BLOCK_FACE_ATOM_1 (1<<17) /// Early returns mob.face_atom() //turf-only flags #define NOJAUNT_1 (1<<0) diff --git a/code/__DEFINES/layers_planes.dm b/code/__DEFINES/layers_planes.dm index cffafcb81d..210e5a4a35 100644 --- a/code/__DEFINES/layers_planes.dm +++ b/code/__DEFINES/layers_planes.dm @@ -11,13 +11,34 @@ #define PLANE_SPACE_PARALLAX_RENDER_TARGET "PLANE_SPACE_PARALLAX" #define OPENSPACE_LAYER 17 //Openspace layer over all -#define OPENSPACE_PLANE -4 //Openspace plane below all turfs -#define OPENSPACE_BACKDROP_PLANE -3 //Black square just over openspace plane to guaranteed cover all in openspace turf +#define OPENSPACE_PLANE -10 //Openspace plane below all turfs +#define OPENSPACE_BACKDROP_PLANE -9 //Black square just over openspace plane to guaranteed cover all in openspace turf -#define FLOOR_PLANE -2 +#define FLOOR_PLANE -8 #define FLOOR_PLANE_RENDER_TARGET "FLOOR_PLANE" -#define GAME_PLANE -1 + +#define WALL_PLANE -7 +#define WALL_PLANE_RENDER_TARGET "WALL_PLANE" + +#define ABOVE_WALL_PLANE -6 +#define ABOVE_WALL_PLANE_RENDER_TARGET "ABOVE_WALL_PLANE" + +#define FIELD_OF_VISION_BLOCKER_PLANE -5 +#define FIELD_OF_VISION_BLOCKER_RENDER_TARGET "*FIELD_OF_VISION_BLOCKER_PLANE" + +#define FIELD_OF_VISION_PLANE -4 +#define FIELD_OF_VISION_RENDER_TARGET "*FIELD_OF_VISION_PLANE" +#define FIELD_OF_VISION_LAYER 17 //used to place the visual (not the mask) shadow cone above any other floor plane stuff. + +#define GAME_PLANE -3 #define GAME_PLANE_RENDER_TARGET "GAME_PLANE" + +#define FIELD_OF_VISION_VISUAL_PLANE -2 //Yea, FoV does require quite a few planes to work with 513 filters to a decent degree. +#define FIELD_OF_VISION_VISUAL_RENDER_TARGET "FIELD_OF_VISION_VISUAL_PLANE" + +#define CHAT_PLANE -1 //We don't want heard messages to be hidden by FoV. +#define CHAT_LAYER 12.1 //Legacy, it doesn't matter that much because we are displayed above the game plane anyway. + #define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals #define BLACKNESS_PLANE_RENDER_TARGET "BLACKNESS_PLANE" @@ -92,8 +113,6 @@ #define MASSIVE_OBJ_LAYER 11 #define POINT_LAYER 12 -#define CHAT_LAYER 12.1 - #define EMISSIVE_BLOCKER_PLANE 12 #define EMISSIVE_BLOCKER_LAYER 12 #define EMISSIVE_BLOCKER_RENDER_TARGET "*EMISSIVE_BLOCKER_PLANE" @@ -137,12 +156,12 @@ #define HUD_LAYER 21 #define HUD_RENDER_TARGET "HUD_PLANE" -#define VOLUMETRIC_STORAGE_BOX_PLANE 23 -#define VOLUMETRIC_STORAGE_BOX_LAYER 23 +#define VOLUMETRIC_STORAGE_BOX_PLANE 22 +#define VOLUMETRIC_STORAGE_BOX_LAYER 22 #define VOLUMETRIC_STORAGE_BOX_RENDER_TARGET "VOLUME_STORAGE_BOX_PLANE" -#define VOLUMETRIC_STORAGE_ITEM_PLANE 24 -#define VOLUMETRIC_STORAGE_ITEM_LAYER 24 +#define VOLUMETRIC_STORAGE_ITEM_PLANE 23 +#define VOLUMETRIC_STORAGE_ITEM_LAYER 23 #define VOLUMETRIC_STORAGE_ACTIVE_ITEM_LAYER 25 #define VOLUMETRIC_STORAGE_ACTIVE_ITEM_PLANE 25 #define VOLUMETRIC_STORAGE_ITEM_RENDER_TARGET "VOLUME_STORAGE_ITEM_PLANE" @@ -154,4 +173,3 @@ #define SPLASHSCREEN_LAYER 90 #define SPLASHSCREEN_PLANE 90 #define SPLASHSCREEN_RENDER_TARGET "SPLASHSCREEN_PLANE" - diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 2edef77dbf..29ab67271c 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -297,3 +297,8 @@ #define GRAB_PIXEL_SHIFT_NECK 16 #define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; + +/// Field of vision defines. +#define FOV_90_DEGREES 90 +#define FOV_180_DEGREES 180 +#define FOV_270_DEGREES 270 diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 679f89f4d2..6c0ffa5caa 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -249,6 +249,15 @@ SEND_SIGNAL(A, COMSIG_ATOM_HEARER_IN_VIEW, processing, .) processing += A.contents +//viewers() but with a signal, for blacklisting. +/proc/get_actual_viewers(depth = world.view, atom/center) + if(!center) + return + . = viewers(depth, center) + for(var/k in .) + var/mob/M = k + SEND_SIGNAL(M, COMSIG_MOB_IS_VIEWER, center, depth, .) + /proc/get_mobs_in_radio_ranges(list/obj/item/radio/radios) . = list() // Returns a list of mobs who can hear any of the radios given in @radios diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index a22f430140..06dd746250 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1,5 +1,3 @@ - - /* * A large number of misc global procs. */ @@ -391,6 +389,16 @@ Turf and target are separate in case you want to teleport some distance from a t break return loc +//Returns a list of all locations the target is within. +/proc/get_nested_locs(atom/movable/M, include_turf = FALSE) + . = list() + var/atom/A = M.loc + while(A && !isturf(A)) + . += A + A = A.loc + if(A && include_turf) //At this point, only the turf is left. + . += A + // returns the turf located at the map edge in the specified direction relative to A // used for mass driver /proc/get_edge_target_turf(atom/A, direction) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 738d72c6bf..5d2f916fba 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -324,10 +324,9 @@ return /atom/proc/ShiftClick(mob/user) - var/flags = SEND_SIGNAL(src, COMSIG_CLICK_SHIFT, user) + var/flags = SEND_SIGNAL(src, COMSIG_CLICK_SHIFT, user) | SEND_SIGNAL(user, COMSIG_MOB_CLICKED_SHIFT_ON, src) if(!(flags & COMPONENT_DENY_EXAMINATE) && user.client && (user.client.eye == user || user.client.eye == user.loc || flags & COMPONENT_ALLOW_EXAMINATE)) user.examinate(src) - return /* Ctrl click @@ -424,32 +423,36 @@ LE.fire() // Simple helper to face what you clicked on, in case it should be needed in more than one place -/mob/proc/face_atom(atom/A) - if( buckled || stat != CONSCIOUS || !A || !x || !y || !A.x || !A.y ) +/mob/proc/face_atom(atom/A, ismousemovement = FALSE) + if( buckled || stat != CONSCIOUS || !loc || !A || !A.x || !A.y ) return - var/dx = A.x - x - var/dy = A.y - y + var/atom/L = loc + if(L.flags_1 & BLOCK_FACE_ATOM_1) + return + var/turf/T = get_turf(src) + var/dx = A.x - T.x + var/dy = A.y - T.y if(!dx && !dy) // Wall items are graphically shifted but on the floor if(A.pixel_y > 16) - setDir(NORTH) + setDir(NORTH, ismousemovement) else if(A.pixel_y < -16) - setDir(SOUTH) + setDir(SOUTH, ismousemovement) else if(A.pixel_x > 16) - setDir(EAST) + setDir(EAST, ismousemovement) else if(A.pixel_x < -16) - setDir(WEST) + setDir(WEST, ismousemovement) return if(abs(dx) < abs(dy)) if(dy > 0) - setDir(NORTH) + setDir(NORTH, ismousemovement) else - setDir(SOUTH) + setDir(SOUTH, ismousemovement) else if(dx > 0) - setDir(EAST) + setDir(EAST, ismousemovement) else - setDir(WEST) + setDir(WEST, ismousemovement) //debug /obj/screen/proc/scale_to(x1,y1) diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm index a0dade37bd..d7557b00d3 100644 --- a/code/_onclick/hud/plane_master.dm +++ b/code/_onclick/hud/plane_master.dm @@ -24,6 +24,10 @@ blend_mode = BLEND_MULTIPLY alpha = 255 +/obj/screen/plane_master/openspace/Initialize() + . = ..() + filters += filter(type="alpha", render_source=FIELD_OF_VISION_RENDER_TARGET, flags=MASK_INVERSE) + /obj/screen/plane_master/openspace/backdrop(mob/mymob) filters = list() filters += filter(type = "drop_shadow", color = "#04080FAA", size = -10) @@ -46,6 +50,26 @@ appearance_flags = PLANE_MASTER blend_mode = BLEND_OVERLAY +/obj/screen/plane_master/wall + name = "wall plane master" + plane = WALL_PLANE + appearance_flags = PLANE_MASTER + +/obj/screen/plane_master/wall/backdrop(mob/mymob) + if(mymob?.client?.prefs.ambientocclusion) + add_filter("ambient_occlusion", 0, AMBIENT_OCCLUSION) + else + remove_filter("ambient_occlusion") + +/obj/screen/plane_master/above_wall + name = "above wall plane master" + plane = ABOVE_WALL_PLANE + appearance_flags = PLANE_MASTER + +/obj/screen/plane_master/above_wall/Initialize() + . = ..() + add_filter("vision_cone", 100, list(type="alpha", render_source=FIELD_OF_VISION_RENDER_TARGET, flags=MASK_INVERSE)) + ///Contains most things in the game world /obj/screen/plane_master/game_world name = "game world plane master" @@ -53,12 +77,50 @@ appearance_flags = PLANE_MASTER //should use client color blend_mode = BLEND_OVERLAY +/obj/screen/plane_master/game_world/Initialize() + . = ..() + add_filter("vision_cone", 100, list(type="alpha", render_source=FIELD_OF_VISION_RENDER_TARGET, flags=MASK_INVERSE)) + /obj/screen/plane_master/game_world/backdrop(mob/mymob) - if(istype(mymob) && mymob.client && mymob.client.prefs && mymob.client.prefs.ambientocclusion) + if(mymob?.client?.prefs.ambientocclusion) add_filter("ambient_occlusion", 0, AMBIENT_OCCLUSION) else remove_filter("ambient_occlusion") - update_filters() + +//Reserved to chat messages, so they are still displayed above the field of vision masking. +/obj/screen/plane_master/chat_messages + name = "chat messages plane master" + plane = CHAT_PLANE + appearance_flags = PLANE_MASTER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +///Contains all shadow cone masks, whose image overrides are displayed only to their respective owners. +/obj/screen/plane_master/field_of_vision + name = "field of vision mask plane master" + plane = FIELD_OF_VISION_PLANE + render_target = FIELD_OF_VISION_RENDER_TARGET + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/obj/screen/plane_master/field_of_vision/Initialize() + . = ..() + filters += filter(type="alpha", render_source=FIELD_OF_VISION_BLOCKER_RENDER_TARGET, flags=MASK_INVERSE) + +///Used to display the owner and its adjacent surroundings through the FoV plane mask. +/obj/screen/plane_master/field_of_vision_blocker + name = "field of vision blocker plane master" + plane = FIELD_OF_VISION_BLOCKER_PLANE + render_target = FIELD_OF_VISION_BLOCKER_RENDER_TARGET + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +///Stores the visible portion of the FoV shadow cone. +/obj/screen/plane_master/field_of_vision_visual + name = "field of vision visual plane master" + plane = FIELD_OF_VISION_VISUAL_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/obj/screen/plane_master/field_of_vision_visual/Initialize() + . = ..() + filters += filter(type="alpha", render_source=FIELD_OF_VISION_BLOCKER_RENDER_TARGET, flags=MASK_INVERSE) ///Contains all lighting objects /obj/screen/plane_master/lighting @@ -87,11 +149,12 @@ /obj/screen/plane_master/emissive/Initialize() . = ..() filters += filter(type="alpha", render_source=EMISSIVE_BLOCKER_RENDER_TARGET, flags=MASK_INVERSE) + filters += filter(type="alpha", render_source=FIELD_OF_VISION_RENDER_TARGET, flags=MASK_INVERSE) /** * Things placed on this always mask the lighting plane. Doesn't render directly. * - * Always masks the light plane, isn't blocked by anything. Use for on mob glows, + * Always masks the light plane, isn't blocked by anything (except Field of Vision). Use for on mob glows, * magic stuff, etc. */ @@ -101,6 +164,10 @@ mouse_opacity = MOUSE_OPACITY_TRANSPARENT render_target = EMISSIVE_UNBLOCKABLE_RENDER_TARGET +/obj/screen/plane_master/emissive_unblockable/Initialize() + . = ..() + filters += filter(type="alpha", render_source=FIELD_OF_VISION_RENDER_TARGET, flags=MASK_INVERSE) + /** * Things placed on this layer mask the emissive layer. Doesn't render directly * diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm index 1dce4ecc42..1ead41d513 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -238,6 +238,7 @@ GLOBAL_LIST_EMPTY(radial_menus) var/mutable_appearance/MA = new /mutable_appearance(E) if(MA) MA.layer = ABOVE_HUD_LAYER + MA.plane = ABOVE_HUD_PLANE MA.appearance_flags |= RESET_TRANSFORM return MA diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index b35f4fe0e7..f8a66fce09 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -507,3 +507,9 @@ //Allows players to set a hexadecimal color of their choice as skin tone, on top of the standard ones. /datum/config_entry/flag/allow_custom_skintones + +/** + * Enables the FoV component, which hides objects and mobs behind the parent from their sight, unless they turn around, duh. + * Camera mobs, AIs, ghosts and some other are of course exempt from this. This also doesn't influence simplemob AI, for the best. + */ +/datum/config_entry/flag/use_field_of_vision diff --git a/code/datums/brain_damage/phobia.dm b/code/datums/brain_damage/phobia.dm index 80a1bd2470..7ff25eb12f 100644 --- a/code/datums/brain_damage/phobia.dm +++ b/code/datums/brain_damage/phobia.dm @@ -44,7 +44,7 @@ return if(world.time > next_check && world.time > next_scare) next_check = world.time + 50 - var/list/seen_atoms = view(7, owner) + var/list/seen_atoms = owner.visible_atoms(7) if(LAZYLEN(trigger_objs)) for(var/obj/O in seen_atoms) diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm index cbcb34ac5e..64d6114f75 100644 --- a/code/datums/chatmessage.dm +++ b/code/datums/chatmessage.dm @@ -129,7 +129,7 @@ // Build message image message = image(loc = message_loc, layer = CHAT_LAYER) - message.plane = GAME_PLANE + message.plane = CHAT_PLANE message.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA | KEEP_APART message.alpha = 0 message.pixel_y = owner.bound_height * 0.95 diff --git a/code/datums/components/field_of_vision.dm b/code/datums/components/field_of_vision.dm new file mode 100644 index 0000000000..b819604168 --- /dev/null +++ b/code/datums/components/field_of_vision.dm @@ -0,0 +1,354 @@ +#define CENTERED_RENDER_SOURCE(img, atom, FoV) \ + atom.render_target = atom.render_target || ref(atom);\ + img.render_source = atom.render_target;\ + if(atom.icon){\ + var/_cached_sizes = FoV.width_n_height_offsets[atom.icon];\ + if(!_cached_sizes){\ + var/icon/_I = icon(atom.icon);\ + var/list/L = list();\ + L += (_I.Width() - world.icon_size)/2;\ + L += (_I.Height() - world.icon_size)/2;\ + _cached_sizes = FoV.width_n_height_offsets[atom.icon] = L\ + }\ + img.pixel_x = _cached_sizes[1];\ + img.pixel_y = _cached_sizes[2];\ + img.loc = atom\ + } + +#define REGISTER_NESTED_LOCS(source, list, comsig, proc) \ + for(var/k in get_nested_locs(source)){\ + var/atom/_A = k;\ + RegisterSignal(_A, comsig, proc);\ + list += _A\ + } + +#define UNREGISTER_NESTED_LOCS(list, comsig, index) \ + for(var/k in index to length(list)){\ + var/atom/_A = list[k];\ + UnregisterSignal(_A, comsig);\ + list -= _A\ + } + +/** + * Field of Vision component. Does totally what you probably think it does, + * ergo preventing players from seeing what's behind them. + */ +/datum/component/field_of_vision + can_transfer = TRUE + +/** + * That special invisible, almost neigh indestructible movable + * that holds both shadow cone mask and image and follows the player around. + */ + var/atom/movable/fov_holder/fov + ///The current screen size this field of vision is meant to fit for. + var/current_fov_size = list(15, 15) + ///How much is the cone rotated clockwise, purely backend. Please use rotate_shadow_cone() if you must. + var/angle = 0 + /// Used to scale the shadow cone when rotating it to fit over the edges of the screen. + var/rot_scale = 1 + /// The inner angle of this cone, right hardset to 90, 180, or 270 degrees, until someone figures out a way to make it dynamic. + var/shadow_angle = FOV_90_DEGREES + /// The mask portion of the cone, placed on a * render target plane so while not visible it still applies the filter. + var/image/shadow_mask + /// The visual portion of the cone, placed on the highest layer of the wall plane + var/image/visual_shadow +/** + * An image whose render_source is kept up to date to prevent the mob (or the topmost movable holding it) from being hidden by the mask. + * Will make it use vis_contents instead once a few byonds bugs with images and vis contents are fixed. + */ + var/image/owner_mask +/** + * A circle image used to somewhat uncover the adjacent portion of the shadow cone, making mobs and objects behind us somewhat visible. + * The owner mask is still required for those mob going over the default 32x32 px size btw. + */ + var/image/adj_mask + /// A list of nested locations the mob is in, to ensure the above image works correctly. + var/list/nested_locs = list() +/** + * A static list of offsets based on icon width and height, because render sources are centered unlike most other visuals, + * and that gives us some problems when the icon is larger or smaller than world.icon_size + */ + var/static/list/width_n_height_offsets = list() + +/datum/component/field_of_vision/Initialize(fov_type = FOV_90_DEGREES, _angle = 0) + if(!ismob(parent)) + return COMPONENT_INCOMPATIBLE + angle = _angle + shadow_angle = fov_type + +/datum/component/field_of_vision/RegisterWithParent() + . = ..() + var/mob/M = parent + if(M.client) + generate_fov_holder(M, angle) + RegisterSignal(M, COMSIG_MOB_CLIENT_LOGIN, .proc/on_mob_login) + RegisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT, .proc/on_mob_logout) + RegisterSignal(M, COMSIG_MOB_GET_VISIBLE_MESSAGE, .proc/on_visible_message) + RegisterSignal(M, COMSIG_MOB_EXAMINATE, .proc/on_examinate) + RegisterSignal(M, COMSIG_MOB_VISIBLE_ATOMS, .proc/on_visible_atoms) + RegisterSignal(M, COMSIG_MOB_CLIENT_CHANGE_VIEW, .proc/on_change_view) + RegisterSignal(M, COMSIG_MOB_RESET_PERSPECTIVE, .proc/on_reset_perspective) + RegisterSignal(M, COMSIG_MOB_IS_VIEWER, .proc/is_viewer) + +/datum/component/field_of_vision/UnregisterFromParent() + . = ..() + var/mob/M = parent + if(!QDELETED(fov)) + if(M.client) + UnregisterSignal(M, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_MOVABLE_MOVED, COMSIG_MOB_DEATH, COMSIG_LIVING_REVIVE)) + M.client.images -= owner_mask + M.client.images -= shadow_mask + M.client.images -= visual_shadow + M.client.images -= adj_mask + qdel(fov, TRUE) // Forced. + fov = null + QDEL_NULL(owner_mask) + QDEL_NULL(adj_mask) + if(length(nested_locs)) + UNREGISTER_NESTED_LOCS(nested_locs, COMSIG_MOVABLE_MOVED, 1) + UnregisterSignal(M, list(COMSIG_MOB_CLIENT_LOGIN, COMSIG_MOB_CLIENT_LOGOUT, + COMSIG_MOB_GET_VISIBLE_MESSAGE, COMSIG_MOB_EXAMINATE, + COMSIG_MOB_VISIBLE_ATOMS, COMSIG_MOB_RESET_PERSPECTIVE, + COMSIG_MOB_CLIENT_CHANGE_VIEW, COMSIG_MOB_IS_VIEWER)) + +/** + * Generates the holder and images (if not generated yet) and adds them to client.images. + * Run when the component is registered to a player mob, or upon login. + */ +/datum/component/field_of_vision/proc/generate_fov_holder(mob/M, _angle = 0) + if(QDELETED(fov)) + fov = new(get_turf(M)) + fov.icon_state = "[shadow_angle]" + fov.dir = M.dir + shadow_mask = image('icons/misc/field_of_vision.dmi', fov, "[shadow_angle]", FIELD_OF_VISION_LAYER) + shadow_mask.plane = FIELD_OF_VISION_PLANE + visual_shadow = image('icons/misc/field_of_vision.dmi', fov, "[shadow_angle]_v", FIELD_OF_VISION_LAYER) + visual_shadow.plane = FIELD_OF_VISION_VISUAL_PLANE + owner_mask = new + owner_mask.appearance_flags = RESET_TRANSFORM + owner_mask.plane = FIELD_OF_VISION_BLOCKER_PLANE + adj_mask = image('icons/misc/field_of_vision.dmi', fov, "adj_mask", FIELD_OF_VISION_LAYER) + adj_mask.appearance_flags = RESET_TRANSFORM + adj_mask.plane = FIELD_OF_VISION_BLOCKER_PLANE + if(_angle) + rotate_shadow_cone(_angle) + fov.alpha = M.stat == DEAD ? 0 : 255 + RegisterSignal(M, COMSIG_MOB_DEATH, .proc/hide_fov) + RegisterSignal(M, COMSIG_LIVING_REVIVE, .proc/show_fov) + RegisterSignal(M, COMSIG_ATOM_DIR_CHANGE, .proc/on_dir_change) + RegisterSignal(M, COMSIG_MOVABLE_MOVED, .proc/on_mob_moved) + RegisterSignal(M, COMSIG_ROBOT_UPDATE_ICONS, .proc/manual_centered_render_source) + var/atom/A = M + if(M.loc && !isturf(M.loc)) + REGISTER_NESTED_LOCS(M, nested_locs, COMSIG_MOVABLE_MOVED, .proc/on_loc_moved) + A = nested_locs[nested_locs.len] + CENTERED_RENDER_SOURCE(owner_mask, A, src) + M.client.images += shadow_mask + M.client.images += visual_shadow + M.client.images += owner_mask + M.client.images += adj_mask + if(M.client.view != "[current_fov_size[1]]x[current_fov_size[2]]") + resize_fov(current_fov_size, getviewsize(M.client.view)) + +///Rotates the shadow cone to a certain degree. Backend shenanigans. +/datum/component/field_of_vision/proc/rotate_shadow_cone(new_angle) + var/simple_degrees = SIMPLIFY_DEGREES(new_angle - angle) + var/to_scale = cos(simple_degrees) * sin(simple_degrees) + if(to_scale) + var/old_rot_scale = rot_scale + rot_scale = 1 + to_scale + if(old_rot_scale != rot_scale) + visual_shadow.transform = shadow_mask.transform = shadow_mask.transform.Scale(rot_scale/old_rot_scale) + visual_shadow.transform = shadow_mask.transform = shadow_mask.transform.Turn(fov.transform, simple_degrees) + +/** + * Resizes the shadow to match the current screen size. + * Run when the client view size is changed, or if the player has a viewsize different than "15x15" on login/comp registration. + */ +/datum/component/field_of_vision/proc/resize_fov(list/old_view, list/view) + current_fov_size = view + var/old_size = max(old_view[1], old_view[2]) + var/new_size = max(view[1], view[2]) + if(old_size == new_size) //longest edges are still of the same length. + return + visual_shadow.transform = shadow_mask.transform = shadow_mask.transform.Scale(new_size/old_size) + +/datum/component/field_of_vision/proc/on_mob_login(mob/source, client/client) + generate_fov_holder(source, angle) + +/datum/component/field_of_vision/proc/on_mob_logout(mob/source, client/client) + UnregisterSignal(source, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_MOVABLE_MOVED, COMSIG_MOB_DEATH, + COMSIG_LIVING_REVIVE, COMSIG_ROBOT_UPDATE_ICONS)) + if(length(nested_locs)) + UNREGISTER_NESTED_LOCS(nested_locs, COMSIG_MOVABLE_MOVED, 1) + +/datum/component/field_of_vision/proc/on_dir_change(mob/source, old_dir, new_dir) + fov.dir = new_dir + +///Hides the shadow, other visibility comsig procs will take it into account. Called when the mob dies. +/datum/component/field_of_vision/proc/hide_fov(mob/source) + fov.alpha = 0 + +/// Shows the shadow. Called when the mob is revived. +/datum/component/field_of_vision/proc/show_fov(mob/source) + fov.alpha = 255 + +/// Hides the shadow when looking through other items, shows it otherwise. +/datum/component/field_of_vision/proc/on_reset_perspective(mob/source, atom/target) + if(source.client.eye == source || source.client.eye == source.loc) + fov.alpha = 255 + else + fov.alpha = 0 + +/// Called when the client view size is changed. +/datum/component/field_of_vision/proc/on_change_view(mob/source, client, list/old_view, list/view) + resize_fov(old_view, view) + +/** + * Called when the owner mob moves around. Used to keep shadow located right behind us, + * As well as modify the owner mask to match the topmost item. + */ +/datum/component/field_of_vision/proc/on_mob_moved(mob/source, atom/oldloc, dir, forced) + var/turf/T + if(!isturf(source.loc)) //Recalculate all nested locations. + UNREGISTER_NESTED_LOCS( nested_locs, COMSIG_MOVABLE_MOVED, 1) + REGISTER_NESTED_LOCS(source, nested_locs, COMSIG_MOVABLE_MOVED, .proc/on_loc_moved) + var/atom/movable/topmost = nested_locs[nested_locs.len] + T = topmost.loc + CENTERED_RENDER_SOURCE(owner_mask, topmost, src) + else + T = source.loc + if(length(nested_locs)) + UNREGISTER_NESTED_LOCS(nested_locs, COMSIG_MOVABLE_MOVED, 1) + CENTERED_RENDER_SOURCE(owner_mask, source, src) + if(T) + fov.forceMove(T, harderforce = TRUE) + +/// Pretty much like the above, but meant for other movables the mob is stored in (bodybags, boxes, mechs etc). +/datum/component/field_of_vision/proc/on_loc_moved(atom/movable/source, atom/oldloc, dir, forced) + if(isturf(source.loc) && isturf(oldloc)) //This is the case of the topmost movable loc moving around the world, skip. + fov.forceMove(source.loc, harderforce = TRUE) + return + var/atom/movable/prev_topmost = nested_locs[nested_locs.len] + if(prev_topmost != source) + UNREGISTER_NESTED_LOCS(nested_locs, COMSIG_MOVABLE_MOVED, nested_locs.Find(source) + 1) + REGISTER_NESTED_LOCS(source, nested_locs, COMSIG_MOVABLE_MOVED, .proc/on_loc_moved) + var/atom/movable/topmost = nested_locs[nested_locs.len] + if(topmost != prev_topmost) + CENTERED_RENDER_SOURCE(owner_mask, topmost, src) + if(topmost.loc) + fov.forceMove(topmost.loc, harderforce = TRUE) + +/// A hacky comsig proc for things that somehow decide to change icon on the go. may make a change_icon_file() proc later but... +/datum/component/field_of_vision/proc/manual_centered_render_source(mob/source, old_icon) + if(!isturf(source.loc)) + return + CENTERED_RENDER_SOURCE(owner_mask, source, src) + +#undef CENTERED_RENDER_SOURCE +#undef REGISTER_NESTED_LOCS +#undef UNREGISTER_NESTED_LOCS + +/** + * Byond doc is not entirely correct on the integrated arctan() proc. + * When both x and y are negative, the output is also negative, cycling clockwise instead of counter-clockwise. + * That's also why I am extensively using the SIMPLIFY_DEGREES macro here. + * + * Overall this is the main macro that calculates wheter a target is within the shadow cone angle or not. + */ +#define FOV_ANGLE_CHECK(mob, target, zero_x_y_statement, success_statement) \ + var/turf/T1 = get_turf(target);\ + var/turf/T2 = get_turf(mob);\ + if(!T1 || !T2){\ + zero_x_y_statement\ + }\ + var/_x = (T1.x - T2.x);\ + var/_y = (T1.y - T2.y);\ + if(ISINRANGE(_x, -1, 1) && ISINRANGE(_y, -1, 1)){\ + zero_x_y_statement\ + }\ + var/dir = (mob.dir & (EAST|WEST)) || mob.dir;\ + var/_degree = -angle;\ + var/_half = shadow_angle/2;\ + switch(dir){\ + if(EAST){\ + _degree += 180;\ + }\ + if(NORTH){\ + _degree += 270;\ + }\ + if(SOUTH){\ + _degree += 90;\ + }\ + }\ + var/_min = SIMPLIFY_DEGREES(_degree - _half);\ + var/_max = SIMPLIFY_DEGREES(_degree + _half);\ + if((_min > _max) ? !ISINRANGE(SIMPLIFY_DEGREES(arctan(_x, _y)), _max, _min) : ISINRANGE(SIMPLIFY_DEGREES(arctan(_x, _y)), _min, _max)){\ + success_statement;\ + } + +/datum/component/field_of_vision/proc/on_examinate(mob/source, atom/target) + if(fov.alpha) + FOV_ANGLE_CHECK(source, target, return, return COMPONENT_DENY_EXAMINATE|COMPONENT_EXAMINATE_BLIND) + +/datum/component/field_of_vision/proc/on_visible_message(mob/source, atom/target, message, range, list/ignored_mobs) + if(fov.alpha) + FOV_ANGLE_CHECK(source, target, return, return COMPONENT_NO_VISIBLE_MESSAGE) + +/datum/component/field_of_vision/proc/on_visible_atoms(mob/source, list/atoms) + if(!fov.alpha) + return + for(var/k in atoms) + var/atom/A = k + FOV_ANGLE_CHECK(source, A, continue, atoms -= A) + +/datum/component/field_of_vision/proc/is_viewer(mob/source, atom/center, depth, list/viewers_list) + if(fov.alpha) + FOV_ANGLE_CHECK(source, center, return, viewers_list -= source) + +#undef FOV_ANGLE_CHECK + +/** + * The shadow cone's mask and visual images holder which can't locate inside the mob, + * lest they inherit the mob opacity and cause a lot of hindrance + */ +/atom/movable/fov_holder + name = "field of vision holder" + pixel_x = -224 //the image is about 480x480 px, ergo 15 tiles (480/32) big, and we gotta center it. + pixel_y = -224 + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + plane = FIELD_OF_VISION_PLANE + anchored = TRUE + +/atom/movable/fov_holder/ConveyorMove() + return + +/atom/movable/fov_holder/has_gravity(turf/T) + return FALSE + +/atom/movable/fov_holder/ex_act(severity) + return FALSE + +/atom/movable/fov_holder/singularity_act() + return + +/atom/movable/fov_holder/singularity_pull() + return + +/atom/movable/fov_holder/blob_act() + return + +/atom/movable/fov_holder/onTransitZ() + return + +/// Prevents people from moving these after creation, because they shouldn't be. +/atom/movable/fov_holder/forceMove(atom/destination, no_tp=FALSE, harderforce = FALSE) + if(harderforce) + return ..() + +/// Last but not least, these shouldn't be deleted by anything but the component itself +/atom/movable/fov_holder/Destroy(force = FALSE) + if(!force) + return QDEL_HINT_LETMELIVE + return ..() diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index 2e4a3db1a0..68ba0e4272 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -571,10 +571,9 @@ return if(rustle_sound) playsound(parent, "rustle", 50, 1, -5) - for(var/mob/viewing in viewers(user, null)) - if(M == viewing) - to_chat(usr, "You put [I] [insert_preposition]to [parent].") - else if(in_range(M, viewing)) //If someone is standing close enough, they can tell what it is... + to_chat(user, "You put [I] [insert_preposition]to [parent].") + for(var/mob/viewing in get_actual_viewers(world.view, user)-M) + if(in_range(M, viewing)) //If someone is standing close enough, they can tell what it is... viewing.show_message("[M] puts [I] [insert_preposition]to [parent].", MSG_VISUAL) else if(I && I.w_class >= 3) //Otherwise they can only see large or normal items from a distance... viewing.show_message("[M] puts [I] [insert_preposition]to [parent].", MSG_VISUAL) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 98027acc61..a63f63f4a4 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -927,14 +927,14 @@ Proc for attack log creation, because really why not target.log_message(reverse_message, LOG_ATTACK, color="orange", log_globally=FALSE) // Filter stuff -/atom/movable/proc/add_filter(name,priority,list/params) +/atom/proc/add_filter(name,priority,list/params) LAZYINITLIST(filter_data) var/list/p = params.Copy() p["priority"] = priority filter_data[name] = p update_filters() -/atom/movable/proc/update_filters() +/atom/proc/update_filters() filters = null filter_data = sortTim(filter_data, /proc/cmp_filter_data_priority, TRUE) for(var/f in filter_data) @@ -943,11 +943,11 @@ Proc for attack log creation, because really why not arguments -= "priority" filters += filter(arglist(arguments)) -/atom/movable/proc/get_filter(name) +/atom/proc/get_filter(name) if(filter_data && filter_data[name]) return filters[filter_data.Find(name)] -/atom/movable/proc/remove_filter(name) +/atom/proc/remove_filter(name) if(filter_data && filter_data[name]) filter_data -= name update_filters() diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index d817ca43f5..206864d44c 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -179,14 +179,15 @@ return TRUE /atom/movable/proc/stop_pulling() - if(pulling) - pulling.pulledby = null - var/mob/living/ex_pulled = pulling - pulling = null - setGrabState(0) - if(isliving(ex_pulled)) - var/mob/living/L = ex_pulled - L.update_mobility()// mob gets up if it was lyng down in a chokehold + if(!pulling) + return + pulling.pulledby = null + var/mob/living/ex_pulled = pulling + pulling = null + setGrabState(0) + if(isliving(ex_pulled)) + var/mob/living/L = ex_pulled + L.update_mobility()// mob gets up if it was lyng down in a chokehold /atom/movable/proc/Move_Pulled(atom/A) if(!pulling) diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 3f2edd7174..0416813f4f 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -37,6 +37,11 @@ reset_chem_buttons() RefreshParts() add_inital_chems() + new_occupant_dir = dir + +/obj/machinery/sleeper/setDir(newdir) + . = ..() + new_occupant_dir = dir /obj/machinery/sleeper/on_deconstruction() var/obj/item/reagent_containers/sleeper_buffer/buffer = new (loc) diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 93121a0753..470c26ed0f 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -110,7 +110,8 @@ Class Procs: var/state_open = FALSE var/critical_machine = FALSE //If this machine is critical to station operation and should have the area be excempted from power failures. var/list/occupant_typecache //if set, turned into typecache in Initialize, other wise, defaults to mob/living typecache - var/atom/movable/occupant = null + var/atom/movable/occupant + var/new_occupant_dir = SOUTH //The direction the occupant will be set to look at when entering the machine. var/speed_process = FALSE // Process as fast as possible? var/obj/item/circuitboard/circuit // Circuit to be created and inserted when the machinery is created // For storing and overriding ui id and dimensions @@ -217,6 +218,7 @@ Class Procs: if(target && !target.has_buckled_mobs() && (!isliving(target) || !mobtarget.buckled)) occupant = target target.forceMove(src) + target.setDir(new_occupant_dir) updateUsrDialog() update_icon() diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 8bf81b6061..8819020e3a 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -3,6 +3,7 @@ desc = "A remote control switch." icon = 'icons/obj/stationobjs.dmi' icon_state = "doorctrl" + plane = ABOVE_WALL_PLANE var/skin = "doorctrl" power_channel = ENVIRON var/obj/item/assembly/device diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index eb7f194229..967ee02f82 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -169,6 +169,7 @@ desc = "Used for watching an empty arena." icon = 'icons/obj/stationobjs.dmi' icon_state = "telescreen" + plane = ABOVE_WALL_PLANE network = list("thunder") density = FALSE circuit = null diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 3bc8aff809..4c12809184 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -470,15 +470,15 @@ if(welded) weld_overlay = get_airlock_overlay("welded", overlays_file) if(obj_integrity < integrity_failure * max_integrity) - damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) else if(obj_integrity < (0.75 * max_integrity)) - damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) if(lights && hasPower()) if(locked) - lights_overlay = get_airlock_overlay("lights_bolts", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + lights_overlay = get_airlock_overlay("lights_bolts", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) else if(emergency) - lights_overlay = get_airlock_overlay("lights_emergency", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + lights_overlay = get_airlock_overlay("lights_emergency", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) if(note) note_overlay = get_airlock_overlay(notetype, note_overlay_file) @@ -496,18 +496,18 @@ else panel_overlay = get_airlock_overlay("panel_closed", overlays_file) if(obj_integrity < integrity_failure * max_integrity) - damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) else if(obj_integrity < (0.75 * max_integrity)) - damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) if(welded) weld_overlay = get_airlock_overlay("welded", overlays_file) - lights_overlay = get_airlock_overlay("lights_denied", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + lights_overlay = get_airlock_overlay("lights_denied", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) if(note) note_overlay = get_airlock_overlay(notetype, note_overlay_file) if(AIRLOCK_EMAG) frame_overlay = get_airlock_overlay("closed", icon) - sparks_overlay = get_airlock_overlay("sparks", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + sparks_overlay = get_airlock_overlay("sparks", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) if(airlock_material) filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file) else @@ -518,9 +518,9 @@ else panel_overlay = get_airlock_overlay("panel_closed", overlays_file) if(obj_integrity < integrity_failure * max_integrity) - damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) else if(obj_integrity < (0.75 * max_integrity)) - damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) if(welded) weld_overlay = get_airlock_overlay("welded", overlays_file) if(note) @@ -533,7 +533,7 @@ else filling_overlay = get_airlock_overlay("fill_closing", icon) if(lights && hasPower()) - lights_overlay = get_airlock_overlay("lights_closing", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + lights_overlay = get_airlock_overlay("lights_closing", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) if(panel_open) if(security_level) panel_overlay = get_airlock_overlay("panel_closing_protected", overlays_file) @@ -554,7 +554,7 @@ else panel_overlay = get_airlock_overlay("panel_open", overlays_file) if(obj_integrity < (0.75 * max_integrity)) - damag_overlay = get_airlock_overlay("sparks_open", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + damag_overlay = get_airlock_overlay("sparks_open", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) if(note) note_overlay = get_airlock_overlay("[notetype]_open", note_overlay_file) @@ -565,7 +565,7 @@ else filling_overlay = get_airlock_overlay("fill_opening", icon) if(lights && hasPower()) - lights_overlay = get_airlock_overlay("lights_opening", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) + lights_overlay = get_airlock_overlay("lights_opening", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE) if(panel_open) if(security_level) panel_overlay = get_airlock_overlay("panel_opening_protected", overlays_file) diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index cd22b2dc05..1d39372dec 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -21,6 +21,7 @@ icon = 'icons/obj/status_display.dmi' icon_state = "frame" desc = "A remote control for a door." + plane = ABOVE_WALL_PLANE req_access = list(ACCESS_SECURITY) density = FALSE var/id // id of linked machinery/lockers diff --git a/code/game/machinery/embedded_controller/simple_vent_controller.dm b/code/game/machinery/embedded_controller/simple_vent_controller.dm index 6c8467dbd3..6416de70f1 100644 --- a/code/game/machinery/embedded_controller/simple_vent_controller.dm +++ b/code/game/machinery/embedded_controller/simple_vent_controller.dm @@ -34,7 +34,7 @@ /obj/machinery/embedded_controller/radio/simple_vent_controller icon = 'icons/obj/airlock_machines.dmi' icon_state = "airlock_control_standby" - + plane = ABOVE_WALL_PLANE name = "vent controller" density = FALSE diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index 6501a5b45e..8869ea396e 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -17,6 +17,7 @@ desc = "\"Pull this in case of emergency\". Thus, keep pulling it forever." icon = 'icons/obj/monitors.dmi' icon_state = "fire0" + plane = ABOVE_WALL_PLANE max_integrity = 250 integrity_failure = 0.4 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 7bef255aff..f4f1aa0637 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -5,6 +5,7 @@ desc = "A wall-mounted flashbulb device." icon = 'icons/obj/stationobjs.dmi' icon_state = "mflash1" + plane = ABOVE_WALL_PLANE max_integrity = 250 integrity_failure = 0.4 light_color = LIGHT_COLOR_WHITE @@ -20,6 +21,7 @@ name = "portable flasher" desc = "A portable flashing device. Wrench to activate and deactivate. Cannot detect slow movements." icon_state = "pflash1-p" + plane = GAME_PLANE strength = 80 anchored = FALSE base_state = "pflash" diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm index 244c300905..141f261688 100644 --- a/code/game/machinery/harvester.dm +++ b/code/game/machinery/harvester.dm @@ -20,6 +20,11 @@ . = ..() if(prob(1)) name = "auto-autopsy" + new_occupant_dir = dir + +/obj/machinery/harvester/setDir(newdir) + . = ..() + new_occupant_dir = dir /obj/machinery/harvester/RefreshParts() interval = 0 diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index b2a35dcf41..421e3433ca 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -5,6 +5,7 @@ name = "light switch" icon = 'icons/obj/power.dmi' icon_state = "light1" + plane = ABOVE_WALL_PLANE desc = "Make dark." var/on = TRUE var/area/area = null diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index 000585a4be..d55faf2343 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -16,6 +16,7 @@ GLOBAL_LIST_EMPTY(allConsoles) desc = "A console intended to send requests to different departments on the station." icon = 'icons/obj/terminals.dmi' icon_state = "req_comp0" + plane = ABOVE_WALL_PLANE var/department = "Unknown" //The list of all departments on the station (Determined from this variable on each unit) Set this to the same thing if you want several consoles in one department var/list/messages = list() //List of all messages var/departmentType = 0 diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index 746c17f225..02bb07b8ed 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -21,6 +21,7 @@ desc = null icon = 'icons/obj/status_display.dmi' icon_state = "frame" + plane = ABOVE_WALL_PLANE density = FALSE use_power = IDLE_POWER_USE idle_power_usage = 10 diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 02115d3e30..32be825489 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -23,11 +23,11 @@ layer = BELOW_MOB_LAYER//icon draw layer infra_luminosity = 15 //byond implementation is bugged. force = 5 - flags_1 = HEAR_1 + flags_1 = HEAR_1|BLOCK_FACE_ATOM_1 var/can_move = 0 //time of next allowed movement - var/mob/living/carbon/occupant = null + var/mob/living/occupant = null var/step_in = 10 //make a step in step_in/10 sec. - var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. + var/dir_in = SOUTH //What direction will the mech face when entered/powered on? Defaults to South. var/normal_step_energy_drain = 10 //How much energy the mech will consume each time it moves. This variable is a backup for when leg actuators affect the energy drain. var/step_energy_drain = 10 var/melee_energy_drain = 15 @@ -495,6 +495,10 @@ occupant_message("Air port connection teared off!") mecha_log_message("Lost connection to gas port.") +/obj/mecha/setDir(newdir) + . = ..() + occupant?.setDir(newdir) + /obj/mecha/Process_Spacemove(var/movement_dir = 0) . = ..() if(.) diff --git a/code/game/mecha/mecha_topic.dm b/code/game/mecha/mecha_topic.dm index 8d6328cf08..b1ab944b49 100644 --- a/code/game/mecha/mecha_topic.dm +++ b/code/game/mecha/mecha_topic.dm @@ -333,10 +333,13 @@ send_byjax(occupant,"exosuit.browser","t_port_connection","[internal_tank.connected_port?"Disconnect from":"Connect to"] gas port") if(href_list["dna_lock"]) - if(occupant && !iscarbon(occupant)) - to_chat(occupant, " You do not have any DNA!") + if(!occupant) return - dna_lock = occupant.dna.unique_enzymes + var/mob/living/carbon/C = occupant + if(!istype(C) || !C.dna) + to_chat(C, " You do not have any DNA!") + return + dna_lock = C.dna.unique_enzymes occupant_message("You feel a prick as the needle takes your DNA sample.") if(href_list["reset_dna"]) diff --git a/code/game/objects/effects/contraband.dm b/code/game/objects/effects/contraband.dm index 6b61eb2ebd..69aa3fb654 100644 --- a/code/game/objects/effects/contraband.dm +++ b/code/game/objects/effects/contraband.dm @@ -49,6 +49,7 @@ var/original_name desc = "A large piece of space-resistant printed paper." icon = 'icons/obj/contraband.dmi' + plane = ABOVE_WALL_PLANE anchored = TRUE var/ruined = FALSE var/random_basetype diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm index 7f6e561f55..0676c439f0 100644 --- a/code/game/objects/items/RCD.dm +++ b/code/game/objects/items/RCD.dm @@ -143,8 +143,8 @@ RLD //if user can't be seen from A (only checks surroundings' opaqueness) and can't see A. //jarring, but it should stop people from targetting atoms they can't see... //excluding darkness, to allow RLD to be used to light pitch black dark areas. - if(!((user in view(view_range, A)) || (user in viewers(view_range, A)))) - to_chat(user, "You focus, pointing \the [src] at whatever outside your field of vision in the given direction... to no avail.") + if(!((user in view(view_range, A)) || (user in get_actual_viewers(view_range, A)))) + to_chat(user, "You focus, pointing \the [src] at whatever outside your field of vision in that direction... to no avail.") return FALSE return TRUE diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 9c94348fc2..1d2a2c9ad1 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -135,7 +135,8 @@ outmsg = "You miss the lens of [C] with [src]!" //catpeople - for(var/mob/living/carbon/human/H in view(1,targloc)) + var/list/viewers = get_actual_viewers(1,targloc) + for(var/mob/living/carbon/human/H in viewers) if(!iscatperson(H) || H.incapacitated() || H.eye_blind ) continue if(!H.lying) @@ -150,7 +151,7 @@ H.visible_message("[H] stares at the light"," You stare at the light... ") //cats! - for(var/mob/living/simple_animal/pet/cat/C in view(1,targloc)) + for(var/mob/living/simple_animal/pet/cat/C in viewers) if(prob(50)) C.visible_message("[C] pounces on the light!","LIGHT!") C.Move(targloc) diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index ada598866b..f4c317c8ba 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -2,6 +2,7 @@ name = "station intercom" desc = "Talk through this." icon_state = "intercom" + plane = ABOVE_WALL_PLANE anchored = TRUE w_class = WEIGHT_CLASS_BULKY canhear_range = 2 diff --git a/code/game/objects/items/flamethrower.dm b/code/game/objects/items/flamethrower.dm index e2140bd0fd..c785c22813 100644 --- a/code/game/objects/items/flamethrower.dm +++ b/code/game/objects/items/flamethrower.dm @@ -196,9 +196,8 @@ sleep(1) previousturf = T operating = FALSE - for(var/mob/M in viewers(1, loc)) - if((M.client && M.machine == src)) - attack_self(M) + if(usr.machine == src) + attack_self(usr) /obj/item/flamethrower/proc/default_ignite(turf/target, release_amount = 0.05) diff --git a/code/game/objects/items/implants/implantpad.dm b/code/game/objects/items/implants/implantpad.dm index 7c863017a1..f0fc76a2e2 100644 --- a/code/game/objects/items/implants/implantpad.dm +++ b/code/game/objects/items/implants/implantpad.dm @@ -66,7 +66,7 @@ if(ismob(loc)) attack_self(loc) else - for(var/mob/M in viewers(1, src)) + for(var/mob/M in get_actual_viewers(1, src)) if(M.client) attack_self(M) add_fingerprint(usr) diff --git a/code/game/objects/items/storage/secure.dm b/code/game/objects/items/storage/secure.dm index e177a9f9fd..ef70bd201d 100644 --- a/code/game/objects/items/storage/secure.dm +++ b/code/game/objects/items/storage/secure.dm @@ -105,10 +105,7 @@ if (length(code) > 5) code = "ERROR" add_fingerprint(usr) - for(var/mob/M in viewers(1, loc)) - if ((M.client && M.machine == src)) - attack_self(M) - return + attack_self(usr) return @@ -158,6 +155,7 @@ /obj/item/storage/secure/safe name = "secure safe" icon = 'icons/obj/storage.dmi' + plane = ABOVE_WALL_PLANE icon_state = "safe" icon_opened = "safe0" icon_locking = "safeb" diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm index b5edb27704..d3272616b3 100644 --- a/code/game/objects/items/teleportation.dm +++ b/code/game/objects/items/teleportation.dm @@ -104,7 +104,7 @@ if (ismob(src.loc)) attack_self(src.loc) else - for(var/mob/M in viewers(1, src)) + for(var/mob/M in get_actual_viewers(1, src)) if (M.client) src.attack_self(M) return diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 199f0c51e2..6916e7c24a 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -123,7 +123,7 @@ /obj/proc/updateUsrDialog() if((obj_flags & IN_USE) && !(obj_flags & USES_TGUI)) var/is_in_use = FALSE - var/list/nearby = viewers(1, src) + var/list/nearby = get_actual_viewers(1, src) for(var/mob/M in nearby) if ((M.client && M.machine == src)) is_in_use = TRUE @@ -152,7 +152,7 @@ if(obj_flags & IN_USE) var/is_in_use = FALSE if(update_viewers) - for(var/mob/M in viewers(1, src)) + for(var/mob/M in get_actual_viewers(1, src)) if ((M.client && M.machine == src)) is_in_use = TRUE src.interact(M) diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm index ae2e1a070a..6256de247a 100644 --- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm +++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm @@ -24,6 +24,7 @@ move_delay = TRUE var/oldloc = loc step(src, direction) + user.setDir(direction) if(oldloc != loc) addtimer(CALLBACK(src, .proc/ResetMoveDelay), (use_mob_movespeed ? user.movement_delay() : CONFIG_GET(number/movedelay/walk_delay)) * move_speed_multiplier) else @@ -42,7 +43,7 @@ Snake = L break if(Snake) - alerted = viewers(7,src) + alerted = get_actual_viewers(world.view,src) ..() if(LAZYLEN(alerted)) egged = world.time + SNAKE_SPAM_TICKS diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index 9b736517be..3bc84deb4d 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -3,6 +3,7 @@ desc = "A small wall mounted cabinet designed to hold a fire extinguisher." icon = 'icons/obj/wallmounts.dmi' icon_state = "extinguisher_closed" + plane = ABOVE_WALL_PLANE anchored = TRUE density = FALSE max_integrity = 200 diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm index e44698f96e..679a755321 100644 --- a/code/game/objects/structures/false_walls.dm +++ b/code/game/objects/structures/false_walls.dm @@ -7,6 +7,7 @@ anchored = TRUE icon = 'icons/turf/walls/wall.dmi' icon_state = "wall" + plane = WALL_PLANE layer = LOW_OBJ_LAYER density = TRUE opacity = 1 diff --git a/code/game/objects/structures/fireaxe.dm b/code/game/objects/structures/fireaxe.dm index 0f3a23dd0f..f4c1dd5ab9 100644 --- a/code/game/objects/structures/fireaxe.dm +++ b/code/game/objects/structures/fireaxe.dm @@ -3,6 +3,7 @@ desc = "There is a small label that reads \"For Emergency use only\" along with details for safe use of the axe. As if." icon = 'icons/obj/wallmounts.dmi' icon_state = "fireaxe" + plane = ABOVE_WALL_PLANE anchored = TRUE density = FALSE armor = list("melee" = 50, "bullet" = 20, "laser" = 0, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 50) diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm index 611a6d024d..b8137d1332 100644 --- a/code/game/objects/structures/guillotine.dm +++ b/code/game/objects/structures/guillotine.dm @@ -130,7 +130,7 @@ // The crowd is pleased // The delay is to making large crowds have a longer laster applause var/delay_offset = 0 - for(var/mob/M in viewers(src, 7)) + for(var/mob/M in get_actual_viewers(world.view, src)) var/mob/living/carbon/human/C = M if (ishuman(M)) addtimer(CALLBACK(C, /mob/.proc/emote, "clap"), delay_offset * 0.3) diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index e32def727a..65e1b7dd9a 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -4,6 +4,7 @@ desc = "Mirror mirror on the wall, who's the most robust of them all?" icon = 'icons/obj/watercloset.dmi' icon_state = "mirror" + plane = ABOVE_WALL_PLANE density = FALSE anchored = TRUE max_integrity = 200 diff --git a/code/game/objects/structures/noticeboard.dm b/code/game/objects/structures/noticeboard.dm index f4bca1f900..68da53aa6c 100644 --- a/code/game/objects/structures/noticeboard.dm +++ b/code/game/objects/structures/noticeboard.dm @@ -3,6 +3,7 @@ desc = "A board for pinning important notices upon." icon = 'icons/obj/stationobjs.dmi' icon_state = "nboard00" + plane = ABOVE_WALL_PLANE density = FALSE anchored = TRUE max_integrity = 150 diff --git a/code/game/objects/structures/signs/_signs.dm b/code/game/objects/structures/signs/_signs.dm index 84add2f65f..a1de5fe6a3 100644 --- a/code/game/objects/structures/signs/_signs.dm +++ b/code/game/objects/structures/signs/_signs.dm @@ -3,6 +3,7 @@ anchored = TRUE opacity = 0 density = FALSE + plane = ABOVE_WALL_PLANE layer = SIGN_LAYER max_integrity = 100 armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm index 99c7ed8231..c7aefbe0a6 100644 --- a/code/game/turfs/closed.dm +++ b/code/game/turfs/closed.dm @@ -1,5 +1,6 @@ /turf/closed layer = CLOSED_TURF_LAYER + plane = WALL_PLANE opacity = 1 density = TRUE blocks_air = 1 diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm index e79784290b..26ae200990 100644 --- a/code/modules/VR/vr_sleeper.dm +++ b/code/modules/VR/vr_sleeper.dm @@ -22,6 +22,11 @@ sparks.set_up(2,0) sparks.attach(src) update_icon() + new_occupant_dir = dir + +/obj/machinery/vr_sleeper/setDir(newdir) + . = ..() + new_occupant_dir = dir /obj/machinery/vr_sleeper/attackby(obj/item/I, mob/user, params) if(!state_open && !occupant) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index e9b8c274a8..289abbe250 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -106,6 +106,7 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list( /client/proc/show_tip, /client/proc/smite, /client/proc/admin_away, + /client/proc/cmd_admin_toggle_fov, /client/proc/roll_dices //CIT CHANGE - Adds dice verb )) GLOBAL_PROTECT(admin_verbs_fun) diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 5530225130..d335cfb171 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1218,6 +1218,58 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggled Hub Visibility", "[GLOB.hub_visibility ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/proc/cmd_admin_toggle_fov() + set category = "Fun" + set name = "Enable/Disable Field of Vision" + + var/static/busy_toggling_fov = FALSE + if(!check_rights(R_ADMIN) || !check_rights(R_FUN)) + return + + var/on_off = CONFIG_GET(flag/use_field_of_vision) + + if(busy_toggling_fov) + to_chat(usr, "A previous call of this function is still busy toggling FoV [on_off ? "on" : "off"]. Have some patiece.") + return + busy_toggling_fov = TRUE + + log_admin("[key_name(usr)] has [on_off ? "disabled" : "enabled"] the Field of Vision configuration.") + CONFIG_SET(flag/use_field_of_vision, !on_off) + + SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggled Field of Vision", "[on_off ? "Enabled" : "Disabled"]")) + + if(on_off) + for(var/k in GLOB.mob_list) + if(!k) + continue + var/mob/M = k + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(!(H.dna?.species.has_field_of_vision)) + continue + else if(!M.has_field_of_vision) + continue + var/datum/component/field_of_vision/FoV = M.GetComponent(/datum/component/field_of_vision) + if(FoV) + qdel(FoV) + CHECK_TICK + else + for(var/k in GLOB.clients) + if(!k) + continue + var/client/C = k + var/mob/M = C.mob + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(!(H.dna?.species.has_field_of_vision)) + continue + else if(!M.has_field_of_vision) + continue + M.LoadComponent(/datum/component/field_of_vision, M.field_of_vision_type) + CHECK_TICK + + busy_toggling_fov = FALSE + /client/proc/smite(mob/living/carbon/human/target as mob) set name = "Smite" set category = "Fun" diff --git a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm index 7998a33c7b..b89b681539 100644 --- a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm +++ b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm @@ -328,8 +328,8 @@ // to_chat(user, "The ritual has been interrupted!") // useLock = FALSE // return - user.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE) - target.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE) + user.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE) + target.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE) target.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, TRUE) target.Jitter(25) target.emote("laugh") @@ -490,13 +490,14 @@ update_icon() /obj/structure/bloodsucker/candelabrum/process() - if(lit) - for(var/mob/living/carbon/human/H in viewers(7, src)) - var/datum/antagonist/vassal/T = H.mind.has_antag_datum(ANTAG_DATUM_VASSAL) - if(AmBloodsucker(H) || T) //We dont want vassals or vampires affected by this - return - H.hallucination = 20 - SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "vampcandle", /datum/mood_event/vampcandle) + if(!lit) + return + for(var/mob/living/carbon/human/H in get_actual_viewers(7, src)) + var/datum/antagonist/vassal/T = H.mind.has_antag_datum(ANTAG_DATUM_VASSAL) + if(AmBloodsucker(H) || T) //We dont want vassals or vampires affected by this + return + H.hallucination = 20 + SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "vampcandle", /datum/mood_event/vampcandle) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // OTHER THINGS TO USE: HUMAN BLOOD. /obj/effect/decal/cleanable/blood diff --git a/code/modules/antagonists/bloodsucker/powers/cloak.dm b/code/modules/antagonists/bloodsucker/powers/cloak.dm index 347700ca9a..f8f2bb4e34 100644 --- a/code/modules/antagonists/bloodsucker/powers/cloak.dm +++ b/code/modules/antagonists/bloodsucker/powers/cloak.dm @@ -19,10 +19,9 @@ if(!.) return // must have nobody around to see the cloak - for(var/mob/living/M in viewers(9, owner)) - if(M != owner) - to_chat(owner, "You may only vanish into the shadows unseen.") - return FALSE + for(var/mob/living/M in get_actual_viewers(9, owner) - owner) + to_chat(owner, "You may only vanish into the shadows unseen.") + return FALSE return TRUE /datum/action/bloodsucker/cloak/ActivatePower() @@ -35,7 +34,7 @@ // Pay Blood Toll (if awake) owner.alpha = max(35, owner.alpha - min(75, 10 + 5 * level_current)) bloodsuckerdatum.AddBloodVolume(-0.2) - + runintent = (user.m_intent == MOVE_INTENT_RUN) var/turf/T = get_turf(user) lum = T.get_lumcount() @@ -50,7 +49,7 @@ if(!runintent) user.toggle_move_intent() REMOVE_TRAIT(user, TRAIT_NORUNNING, "cloak of darkness") - + sleep(5) // Check every few ticks /datum/action/bloodsucker/cloak/ContinueActive(mob/living/user, mob/living/target) diff --git a/code/modules/antagonists/bloodsucker/powers/feed.dm b/code/modules/antagonists/bloodsucker/powers/feed.dm index 8ac4fcebc1..da53b5cc81 100644 --- a/code/modules/antagonists/bloodsucker/powers/feed.dm +++ b/code/modules/antagonists/bloodsucker/powers/feed.dm @@ -169,8 +169,8 @@ vision_distance = notice_range, ignored_mobs = target) // Only people who AREN'T the target will notice this action. // Warn Feeder about Witnesses... var/was_unnoticed = TRUE - for(var/mob/living/M in viewers(notice_range, owner)) - if(M != owner && M != target && iscarbon(M) && M.mind && !M.silicon_privileges && !M.eye_blind && !M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)) + for(var/mob/living/M in get_actual_viewers(notice_range, owner) - owner - target) + if(M.client && !M.silicon_privileges && !M.eye_blind && !M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)) was_unnoticed = FALSE break if(was_unnoticed) diff --git a/code/modules/antagonists/bloodsucker/powers/go_home.dm b/code/modules/antagonists/bloodsucker/powers/go_home.dm index 4788d7639e..4892530d63 100644 --- a/code/modules/antagonists/bloodsucker/powers/go_home.dm +++ b/code/modules/antagonists/bloodsucker/powers/go_home.dm @@ -64,8 +64,8 @@ var/turf/T = get_turf(user) if(T && T.lighting_object && T.get_lumcount()>= 0.1) // B) Check for Viewers - for(var/mob/living/M in viewers(get_turf(owner))) - if(M != owner && isliving(M) && M.mind && !M.silicon_privileges && !M.eye_blind) // M.client <--- add this in after testing! + for(var/mob/living/M in get_actual_viewers(world.view, get_turf(owner)) - owner) + if(M.client && !M.silicon_privileges && !M.eye_blind) am_seen = TRUE if (!M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)) drop_item = TRUE diff --git a/code/modules/antagonists/bloodsucker/powers/mesmerize.dm b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm index 814ad2abd7..04d3be439f 100644 --- a/code/modules/antagonists/bloodsucker/powers/mesmerize.dm +++ b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm @@ -64,7 +64,7 @@ to_chat(owner, "Your victim's eyes are glazed over. They cannot perceive you.") return FALSE // Check: Target See Me? (behind wall) - if(!(target in viewers(target_range, get_turf(owner)))) + if(!(owner in target.visible_atoms())) // Sub-Check: GET CLOSER //if (!(owner in range(target_range, get_turf(target))) // if (display_error) @@ -137,7 +137,7 @@ if(istype(target) && success) target.notransform = FALSE REMOVE_TRAIT(target, TRAIT_COMBAT_MODE_LOCKED, src) - if(istype(L) && target.stat == CONSCIOUS && (target in view(10, get_turf(L)))) // They Woke Up! (Notice if within view) + if(istype(L) && target.stat == CONSCIOUS && (target in L.visible_atoms(10))) // They Woke Up! (Notice if within view) to_chat(L, "[target] has snapped out of their trance.") diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index 2638a6a8e3..5ee673dddc 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -27,6 +27,7 @@ sight = SEE_SELF throwforce = 0 blood_volume = 0 + has_field_of_vision = FALSE //we are a spoopy ghost see_in_dark = 8 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm index 21eed2caf4..733c760855 100644 --- a/code/modules/atmospherics/machinery/airalarm.dm +++ b/code/modules/atmospherics/machinery/airalarm.dm @@ -67,6 +67,7 @@ desc = "A machine that monitors atmosphere levels. Goes off if the area is dangerous." icon = 'icons/obj/monitors.dmi' icon_state = "alarm0" + plane = ABOVE_WALL_PLANE use_power = IDLE_POWER_USE idle_power_usage = 4 active_power_usage = 8 diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 989ccaf450..ab1db47db6 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -902,23 +902,27 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) new_size = "15x15" //END OF CIT CHANGES + var/list/old_view = getviewsize(view) view = new_size - apply_clickcatcher() + var/list/actualview = getviewsize(view) + apply_clickcatcher(actualview) mob.reload_fullscreen() if (isliving(mob)) var/mob/living/M = mob M.update_damage_hud() if (prefs.auto_fit_viewport) fit_viewport() + SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_CHANGE_VIEW, src, old_view, actualview) /client/proc/generate_clickcatcher() if(!void) void = new() screen += void -/client/proc/apply_clickcatcher() +/client/proc/apply_clickcatcher(list/actualview) generate_clickcatcher() - var/list/actualview = getviewsize(view) + if(!actualview) + actualview = getviewsize(view) void.UpdateGreed(actualview[1],actualview[2]) /client/proc/AnnouncePR(announcement) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index bb105f2652..100dbc66ae 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -2385,8 +2385,10 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("ambientocclusion") ambientocclusion = !ambientocclusion if(parent && parent.screen && parent.screen.len) - var/obj/screen/plane_master/game_world/PM = locate(/obj/screen/plane_master/game_world) in parent.screen + var/obj/screen/plane_master/game_world/PM = parent.mob.hud_used.plane_masters["[GAME_PLANE]"] + var/obj/screen/plane_master/wall/W = parent.mob.hud_used.plane_masters["[WALL_PLANE]"] PM.backdrop(parent.mob) + W.backdrop(parent.mob) if("auto_fit_viewport") auto_fit_viewport = !auto_fit_viewport diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 20b6034cd4..d5aa49072d 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -915,8 +915,8 @@ /obj/item/clothing/head/helmet/space/hardsuit/lavaknight/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE) . = ..() if(!isinhands) - var/mutable_appearance/energy_overlay = mutable_appearance(icon_file, "knight_cydonia_overlay", ABOVE_LIGHTING_LAYER) - energy_overlay.plane = ABOVE_LIGHTING_LAYER + var/mutable_appearance/energy_overlay = mutable_appearance(icon_file, "knight_cydonia_overlay", EMISSIVE_LAYER) + energy_overlay.plane = EMISSIVE_PLANE energy_overlay.color = energy_color . += energy_overlay @@ -946,8 +946,8 @@ /obj/item/clothing/suit/space/hardsuit/lavaknight/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE) . = ..() if(!isinhands) - var/mutable_appearance/energy_overlay = mutable_appearance(icon_file, "knight_cydonia_overlay", ABOVE_LIGHTING_LAYER) - energy_overlay.plane = ABOVE_LIGHTING_LAYER + var/mutable_appearance/energy_overlay = mutable_appearance(icon_file, "knight_cydonia_overlay", EMISSIVE_LAYER) + energy_overlay.plane = EMISSIVE_PLANE energy_overlay.color = energy_color . += energy_overlay diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 0c2cbc0c67..f361a72d17 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -679,9 +679,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( var/list/mob/living/carbon/people = list() var/mob/living/carbon/person = null var/datum/language/understood_language = target.get_random_understood_language() - for(var/mob/living/carbon/H in view(target)) - if(H == target) - continue + for(var/mob/living/carbon/H in view(target) - target) if(!person) person = H else @@ -1064,6 +1062,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( qdel(src) /obj/effect/hallucination/danger + layer = TURF_LAYER + plane = FLOOR_PLANE var/image/image /obj/effect/hallucination/danger/proc/show_icon() @@ -1087,7 +1087,8 @@ GLOBAL_LIST_INIT(hallucination_list, list( name = "lava" /obj/effect/hallucination/danger/lava/show_icon() - image = image('icons/turf/floors/lava.dmi',src,"smooth",TURF_LAYER) + image = image('icons/turf/floors/lava.dmi',src,"smooth",layer) + image.plane = plane if(target.client) target.client.images += image @@ -1257,7 +1258,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( ..() if(!target.halbody) var/list/possible_points = list() - for(var/turf/open/floor/F in view(target,world.view)) + for(var/turf/open/floor/F in target.visible_atoms(world.view)) possible_points += F if(possible_points.len) var/turf/open/floor/husk_point = pick(possible_points) @@ -1288,7 +1289,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( set waitfor = FALSE ..() var/list/turf/startlocs = list() - for(var/turf/open/T in view(world.view+1,target)-view(world.view,target)) + for(var/turf/open/T in target.visible_atoms(world.view+1)-view(world.view,target)) startlocs += T if(!startlocs.len) qdel(src) diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm index ef63ba97f8..384991e976 100644 --- a/code/modules/integrated_electronics/core/assemblies.dm +++ b/code/modules/integrated_electronics/core/assemblies.dm @@ -873,3 +873,8 @@ pixel_x = -31 if(WEST) pixel_x = 31 + plane = ABOVE_WALL_PLANE + +/obj/item/electronic_assembly/wallmount/Moved(atom/OldLoc, Dir, Forced = FALSE) //reset the plane if moved off the wall. + . = ..() + plane = GAME_PLANE diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index b8e56f3d7c..942a99abd9 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -707,7 +707,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp //this is a mob verb instead of atom for performance reasons //see /mob/verb/examinate() in mob.dm for more info //overridden here and in /mob/living for different point span classes and sanity checks -/mob/dead/observer/pointed(atom/A as mob|obj|turf in view()) +/mob/dead/observer/pointed(atom/A as mob|obj|turf in visible_atoms()) if(!..()) return 0 usr.visible_message("[src] points to [A].") diff --git a/code/modules/mob/living/bloodcrawl.dm b/code/modules/mob/living/bloodcrawl.dm index e8a9b50e98..3547d5f846 100644 --- a/code/modules/mob/living/bloodcrawl.dm +++ b/code/modules/mob/living/bloodcrawl.dm @@ -164,7 +164,7 @@ if(!B) return forceMove(B.loc) - src.client.eye = src + reset_perspective(src) src.visible_message("[src] rises out of the pool of blood!") exit_blood_effect(B) if(iscarbon(src)) diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm index 4bca3e7f62..08d415fc3c 100644 --- a/code/modules/mob/living/brain/brain.dm +++ b/code/modules/mob/living/brain/brain.dm @@ -4,6 +4,7 @@ var/emp_damage = 0//Handles a type of MMI damage var/datum/dna/stored/stored_dna // dna var for brain. Used to store dna, brain dna is not considered like actual dna, brain.has_dna() returns FALSE. stat = DEAD //we start dead by default + has_field_of_vision = FALSE //Not really worth it. see_invisible = SEE_INVISIBLE_LIVING possible_a_intents = list(INTENT_HELP, INTENT_HARM) //for mechas speech_span = SPAN_ROBOT diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index e0eec0e599..e7be540eb9 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -9,6 +9,7 @@ /// Enable stamina combat combat_flags = COMBAT_FLAGS_DEFAULT status_flags = CANSTUN|CANKNOCKDOWN|CANUNCONSCIOUS|CANPUSH|CANSTAGGER + has_field_of_vision = FALSE //Handled by species. blocks_emissive = EMISSIVE_BLOCK_UNIQUE diff --git a/code/modules/mob/living/carbon/human/login.dm b/code/modules/mob/living/carbon/human/login.dm new file mode 100644 index 0000000000..a89921143a --- /dev/null +++ b/code/modules/mob/living/carbon/human/login.dm @@ -0,0 +1,4 @@ +/mob/living/carbon/human/Login() + ..() + if(dna?.species?.has_field_of_vision && CONFIG_GET(flag/use_field_of_vision)) + LoadComponent(/datum/component/field_of_vision, field_of_vision_type) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index c3f43cd6f0..65d09f6cf2 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -10,6 +10,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/default_color = "#FFF" // if alien colors are disabled, this is the color that will be used by that race var/sexes = 1 // whether or not the race has sexual characteristics. at the moment this is only 0 for skeletons and shadows + var/has_field_of_vision = TRUE //Species Icon Drawing Offsets - Pixel X, Pixel Y, Aka X = Horizontal and Y = Vertical, from bottom left corner var/list/offset_features = list( @@ -331,6 +332,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if(mutant_bodyparts["meat_type"]) //I can't believe it's come to the meat H.type_of_meat = GLOB.meat_types[H.dna.features["meat_type"]] + if(H.client && has_field_of_vision && CONFIG_GET(flag/use_field_of_vision)) + H.LoadComponent(/datum/component/field_of_vision, H.field_of_vision_type) + C.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/species, TRUE, multiplicative_slowdown = speedmod) SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species) @@ -364,6 +368,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) C.dna.mutation_index[location] = new_species.inert_mutation C.dna.mutation_index[new_species.inert_mutation] = create_sequence(new_species.inert_mutation) + if(!new_species.has_field_of_vision && has_field_of_vision && ishuman(C) && CONFIG_GET(flag/use_field_of_vision)) + var/datum/component/field_of_vision/F = GetComponent(/datum/component/field_of_vision) + if(F) + qdel(F) + SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src) /datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour) @@ -1544,9 +1553,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) else if(aim_for_mouth && ( target_on_help || target_restrained || target_aiming_for_mouth)) playsound(target.loc, 'sound/weapons/slap.ogg', 50, 1, -1) - user.visible_message(\ - "\The [user] slaps \the [target] in the face!",\ - "You slap [user == target ? "yourself" : "\the [target]"] in the face! ",\ + target.visible_message(\ + "\The [user] slaps \the [target] in the face!",\ + "You [user == target ? "slap yourself" : "are slapped by \the [target]"] in the face! ",\ "You hear a slap." ) user.do_attack_animation(target, ATTACK_EFFECT_FACE_SLAP) @@ -1566,9 +1575,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) if (!HAS_TRAIT(target, TRAIT_PERMABONER)) stop_wagging_tail(target) playsound(target.loc, 'sound/weapons/slap.ogg', 50, 1, -1) - user.visible_message(\ + target.visible_message(\ "\The [user] slaps \the [target]'s ass!",\ - "You slap [user == target ? "your" : "\the [target]'s"] ass!",\ + "You slap [user == target ? "slap your" : "are slapped by \the [target]'s in the"] ass!",\ "You hear a slap." ) return FALSE @@ -1864,19 +1873,19 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/targetatrest = !CHECK_MOBILITY(target, MOBILITY_STAND) if((directional_blocked || !(target_collateral_human || target_shove_turf.shove_act(target, user))) && !targetatrest) target.DefaultCombatKnockdown(SHOVE_KNOCKDOWN_SOLID) - user.visible_message("[user.name] shoves [target.name], knocking them down!", - "You shove [target.name], knocking them down!", null, COMBAT_MESSAGE_RANGE) + target.visible_message("[user.name] shoves [target.name], knocking them down!", + "You are shoved by [user.name] and knocked down!", null, COMBAT_MESSAGE_RANGE) log_combat(user, target, "shoved", "knocking them down") else if(target_collateral_human && !targetatrest) target.DefaultCombatKnockdown(SHOVE_KNOCKDOWN_HUMAN) target_collateral_human.DefaultCombatKnockdown(SHOVE_KNOCKDOWN_COLLATERAL) - user.visible_message("[user.name] shoves [target.name] into [target_collateral_human.name]!", - "You shove [target.name] into [target_collateral_human.name]!", null, COMBAT_MESSAGE_RANGE) + target.visible_message("[user.name] shoves [target.name] into [target_collateral_human.name]!", + "You are shoved by [user.name] into [target_collateral_human.name]!", null, COMBAT_MESSAGE_RANGE) append_message += ", into [target_collateral_human.name]" else - user.visible_message("[user.name] shoves [target.name]!", - "You shove [target.name]!", null, COMBAT_MESSAGE_RANGE) + target.visible_message("[user.name] shoves [target.name]!", + "You are shoved by [user.name]!", null, COMBAT_MESSAGE_RANGE) var/obj/item/target_held_item = target.get_active_held_item() if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types)) target_held_item = null diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index ef730da219..1d19f28aa8 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -13,6 +13,7 @@ blacklisted = TRUE limbs_id = "human" skinned_type = /obj/item/stack/sheet/animalhide/human + has_field_of_vision = FALSE //Too much of a trouble, their vision is already bound to their severed head. var/pumpkin = FALSE var/obj/item/dullahan_relay/myhead @@ -132,13 +133,13 @@ return INITIALIZE_HINT_QDEL owner = new_owner START_PROCESSING(SSobj, src) - RegisterSignal(owner, COMSIG_CLICK_SHIFT, .proc/examinate_check) + RegisterSignal(owner, COMSIG_MOB_CLICKED_SHIFT_ON, .proc/examinate_check) RegisterSignal(src, COMSIG_ATOM_HEARER_IN_VIEW, .proc/include_owner) RegisterSignal(owner, COMSIG_LIVING_REGENERATE_LIMBS, .proc/unlist_head) RegisterSignal(owner, COMSIG_LIVING_REVIVE, .proc/retrieve_head) -/obj/item/dullahan_relay/proc/examinate_check(atom/source, mob/user) - if(user.client.eye == src) +/obj/item/dullahan_relay/proc/examinate_check(mob/source, atom/target) + if(source.client.eye == src) return COMPONENT_ALLOW_EXAMINATE /obj/item/dullahan_relay/proc/include_owner(datum/source, list/processing_list, list/hearers) diff --git a/code/modules/mob/living/carbon/human/species_types/dwarves.dm b/code/modules/mob/living/carbon/human/species_types/dwarves.dm index 5d9269b2f0..e3beafa18a 100644 --- a/code/modules/mob/living/carbon/human/species_types/dwarves.dm +++ b/code/modules/mob/living/carbon/human/species_types/dwarves.dm @@ -121,7 +121,7 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) // return //Filth Reactions - Since miasma now exists var/filth_counter = 0 //Holder for the filth check cycle, basically contains how much filth dwarf sees numerically. - for(var/fuck in view(owner,7)) //hello byond for view loop. + for(var/fuck in owner.visible_atoms(7)) //hello byond for view loop. if(istype(fuck, /mob/living/carbon/human)) var/mob/living/carbon/human/H = fuck if(H.stat == DEAD || (HAS_TRAIT(H, TRAIT_FAKEDEATH))) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index ced9fa3200..c36568ce37 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -379,7 +379,7 @@ stop_pulling() //same as above -/mob/living/pointed(atom/A as mob|obj|turf in view()) +/mob/living/pointed(atom/A as mob|obj|turf in visible_atoms()) if(incapacitated()) return FALSE if(HAS_TRAIT(src, TRAIT_DEATHCOMA)) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 1c92ffe9a5..f8abf94083 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -4,6 +4,7 @@ see_in_dark = 2 hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD,RAD_HUD) pressure_resistance = 10 + has_field_of_vision = TRUE typing_indicator_enabled = TRUE diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index dff5584617..4e3093dda2 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -28,6 +28,7 @@ sec_hud = DATA_HUD_SECURITY_BASIC d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED mob_size = MOB_SIZE_LARGE + has_field_of_vision = FALSE //Vision through cameras. var/list/network = list("ss13") var/obj/machinery/camera/current var/list/connected_robots = list() @@ -868,30 +869,33 @@ light_cameras() if(istype(A, /obj/machinery/camera)) current = A - if(client) - if(ismovable(A)) - if(A != GLOB.ai_camera_room_landmark) - end_multicam() - client.perspective = EYE_PERSPECTIVE - client.eye = A - else + if(!client) + return + if(ismovable(A)) + if(A != GLOB.ai_camera_room_landmark) end_multicam() - if(isturf(loc)) - if(eyeobj) - client.eye = eyeobj - client.perspective = EYE_PERSPECTIVE - else - client.eye = client.mob - client.perspective = MOB_PERSPECTIVE - else + client.perspective = EYE_PERSPECTIVE + client.eye = A + else + end_multicam() + if(isturf(loc)) + if(eyeobj) + client.eye = eyeobj client.perspective = EYE_PERSPECTIVE - client.eye = loc - update_sight() - if(client.eye != src) - var/atom/AT = client.eye - AT.get_remote_view_fullscreens(src) + else + client.eye = client.mob + client.perspective = MOB_PERSPECTIVE else - clear_fullscreen("remote_view", 0) + client.perspective = EYE_PERSPECTIVE + client.eye = loc + update_sight() + if(client.eye != src) + var/atom/AT = client.eye + AT.get_remote_view_fullscreens(src) + else + clear_fullscreen("remote_view", 0) + SEND_SIGNAL(src, COMSIG_MOB_RESET_PERSPECTIVE, A) + return TRUE /mob/living/silicon/ai/revive(full_heal = 0, admin_revive = 0) . = ..() diff --git a/code/modules/mob/living/silicon/robot/update_icons.dm b/code/modules/mob/living/silicon/robot/update_icons.dm index 72ad21c51e..a567446e9e 100644 --- a/code/modules/mob/living/silicon/robot/update_icons.dm +++ b/code/modules/mob/living/silicon/robot/update_icons.dm @@ -57,3 +57,5 @@ cut_overlays() else icon_state = "[module.cyborg_base_icon]" + + SEND_SIGNAL(src, COMSIG_ROBOT_UPDATE_ICONS) diff --git a/code/modules/mob/living/simple_animal/astral.dm b/code/modules/mob/living/simple_animal/astral.dm index ebc89e2577..8e10376265 100644 --- a/code/modules/mob/living/simple_animal/astral.dm +++ b/code/modules/mob/living/simple_animal/astral.dm @@ -5,6 +5,7 @@ icon_state = "ghost" icon_living = "ghost" mob_biotypes = MOB_SPIRIT + has_field_of_vision = FALSE //we are a spoopy ghost attacktext = "raises the hairs on the neck of" response_harm = "disrupts the concentration of" response_disarm = "wafts" diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index e4e81bb5db..d3d5a65eb2 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -418,7 +418,7 @@ else ..() -/mob/living/simple_animal/bot/medbot/examinate(atom/A as mob|obj|turf in view()) +/mob/living/simple_animal/bot/medbot/examinate(atom/A as mob|obj|turf in visible_atoms()) ..() if(!is_blind(src)) chemscan(src, A) diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm index 2045e194d2..8aec10ec48 100644 --- a/code/modules/mob/living/simple_animal/hostile/alien.dm +++ b/code/modules/mob/living/simple_animal/hostile/alien.dm @@ -80,6 +80,7 @@ icon_state = "alienq" icon_living = "alienq" icon_dead = "alienq_dead" + pixel_x = -16 threat = 8 health = 250 maxHealth = 250 diff --git a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm index dbb957a96b..b1a4f05e8c 100644 --- a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm +++ b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm @@ -7,6 +7,7 @@ status_flags = 0 a_intent = INTENT_HARM gender = NEUTER + has_field_of_vision = FALSE //You are a frikkin boss var/list/boss_abilities = list() //list of /datum/action/boss var/datum/boss_active_timed_battle/atb var/point_regen_delay = 1 diff --git a/code/modules/mob/living/simple_animal/hostile/eyeballs.dm b/code/modules/mob/living/simple_animal/hostile/eyeballs.dm index dbb9048ca4..19d4dad6e8 100644 --- a/code/modules/mob/living/simple_animal/hostile/eyeballs.dm +++ b/code/modules/mob/living/simple_animal/hostile/eyeballs.dm @@ -27,3 +27,4 @@ faction = list("spooky") del_on_death = 1 + field_of_vision_type = FOV_270_DEGREES //Obviously, it's one eyeball. diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index f19aa0a2a7..b556cce8fe 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -49,6 +49,7 @@ gold_core_spawnable = HOSTILE_SPAWN see_in_dark = 4 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + has_field_of_vision = FALSE // 360° vision. var/playable_spider = FALSE var/datum/action/innate/spider/lay_web/lay_web var/directive = "" //Message passed down to children, to relay the creator's orders diff --git a/code/modules/mob/living/simple_animal/hostile/illusion.dm b/code/modules/mob/living/simple_animal/hostile/illusion.dm index 89303702f3..6936a18cb9 100644 --- a/code/modules/mob/living/simple_animal/hostile/illusion.dm +++ b/code/modules/mob/living/simple_animal/hostile/illusion.dm @@ -20,6 +20,7 @@ var/multiply_chance = 0 //if we multiply on hit del_on_death = 1 deathmessage = "vanishes into thin air! It was a fake!" + has_field_of_vision = FALSE //not meant to be played anyway. /mob/living/simple_animal/hostile/illusion/Life() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index d2680fbf61..1e63767649 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -232,6 +232,7 @@ Difficulty: Hard density = FALSE faction = list("mining", "boss") weather_immunities = list("lava","ash") + has_field_of_vision = FALSE /mob/living/simple_animal/hostile/asteroid/hivelordbrood/slaughter/CanPass(atom/movable/mover, turf/target) if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum)) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index 78987ebaa0..c406baf2a6 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -27,6 +27,7 @@ mob_size = MOB_SIZE_LARGE layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1 + has_field_of_vision = FALSE //You are a frikkin boss /// Crusher loot dropped when fauna killed with a crusher var/list/crusher_loot var/medal_type diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm index 4d20d4c7fb..2810bf6c17 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm @@ -82,6 +82,7 @@ crusher_loot = /obj/item/crusher_trophy/watcher_wing loot = list() butcher_results = list(/obj/item/stack/ore/diamond = 2, /obj/item/stack/sheet/sinew = 2, /obj/item/stack/sheet/bone = 1) + field_of_vision_type = FOV_270_DEGREES //Obviously, it's one eyeball. /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random/Initialize() . = ..() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm index 2114612fce..81b541dc7b 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm @@ -21,6 +21,7 @@ layer = LARGE_MOB_LAYER sentience_type = SENTIENCE_BOSS hud_type = /datum/hud/lavaland_elite + has_field_of_vision = FALSE //You are a frikkin mini-boss var/chosen_attack = 1 var/list/attack_action_types = list() var/can_talk = FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/ghost.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/ghost.dm index 48d16f63f3..7d1f08201f 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/ghost.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/ghost.dm @@ -32,6 +32,7 @@ pressure_resistance = 300 gold_core_spawnable = NO_SPAWN //too spooky for science blood_volume = 0 + has_field_of_vision = FALSE var/ghost_hair_style var/ghost_hair_color var/mutable_appearance/ghost_hair diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm index 29b0c0b701..16358f736b 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm @@ -23,6 +23,7 @@ obj_damage = 0 environment_smash = ENVIRONMENT_SMASH_NONE del_on_death = 0 + has_field_of_vision = FALSE //Legacy. Also they only have one dir visually. do_footstep = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm index 8c622c7f29..d693c96c8f 100644 --- a/code/modules/mob/living/simple_animal/hostile/statue.dm +++ b/code/modules/mob/living/simple_animal/hostile/statue.dm @@ -125,14 +125,10 @@ // This loop will, at most, loop twice. for(var/atom/check in check_list) - for(var/mob/living/M in viewers(world.view + 1, check) - src) + for(var/mob/living/M in get_actual_viewers(world.view + 1, check) - src) if(M.client && CanAttack(M) && !M.silicon_privileges) if(!M.eye_blind) return M - for(var/obj/mecha/M in view(world.view + 1, check)) //assuming if you can see them they can see you - if(M.occupant && M.occupant.client) - if(!M.occupant.eye_blind) - return M.occupant return null // Cannot talk diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index 6aa7f8b401..1d22313edd 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -32,6 +32,7 @@ del_on_death = TRUE initial_language_holder = /datum/language_holder/construct blood_volume = 0 + has_field_of_vision = FALSE //we are a spoopy ghost /mob/living/simple_animal/shade/death() deathmessage = "lets out a contented sigh as [p_their()] form unwinds." diff --git a/code/modules/mob/living/simple_animal/slime/powers.dm b/code/modules/mob/living/simple_animal/slime/powers.dm index 0c7f126ad5..007140417c 100644 --- a/code/modules/mob/living/simple_animal/slime/powers.dm +++ b/code/modules/mob/living/simple_animal/slime/powers.dm @@ -28,7 +28,7 @@ return 0 var/list/choices = list() - for(var/mob/living/C in view(1,src)) + for(var/mob/living/C in visible_atoms(1,src)) if(C!=src && Adjacent(C)) choices += C diff --git a/code/modules/mob/living/update_icons.dm b/code/modules/mob/living/update_icons.dm index 8bf0dc98a9..b10d78aeb3 100644 --- a/code/modules/mob/living/update_icons.dm +++ b/code/modules/mob/living/update_icons.dm @@ -2,7 +2,6 @@ /mob/living/update_transform() var/matrix/ntransform = matrix(transform) //aka transform.Copy() var/final_pixel_y = pixel_y - var/final_dir = dir var/changed = 0 if(lying != lying_prev && rotate_on_lying) changed++ @@ -14,7 +13,7 @@ pixel_y = get_standard_pixel_y_offset() final_pixel_y = get_standard_pixel_y_offset(lying) if(dir & (EAST|WEST)) //Facing east or west - final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass + setDir(pick(NORTH, SOUTH)) //So you fall on your side rather than your face or ass if(resize != RESIZE_DEFAULT_SIZE) changed++ @@ -22,5 +21,5 @@ resize = RESIZE_DEFAULT_SIZE if(changed) - animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, dir = final_dir, easing = EASE_IN|EASE_OUT) + animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, easing = EASE_IN|EASE_OUT) setMovetype(movement_type & ~FLOATING) // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life(). diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index c266bbddd3..b7546becd8 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -55,3 +55,5 @@ log_message("Client [key_name(src)] has taken ownership of mob [src]([src.type])", LOG_OWNERSHIP) SEND_SIGNAL(src, COMSIG_MOB_CLIENT_LOGIN, client) + if(has_field_of_vision && CONFIG_GET(flag/use_field_of_vision)) + LoadComponent(/datum/component/field_of_vision, field_of_vision_type) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 6e4bd534af..7eec11892e 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -148,7 +148,8 @@ msg = blind_message else if(T.lighting_object && T.lighting_object.invisibility <= M.see_invisible && T.is_softly_lit() && !in_range(T,M)) //the light object is dark and not invisible to us, darkness does not matter if you're directly next to the target msg = blind_message - + else if(SEND_SIGNAL(M, COMSIG_MOB_GET_VISIBLE_MESSAGE, src, message, vision_distance, ignored_mobs) & COMPONENT_NO_VISIBLE_MESSAGE) + msg = blind_message if(!msg) continue M.show_message(msg, MSG_VISUAL,blind_message, MSG_AUDIBLE) @@ -282,41 +283,48 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA // reset_perspective(thing) set the eye to the thing (if it's equal to current default reset to mob perspective) // reset_perspective() set eye to common default : mob on turf, loc otherwise /mob/proc/reset_perspective(atom/A) - if(client) - if(A) - if(ismovable(A)) - //Set the the thing unless it's us - if(A != src) - client.perspective = EYE_PERSPECTIVE - client.eye = A - else - client.eye = client.mob - client.perspective = MOB_PERSPECTIVE - else if(isturf(A)) - //Set to the turf unless it's our current turf - if(A != loc) - client.perspective = EYE_PERSPECTIVE - client.eye = A - else - client.eye = client.mob - client.perspective = MOB_PERSPECTIVE + if(!client) + return + if(A) + if(ismovable(A)) + //Set the the thing unless it's us + if(A != src) + client.perspective = EYE_PERSPECTIVE + client.eye = A else - //Do nothing - else - //Reset to common defaults: mob if on turf, otherwise current loc - if(isturf(loc)) client.eye = client.mob client.perspective = MOB_PERSPECTIVE - else + else if(isturf(A)) + //Set to the turf unless it's our current turf + if(A != loc) client.perspective = EYE_PERSPECTIVE - client.eye = loc - return 1 + client.eye = A + else + client.eye = client.mob + client.perspective = MOB_PERSPECTIVE + else + //Do nothing + else + //Reset to common defaults: mob if on turf, otherwise current loc + if(isturf(loc)) + client.eye = client.mob + client.perspective = MOB_PERSPECTIVE + else + client.perspective = EYE_PERSPECTIVE + client.eye = loc + SEND_SIGNAL(src, COMSIG_MOB_RESET_PERSPECTIVE, A) + return TRUE /mob/proc/show_inv(mob/user) return +//view() but with a signal, to allow blacklisting some of the otherwise visible atoms. +/mob/proc/visible_atoms(dist = world.view) + . = view(dist, src) + SEND_SIGNAL(src, COMSIG_MOB_VISIBLE_ATOMS, .) + //mob verbs are faster than object verbs. See https://secure.byond.com/forum/?post=1326139&page=2#comment8198716 for why this isn't atom/verb/examine() -/mob/verb/examinate(atom/A as mob|obj|turf in view()) //It used to be oview(12), but I can't really say why +/mob/verb/examinate(atom/A as mob|obj|turf in visible_atoms()) //It used to be oview(12), but I can't really say why set name = "Examine" set category = "IC" @@ -329,15 +337,19 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA return face_atom(A) + var/flags = SEND_SIGNAL(src, COMSIG_MOB_EXAMINATE, A) + if(flags & COMPONENT_DENY_EXAMINATE) + if(flags & COMPONENT_EXAMINATE_BLIND) + to_chat(src, "Something is there but you can't see it!") + return var/list/result = A.examine(src) to_chat(src, result.Join("\n")) - SEND_SIGNAL(src, COMSIG_MOB_EXAMINATE, A) //same as above //note: ghosts can point, this is intended //visible_message will handle invisibility properly //overridden here and in /mob/dead/observer for different point span classes and sanity checks -/mob/verb/pointed(atom/A as mob|obj|turf in view()) +/mob/verb/pointed(atom/A as mob|obj|turf in visible_atoms()) set name = "Point To" set category = "Object" diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 46249dbfa6..e551316e25 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -131,8 +131,9 @@ var/voluntary_ghosted = FALSE //whether or not they voluntarily ghosted. - var/flavor_text = "" - var/flavor_text_2 = "" //version of the above that only lasts for the current round. + var/has_field_of_vision = FALSE + var/field_of_vision_type = FOV_90_DEGREES + ///////TYPING INDICATORS/////// /// Set to true if we want to show typing indicators. diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 6531df5e68..2bcfd47344 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -100,7 +100,8 @@ if(mob.throwing) mob.throwing.finalize(FALSE) - if(L.pulling && !(L.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) + var/atom/movable/AM = L.pulling + if(AM && AM.density && !(L.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) && !ismob(AM)) L.setDir(turn(L.dir, 180)) SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOVE, src, direction, n, oldloc) diff --git a/code/modules/mob/status_procs.dm b/code/modules/mob/status_procs.dm index cf86e962bd..6be1afb5de 100644 --- a/code/modules/mob/status_procs.dm +++ b/code/modules/mob/status_procs.dm @@ -88,18 +88,20 @@ /mob/proc/add_eyeblur() if(!client) return - var/obj/screen/plane_master/game_world/GW = locate(/obj/screen/plane_master/game_world) in client.screen - var/obj/screen/plane_master/floor/F = locate(/obj/screen/plane_master/floor) in client.screen - GW.add_filter("blurry_eyes", 2, EYE_BLUR(clamp(eye_blurry*0.1,0.6,3))) - F.add_filter("blurry_eyes", 2, EYE_BLUR(clamp(eye_blurry*0.1,0.6,3))) + var/list/screens = list(hud_used.plane_masters["[GAME_PLANE]"], hud_used.plane_masters["[FLOOR_PLANE]"], + hud_used.plane_masters["[WALL_PLANE]"], hud_used.plane_masters["[ABOVE_WALL_PLANE]"]) + for(var/A in screens) + var/obj/screen/plane_master/P = A + P.add_filter("blurry_eyes", 2, EYE_BLUR(clamp(eye_blurry*0.1,0.6,3))) /mob/proc/remove_eyeblur() if(!client) return - var/obj/screen/plane_master/game_world/GW = locate(/obj/screen/plane_master/game_world) in client.screen - var/obj/screen/plane_master/floor/F = locate(/obj/screen/plane_master/floor) in client.screen - GW.remove_filter("blurry_eyes") - F.remove_filter("blurry_eyes") + var/list/screens = list(hud_used.plane_masters["[GAME_PLANE]"], hud_used.plane_masters["[FLOOR_PLANE]"], + hud_used.plane_masters["[WALL_PLANE]"], hud_used.plane_masters["[ABOVE_WALL_PLANE]"]) + for(var/A in screens) + var/obj/screen/plane_master/P = A + P.remove_filter("blurry_eyes") ///Adjust the drugginess of a mob /mob/proc/adjust_drugginess(amount) diff --git a/code/modules/newscaster/newscaster_machine.dm b/code/modules/newscaster/newscaster_machine.dm index 95a24817a0..cb2d49fc64 100644 --- a/code/modules/newscaster/newscaster_machine.dm +++ b/code/modules/newscaster/newscaster_machine.dm @@ -12,6 +12,7 @@ GLOBAL_LIST_EMPTY(allCasters) desc = "A standard Nanotrasen-licensed newsfeed handler for use in commercial space stations. All the news you absolutely have no use for, in one place!" icon = 'icons/obj/terminals.dmi' icon_state = "newscaster_normal" + plane = ABOVE_WALL_PLANE verb_say = "beeps" verb_ask = "beeps" verb_exclaim = "beeps" diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index fd04ef0a1f..f1108189c7 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -48,7 +48,7 @@ /obj/machinery/power/apc name = "area power controller" desc = "A control terminal for the area's electrical systems." - + plane = ABOVE_WALL_PLANE icon_state = "apc0" use_power = NO_POWER_USE req_access = null diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index ed3a098dae..1522a6e099 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -127,7 +127,7 @@ /obj/singularity/narsie/mezzer() - for(var/mob/living/carbon/M in viewers(consume_range, src)) + for(var/mob/living/carbon/M in get_actual_viewers(consume_range, src)) if(M.stat == CONSCIOUS) if(!iscultist(M)) to_chat(M, "You feel conscious thought crumble away in an instant as you gaze upon [src.name]...") diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 28e9194eef..1d69ba3be8 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -433,7 +433,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) env.merge(removed) air_update_turf() - for(var/mob/living/carbon/human/l in view(src, HALLUCINATION_RANGE(power))) // If they can see it without mesons on. Bad on them. + for(var/mob/living/carbon/human/l in get_actual_viewers(HALLUCINATION_RANGE(power), src)) // If they can see it without mesons on. Bad on them. if(!istype(l.glasses, /obj/item/clothing/glasses/meson)) var/D = sqrt(1 / max(1, get_dist(l, src))) l.hallucination += power * config_hallucination_power * D @@ -666,9 +666,10 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) //Some poor sod got eaten, go ahead and irradiate people nearby. radiation_pulse(src, 3000, 2, TRUE) + var/list/viewers = get_actual_viewers(world.view, src) for(var/mob/living/L in range(10)) investigate_log("has irradiated [key_name(L)] after consuming [AM].", INVESTIGATE_SUPERMATTER) - if(L in view()) + if(L in viewers) L.show_message("As \the [src] slowly stops resonating, you find your skin covered in new radiation burns.", MSG_VISUAL,\ "The unearthly ringing subsides and you notice you have new radiation burns.", MSG_AUDIBLE) else diff --git a/code/modules/projectiles/projectile/special/hallucination.dm b/code/modules/projectiles/projectile/special/hallucination.dm index 3aa85c085f..5c65571ab0 100644 --- a/code/modules/projectiles/projectile/special/hallucination.dm +++ b/code/modules/projectiles/projectile/special/hallucination.dm @@ -50,7 +50,7 @@ if(M == hal_target) to_chat(hal_target, "[M] is hit by \a [src] in the chest!") hal_apply_effect() - else if(M in view(hal_target)) + else if(M in hal_target.visible_atoms()) to_chat(hal_target, "[M] is hit by \a [src] in the chest!!") if(damage_type == BRUTE) var/splatter_dir = dir diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index f4c06f39cc..7182f13646 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -535,7 +535,7 @@ add_reagent(P, cached_results[P]*multiplier, null, chem_temp) - var/list/seen = viewers(4, get_turf(my_atom))//Sound and sight checkers + var/list/seen = get_actual_viewers(4, get_turf(my_atom))//Sound and sight checkers var/iconhtml = icon2html(cached_my_atom, seen) if(cached_my_atom) if(!ismob(cached_my_atom)) // No bubbling mobs @@ -617,10 +617,7 @@ handle_reactions() update_total() //Reaction sounds and words - var/list/seen = viewers(5, get_turf(my_atom)) - var/iconhtml = icon2html(my_atom, seen) - for(var/mob/M in seen) - to_chat(M, "[iconhtml] [C.mix_message]") + my_atom.visible_message("[icon2html(my_atom, viewers(DEFAULT_MESSAGE_RANGE, src))] [C.mix_message]") /datum/reagents/proc/fermiReact(selected_reaction, cached_temp, cached_pH, reactedVol, targetVol, cached_required_reagents, cached_results, multiplier) var/datum/chemical_reaction/C = selected_reaction diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 141367c9d9..3ba883a2c7 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -765,7 +765,9 @@ /datum/reagent/toxin/rotatium/on_mob_life(mob/living/carbon/M) if(M.hud_used) if(current_cycle >= 20 && current_cycle%20 == 0) - var/list/screens = list(M.hud_used.plane_masters["[FLOOR_PLANE]"], M.hud_used.plane_masters["[GAME_PLANE]"], M.hud_used.plane_masters["[LIGHTING_PLANE]"]) + var/list/screens = list(M.hud_used.plane_masters["[FLOOR_PLANE]"], M.hud_used.plane_masters["[GAME_PLANE]"], + M.hud_used.plane_masters["[LIGHTING_PLANE]"], M.hud_used.plane_masters["[WALL_PLANE]"], + M.hud_used.plane_masters["[ABOVE_WALL_PLANE]"]) var/rotation = min(round(current_cycle/20), 89) // By this point the player is probably puking and quitting anyway for(var/whole_screen in screens) animate(whole_screen, transform = matrix(rotation, MATRIX_ROTATE), time = 5, easing = QUAD_EASING, loop = -1) @@ -793,15 +795,17 @@ /* if(M.hud_used) if(current_cycle >= 5 && current_cycle % 3 == 0) - var/list/screens = list(M.hud_used.plane_masters["[FLOOR_PLANE]"], M.hud_used.plane_masters["[GAME_PLANE]"], M.hud_used.plane_masters["[LIGHTING_PLANE]"]) - var/matrix/skew = matrix() + var/list/screens = list(M.hud_used.plane_masters["[FLOOR_PLANE]"], M.hud_used.plane_masters["[GAME_PLANE]"], + M.hud_used.plane_masters["[LIGHTING_PLANE]"], M.hud_used.plane_masters["[WALL_PLANE]"], + M.hud_used.plane_masters["[ABOVE_WALL_PLANE]"]) var/matrix/skew = matrix() var/intensity = 8 skew.set_skew(rand(-intensity,intensity), rand(-intensity,intensity)) var/matrix/newmatrix = skew if(prob(33)) // 1/3rd of the time, let's make it stack with the previous matrix! Mwhahahaha! - var/obj/screen/plane_master/PM = M.hud_used.plane_masters["[GAME_PLANE]"] - newmatrix = skew * PM.transform + for(var/whole_screen in screens) + var/obj/screen/plane_master/PM = whole_screen + newmatrix = skew * PM.transform for(var/whole_screen in screens) animate(whole_screen, transform = newmatrix, time = 5, easing = QUAD_EASING, loop = -1) diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index ce1940994d..618ac256e2 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -440,9 +440,8 @@ mob_react = FALSE /datum/chemical_reaction/foam/on_reaction(datum/reagents/holder, multiplier) - var/location = get_turf(holder.my_atom) - for(var/mob/M in viewers(5, location)) - to_chat(M, "The solution spews out foam!") + var/turf/location = get_turf(holder.my_atom) + location.visible_message("The solution spews out foam!") var/datum/effect_system/foam_spread/s = new() s.set_up(multiplier*2, location, holder) s.start() @@ -457,11 +456,8 @@ mob_react = FALSE /datum/chemical_reaction/metalfoam/on_reaction(datum/reagents/holder, multiplier) - var/location = get_turf(holder.my_atom) - - for(var/mob/M in viewers(5, location)) - to_chat(M, "The solution spews out a metallic foam!") - + var/turf/location = get_turf(holder.my_atom) + location.visible_message("The solution spews out a metallic foam!") var/datum/effect_system/foam_spread/metal/s = new() s.set_up(multiplier*5, location, holder, 1) s.start() @@ -488,9 +484,8 @@ mob_react = FALSE /datum/chemical_reaction/ironfoam/on_reaction(datum/reagents/holder, multiplier) - var/location = get_turf(holder.my_atom) - for(var/mob/M in viewers(5, location)) - to_chat(M, "The solution spews out a metallic foam!") + var/turf/location = get_turf(holder.my_atom) + location.visible_message("The solution spews out metallic foam!") var/datum/effect_system/foam_spread/metal/s = new() s.set_up(multiplier*5, location, holder, 2) s.start() diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index f326b94e44..fbdbb5f656 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -160,11 +160,8 @@ /obj/item/reagent_containers/microwave_act(obj/machinery/microwave/M) reagents.expose_temperature(1000) if(container_flags & TEMP_WEAK) - var/list/seen = viewers(5, get_turf(src)) - var/iconhtml = icon2html(src, seen) - for(var/mob/H in seen) - to_chat(H, "[iconhtml] \The [src]'s melts from the temperature!") - playsound(get_turf(src), 'sound/FermiChem/heatmelt.ogg', 80, 1) + visible_message("[icon2html(src, viewers(DEFAULT_MESSAGE_RANGE, src))] [src]'s melts from the temperature!") + playsound(src, 'sound/FermiChem/heatmelt.ogg', 80, 1) qdel(src) ..() @@ -184,7 +181,7 @@ if((reagents.pH < 1.5) || (reagents.pH > 12.5)) START_PROCESSING(SSobj, src) else if((reagents.pH < -3) || (reagents.pH > 17)) - visible_message("[icon2html(src, viewers(src))] \The [src] is damaged by the super pH and begins to deform!") + visible_message("[icon2html(src, viewers(DEFAULT_MESSAGE_RANGE, src))] \The [src] is damaged by the super pH and begins to deform!") reagents.pH = clamp(reagents.pH, -3, 17) container_HP -= 1 @@ -221,15 +218,11 @@ container_HP -= damage - var/list/seen = viewers(5, get_turf(src)) - var/iconhtml = icon2html(src, seen) - var/damage_percent = ((container_HP / initial(container_HP)*100)) switch(damage_percent) if(-INFINITY to 0) - for(var/mob/M in seen) - to_chat(M, "[iconhtml] \The [src]'s melts [cause]!") - playsound(get_turf(src), 'sound/FermiChem/acidmelt.ogg', 80, 1) + visible_message("[icon2html(src, viewers(DEFAULT_MESSAGE_RANGE, src))] [src]'s melts [cause]!") + playsound(src, 'sound/FermiChem/acidmelt.ogg', 80, 1) SSblackbox.record_feedback("tally", "fermi_chem", 1, "Times beakers have melted") STOP_PROCESSING(SSobj, src) qdel(src) @@ -246,5 +239,4 @@ update_icon() if(prob(25)) - for(var/mob/M in seen) - to_chat(M, "[iconhtml] \The [src]'s is damaged by [cause] and begins to deform!") + visible_message("[icon2html(src, viewers(DEFAULT_MESSAGE_RANGE, src))] [src]'s is damaged by [cause] and begins to deform!") diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index e6fa18ba9c..36e3b0d47e 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -72,9 +72,8 @@ to_chat(user, "[target] is full.") return - to_chat(user, "You dissolve [src] in [target].") - for(var/mob/O in viewers(2, user)) //viewers is necessary here because of the small radius - to_chat(O, "[user] slips something into [target]!") + user.visible_message("[user] slips something into [target]!", + "You dissolve [src] in [target].", vision_distance = 2) reagents.trans_to(target, reagents.total_volume) qdel(src) diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 54ec327fdb..c8e50667a8 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -169,6 +169,7 @@ name = "pepper spray refiller" desc = "Contains condensed capsaicin for use in law \"enforcement.\"" icon_state = "pepper" + plane = ABOVE_WALL_PLANE anchored = TRUE density = FALSE reagent_id = /datum/reagent/consumable/condensedcapsaicin @@ -182,6 +183,7 @@ name = "virus food dispenser" desc = "A dispenser of low-potency virus mutagenic." icon_state = "virus_food" + plane = ABOVE_WALL_PLANE anchored = TRUE density = FALSE reagent_id = /datum/reagent/consumable/virus_food diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm index b9dc9b92d3..04f2de9bec 100644 --- a/code/modules/security_levels/keycard_authentication.dm +++ b/code/modules/security_levels/keycard_authentication.dm @@ -9,6 +9,7 @@ GLOBAL_DATUM_INIT(keycard_events, /datum/events, new) desc = "This device is used to trigger station functions, which require more than one ID card to authenticate." icon = 'icons/obj/monitors.dmi' icon_state = "auth_off" + plane = ABOVE_WALL_PLANE use_power = IDLE_POWER_USE idle_power_usage = 2 active_power_usage = 6 diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm index b0808cef5e..33fc4d8703 100644 --- a/code/modules/tgui/states.dm +++ b/code/modules/tgui/states.dm @@ -99,7 +99,7 @@ * return UI_state The state of the UI. **/ /mob/living/proc/shared_living_ui_distance(atom/movable/src_object) - if(!(src_object in view(src))) // If the object is obscured, close it. + if(!(src_object in visible_atoms())) // If the object is obscured, close it. return UI_CLOSE var/dist = get_dist(src_object, src) diff --git a/code/modules/vehicles/sealed.dm b/code/modules/vehicles/sealed.dm index fc81e2ec00..edaab8b982 100644 --- a/code/modules/vehicles/sealed.dm +++ b/code/modules/vehicles/sealed.dm @@ -1,5 +1,6 @@ /obj/vehicle/sealed var/enter_delay = 20 + flags_1 = BLOCK_FACE_ATOM_1 /obj/vehicle/sealed/generate_actions() . = ..() @@ -51,7 +52,7 @@ if(randomstep) var/turf/target_turf = get_step(exit_location(M), pick(GLOB.cardinals)) M.throw_at(target_turf, 5, 10) - + if(!silent) M.visible_message("[M] drops out of \the [src]!") return TRUE @@ -102,7 +103,13 @@ if(iscarbon(i)) var/mob/living/carbon/C = i C.DefaultCombatKnockdown(40) - - + + /obj/vehicle/sealed/AllowDrop() return FALSE + +/obj/vehicle/sealed/setDir(newdir) + . = ..() + for(var/k in occupants) + var/mob/M = k + M.setDir(newdir) diff --git a/config/game_options.txt b/config/game_options.txt index 2d326493bb..f65c1bfff5 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -658,4 +658,8 @@ THRESHOLD_BODY_SIZE_SLOWDOWN 0.85 BODY_SIZE_SLOWDOWN_MULTIPLIER 0.25 ## Allows players to set a hexadecimal color of their choice as skin tone, on top of the standard ones. -ALLOW_CUSTOM_SKINTONES \ No newline at end of file +ALLOW_CUSTOM_SKINTONES + +## Enables the FoV component, which hides objects and mobs behind the parent from their sight, unless they turn around, duh. +## Camera mobs, AIs, ghosts and some other are of course exempt from this. This also doesn't influence simplemob AI, for the best. +#USE_FIELD_OF_VISION \ No newline at end of file diff --git a/icons/misc/field_of_vision.dmi b/icons/misc/field_of_vision.dmi new file mode 100644 index 0000000000..015cecd88c Binary files /dev/null and b/icons/misc/field_of_vision.dmi differ diff --git a/modular_citadel/code/_onclick/click.dm b/modular_citadel/code/_onclick/click.dm index f077b3a71b..004cc2fe80 100644 --- a/modular_citadel/code/_onclick/click.dm +++ b/modular_citadel/code/_onclick/click.dm @@ -72,30 +72,3 @@ /mob/proc/AltRangedAttack(atom/A, params) return FALSE - -/mob/proc/mouse_face_atom(atom/A) //Basically a copy of face_atom but with ismousemovement set to TRUE - if( buckled || stat != CONSCIOUS || !A || !x || !y || !A.x || !A.y ) - return - var/dx = A.x - x - var/dy = A.y - y - if(!dx && !dy) // Wall items are graphically shifted but on the floor - if(A.pixel_y > 16) - setDir(NORTH, ismousemovement = TRUE) - else if(A.pixel_y < -16) - setDir(SOUTH, ismousemovement = TRUE) - else if(A.pixel_x > 16) - setDir(EAST, ismousemovement = TRUE) - else if(A.pixel_x < -16) - setDir(WEST, ismousemovement = TRUE) - return - - if(abs(dx) < abs(dy)) - if(dy > 0) - setDir(NORTH, ismousemovement = TRUE) - else - setDir(SOUTH, ismousemovement = TRUE) - else - if(dx > 0) - setDir(EAST, ismousemovement = TRUE) - else - setDir(WEST, ismousemovement = TRUE) diff --git a/modular_citadel/code/_onclick/other_mobs.dm b/modular_citadel/code/_onclick/other_mobs.dm index 6d9ffdd6ca..2838a363ea 100644 --- a/modular_citadel/code/_onclick/other_mobs.dm +++ b/modular_citadel/code/_onclick/other_mobs.dm @@ -12,7 +12,7 @@ if(isturf(A) || incapacitated()) // pretty annoying to wave your fist at floors and walls. And useless. return TRUE changeNext_move(CLICK_CD_RANGE) - var/list/target_viewers = viewers(11, A) //Byond proc, doesn't check for blindness. + var/list/target_viewers = get_actual_viewers(11, A) //doesn't check for blindness. if(!(src in target_viewers)) //click catcher issuing calls for out of view objects. return TRUE if(!has_active_hand()) @@ -26,16 +26,16 @@ switch(a_intent) if(INTENT_DISARM) the_action = "shoos away [A]" - what_action = "shoo away something you can't see" + what_action = "shoo away something out of your vision" self_action = "shoo away [A]" if(INTENT_GRAB) the_action = "beckons [A] to come" - what_action = "beckons something you can't see to come" + what_action = "beckons something out of your vision to come" self_action = "beckon [A] to come" if(INTENT_HARM) var/pronoun = "[p_their()]" the_action = "shakes [pronoun] fist at [A]" - what_action = "shakes [pronoun] fist at something you can't see" + what_action = "shakes [pronoun] fist at something out of your vision" self_action = "shake your fist at [A]" if(!eye_blind) diff --git a/modular_citadel/code/modules/mob/living/carbon/carbon.dm b/modular_citadel/code/modules/mob/living/carbon/carbon.dm index e95948e562..0532f59afa 100644 --- a/modular_citadel/code/modules/mob/living/carbon/carbon.dm +++ b/modular_citadel/code/modules/mob/living/carbon/carbon.dm @@ -25,5 +25,5 @@ /mob/living/carbon/onMouseMove(object, location, control, params) if(!(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) return - mouse_face_atom(object) + face_atom(object, TRUE) lastmousedir = dir diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm index 89a7f58cee..cd672e2c6a 100644 --- a/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm +++ b/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm @@ -194,14 +194,14 @@ Creating a chem with a low purity will make you permanently fall in love with so if (M.ckey == creatorID && creatorName == M.real_name)//If the creator drinks it, they fall in love randomly. If someone else drinks it, the creator falls in love with them. if(M.has_status_effect(STATUS_EFFECT_INLOVE))//Can't be enthralled when enthralled, so to speak. return - var/list/seen = viewers(7, get_turf(M)) + var/list/seen = (M.visible_atoms(M.client?.view || world.view) - M) | viewers(M.client?.view || world.view, M) for(var/victim in seen) if(ishuman(victim)) var/mob/living/carbon/V = victim - if((V == M) || (!V.client) || (V.stat == DEAD)) - seen = seen - victim + if(!V.client || V.stat == DEAD) + seen -= victim else - seen = seen - victim + seen -= victim if(LAZYLEN(seen)) return @@ -213,7 +213,7 @@ Creating a chem with a low purity will make you permanently fall in love with so var/mob/living/carbon/C = get_mob_by_key(creatorID) if(M.has_status_effect(STATUS_EFFECT_INLOVE)) return - if((C in viewers(7, get_turf(M))) && (C.client)) + if(C.client && (M in C.visible_atoms(C.client.view))) M.reagents.del_reagent(type) FallInLove(C, M) return @@ -279,15 +279,13 @@ Creating a chem with a low purity will make you permanently fall in love with so if(HAS_TRAIT(M, TRAIT_MINDSHIELD)) return ..() if(!M.has_status_effect(STATUS_EFFECT_INLOVE)) - var/list/seen = viewers(7, get_turf(M))//Sound and sight checkers + var/list/seen = (M.visible_atoms(M.client?.view || world.view) - M) | viewers(M.client?.view || world.view, M) for(var/victim in seen) - if((istype(victim, /mob/living/simple_animal/pet/)) || (victim == M) || (M.stat == DEAD) || (!isliving(victim))) - seen = seen - victim - if(seen.len == 0) + if((isanimal(victim)) || (!isliving(victim))) + seen -= victim + if(!length(seen)) return love = pick(seen) - if(!love) - return M.apply_status_effect(STATUS_EFFECT_INLOVE, love) lewd = (M.client?.prefs.cit_toggles & HYPNO) && (love.client?.prefs.cit_toggles & HYPNO) to_chat(M, "[(lewd?"":"")][(lewd?"You develop a sudden crush on [love], your heart beginning to race as you look upon them with new eyes.":"You suddenly feel like making friends with [love].")] You feel strangely drawn towards them.") diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm index dac6d97eef..8bc67b6d1d 100644 --- a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm +++ b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm @@ -98,10 +98,8 @@ M.emote("cough") var/obj/item/toy/plush/P = pick(subtypesof(/obj/item/toy/plush)) new P(T) - to_chat(M, "You feel a lump form in your throat, as you suddenly cough up what seems to be a hairball?") - var/list/seen = viewers(8, T) - for(var/mob/S in seen) - to_chat(S, "[M] suddenly coughs up a [P.name]!") + M.visible_message("[M] suddenly coughs up a [P.name]!",\ + "You feel a lump form in your throat, as you suddenly cough up what seems to be a hairball?") var/T2 = get_random_station_turf() P.throw_at(T2, 8, 1) ..() @@ -120,10 +118,10 @@ to_chat(M, "You find yourself unable to supress the desire to howl!") M.emote("awoo") if(prob(20)) - var/list/seen = viewers(5, get_turf(M))//Sound and sight checkers + var/list/seen = M.visible_atoms() - M //Sound and sight checkers for(var/victim in seen) - if((istype(victim, /mob/living/simple_animal/pet/)) || (victim == M) || (!isliving(victim))) - seen = seen - victim + if(isanimal(victim) || !isliving(victim)) + seen -= victim if(LAZYLEN(seen)) to_chat(M, "You notice [pick(seen)]'s bulge [pick("OwO!", "uwu!")]") if(16) @@ -141,10 +139,10 @@ to_chat(M, "You find yourself unable to supress the desire to howl!") M.emote("awoo") if(prob(5)) - var/list/seen = viewers(5, get_turf(M))//Sound and sight checkers + var/list/seen = M.visible_atoms() - M //Sound and sight checkers for(var/victim in seen) - if((istype(victim, /mob/living/simple_animal/pet/)) || (victim == M) || (!isliving(victim))) - seen = seen - victim + if(isanimal(victim) || !isliving(victim)) + seen -= victim if(LAZYLEN(seen)) to_chat(M, "You notice [pick(seen)]'s bulge [pick("OwO!", "uwu!")]") ..() @@ -301,9 +299,8 @@ datum/reagent/fermi/nanite_b_gone/reaction_obj(obj/O, reac_volume) else var/datum/chemical_reaction/Ferm = GLOB.chemical_reagents_list[reagent.type] Ferm.on_reaction(holder, reagent.volume) - for(var/mob/M in viewers(8, location)) - to_chat(M, "The solution reacts dramatically, with a meow!") - playsound(get_turf(M), 'modular_citadel/sound/voice/merowr.ogg', 50, 1) + holder.my_atom.visible_message("The solution reacts dramatically, with a meow!") + playsound(holder.my_atom, 'modular_citadel/sound/voice/merowr.ogg', 50, 1) holder.clear_reagents() /datum/reagent/fermi/acidic_buffer @@ -321,10 +318,8 @@ datum/reagent/fermi/nanite_b_gone/reaction_obj(obj/O, reac_volume) if(LAZYLEN(holder.reagent_list) == 1) return ..() holder.pH = ((holder.pH * holder.total_volume)+(pH * (volume)))/(holder.total_volume + (volume)) - var/list/seen = viewers(5, get_turf(holder)) - for(var/mob/M in seen) - to_chat(M, "The beaker fizzes as the pH changes!") - playsound(get_turf(holder.my_atom), 'sound/FermiChem/bufferadd.ogg', 50, 1) + holder.my_atom.visible_message("The beaker fizzes as the pH changes!") + playsound(holder.my_atom, 'sound/FermiChem/bufferadd.ogg', 50, 1) holder.remove_reagent(type, volume, ignore_pH = TRUE) ..() @@ -342,10 +337,8 @@ datum/reagent/fermi/nanite_b_gone/reaction_obj(obj/O, reac_volume) if(LAZYLEN(holder.reagent_list) == 1) return ..() holder.pH = ((holder.pH * holder.total_volume)+(pH * (volume)))/(holder.total_volume + (volume)) - var/list/seen = viewers(5, get_turf(holder)) - for(var/mob/M in seen) - to_chat(M, "The beaker froths as the pH changes!") - playsound(get_turf(holder.my_atom), 'sound/FermiChem/bufferadd.ogg', 50, 1) + holder.my_atom.visible_message("The beaker froths as the pH changes!") + playsound(holder.my_atom, 'sound/FermiChem/bufferadd.ogg', 50, 1) holder.remove_reagent(type, volume, ignore_pH = TRUE) ..() diff --git a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm index 837524d49f..a8998a51ac 100644 --- a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm +++ b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm @@ -172,9 +172,7 @@ S.rabid = 1//Make them an angery boi S.color = "#810010" my_atom.reagents.clear_reagents() - var/list/seen = viewers(8, get_turf(my_atom)) - for(var/mob/M in seen) - to_chat(M, "The cells clump up into a horrifying tumour!") + my_atom.visible_message("An horrifying tumoural mass forms in [my_atom]!") /datum/chemical_reaction/fermi/breast_enlarger name = "Sucubus milk" @@ -211,9 +209,7 @@ /datum/chemical_reaction/fermi/breast_enlarger/FermiExplode(datum/reagents, var/atom/my_atom, volume, temp, pH) var/obj/item/organ/genital/breasts/B = new /obj/item/organ/genital/breasts(get_turf(my_atom)) - var/list/seen = viewers(8, get_turf(my_atom)) - for(var/mob/M in seen) - to_chat(M, "The reaction suddenly condenses, creating a pair of breasts!") + my_atom.visible_message("The reaction suddenly condenses, creating a pair of breasts!") var/datum/reagent/fermi/breast_enlarger/BE = locate(/datum/reagent/fermi/breast_enlarger) in my_atom.reagents.reagent_list B.size = ((BE.volume * BE.purity) / 10) //half as effective. my_atom.reagents.clear_reagents() @@ -243,9 +239,7 @@ /datum/chemical_reaction/fermi/penis_enlarger/FermiExplode(datum/reagents, var/atom/my_atom, volume, temp, pH) var/obj/item/organ/genital/penis/P = new /obj/item/organ/genital/penis(get_turf(my_atom)) - var/list/seen = viewers(8, get_turf(my_atom)) - for(var/mob/M in seen) - to_chat(M, "The reaction suddenly condenses, creating a penis!") + my_atom.visible_message("The reaction suddenly condenses, creating a penis!") var/datum/reagent/fermi/penis_enlarger/PE = locate(/datum/reagent/fermi/penis_enlarger) in my_atom.reagents.reagent_list P.length = ((PE.volume * PE.purity) / 10)//half as effective. my_atom.reagents.clear_reagents() @@ -383,9 +377,7 @@ for(var/i in 1 to amount_to_spawn) var/obj/item/clothing/head/hattip/hat = new /obj/item/clothing/head/hattip(get_turf(my_atom)) hat.animate_atom_living() - var/list/seen = viewers(8, get_turf(my_atom)) - for(var/mob/M in seen) - to_chat(M, "The [my_atom] makes an off sounding pop, as a hat suddenly climbs out of it!") + my_atom.visible_message("The [my_atom] makes an off sounding pop, as a hat suddenly climbs out of it!") my_atom.reagents.clear_reagents() /datum/chemical_reaction/fermi/furranium @@ -543,9 +535,7 @@ /datum/chemical_reaction/fermi/secretcatchem/FermiExplode(datum/reagents, var/atom/my_atom, volume, temp, pH) var/mob/living/simple_animal/pet/cat/custom_cat/catto = new(get_turf(my_atom)) - var/list/seen = viewers(8, get_turf(my_atom)) - for(var/mob/M in seen) - to_chat(M, "The reaction suddenly gives out a meow, condensing into a chemcat!")//meow! + my_atom.visible_message("The reaction suddenly gives out a meow, condensing into a chemcat!")//meow! playsound(get_turf(my_atom), 'modular_citadel/sound/voice/merowr.ogg', 50, 1, -1) catto.name = "Chemcat" catto.desc = "A cute chem cat, created by a lot of compicated and confusing chemistry!" diff --git a/modular_citadel/code/modules/reagents/objects/clothes.dm b/modular_citadel/code/modules/reagents/objects/clothes.dm index 34af39bbe0..de4cb38360 100644 --- a/modular_citadel/code/modules/reagents/objects/clothes.dm +++ b/modular_citadel/code/modules/reagents/objects/clothes.dm @@ -5,7 +5,7 @@ name = "Synthetic hat" icon = 'icons/obj/clothing/hats.dmi' icon_state = "cowboy" - desc = "A synthesized hat, you can't seem to take it off. And tips their hat." + desc = "A synthesized hat. You feel compelled to keep it on all times." armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) //item_flags = NODROP //Tips their hat! @@ -49,9 +49,8 @@ /obj/item/clothing/head/hattip/proc/root_and_toot(obj/item/clothing/head/hattip/hat) hat.animate_atom_living() - var/list/seen = viewers(6, get_turf(hat)) - for(var/mob/M2 in seen) - to_chat(M2, "[hat] exclaims, \"[pick("Whooee! Time for a hootenanny!", "Rough 'em up boys!", "Yeehaw! Freedom at last!", "Y'all about to get a good old fashioned spanking!")]\"") + var/mob/living/simple_animal/hostile/mimic/M = loc + M.say(pick("Whooee! Time for a hootenanny!", "Rough 'em up boys!", "Yeehaw! Freedom at last!", "Y'all about to get a good old fashioned spanking!")) /obj/item/clothing/head/hattip/proc/handle_speech(datum/source, mob/speech_args) var/message = speech_args[SPEECH_MESSAGE] diff --git a/tgstation.dme b/tgstation.dme index aa24a781fe..619f6767bb 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -397,6 +397,7 @@ #include "code\datums\components\edit_complainer.dm" #include "code\datums\components\empprotection.dm" #include "code\datums\components\explodable.dm" +#include "code\datums\components\field_of_vision.dm" #include "code\datums\components\footstep.dm" #include "code\datums\components\forced_gravity.dm" #include "code\datums\components\identification.dm" @@ -2375,6 +2376,7 @@ #include "code\modules\mob\living\carbon\human\human_movement.dm" #include "code\modules\mob\living\carbon\human\inventory.dm" #include "code\modules\mob\living\carbon\human\life.dm" +#include "code\modules\mob\living\carbon\human\login.dm" #include "code\modules\mob\living\carbon\human\physiology.dm" #include "code\modules\mob\living\carbon\human\say.dm" #include "code\modules\mob\living\carbon\human\species.dm"