diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index f6d73c48c5..9ace5cf95b 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -10444,7 +10444,7 @@ /area/maintenance/port/fore) "avV" = ( /obj/structure/table/wood, -/obj/item/reagent_containers/food/snacks/burger/ghost, +/obj/item/ectoplasm, /turf/open/floor/wood, /area/maintenance/port/fore) "avW" = ( diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index d4cad66beb..4fb880fb4d 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -16329,7 +16329,7 @@ /obj/docking_port/stationary{ dwidth = 1; height = 4; - id = "pod4_away"; + id = "pod_4_away"; name = "recovery ship"; width = 3 }, @@ -16339,7 +16339,7 @@ /obj/docking_port/stationary{ dwidth = 1; height = 4; - id = "pod3_away"; + id = "pod_3_away"; name = "recovery ship"; width = 3 }, @@ -16489,7 +16489,7 @@ dir = 4; dwidth = 1; height = 4; - id = "pod2_away"; + id = "pod_2_away"; name = "recovery ship"; width = 3 }, diff --git a/code/__DEFINES/cargo.dm b/code/__DEFINES/cargo.dm index 40a50fe1e6..85e5e9d2ac 100644 --- a/code/__DEFINES/cargo.dm +++ b/code/__DEFINES/cargo.dm @@ -60,6 +60,5 @@ GLOBAL_LIST_INIT(podstyles, list(\ )) //cit -#define PACK_GOODY_NONE 0 -#define PACK_GOODY_PUBLIC 1 //can be bought by both privates and cargo -#define PACK_GOODY_PRIVATE 2 //can be bought only by privates +#define PACK_GOODY_NONE 0 // can be bought by cargo and privates +#define PACK_GOODY_PRIVATE 1 // can be bought only by privates diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 7933fab83f..88f9759940 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -266,6 +266,7 @@ #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_APPLY_DAMAGE "mob_apply_damage" //from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness) #define COMSIG_MOB_SPELL_CAN_CAST "mob_spell_can_cast" //from base of /obj/effect/proc_holder/spell/can_cast(): (spell) #define COMSIG_MOB_SWAP_HANDS "mob_swap_hands" //from base of mob/swap_hand(): (obj/item) @@ -303,6 +304,10 @@ #define COMSIG_LIVING_ACTIVE_PARRY_START "active_parry_start" //from base of mob/living/initiate_parry_sequence(): (parrying_method, datum/parrying_item_mob_or_art, list/backup_items, list/override) #define COMPONENT_PREVENT_PARRY_START 1 +#define COMSIG_LIVING_ATTACKER_SET "living_attacker_set" // from base of /mob/living/set_last_attacker(): (attacker) + +#define COMSIG_LIVING_SET_AS_ATTACKER "living_set_as_attacker" // from base of /mob/living/set_last_attacker(): (target) + //ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS! #define COMSIG_LIVING_STATUS_STUN "living_stun" //from base of mob/living/Stun() (amount, update, ignore) #define COMSIG_LIVING_STATUS_KNOCKDOWN "living_knockdown" //from base of mob/living/Knockdown() (amount, update, ignore) @@ -357,7 +362,6 @@ // /obj/item signals #define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user) -#define COMSIG_MOB_APPLY_DAMGE "mob_apply_damage" //from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone) #define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob) #define COMPONENT_NO_INTERACT 1 #define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj" //from base of obj/item/attack_obj(): (/obj, /mob) @@ -455,6 +459,10 @@ // /datum/mutation signals #define COMSIG_HUMAN_MUTATION_LOSS "human_mutation_loss" //from datum/mutation/human/on_losing(): (datum/mutation/human/lost_mutation) +///from base of mob/living/death(): (gibbed) +// Sent before any of the other death code has run, mob is still alive. +#define COMSIG_LIVING_PREDEATH "living_predeath" + /*******Component Specific Signals*******/ //Janitor #define COMSIG_TURF_IS_WET "check_turf_wet" //(): Returns bitflags of wet values. diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index e56ab8dd24..32d961d77d 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -30,7 +30,8 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s //Human Overlays Indexes///////// //LOTS OF CIT CHANGES HERE. BE CAREFUL WHEN UPSTREAM ADDS MORE LAYERS -#define MUTATIONS_LAYER 33 //mutations. Tk headglows, cold resistance glow, etc +#define MUTATIONS_LAYER 34 //mutations. Tk headglows, cold resistance glow, etc +#define ANTAG_LAYER 33 //stuff for things like cultism indicators (clock cult glow, cultist red halos, whatever else new that comes up) #define GENITALS_BEHIND_LAYER 32 //Some genitalia needs to be behind everything, such as with taurs (Taurs use body_behind_layer #define BODY_BEHIND_LAYER 31 //certain mutantrace features (tail when looking south) that must appear behind the body parts #define BODYPARTS_LAYER 30 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag @@ -63,7 +64,7 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s #define HANDS_LAYER 3 #define BODY_FRONT_LAYER 2 #define FIRE_LAYER 1 //If you're on fire -#define TOTAL_LAYERS 33 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; +#define TOTAL_LAYERS 34 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; //Human Overlay Index Shortcuts for alternate_worn_layer, layers //Because I *KNOW* somebody will think layer+1 means "above" @@ -158,7 +159,7 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache) #define BLOOD_COLOR_SLIME "#00ff90" #define BLOOD_COLOR_LIZARD "#db004D" #define BLOOD_COLOR_UNIVERSAL "#db3300" -#define BLOOD_COLOR_BUG "#a37c0f" +#define BLOOD_COLOR_BUG "#ffc933" #define BLOOD_COLOR_PLANT "#3d610e" diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 31f34c5d4c..7e2abebcf9 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -15,7 +15,7 @@ #define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } } #define LAZYADD(L, I) if(!L) { L = list(); } L += I; #define LAZYOR(L, I) if(!L) { L = list(); } L |= I; -#define LAZYFIND(L, V) L ? L.Find(V) : 0 +#define LAZYFIND(L, V) (L ? L.Find(V) : 0) #define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null) #define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V; #define LAZYLEN(L) length(L) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index b9cbfb7e35..d44a4b3898 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -11,6 +11,23 @@ GLOBAL_LIST_INIT(bitfields, list( "TILE_BOUND" = TILE_BOUND, "PIXEL_SCALE" = PIXEL_SCALE ), + "area_flags" = list( + "ABDUCTOR_PROOF" = ABDUCTOR_PROOF, + "BLOBS_ALLOWED" = BLOBS_ALLOWED, + "BLOCK_SUICIDE" = BLOCK_SUICIDE, + // "CULT_PERMITTED" = CULT_PERMITTED, + "FLORA_ALLOWED" = FLORA_ALLOWED, + "HIDDEN_AREA" = HIDDEN_AREA, + "MEGAFAUNA_SPAWN_ALLOWED" = MEGAFAUNA_SPAWN_ALLOWED, + "MOB_SPAWN_ALLOWED" = MOB_SPAWN_ALLOWED, + "NO_ALERTS" = NO_ALERTS, + "NOTELEPORT" = NOTELEPORT, + "CAVES_ALLOWED" = CAVES_ALLOWED, + "UNIQUE_AREA" = UNIQUE_AREA, + "VALID_TERRITORY" = VALID_TERRITORY, + "XENOBIOLOGY_COMPATIBLE" = XENOBIOLOGY_COMPATIBLE, + "NO_ALERTS" = NO_ALERTS, + ) , "sight" = list( "SEE_INFRA" = SEE_INFRA, "SEE_SELF" = SEE_SELF, diff --git a/code/_onclick/hud/screen_objects/storage.dm b/code/_onclick/hud/screen_objects/storage.dm index 72b2d035a3..1ce1df24c6 100644 --- a/code/_onclick/hud/screen_objects/storage.dm +++ b/code/_onclick/hud/screen_objects/storage.dm @@ -85,21 +85,16 @@ makeItemInactive() /obj/screen/storage/volumetric_box/proc/makeItemInactive() - if(!our_item) - return - our_item.layer = VOLUMETRIC_STORAGE_ITEM_LAYER - our_item.plane = VOLUMETRIC_STORAGE_ITEM_PLANE + return /obj/screen/storage/volumetric_box/proc/makeItemActive() - if(!our_item) - return - our_item.layer = VOLUMETRIC_STORAGE_ACTIVE_ITEM_LAYER //make sure we display infront of the others! - our_item.plane = VOLUMETRIC_STORAGE_ACTIVE_ITEM_PLANE + return /obj/screen/storage/volumetric_box/center icon_state = "stored_continue" var/obj/screen/storage/volumetric_edge/stored_left/left var/obj/screen/storage/volumetric_edge/stored_right/right + var/obj/screen/storage/item_holder/holder var/pixel_size /obj/screen/storage/volumetric_box/center/Initialize(mapload, new_master, our_item) @@ -110,6 +105,9 @@ /obj/screen/storage/volumetric_box/center/Destroy() QDEL_NULL(left) QDEL_NULL(right) + vis_contents.Cut() + if(holder) + QDEL_NULL(holder) return ..() /obj/screen/storage/volumetric_box/center/proc/on_screen_objects() @@ -123,13 +121,36 @@ return pixel_size = pixels cut_overlays() + vis_contents.Cut() //our icon size is 32 pixels. - transform = matrix((pixels - (VOLUMETRIC_STORAGE_BOX_BORDER_SIZE * 2)) / VOLUMETRIC_STORAGE_BOX_ICON_SIZE, 0, 0, 0, 1, 0) + var/multiplier = (pixels - (VOLUMETRIC_STORAGE_BOX_BORDER_SIZE * 2)) / VOLUMETRIC_STORAGE_BOX_ICON_SIZE + transform = matrix(multiplier, 0, 0, 0, 1, 0) + if(our_item) + if(holder) + qdel(holder) + holder = new(null, src, our_item) + holder.transform = matrix(1 / multiplier, 0, 0, 0, 1, 0) + holder.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + holder.appearance_flags &= ~RESET_TRANSFORM + makeItemInactive() + vis_contents += holder left.pixel_x = -((pixels - VOLUMETRIC_STORAGE_BOX_ICON_SIZE) * 0.5) - VOLUMETRIC_STORAGE_BOX_BORDER_SIZE right.pixel_x = ((pixels - VOLUMETRIC_STORAGE_BOX_ICON_SIZE) * 0.5) + VOLUMETRIC_STORAGE_BOX_BORDER_SIZE add_overlay(left) add_overlay(right) +/obj/screen/storage/volumetric_box/center/makeItemInactive() + if(!holder) + return + holder.layer = VOLUMETRIC_STORAGE_ITEM_LAYER + holder.plane = VOLUMETRIC_STORAGE_ITEM_PLANE + +/obj/screen/storage/volumetric_box/center/makeItemActive() + if(!holder) + return + holder.our_item.layer = VOLUMETRIC_STORAGE_ACTIVE_ITEM_LAYER //make sure we display infront of the others! + holder.our_item.plane = VOLUMETRIC_STORAGE_ACTIVE_ITEM_PLANE + /obj/screen/storage/volumetric_edge layer = VOLUMETRIC_STORAGE_BOX_LAYER plane = VOLUMETRIC_STORAGE_BOX_PLANE @@ -157,3 +178,20 @@ /obj/screen/storage/volumetric_edge/stored_right icon_state = "stored_end" appearance_flags = APPEARANCE_UI | KEEP_APART | RESET_TRANSFORM + +/obj/screen/storage/item_holder + var/obj/item/our_item + vis_flags = NONE + +/obj/screen/storage/item_holder/Initialize(mapload, new_master, obj/item/I) + . = ..() + our_item = I + vis_contents += I + +/obj/screen/storage/item_holder/Destroy() + vis_contents.Cut() + our_item = null + return ..() + +/obj/screen/storage/item_holder/Click(location, control, params) + return our_item.Click(location, control, params) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index f63e594675..faf911e55b 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -94,8 +94,7 @@ else if(hitsound) playsound(loc, hitsound, get_clamped_volume(), 1, -1) - M.lastattacker = user.real_name - M.lastattackerckey = user.ckey + M.set_last_attacker(user) if(force && M == user && user.client) user.client.give_award(/datum/award/achievement/misc/selfouch, user) diff --git a/code/datums/components/activity.dm b/code/datums/components/activity.dm new file mode 100644 index 0000000000..ae18ca01a2 --- /dev/null +++ b/code/datums/components/activity.dm @@ -0,0 +1,63 @@ +/datum/component/activity + var/activity_level = 0 + var/not_moved_counter = 0 + var/list/historical_activity_levels = list() + +/datum/component/activity/Initialize() + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE + var/mob/living/L = parent + + RegisterSignal(L, COMSIG_LIVING_SET_AS_ATTACKER, .proc/on_set_as_attacker) + RegisterSignal(L, COMSIG_LIVING_ATTACKER_SET, .proc/on_attacker_set) + RegisterSignal(L, COMSIG_MOB_DEATH, .proc/on_death) + RegisterSignal(L, COMSIG_EXIT_AREA, .proc/on_exit_area) + RegisterSignal(L, COMSIG_LIVING_LIFE, .proc/on_life) + RegisterSignal(L, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_ATTACK_RANGED, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_THROW, COMSIG_MOVABLE_TELEPORTED, COMSIG_LIVING_GUN_PROCESS_FIRE, COMSIG_MOB_APPLY_DAMAGE), .proc/minor_activity) + +/datum/component/activity/proc/log_activity() + historical_activity_levels[world.time] = activity_level + +/datum/component/activity/proc/minor_activity(datum/source) + activity_level += 1 + +/datum/component/activity/proc/on_attacker_set(datum/source, mob/attacker) + activity_level += 10 + if(attacker?.mind) + activity_level += 10 + log_activity() + +/datum/component/activity/proc/on_set_as_attacker(datum/source, mob/target) + activity_level += 10 + if(target?.mind) + activity_level += 20 + log_activity() + +/datum/component/activity/proc/on_death(datum/source) + activity_level += 100 // dying means you're doing SOMETHING + log_activity() + +/datum/component/activity/proc/on_exit_area(datum/source) + activity_level += 1 + not_moved_counter = 0 + +/datum/component/activity/proc/on_life(datum/source, seconds, times_fired) + var/mob/living/L = source + if(L.stat >= UNCONSCIOUS) // can't expect the unconscious to move + return + not_moved_counter += seconds + var/should_log = FALSE + switch(not_moved_counter) + if(60 to 120) + activity_level -= 1 + if(120 to 600) + activity_level -= 5 + if(600 to 1200) + activity_level -= 10 + should_log = TRUE + if(1200 to INFINITY) + activity_level -= 20 + should_log = TRUE + activity_level = max(activity_level, 0) + if(should_log) + log_activity() diff --git a/code/datums/components/explodable.dm b/code/datums/components/explodable.dm index 2e5834c025..6e4be8497d 100644 --- a/code/datums/components/explodable.dm +++ b/code/datums/components/explodable.dm @@ -56,10 +56,10 @@ detonate() /datum/component/explodable/proc/on_equip(datum/source, mob/equipper, slot) - RegisterSignal(equipper, COMSIG_MOB_APPLY_DAMGE, .proc/explodable_attack_zone, TRUE) + RegisterSignal(equipper, COMSIG_MOB_APPLY_DAMAGE, .proc/explodable_attack_zone, TRUE) /datum/component/explodable/proc/on_drop(datum/source, mob/user) - UnregisterSignal(user, COMSIG_MOB_APPLY_DAMGE) + UnregisterSignal(user, COMSIG_MOB_APPLY_DAMAGE) /// Checks if we're hitting the zone this component is covering /datum/component/explodable/proc/is_hitting_zone(def_zone) diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm index 2fa5a20d7a..7968caed54 100644 --- a/code/datums/components/storage/storage.dm +++ b/code/datums/components/storage/storage.dm @@ -47,18 +47,8 @@ var/display_numerical_stacking = FALSE //stack things of the same type and show as a single object with a number. - /// "legacy"/default view mode's storage "boxes" - var/obj/screen/storage/boxes/ui_boxes - /// New volumetric storage display mode's left side - var/obj/screen/storage/left/ui_left - /// New volumetric storage display mode's center 'blocks' - var/obj/screen/storage/continuous/ui_continuous - /// The close button, used in all modes. Frames right side in volumetric mode. - var/obj/screen/storage/close/ui_close - /// Associative list of list(item = screen object) for volumetric storage item screen blocks - var/list/ui_item_blocks - - var/current_maxscreensize + /// Ui objects by person. mob = list(objects) + var/list/ui_by_mob = list() var/allow_big_nesting = FALSE //allow storage objects of the same or greater size. @@ -125,18 +115,16 @@ /datum/component/storage/Destroy() close_all() - QDEL_NULL(ui_boxes) - QDEL_NULL(ui_close) - QDEL_NULL(ui_continuous) - QDEL_NULL(ui_left) - // DO NOT USE QDEL_LIST_ASSOC. - if(ui_item_blocks) - for(var/i in ui_item_blocks) - qdel(ui_item_blocks[i]) //qdel the screen object not the item - ui_item_blocks.Cut() + wipe_ui_objects() LAZYCLEARLIST(is_using) return ..() +/datum/component/storage/proc/wipe_ui_objects() + for(var/i in ui_by_mob) + var/list/objects = ui_by_mob[i] + QDEL_LIST(objects) + ui_by_mob.Cut() + /datum/component/storage/PreTransfer() update_actions() @@ -351,13 +339,6 @@ return master._removal_reset(thing) /datum/component/storage/proc/_remove_and_refresh(datum/source, atom/movable/thing) - if(LAZYACCESS(ui_item_blocks, thing)) - var/obj/screen/storage/volumetric_box/center/C = ui_item_blocks[thing] - for(var/i in can_see_contents()) //runtimes result if mobs can access post deletion. - var/mob/M = i - M.client?.screen -= C.on_screen_objects() - ui_item_blocks -= thing - qdel(C) _removal_reset(thing) // THIS NEEDS TO HAPPEN AFTER SO LAYERING DOESN'T BREAK! refresh_mob_views() @@ -467,14 +448,14 @@ return A.add_fingerprint(M) -/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE, ghost = FALSE) +/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE) var/atom/A = parent if(!istype(M)) return FALSE A.add_fingerprint(M) if(!force && (check_locked(null, M) || !M.CanReach(parent, view_only = TRUE))) return FALSE - ui_show(M, !ghost) + ui_show(M) /datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M) if(isitem(O)) @@ -596,7 +577,7 @@ return can_be_inserted(I, silent, M) /datum/component/storage/proc/show_to_ghost(datum/source, mob/dead/observer/M) - return user_show_to_mob(M, TRUE, TRUE) + return user_show_to_mob(M, TRUE) /datum/component/storage/proc/signal_show_attempt(datum/source, mob/showto, force = FALSE) return user_show_to_mob(showto, force) diff --git a/code/datums/components/storage/ui.dm b/code/datums/components/storage/ui.dm index c7ac0d3549..044afa2850 100644 --- a/code/datums/components/storage/ui.dm +++ b/code/datums/components/storage/ui.dm @@ -7,7 +7,7 @@ if(QDELETED(I)) continue if(!.[I.type]) - .[I.type] = new /datum/numbered_display(I, 1) + .[I.type] = new /datum/numbered_display(I, 1, src) else var/datum/numbered_display/ND = .[I.type] ND.number++ @@ -20,6 +20,8 @@ . = list() var/list/accessible_contents = accessible_items() var/adjusted_contents = length(accessible_contents) + var/obj/screen/storage/close/ui_close + var/obj/screen/storage/boxes/ui_boxes //Numbered contents display var/list/datum/numbered_display/numbered_contents @@ -60,12 +62,13 @@ for(var/obj/O in accessible_items()) if(QDELETED(O)) continue - O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip" - O.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]" + var/obj/screen/storage/item_holder/D = new(null, src, O) + D.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip" + D.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]" O.maptext = "" O.layer = ABOVE_HUD_LAYER O.plane = ABOVE_HUD_PLANE - . += O + . += D cx++ if(cx - screen_start_x >= columns) cx = screen_start_x @@ -78,6 +81,9 @@ */ /datum/component/storage/proc/orient2hud_volumetric(mob/user, maxcolumns) . = list() + var/obj/screen/storage/left/ui_left + var/obj/screen/storage/continuous/ui_continuous + var/obj/screen/storage/close/ui_close // Generate ui_item_blocks for missing ones and render+orient. var/list/atom/contents = accessible_items() @@ -128,14 +134,10 @@ var/first = TRUE var/row = 1 - LAZYINITLIST(ui_item_blocks) - for(var/i in percentage_by_item) I = i var/percent = percentage_by_item[I] - if(!ui_item_blocks[I]) - ui_item_blocks[I] = new /obj/screen/storage/volumetric_box/center(null, src, I) - var/obj/screen/storage/volumetric_box/center/B = ui_item_blocks[I] + var/obj/screen/storage/volumetric_box/center/B = new /obj/screen/storage/volumetric_box/center(null, src, I) var/pixels_to_use = overrun? MINIMUM_PIXELS_PER_ITEM : max(using_horizontal_pixels * percent, MINIMUM_PIXELS_PER_ITEM) var/addrow = FALSE if(CEILING(pixels_to_use, 1) >= FLOOR(horizontal_pixels - current_pixel - VOLUMETRIC_STORAGE_EDGE_PADDING, 1)) @@ -143,25 +145,17 @@ addrow = TRUE // now that we have pixels_to_use, place our thing and add it to the returned list. - B.screen_loc = I.screen_loc = "[screen_start_x]:[round(current_pixel + (pixels_to_use * 0.5) + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING), 1)],[screen_start_y+row-1]:[screen_pixel_y]" + B.screen_loc = "[screen_start_x]:[round(current_pixel + (pixels_to_use * 0.5) + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING), 1)],[screen_start_y+row-1]:[screen_pixel_y]" // add the used pixels to pixel after we place the object current_pixel += pixels_to_use + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING) first = FALSE //apply padding to everything after this // set various things B.set_pixel_size(pixels_to_use) - B.layer = VOLUMETRIC_STORAGE_BOX_LAYER - B.plane = VOLUMETRIC_STORAGE_BOX_PLANE B.name = I.name - I.mouse_opacity = MOUSE_OPACITY_ICON - I.maptext = "" - I.layer = VOLUMETRIC_STORAGE_ITEM_LAYER - I.plane = VOLUMETRIC_STORAGE_ITEM_PLANE - // finally add our things. . += B.on_screen_objects() - . += I // go up a row if needed if(addrow) @@ -185,18 +179,19 @@ /** * Shows our UI to a mob. */ -/datum/component/storage/proc/ui_show(mob/M, set_screen_size = TRUE) +/datum/component/storage/proc/ui_show(mob/M) if(!M.client) return FALSE + if(ui_by_mob[M] || LAZYFIND(is_using, M)) + // something went horribly wrong + // hide first + ui_hide(M) var/list/cview = getviewsize(M.client.view) // in tiles var/maxallowedscreensize = cview[1]-8 - if(set_screen_size) - current_maxscreensize = maxallowedscreensize - else if(current_maxscreensize) - maxallowedscreensize = current_maxscreensize // we got screen size, register signal RegisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT, .proc/on_logout, override = TRUE) + RegisterSignal(M, COMSIG_PARENT_QDELETING, .proc/on_logout, override = TRUE) if(M.active_storage != src) if(M.active_storage) M.active_storage.ui_hide(M) @@ -204,10 +199,14 @@ LAZYOR(is_using, M) if(!M.client?.prefs?.no_tetris_storage && volumetric_ui()) //new volumetric ui bay-style - M.client.screen |= orient2hud_volumetric(M, maxallowedscreensize) + var/list/objects = orient2hud_volumetric(M, maxallowedscreensize) + M.client.screen |= objects + ui_by_mob[M] = objects else //old ui - M.client.screen |= orient2hud_legacy(M, maxallowedscreensize) + var/list/objects = orient2hud_legacy(M, maxallowedscreensize) + M.client.screen |= objects + ui_by_mob[M] = objects return TRUE /** @@ -236,8 +235,10 @@ /datum/component/storage/proc/ui_hide(mob/M) if(!M.client) return TRUE - UnregisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT) - M.client.screen -= list(ui_boxes, ui_close, ui_left, ui_continuous) + get_ui_item_objects_hide(M) + UnregisterSignal(M, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_CLIENT_LOGOUT)) + M.client.screen -= ui_by_mob[M] + var/list/objects = ui_by_mob[M] + QDEL_LIST(objects) if(M.active_storage == src) M.active_storage = null LAZYREMOVE(is_using, M) @@ -250,48 +251,26 @@ var/atom/real_location = real_location() return (storage_flags & STORAGE_LIMIT_VOLUME) && (length(real_location.contents) <= MAXIMUM_VOLUMETRIC_ITEMS) && !display_numerical_stacking -/** - * Gets the ui item objects to ui_hide. - */ -/datum/component/storage/proc/get_ui_item_objects_hide(mob/M) - if(!volumetric_ui() || M.client?.prefs?.no_tetris_storage) - var/atom/real_location = real_location() - return real_location.contents - else - . = list() - for(var/i in ui_item_blocks) - // get both the box and the item - . += ui_item_blocks[i] - . += i - /** * Gets our ui_boxes, making it if it doesn't exist. */ /datum/component/storage/proc/get_ui_boxes() - if(!ui_boxes) - ui_boxes = new(null, src) - return ui_boxes + return new /obj/screen/storage/boxes(null, src) /** * Gets our ui_left, making it if it doesn't exist. */ /datum/component/storage/proc/get_ui_left() - if(!ui_left) - ui_left = new(null, src) - return ui_left + return new /obj/screen/storage/left(null, src) /** * Gets our ui_close, making it if it doesn't exist. */ /datum/component/storage/proc/get_ui_close() - if(!ui_close) - ui_close = new(null, src) - return ui_close + return new /obj/screen/storage/close(null, src) /** * Gets our ui_continuous, making it if it doesn't exist. */ /datum/component/storage/proc/get_ui_continuous() - if(!ui_continuous) - ui_continuous = new(null, src) - return ui_continuous + return new /obj/screen/storage/continuous(null, src) diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index c19f23ed2a..f563826eb9 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -384,7 +384,7 @@ var/temp_rate = 1 threshold_desc = list( "Transmission 6" = "Additionally increases temperature adjustment rate and heals those who love toxins", - "Resistance 7" = "Increases healing speed.", + "Stage Speed 7" = "Increases healing speed.", ) /datum/symptom/heal/plasma/Start(datum/disease/advance/A) if(!..()) diff --git a/code/datums/elements/mob_holder.dm b/code/datums/elements/mob_holder.dm index 619f674969..f73826d899 100644 --- a/code/datums/elements/mob_holder.dm +++ b/code/datums/elements/mob_holder.dm @@ -141,7 +141,7 @@ /obj/item/clothing/head/mob_holder/dropped(mob/user) . = ..() - if(held_mob && !ismob(loc))//don't release on soft-drops + if(held_mob && !ismob(loc) && !istype(loc,/obj/item/storage))//don't release on soft-drops release() /obj/item/clothing/head/mob_holder/proc/release() diff --git a/code/datums/elements/wuv.dm b/code/datums/elements/wuv.dm index 6476a204cf..d4f1955afc 100644 --- a/code/datums/elements/wuv.dm +++ b/code/datums/elements/wuv.dm @@ -34,13 +34,13 @@ . = ..() UnregisterSignal(source, COMSIG_MOB_ATTACK_HAND) -/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user) +/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user, act_intent) var/mob/living/L = source if(L.stat == DEAD) return //we want to delay the effect to be displayed after the mob is petted, not before. - switch(user.a_intent) + switch(act_intent) if(INTENT_HARM) addtimer(CALLBACK(src, .proc/kick_the_dog, source, user), 1) if(INTENT_HELP) diff --git a/code/datums/mutations/_mutations.dm b/code/datums/mutations/_mutations.dm index b72874c329..33a082372e 100644 --- a/code/datums/mutations/_mutations.dm +++ b/code/datums/mutations/_mutations.dm @@ -129,7 +129,7 @@ /datum/mutation/human/proc/get_spans() return list() -/mob/living/carbon/proc/update_mutations_overlay() +/mob/living/proc/update_mutations_overlay() return /mob/living/carbon/human/update_mutations_overlay() @@ -142,13 +142,12 @@ if(overlays_standing[CM.layer_used]) mut_overlay = overlays_standing[CM.layer_used] var/mutable_appearance/V = CM.get_visual_indicator() - if(!mut_overlay.Find(V)) //either we lack the visual indicator or we have the wrong one - remove_overlay(CM.layer_used) - for(var/mutable_appearance/MA in CM.visual_indicators[CM.type]) - mut_overlay.Remove(MA) - mut_overlay |= V - overlays_standing[CM.layer_used] = mut_overlay - apply_overlay(CM.layer_used) + remove_overlay(CM.layer_used) //trying to find its existence defeats the point because if cut_overlays is called it doesn't bother reloading it. + for(var/mutable_appearance/MA in CM.visual_indicators[CM.type]) + mut_overlay.Remove(MA) + mut_overlay |= V + overlays_standing[CM.layer_used] = mut_overlay + apply_overlay(CM.layer_used) /datum/mutation/human/proc/modify() //called when a genome is applied so we can properly update some stats without having to remove and reapply the mutation from someone if(modified || !power || !owner) diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm index c859d38587..fbee213585 100644 --- a/code/datums/mutations/actions.dm +++ b/code/datums/mutations/actions.dm @@ -283,7 +283,7 @@ desc = "Allows a creature to voluntary discard a random appendage." quality = POSITIVE text_gain_indication = "Your joints feel loose." - instability = 30 + instability = 20 power = /obj/effect/proc_holder/spell/self/self_amputation energy_coeff = 1 @@ -316,7 +316,7 @@ return var/obj/item/bodypart/BP = pick(parts) - BP.dismember() + BP.dismember(harmless=TRUE) //spider webs /datum/mutation/human/webbing diff --git a/code/datums/mutations/radioactive.dm b/code/datums/mutations/radioactive.dm index 2e6aa50d46..687f306dab 100644 --- a/code/datums/mutations/radioactive.dm +++ b/code/datums/mutations/radioactive.dm @@ -1,6 +1,6 @@ /datum/mutation/human/radioactive name = "Radioactivity" - desc = "A volatile mutation that causes the host to sent out deadly beta radiation. This affects both the hosts and their surroundings." + desc = "A volatile mutation that causes the host to send out deadly beta radiation. This affects both the hosts and their surroundings." quality = NEGATIVE text_gain_indication = "You can feel it in your bones!" time_coeff = 5 diff --git a/code/datums/mutations/space_adaptation.dm b/code/datums/mutations/space_adaptation.dm index 94c829d367..a3a2f10f2f 100644 --- a/code/datums/mutations/space_adaptation.dm +++ b/code/datums/mutations/space_adaptation.dm @@ -1,7 +1,7 @@ //Cold Resistance gives your entire body an orange halo, and makes you immune to the effects of vacuum and cold. /datum/mutation/human/space_adaptation name = "Space Adaptation" - desc = "A strange mutation that renders the host immune to the vacuum if space. Will still need an oxygen supply." + desc = "A strange mutation that renders the host immune to the vacuum of space. Will still need an oxygen supply." quality = POSITIVE difficulty = 16 text_gain_indication = "Your body feels warm!" diff --git a/code/datums/numbered_display.dm b/code/datums/numbered_display.dm index 9aa880aa75..fc2035b39f 100644 --- a/code/datums/numbered_display.dm +++ b/code/datums/numbered_display.dm @@ -3,8 +3,8 @@ var/obj/item/sample_object var/number -/datum/numbered_display/New(obj/item/sample, _number = 1) +/datum/numbered_display/New(obj/item/sample, _number = 1, datum/component/storage/parent) if(!istype(sample)) qdel(src) - sample_object = sample + sample_object = new /obj/screen/storage/item_holder(null, parent, sample) number = _number diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 1c25cde286..a958d20276 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -160,7 +160,7 @@ /obj/screen/alert/status_effect/mesmerized name = "Mesmerized" - desc = "You cant tear your sight from who is in front of you... their gaze is simply too enthralling.." + desc = "You can't tear your sight from who is in front of you... their gaze is simply too enthralling.." icon = 'icons/mob/actions/bloodsucker.dmi' icon_state = "power_mez" diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 674787fbd4..fcfddb2788 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -412,6 +412,24 @@ If not set, defaults to check_completion instead. Set it. It's used by cryo. counter++ return counter >= 8 +/datum/objective/freedom + name = "freedom" + explanation_text = "Don't get captured by nanotrasen." + team_explanation_text = "Have all members of your team free of nanotrasen custody." + +/datum/objective/freedom/check_completion() + var/list/datum/mind/owners = get_owners() + for(var/m in owners) + var/datum/mind/M = m + if(!considered_alive(M)) + return FALSE + if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME) + return FALSE + var/turf/location = get_turf(M.current) + if(!location || istype(location, /turf/open/floor/plasteel/shuttle/red) || istype(location, /turf/open/floor/mineral/plastitanium/red/brig)) // Fails if they are in the shuttle brig + return FALSE + return TRUE + /datum/objective/escape name = "escape" explanation_text = "Escape on the shuttle or an escape pod alive and without being in custody." diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index e37fd13106..d26f8c5639 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -519,6 +519,8 @@ Class Procs: //called on machinery construction (i.e from frame to machinery) but not on initialization /obj/machinery/proc/on_construction() + for(var/obj/I in contents) + I.moveToNullspace() return //called on deconstruction before the final deletion diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index adadb40bc9..d034ba382e 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -13,7 +13,7 @@ var/temp = "Inactive" var/scantemp_ckey var/scantemp_name - var/scantemp = "Ready to Scan" + var/scantemp = "Inactive" var/menu = 1 //Which menu screen to display var/datum/data/record/active_record = null var/obj/item/disk/data/diskette = null //Mostly so the geneticist can steal everything. @@ -132,7 +132,6 @@ src.diskette = W to_chat(user, "You insert [W].") playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) - src.updateUsrDialog() else if(W.tool_behaviour == TOOL_MULTITOOL) if(istype(W.buffer, clonepod_type)) if(get_area(W.buffer) != get_area(src)) @@ -151,311 +150,233 @@ else return ..() -/obj/machinery/computer/cloning/ui_interact(mob/user) +/obj/machinery/computer/cloning/AltClick(mob/user) . = ..() + EjectDisk(user) - updatemodules(TRUE) +/obj/machinery/computer/cloning/proc/EjectDisk(mob/user) + if(diskette) + scantemp = "Disk Ejected" + diskette.forceMove(drop_location()) + usr.put_in_active_hand(diskette) + diskette = null + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) - var/dat = "" - dat += "Refresh" +/obj/machinery/computer/cloning/proc/Save(mob/user, target) + var/datum/data/record/GRAB = null + for(var/datum/data/record/record in records) + if(record.fields["id"] == target) + GRAB = record + break + else + continue + if(!GRAB || !GRAB.fields) + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + scantemp = "Failed saving to disk: Data Corruption" + return FALSE + if(!diskette || diskette.read_only) + scantemp = !diskette ? "Failed saving to disk: No disk." : "Failed saving to disk: Disk refuses override attempt." + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + return + diskette.fields = GRAB.fields.Copy() + diskette.name = "data disk - '[src.diskette.fields["name"]]'" + scantemp = "Saved to disk successfully." + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) +/obj/machinery/computer/cloning/proc/DeleteRecord(mob/user, target) + var/datum/data/record/GRAB = null + for(var/datum/data/record/record in records) + if(record.fields["id"] == target) + GRAB = record + break + else + continue + if(!GRAB) + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + scantemp = "Cannot delete: Data Corrupted." + return FALSE + var/obj/item/card/id/C = usr.get_idcard(hand_first = TRUE) + if(istype(C) || istype(C, /obj/item/pda) || istype(C, /obj/item/modular_computer/tablet)) + if(check_access(C)) + scantemp = "[GRAB.fields["name"]] => Record deleted." + records.Remove(GRAB) + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) + var/obj/item/circuitboard/computer/cloning/board = circuit + board.records = records + return TRUE + scantemp = "Cannot delete: Access Denied." + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + +/obj/machinery/computer/cloning/proc/Load(mob/user) + if(!diskette || !istype(diskette.fields) || !diskette.fields["name"] || !diskette.fields) + scantemp = "Failed loading: Load error." + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + return + for(var/datum/data/record/R in records) + if(R.fields["key"] == diskette.fields["key"]) + scantemp = "Failed loading: Data already exists!" + return FALSE + var/datum/data/record/R = new(src) + for(var/key in diskette.fields) + R.fields[key] = diskette.fields[key] + records += R + scantemp = "Loaded into internal storage successfully." + var/obj/item/circuitboard/computer/cloning/board = circuit + board.records = records + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) + +/obj/machinery/computer/cloning/proc/Clone(mob/user, target) + var/datum/data/record/C = find_record("id", target, records) + //Look for that player! They better be dead! + if(C) + var/obj/machinery/clonepod/pod = GetAvailablePod() + //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs. + if(!LAZYLEN(pods)) + temp = "Error: No Clonepods detected." + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + else if(!pod) + temp = "Error: No Clonepods available." + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + else if(!CONFIG_GET(flag/revival_cloning)) + temp = "Error: Unable to initiate cloning cycle." + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + else if(pod.occupant) + temp = "Warning: Cloning cycle already in progress." + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + else if(pod.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["blood_type"], C.fields["mrace"], C.fields["features"], C.fields["factions"], C.fields["quirks"], C.fields["bank_account"], C.fields["traumas"])) + temp = "Notice: [C.fields["name"]] => Cloning cycle in progress..." + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) + records.Remove(C) + else + temp = "Error: [C.fields["name"]] => Initialisation failure." + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + + else + temp = "Failed to clone: Data corrupted." + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + . = TRUE + +/obj/machinery/computer/cloning/proc/Toggle_lock(mob/user) + if(!scanner.is_operational()) + return + if(!scanner.locked && !scanner.occupant) //I figured out that if you're fast enough, you can lock an open pod + return + scanner.locked = !scanner.locked + playsound(src, scanner.locked ? 'sound/machines/terminal_prompt_deny.ogg' : 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) + . = TRUE + +/obj/machinery/computer/cloning/proc/Scan(mob/user) + if(!scanner.is_operational() || !scanner.occupant) + return + scantemp = "[scantemp_name] => Scanning..." + loading = TRUE + playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) + say("Initiating scan...") + var/prev_locked = scanner.locked + scanner.locked = TRUE + addtimer(CALLBACK(src, .proc/finish_scan, scanner.occupant, prev_locked), 2 SECONDS) + . = TRUE + +/obj/machinery/computer/cloning/proc/Toggle_autoprocess(mob/user) + autoprocess = !autoprocess + if(autoprocess) + START_PROCESSING(SSmachines, src) + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) + else + STOP_PROCESSING(SSmachines, src) + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) + . = TRUE + +/obj/machinery/computer/cloning/ui_data(mob/user) + var/list/data = list() + data["useRecords"] = use_records + var/list/records_to_send = list() if(use_records) if(scanner && HasEfficientPod() && scanner.scan_level >= AUTOCLONING_MINIMAL_LEVEL) - if(!autoprocess) - dat += "Autoclone" - else - dat += "Stop autoclone" - else - dat += "Autoclone" - dat += "