diff --git a/sandcode/code/_HELPERS/unsorted.dm b/sandcode/code/_HELPERS/unsorted.dm new file mode 100644 index 0000000000..945032216e --- /dev/null +++ b/sandcode/code/_HELPERS/unsorted.dm @@ -0,0 +1,12 @@ +//i have no idea what the difference is between this and get_turf() +/proc/get_turf_global(atom/A, recursion_limit = 5) + var/turf/T = get_turf(A) + if(!T) + return + if(recursion_limit <= 0) + return T + if(T.loc) + var/area/R = T.loc + if(R.global_turf_object) + return get_turf_global(R.global_turf_object, recursion_limit - 1) + return T diff --git a/sandcode/code/__DEFINES/combat.dm b/sandcode/code/__DEFINES/combat.dm new file mode 100644 index 0000000000..d7c1d86228 --- /dev/null +++ b/sandcode/code/__DEFINES/combat.dm @@ -0,0 +1,6 @@ +/*ALL DEFINES RELATED TO COMBAT GO HERE*/ + +//Actual combat defines + +//click cooldowns, in tenths of a second, used for various combat actions +#define CLICK_CD_BREAKOUT 100 diff --git a/sandcode/code/controllers/subsystem/bluespace_locker.dm b/sandcode/code/controllers/subsystem/bluespace_locker.dm new file mode 100644 index 0000000000..0412d6e82c --- /dev/null +++ b/sandcode/code/controllers/subsystem/bluespace_locker.dm @@ -0,0 +1,69 @@ +SUBSYSTEM_DEF(bluespace_locker) + name = "Bluespace Locker" + flags = SS_NO_FIRE + var/obj/structure/closet/bluespace/internal/internal_locker = null + var/obj/structure/closet/bluespace/external/external_locker = null + +/datum/controller/subsystem/bluespace_locker/Initialize() + bluespaceify_random_locker() + if(external_locker) + external_locker.take_contents() + return ..() + +/datum/controller/subsystem/bluespace_locker/proc/bluespaceify_random_locker() + if(external_locker) + return + // basically any normal-looking locker that isn't a secure one + var/list/valid_lockers = typecacheof(typesof(/obj/structure/closet) - typesof(/obj/structure/closet/body_bag)\ + - typesof(/obj/structure/closet/secure_closet) - typesof(/obj/structure/closet/cabinet)\ + - typesof(/obj/structure/closet/cardboard) - typesof(/obj/structure/closet/crate)\ + - typesof(/obj/structure/closet/supplypod) - typesof(/obj/structure/closet/stasis)\ + - typesof(/obj/structure/closet/abductor) - typesof(/obj/structure/closet/bluespace), only_root_path = TRUE) + + var/list/lockers_list = list() + for(var/obj/structure/closet/L in world) + if(is_station_level(L.z) && is_type_in_typecache(L, valid_lockers)) + lockers_list += L + if(!lockers_list.len) + // Congratulations, you managed to destroy all the lockers somehow. + // Now let's make a new one. + var/targetturf = find_safe_turf() + if(!targetturf) + if(GLOB.blobstart.len > 0) + targetturf = get_turf(pick(GLOB.blobstart)) + else + CRASH("Unable to find a blobstart landmark") + lockers_list += new /obj/structure/closet(targetturf) + var/obj/structure/closet/L = pick(lockers_list) + + var/obj/structure/closet/bluespace/external/E = new(L.loc) + E.contents = L.contents + E.name = L.name + E.desc = L.desc + if(L.opened) + E.open() + E.icon = L.icon + E.icon_state = L.icon_state + E.icon_door = L.icon_door + E.icon_door_override = L.icon_door_override + E.icon_welded = L.icon_welded + qdel(L) + + relink_lockers() + +/datum/controller/subsystem/bluespace_locker/proc/relink_lockers() + if(!internal_locker) + return + //var/area/A = get_area(internal_locker) + //A.global_turf_object = external_locker + internal_locker.update_mirage() + if(!external_locker) + return + if(external_locker.opened) + internal_locker.close() + else + internal_locker.contents += external_locker.contents + internal_locker.open() + internal_locker.dump_contents() + internal_locker.update_icon() + external_locker.update_icon() \ No newline at end of file diff --git a/sandcode/code/game/area/Space_Station_13_areas.dm b/sandcode/code/game/area/Space_Station_13_areas.dm new file mode 100644 index 0000000000..e278cb774c --- /dev/null +++ b/sandcode/code/game/area/Space_Station_13_areas.dm @@ -0,0 +1,7 @@ +/area/bluespace_locker + name = "Bluespace Locker" + icon_state = "away" + requires_power = FALSE + has_gravity = STANDARD_GRAVITY + noteleport = TRUE + hidden = TRUE diff --git a/sandcode/code/game/objects/structures/crates_lockers/closets/bluespace_locker.dm b/sandcode/code/game/objects/structures/crates_lockers/closets/bluespace_locker.dm new file mode 100644 index 0000000000..d1456979e4 --- /dev/null +++ b/sandcode/code/game/objects/structures/crates_lockers/closets/bluespace_locker.dm @@ -0,0 +1,283 @@ +/obj/structure/closet/bluespace + name = "bluespace locker" + +/obj/structure/closet/bluespace/proc/get_other_locker() + return SSbluespace_locker.internal_locker + +/obj/structure/closet/bluespace/open() + var/obj/structure/closet/other = get_other_locker() + if(!other) + return ..() + if(!opened) + . = ..() + other.close() + dump_contents() + +/obj/structure/closet/bluespace/close() + var/obj/structure/closet/other = get_other_locker() + if(!other) + return ..() + if(opened) + . = ..() + other.contents += contents + other.open() + +/obj/structure/closet/bluespace/internal + name = "bluespace locker portal" + icon_state = null + desc = "" + cutting_tool = null + can_weld_shut = FALSE + anchored = TRUE + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/list/mirage_whitelist = list() + +/obj/structure/closet/bluespace/internal/Initialize() + if(SSbluespace_locker.internal_locker && SSbluespace_locker.internal_locker != src) + qdel(src) + return + SSbluespace_locker.internal_locker = src + ..() + +/obj/structure/closet/bluespace/internal/get_other_locker() + return SSbluespace_locker.external_locker + +/obj/structure/closet/bluespace/internal/can_open(user) + var/obj/structure/closet/other = get_other_locker() + if(!other) + return FALSE + if(!other.opened) + return TRUE + return other.can_close(user) +/obj/structure/closet/bluespace/internal/can_close(user) + var/obj/structure/closet/other = get_other_locker() + if(!other || other.opened) + return TRUE + return other.can_open(user) + +/obj/structure/closet/bluespace/internal/tool_interact(obj/item/W, mob/user) + return + +/obj/structure/closet/bluespace/internal/attack_hand(mob/living/user) + var/obj/structure/closet/other = get_other_locker() + if(!other) + return ..() + if(!other.opened && (other.welded || other.locked)) + if(ismovable(other.loc)) + user.changeNext_move(CLICK_CD_BREAKOUT) + user.last_special = world.time + CLICK_CD_BREAKOUT + var/atom/movable/AM = other.loc + AM.relay_container_resist(user, other) + 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 + other.visible_message("[other] begins to shake violently!") + to_chat(user, "You start pushing the door open... (this will take about [DisplayTimeText(other.breakout_time)].)") + if(do_after(user,(other.breakout_time), target = src)) + if(!user || user.stat != CONSCIOUS || other.opened || (!other.locked && !other.welded)) + return + //we check after a while whether there is a point of resisting anymore and whether the user is capable of resisting + other.bust_open() + user.visible_message("[user] successfully broke out of [other]!", + "You successfully break out of [other]!") + else + if(!other.opened) //so we don't get the message if we resisted multiple times and succeeded. + to_chat(user, "You fail to break out of [other]!") + else + return ..() + +/obj/structure/closet/bluespace/internal/update_icon() + cut_overlays() + var/obj/structure/closet/other = get_other_locker() + if(!other) + other = src + var/mutable_appearance/masked_icon = mutable_appearance('sandcode/icons/obj/closet.dmi', "bluespace_locker_mask") + masked_icon.appearance_flags = KEEP_TOGETHER + var/mutable_appearance/masking_icon = mutable_appearance(other.icon, other.icon_state) + masking_icon.blend_mode = BLEND_MULTIPLY + masked_icon.add_overlay(masking_icon) + //add_overlay(image('yogstation/icons/obj/closet.dmi', "bluespace_locker_frame")) + add_overlay(masked_icon) + if(!opened) + layer = OBJ_LAYER + if(other.icon_door) + add_overlay(image(other.icon, "[other.icon_door]_door")) + else + add_overlay(image(other.icon, "[other.icon_state]_door")) + else + layer = BELOW_OBJ_LAYER + if(other.icon_door_override) + add_overlay(image(other.icon, "[other.icon_door]_open")) + else + add_overlay(image(other.icon, "[other.icon_state]_open")) + +/obj/structure/closet/bluespace/external/onTransitZ(old_z,new_z) + var/obj/structure/closet/O = get_other_locker() + if(O) + var/area/A = get_area(O) + if(A) + for(var/atom/movable/M in A) + M.onTransitZ(old_z,new_z) + return ..() + +/obj/structure/closet/bluespace/internal/proc/update_mirage() + var/area/A = get_area(src) + for(var/atom/movable/M in A) + if(M == src) // in case someone somehow manages to teleport the bluespace locker inside of itself + continue + M.update_parallax_contents() + var/turf/internal_origin + var/turf/external_origin = get_turf(get_other_locker()) + for(var/obj/effect/landmark/bluespace_locker_origin/L in A) + internal_origin = get_turf(L) + break + mirage_whitelist.len = 0 + for(var/turf/T in view(11, external_origin)) + mirage_whitelist[T] = TRUE + for(var/turf/open/space/bluespace_locker_mirage/T in A) + T.internal_origin = internal_origin + T.external_origin = external_origin + T.turf_whitelist = mirage_whitelist + T.update_mirage() + +/obj/structure/closet/bluespace/external/Initialize() + if(SSbluespace_locker.external_locker && SSbluespace_locker.external_locker != src) + qdel(src) + return + SSbluespace_locker.external_locker = src + ..() + +/obj/structure/closet/bluespace/external/Destroy() + SSbluespace_locker.external_locker = null + SSbluespace_locker.bluespaceify_random_locker() + return ..() + +/obj/structure/closet/bluespace/external/can_open() + if(welded || locked) + return FALSE + return TRUE + +/obj/structure/closet/bluespace/external/can_close() + if(welded || locked) + return FALSE + return TRUE + + +/obj/structure/closet/bluespace/external/Moved() + var/obj/structure/closet/bluespace/internal/C = get_other_locker() + if(C) + C.update_mirage() + return ..() + +/obj/structure/closet/bluespace/external/afterShuttleMove() + var/obj/structure/closet/bluespace/internal/C = get_other_locker() + if(C) + C.update_mirage() + return ..() + +/obj/effect/landmark/bluespace_locker_origin + name = "bluespace locker origin" + +/turf/open/space/bluespace_locker_mirage + density = 1 + icon_state = "black" + blocks_air = 1 + name = "holographic projection" + desc = "A holographic projection of the area surrounding the bluespace locker" + flags_1 = NOJAUNT_1 + var/turf/internal_origin + var/turf/external_origin + var/turf/external_origin_prev + var/turf/external_origin_prev_prev + var/turf/external_origin_prev_time = -1 + var/list/turf_whitelist + var/reset_timer_id + +/turf/open/space/bluespace_locker_mirage/CanBuildHere() + return FALSE + +/turf/open/space/bluespace_locker_mirage/proc/update_mirage() + if(!internal_origin || !external_origin) + vis_contents = list() + return + if(external_origin == external_origin_prev) + return + if(world.time == external_origin_prev_time) + external_origin_prev = external_origin_prev_prev + cut_overlays() + var/glide_dir = 0 + if(external_origin_prev && external_origin_prev.z != external_origin && abs(external_origin.x - external_origin_prev.x) <= 1 && abs(external_origin.y - external_origin_prev.y) <= 1) + glide_dir = get_dir(external_origin_prev, external_origin) + var/turf/target_turf = locate(external_origin.x + x - internal_origin.x, external_origin.y + y - internal_origin.y, external_origin.z) + if(!target_turf || (turf_whitelist && !turf_whitelist[target_turf])) + vis_contents = list() + var/mutable_appearance/M = mutable_appearance('icons/turf/space.dmi', "black") + M.layer = TURF_LAYER + M.plane = FLOOR_PLANE + add_overlay(M) + else + vis_contents = list(target_turf) + if(glide_dir) + var/dx = 0 + var/dy = 0 + var/px = 0 + var/py = 0 + var/add_reset_timer + if(glide_dir & 1) + dy++ + if(glide_dir & 2) + dy-- + if(glide_dir & 4) + dx++ + if(glide_dir & 8) + dx-- + var/list/fullbrights = list() + var/area/A = target_turf.loc + if(!IS_DYNAMIC_LIGHTING(A)) + fullbrights += new /obj/effect/fullbright() + for(var/cdir in GLOB.cardinals) + if(!(glide_dir & cdir)) + continue + var/odir = turn(cdir, 180) + var/turf/T = get_step(src, odir) + if(T && T.type != /turf/open/space/bluespace_locker_mirage) + vis_contents += get_step(target_turf, odir) + add_reset_timer = TRUE + if(odir == 8) + px = -32 + if(odir == 2) + py = -32 + A = target_turf.loc + if(!IS_DYNAMIC_LIGHTING(A)) + var/obj/effect/fullbright/F = new() + switch(odir) + if(1) + F.pixel_y = 32 + if(2) + F.pixel_y = -32 + if(4) + F.pixel_x = 32 + if(8) + F.pixel_x = -32 + fullbrights += F + for(var/V in fullbrights) + var/obj/F = V + F.pixel_x -= px // cancel out the pixel_x/y in the parent + F.pixel_y -= py + add_overlay(fullbrights) + if(add_reset_timer) + reset_timer_id = addtimer(CALLBACK(src, /turf/open/space/bluespace_locker_mirage.proc/reset_to_self), world.tick_lag * 4, TIMER_UNIQUE | TIMER_NO_HASH_WAIT | TIMER_OVERRIDE | TIMER_STOPPABLE) + else if(reset_timer_id) + deltimer(reset_timer_id) + pixel_x = px + dx*32 + pixel_y = py + dy*32 + animate(src, pixel_x = px, pixel_y = py, time = world.tick_lag * 4, flags = ANIMATION_END_NOW) + external_origin_prev_time = world.time + external_origin_prev_prev = external_origin_prev + external_origin_prev = external_origin + +/turf/open/space/bluespace_locker_mirage/proc/reset_to_self() + reset_timer_id = null + update_mirage() diff --git a/sandcode/icons/obj/closet.dmi b/sandcode/icons/obj/closet.dmi new file mode 100644 index 0000000000..d4c1781946 Binary files /dev/null and b/sandcode/icons/obj/closet.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 0497784ce4..62ec8ee34f 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3560,12 +3560,15 @@ #include "modular_citadel\code\modules\reagents\chemistry\recipes\fermi.dm" #include "modular_citadel\code\modules\reagents\objects\clothes.dm" #include "modular_citadel\code\modules\reagents\objects\items.dm" +#include "sandcode\code\__DEFINES\combat.dm" #include "sandcode\code\__DEFINES\language.dm" #include "sandcode\code\__DEFINES\runechat_defines.dm" #include "sandcode\code\__DEFINES\status_effects.dm" #include "sandcode\code\__DEFINES\traits.dm" #include "sandcode\code\__DEFINES\wires.dm" +#include "sandcode\code\_HELPERS\unsorted.dm" #include "sandcode\code\_onclick\hud\screen_objects.dm" +#include "sandcode\code\controllers\subsystem\bluespace_locker.dm" #include "sandcode\code\datums\action.dm" #include "sandcode\code\datums\shuttles.dm" #include "sandcode\code\datums\mutations\combined.dm" @@ -3577,6 +3580,7 @@ #include "sandcode\code\datums\traits\neutral.dm" #include "sandcode\code\datums\wires\firealarm.dm" #include "sandcode\code\game\area\ship_area.dm" +#include "sandcode\code\game\area\Space_Station_13_areas.dm" #include "sandcode\code\game\area\areas\ruins\lavaland.dm" #include "sandcode\code\game\machinery\autodoc.dm" #include "sandcode\code\game\machinery\cryopod.dm" @@ -3600,6 +3604,7 @@ #include "sandcode\code\game\objects\items\storage\backpack.dm" #include "sandcode\code\game\objects\items\storage\bags.dm" #include "sandcode\code\game\objects\structures\ladders.dm" +#include "sandcode\code\game\objects\structures\crates_lockers\closets\bluespace_locker.dm" #include "sandcode\code\game\objects\structures\lavaland\necropolis_tendril.dm" #include "sandcode\code\game\turfs\simulated\floor\asteroid.dm" #include "sandcode\code\interactions\_interaction.dm"