diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 6dae40ce07..1b9dccc6d9 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -110,6 +110,7 @@ #define CLICK_CD_RAPID 2 #define CLICK_CD_CLICK_ABILITY 6 #define CLICK_CD_HANDCUFFED 10 +#define CLICK_CD_BREAKOUT 10 #define CLICK_CD_RESIST 20 #define CLICK_CD_GRABBING 10 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 331ef3cca9..246bd60b78 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -335,6 +335,8 @@ #define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; #define INTERACTING_WITH(X, Y) (Y in X.do_afters) +#define DOING_INTERACTION_WITH_TARGET(user, target) (LAZYACCESS(user.do_afters, target)) + // / Field of vision defines. #define FOV_90_DEGREES 90 #define FOV_180_DEGREES 180 diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index bebad85775..2ae21f4838 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1189,13 +1189,14 @@ GLOBAL_REAL_VAR(list/stack_trace_storage) str = "0" + str . = str -/atom/proc/Shake(pixelshiftx = 15, pixelshifty = 15, duration = 250) +/// Perform a shake on an atom, resets its position afterwards +/atom/proc/Shake(pixelshiftx = 2, pixelshifty = 2, duration = 2.5 SECONDS, shake_interval = 0.02 SECONDS) var/initialpixelx = pixel_x var/initialpixely = pixel_y - var/shiftx = rand(-pixelshiftx,pixelshiftx) - var/shifty = rand(-pixelshifty,pixelshifty) - animate(src, pixel_x = shiftx, pixel_y = shifty, time = 0.2, loop = duration, flags = ANIMATION_RELATIVE) - animate(pixel_x = initialpixelx, pixel_y = initialpixely, time = 0.2) + animate(src, pixel_x = initialpixelx + rand(-pixelshiftx,pixelshiftx), pixel_y = initialpixelx + rand(-pixelshifty,pixelshifty), time = shake_interval, flags = ANIMATION_PARALLEL) + for (var/i in 3 to ((duration / shake_interval))) // Start at 3 because we already applied one, and need another to reset + animate(pixel_x = initialpixelx + rand(-pixelshiftx,pixelshiftx), pixel_y = initialpixely + rand(-pixelshifty,pixelshifty), time = shake_interval) + animate(pixel_x = initialpixelx, pixel_y = initialpixely, time = shake_interval) /atom/proc/do_jiggle(targetangle = 25, timer = 20) var/matrix/OM = matrix(transform) diff --git a/code/_rendering/parallax/types/space.dm b/code/_rendering/parallax/types/space.dm index 1a6cf191cb..74e1e218d6 100644 --- a/code/_rendering/parallax/types/space.dm +++ b/code/_rendering/parallax/types/space.dm @@ -43,7 +43,7 @@ /atom/movable/screen/parallax_layer/space/random blend_mode = BLEND_OVERLAY - speed = 3 + speed = 2 layer = 3 parallax_intensity = PARALLAX_INSANE @@ -56,7 +56,7 @@ /atom/movable/screen/parallax_layer/space/planet icon_state = "planet" blend_mode = BLEND_OVERLAY - absolute = TRUE //Status of seperation + absolute = FALSE //Status of seperation speed = 3 layer = 30 dynamic_self_tile = FALSE diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index 945136466f..28c8226ed8 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -67,6 +67,14 @@ // layer = ATMOS_GROUP_LAYER // plane = ATMOS_GROUP_PLANE +/// Door overlay for animating closets +/obj/effect/overlay/closet_door + anchored = TRUE + plane = FLOAT_PLANE + layer = FLOAT_LAYER + vis_flags = VIS_INHERIT_ID + appearance_flags = KEEP_TOGETHER | LONG_GLIDE | PIXEL_SCALE + // /obj/effect/overlay/light_visible // name = "" // icon = 'icons/effects/light_overlays/light_32.dmi' diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index bc5b37655b..90b121b11c 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -8,8 +8,20 @@ integrity_failure = 0.25 armor = list(MELEE = 20, BULLET = 10, LASER = 10, ENERGY = 0, BOMB = 10, BIO = 0, RAD = 0, FIRE = 70, ACID = 60) + /// The overlay for the closet's door + var/obj/effect/overlay/closet_door/door_obj + /// Whether or not this door is being animated + var/is_animating_door = FALSE + /// Vertical squish of the door + var/door_anim_squish = 0.12 + /// The maximum angle the door will be drawn at + var/door_anim_angle = 136 + /// X position of the closet door hinge + var/door_hinge_x = -6.5 + /// Amount of time it takes for the door animation to play + var/door_anim_time = 1.5 // set to 0 to make the door not animate at all + var/icon_door = null - var/icon_door_override = FALSE //override to have open overlay use icon different to its base's var/has_door_icon = TRUE // Set to false to skip trying to draw a door icon. var/secure = FALSE //secure locker or not, also used if overriding a non-secure locker with a secure door overlay to add fancy lights var/opened = FALSE @@ -18,6 +30,12 @@ var/large = TRUE var/wall_mounted = 0 //never solid (You can always pass over it) var/breakout_time = 1200 + + /// How many pixels the closet can shift on the x axis when shaking + var/x_shake_pixel_shift = 2 + /// How many pixels the closet can shift on the y axes when shaking + var/y_shake_pixel_shift = 1 + var/message_cooldown var/can_weld_shut = TRUE var/horizontal = FALSE @@ -58,6 +76,7 @@ /obj/structure/closet/Destroy() dump_contents(override = FALSE) + QDEL_NULL(door_obj) return ..() /obj/structure/closet/update_icon() @@ -73,24 +92,75 @@ /obj/structure/closet/proc/closet_update_overlays(list/new_overlays) . = new_overlays - if(opened) - . += "[icon_door_override ? icon_door : icon_state]_open" + if(!is_animating_door) + if(opened) + . += "[icon_state]_open" + return + + if(has_door_icon) + . += "[icon_door || icon_state]_door" + + if(welded) + . += icon_welded + + if(!secure) + return + if(broken) + . += "off" + . += "sparking" + //Overlay is similar enough for both that we can use the same mask for both + . += emissive_appearance(icon, "locked", alpha = src.alpha) + . += locked ? "locked" : "unlocked" + +/// Animates the closet door opening and closing +/obj/structure/closet/proc/animate_door(closing = FALSE) + if(!door_anim_time) return + if(!door_obj) + door_obj = new + var/default_door_icon = "[icon_door || icon_state]_door" + vis_contents += door_obj + door_obj.icon = icon + door_obj.icon_state = default_door_icon + is_animating_door = TRUE + var/num_steps = door_anim_time / world.tick_lag - if(has_door_icon) - . += "[icon_door || icon_state]_door" - if(welded) - . += icon_welded + for(var/step in 0 to num_steps) + var/angle = door_anim_angle * (closing ? 1 - (step/num_steps) : (step/num_steps)) + var/matrix/door_transform = get_door_transform(angle) + var/door_state + var/door_layer - if(!secure) - return - if(broken) - . += "off" - . += "sparking" - //Overlay is similar enough for both that we can use the same mask for both - . += emissive_appearance(icon, "locked", alpha = src.alpha) - . += locked ? "locked" : "unlocked" + if (angle >= 90) + door_state = "[icon_state]_back" + door_layer = FLOAT_LAYER + else + door_state = default_door_icon + door_layer = ABOVE_MOB_LAYER + if(step == 0) + door_obj.transform = door_transform + door_obj.icon_state = door_state + door_obj.layer = door_layer + else if(step == 1) + animate(door_obj, transform = door_transform, icon_state = door_state, layer = door_layer, time = world.tick_lag, flags = ANIMATION_END_NOW) + else + animate(transform = door_transform, icon_state = door_state, layer = door_layer, time = world.tick_lag) + addtimer(CALLBACK(src, .proc/end_door_animation), door_anim_time, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_CLIENT_TIME) + +/// Ends the door animation and removes the animated overlay +/obj/structure/closet/proc/end_door_animation() + is_animating_door = FALSE + vis_contents -= door_obj + update_icon() + +/// Calculates the matrix to be applied to the animated door overlay +/obj/structure/closet/proc/get_door_transform(angle) + var/matrix/door_matrix = matrix() + door_matrix.Translate(-door_hinge_x, 0) + door_matrix.Multiply(matrix(cos(angle), 0, 0, -sin(angle) * door_anim_squish, 1, 0)) + door_matrix.Translate(door_hinge_x, 0) + return door_matrix /obj/structure/closet/examine(mob/user) . = ..() @@ -176,6 +246,7 @@ density = FALSE climb_time *= 0.5 //it's faster to climb onto an open thing dump_contents() + animate_door(FALSE) update_icon() after_open(user, force) return TRUE @@ -242,6 +313,7 @@ playsound(loc, close_sound, 15, TRUE, -3) opened = FALSE density = TRUE + animate_door(TRUE) update_icon() after_close(user) return TRUE @@ -452,34 +524,38 @@ return TRUE /obj/structure/closet/container_resist(mob/living/user) + if(isstructure(loc)) + relay_container_resist(user, loc) if(opened) return if(ismovable(loc)) - // user.changeNext_move(CLICK_CD_BREAKOUT) - // user.last_special = world.time + CLICK_CD_BREAKOUT + user.changeNext_move(CLICK_CD_BREAKOUT) + user.last_special = world.time + CLICK_CD_BREAKOUT var/atom/movable/AM = loc AM.relay_container_resist(user, src) return if(!welded && !locked) open() return - //okay, so the closet is either welded or locked... resist!!! - // user.changeNext_move(CLICK_CD_BREAKOUT) - // user.last_special = world.time + CLICK_CD_BREAKOUT - user.visible_message("[src] begins to shake violently!", \ - "You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)", \ - "You hear banging from [src].") - if(do_after(user, breakout_time, src, IGNORE_TARGET_LOC_CHANGE|IGNORE_HELD_ITEM)) + user.changeNext_move(CLICK_CD_BREAKOUT) + user.last_special = world.time + CLICK_CD_BREAKOUT + user.visible_message(span_warning("[src] begins to shake violently!"), \ + span_notice("You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)"), \ + span_hear("You hear banging from [src].")) + + addtimer(CALLBACK(src, PROC_REF(check_if_shake)), 1 SECONDS) + + if(do_after(user,(breakout_time), target = src)) if(!user || user.stat != CONSCIOUS || user.loc != src || opened || (!locked && !welded) ) return //we check after a while whether there is a point of resisting anymore and whether the user is capable of resisting - user.visible_message("[user] successfully broke out of [src]!", - "You successfully break out of [src]!") + user.visible_message(span_danger("[user] successfully broke out of [src]!"), + span_notice("You successfully break out of [src]!")) bust_open() else if(user.loc == src) //so we don't get the message if we resisted multiple times and succeeded. - to_chat(user, "You fail to break out of [src]!") + to_chat(user, span_warning("You fail to break out of [src]!")) /obj/structure/closet/proc/bust_open() welded = FALSE //applies to all lockers @@ -487,6 +563,27 @@ broken = TRUE //applies to secure lockers only open() +/// Check if someone is still resisting inside, and choose to either keep shaking or stop shaking the closet +/obj/structure/closet/proc/check_if_shake() + // Assuming we decide to shake again, how long until we check to shake again + var/next_check_time = 1 SECONDS + + // How long we shake between different calls of Shake(), so that it starts shaking and stops, instead of a steady shake + var/shake_duration = 0.3 SECONDS + + for(var/mob/living/mob in contents) + if(DOING_INTERACTION_WITH_TARGET(mob, src)) + // Shake and queue another check_if_shake + Shake(x_shake_pixel_shift, y_shake_pixel_shift, shake_duration, shake_interval = 0.1 SECONDS) + addtimer(CALLBACK(src, PROC_REF(check_if_shake)), next_check_time) + return TRUE + + // If we reach here, nobody is resisting, so dont shake + return FALSE + +/obj/structure/closet/relay_container_resist(mob/living/user, obj/container) + container.container_resist() + /obj/structure/closet/AltClick(mob/user) ..() if(!user.canUseTopic(src, BE_CLOSE) || !isturf(loc)) diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm index f6e1e6fe9e..75072405b5 100644 --- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm +++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm @@ -14,6 +14,7 @@ material_drop = /obj/item/stack/sheet/cardboard delivery_icon = "deliverybox" anchorable = FALSE + door_anim_time = 0 // no animation var/move_speed_multiplier = 1 var/move_delay = FALSE var/egged = 0 diff --git a/code/game/objects/structures/crates_lockers/closets/gimmick.dm b/code/game/objects/structures/crates_lockers/closets/gimmick.dm index b33962d8de..2b57aada68 100644 --- a/code/game/objects/structures/crates_lockers/closets/gimmick.dm +++ b/code/game/objects/structures/crates_lockers/closets/gimmick.dm @@ -6,6 +6,7 @@ max_integrity = 70 material_drop = /obj/item/stack/sheet/mineral/wood cutting_tool = TOOL_SCREWDRIVER + door_anim_time = 0 // no animation /obj/structure/closet/acloset name = "strange closet" diff --git a/code/game/objects/structures/crates_lockers/closets/secure/bar.dm b/code/game/objects/structures/crates_lockers/closets/secure/bar.dm index 36b770cd25..59a6bdc20e 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/bar.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/bar.dm @@ -6,6 +6,7 @@ max_integrity = 70 material_drop = /obj/item/stack/sheet/mineral/wood cutting_tool = TOOL_SCREWDRIVER + door_anim_time = 0 // no animation /obj/structure/closet/secure_closet/bar/PopulateContents() ..() diff --git a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm index e5a7793ab7..057bbd4377 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm @@ -1,6 +1,9 @@ /obj/structure/closet/secure_closet/freezer icon_state = "freezer" var/jones = FALSE + door_anim_squish = 0.22 + door_anim_angle = 123 + door_anim_time = 4 /obj/structure/closet/secure_closet/freezer/Destroy() recursive_organ_check(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm index 52abb00cc7..778ae6b68a 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm @@ -3,6 +3,7 @@ name = "personal closet" req_access = list(ACCESS_ALL_PERSONAL_LOCKERS) var/registered_name = null + door_anim_time = 0 // no animation /obj/structure/closet/secure_closet/personal/PopulateContents() ..() diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index a2239caa92..50c73684f3 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -163,6 +163,7 @@ max_integrity = 70 material_drop = /obj/item/stack/sheet/mineral/wood cutting_tool = TOOL_SCREWDRIVER + door_anim_time = 0 // no animation /obj/structure/closet/secure_closet/detective/PopulateContents() ..() diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 650fdf040b..70293d1097 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -12,6 +12,9 @@ climbable = TRUE climb_time = 10 //real fast, because let's be honest stepping into or onto a crate is easy climb_stun = 0 //climbing onto crates isn't hard, guys + door_anim_time = 0 // no animation + x_shake_pixel_shift = 1 + y_shake_pixel_shift = 2 delivery_icon = "deliverycrate" material_drop = /obj/item/stack/sheet/plasteel material_drop_amount = 5 diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm index 7506ae8975..95d8abce4f 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm +++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm @@ -825,6 +825,7 @@ icon_state = "abductor" icon_door = "abductor" can_weld_shut = FALSE + door_anim_time = 0 material_drop = /obj/item/stack/sheet/mineral/abductor /obj/structure/door_assembly/door_assembly_abductor diff --git a/icons/effects/parallax.dmi b/icons/effects/parallax.dmi deleted file mode 100755 index 36dd36ee7a..0000000000 Binary files a/icons/effects/parallax.dmi and /dev/null differ diff --git a/icons/obj/closet.dmi b/icons/obj/closet.dmi index 70f5001c26..ec01f4b9a3 100644 Binary files a/icons/obj/closet.dmi and b/icons/obj/closet.dmi differ diff --git a/icons/screen/parallax.dmi b/icons/screen/parallax.dmi index 36dd36ee7a..5fe1b16520 100644 Binary files a/icons/screen/parallax.dmi and b/icons/screen/parallax.dmi differ diff --git a/modular_splurt/code/game/objects/effects/decals/turfdecal/markings.dm b/modular_splurt/code/game/objects/effects/decals/turfdecal/markings.dm index 7480a9d37e..6f595c8f06 100644 --- a/modular_splurt/code/game/objects/effects/decals/turfdecal/markings.dm +++ b/modular_splurt/code/game/objects/effects/decals/turfdecal/markings.dm @@ -133,47 +133,3 @@ /obj/effect/turf_decal/siding/wideplating/dark/end icon_state = "siding_wideplating_end" - -// Replaces the SS13 plaque -/obj/effect/turf_decal/plaque - icon = 'modular_splurt/icons/turf/decals.dmi' - icon_state = "L1" - -/obj/effect/turf_decal/plaque - icon_state = "L2" - -/obj/effect/turf_decal/plaque - icon_state = "L3" - -/obj/effect/turf_decal/plaque - icon_state = "L4" - -/obj/effect/turf_decal/plaque - icon_state = "L5" - -/obj/effect/turf_decal/plaque - icon_state = "L6" - -/obj/effect/turf_decal/plaque - icon_state = "L7" - -/obj/effect/turf_decal/plaque - icon_state = "L8" - -/obj/effect/turf_decal/plaque - icon_state = "L9" - -/obj/effect/turf_decal/plaque - icon_state = "L10" - -/obj/effect/turf_decal/plaque - icon_state = "L11" - -/obj/effect/turf_decal/plaque - icon_state = "L12" - -/obj/effect/turf_decal/plaque - icon_state = "L13" - -/obj/effect/turf_decal/plaque - icon_state = "L14" diff --git a/modular_splurt/icons/obj/closet.dmi b/modular_splurt/icons/obj/closet.dmi index 35655f23e4..ece32676c8 100644 Binary files a/modular_splurt/icons/obj/closet.dmi and b/modular_splurt/icons/obj/closet.dmi differ diff --git a/modular_splurt/icons/turf/decals.dmi b/modular_splurt/icons/turf/decals.dmi index 715e3fd253..2f434059d6 100644 Binary files a/modular_splurt/icons/turf/decals.dmi and b/modular_splurt/icons/turf/decals.dmi differ