diff --git a/_maps/map_files/RandomZLevels/beach.dmm b/_maps/map_files/RandomZLevels/beach.dmm index 2931d7175b7..fe4060ad52d 100644 --- a/_maps/map_files/RandomZLevels/beach.dmm +++ b/_maps/map_files/RandomZLevels/beach.dmm @@ -13,7 +13,7 @@ /turf/unsimulated/beach/water/deep/rock_wall, /area/awaymission/undersea) "ae" = ( -/obj/structure/ladder/dive_point/anchor{ +/obj/structure/ladder/unbreakable/dive_point/anchor{ id = "pirate" }, /turf/unsimulated/beach/water/deep/sand_floor, @@ -217,7 +217,7 @@ /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "aU" = ( -/obj/structure/ladder/dive_point/anchor, +/obj/structure/ladder/unbreakable/dive_point/anchor, /turf/unsimulated/beach/water/deep/sand_floor, /area/awaymission/undersea) "aV" = ( @@ -1113,7 +1113,7 @@ /turf/unsimulated/beach/water/deep, /area/awaymission/beach) "dv" = ( -/obj/structure/ladder/dive_point/buoy, +/obj/structure/ladder/unbreakable/dive_point/buoy, /turf/unsimulated/beach/water/deep, /area/awaymission/beach) "dw" = ( @@ -1306,7 +1306,7 @@ }, /area/awaymission/beach) "dW" = ( -/obj/structure/ladder/dive_point/buoy{ +/obj/structure/ladder/unbreakable/dive_point/buoy{ id = "pirate" }, /turf/unsimulated/beach/water, diff --git a/_maps/map_files/RandomZLevels/example.dmm b/_maps/map_files/RandomZLevels/example.dmm index fd85d240c36..779a0bede34 100644 --- a/_maps/map_files/RandomZLevels/example.dmm +++ b/_maps/map_files/RandomZLevels/example.dmm @@ -261,7 +261,7 @@ /turf/simulated/floor/plasteel, /area/awaymission/example) "aH" = ( -/obj/structure/ladder{ +/obj/structure/ladder/unbreakable{ height = 1; id = "example" }, @@ -782,7 +782,7 @@ /turf/simulated/floor/plasteel, /area/awaymission/example) "ca" = ( -/obj/structure/ladder{ +/obj/structure/ladder/unbreakable{ id = "example" }, /turf/simulated/floor/plating, diff --git a/_maps/map_files/RandomZLevels/spacehotel.dmm b/_maps/map_files/RandomZLevels/spacehotel.dmm index 8394ca1b635..4b45711ff3d 100644 --- a/_maps/map_files/RandomZLevels/spacehotel.dmm +++ b/_maps/map_files/RandomZLevels/spacehotel.dmm @@ -540,7 +540,7 @@ requires_power = 0 }) "aZ" = ( -/obj/structure/ladder/dive_point/anchor{ +/obj/structure/ladder/unbreakable/dive_point/anchor{ id = "weirddive"; layer = 4.5 }, @@ -1996,7 +1996,7 @@ requires_power = 0 }) "dA" = ( -/obj/structure/ladder/dive_point/anchor{ +/obj/structure/ladder/unbreakable/dive_point/anchor{ layer = 4.5 }, /turf/unsimulated/beach/water/deep/wood_floor{ @@ -2721,8 +2721,8 @@ /turf/unsimulated/beach/water/drop, /area/awaymission/spacehotel) "fC" = ( -/obj/structure/ladder/dive_point/buoy, -/obj/structure/ladder/dive_point/buoy{ +/obj/structure/ladder/unbreakable/dive_point/buoy, +/obj/structure/ladder/unbreakable/dive_point/buoy{ id = "weirddive"; invisibility = 101 }, @@ -3465,7 +3465,7 @@ }, /area/awaymission/spacehotel) "hG" = ( -/obj/structure/ladder{ +/obj/structure/ladder/unbreakable{ height = 1; icon_state = "ladder10"; id = "service" @@ -3525,7 +3525,7 @@ /area/awaymission/spacehotel) "hN" = ( /obj/effect/decal/cleanable/blood/old, -/obj/structure/ladder{ +/obj/structure/ladder/unbreakable{ height = 2; icon_state = "ladder01"; id = "service" diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index d9d21c60e1e..ad3798a010e 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -42,6 +42,7 @@ GLOBAL_LIST_INIT(global_radios, list()) //list of all radios, across all z-le GLOBAL_LIST_INIT(meteor_list, list()) //list of all meteors GLOBAL_LIST_INIT(poi_list, list()) //list of points of interest for observe/follow GLOBAL_LIST_INIT(active_jammers, list()) // List of active radio jammers +GLOBAL_LIST_EMPTY(ladders) GLOBAL_LIST_INIT(active_diseases, list()) //List of Active disease in all mobs; purely for quick referencing. diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm index 97bd986ceb9..14ee2765d02 100644 --- a/code/game/objects/structures/ladders.dm +++ b/code/game/objects/structures/ladders.dm @@ -1,33 +1,59 @@ +// Basic ladder. By default links to the z-level above/below. /obj/structure/ladder name = "ladder" desc = "A sturdy metal ladder." icon = 'icons/obj/structures.dmi' icon_state = "ladder11" - anchored = 1 - var/id = null - var/height = 0 //the 'height' of the ladder. higher numbers are considered physically higher - var/obj/structure/ladder/down = null //the ladder below this one - var/obj/structure/ladder/up = null //the ladder above this one - var/use_verb = "climbs" + anchored = TRUE + var/obj/structure/ladder/down //the ladder below this one + var/obj/structure/ladder/up //the ladder above this one + var/use_verb = "climb" -/obj/structure/ladder/New() - spawn(8) - for(var/obj/structure/ladder/L in world) - if(L.id == id) - if(L.height == (height - 1)) - down = L - if(isnull(L.up)) - L.up = src - continue - if(L.height == (height + 1)) - up = L - if(isnull(L.down)) - L.down = src - continue +/obj/structure/ladder/Initialize(mapload, obj/structure/ladder/up, obj/structure/ladder/down) + . = ..() + if (up) + src.up = up + up.down = src + up.update_icon() + if (down) + src.down = down + down.up = src + down.update_icon() + return INITIALIZE_HINT_LATELOAD - if(up && down) //if both our connections are filled - break - update_icon() +/obj/structure/ladder/Destroy(force) + if((resistance_flags & INDESTRUCTIBLE) && !force) + return QDEL_HINT_LETMELIVE + disconnect() + return ..() + +/obj/structure/ladder/LateInitialize() + // By default, discover ladders above and below us vertically + var/turf/T = get_turf(src) + + if(!down) + for(var/obj/structure/ladder/L in locate(T.x, T.y, T.z - 1)) + down = L + L.up = src // Don't waste effort looping the other way + L.update_icon() + break + if(!up) + for (var/obj/structure/ladder/L in locate(T.x, T.y, T.z + 1)) + up = L + L.down = src // Don't waste effort looping the other way + L.update_icon() + break + + update_icon() + +/obj/structure/ladder/proc/disconnect() + if(up && up.down == src) + up.down = null + up.update_icon() + if(down && down.up == src) + down.up = null + down.update_icon() + up = down = null /obj/structure/ladder/update_icon() if(up && down) @@ -42,61 +68,134 @@ else //wtf make your ladders properly assholes icon_state = "ladder00" -/obj/structure/ladder/attack_hand(mob/user as mob) +/obj/structure/ladder/singularity_pull() + if(!(resistance_flags & INDESTRUCTIBLE)) + visible_message("[src] is torn to pieces by the gravitational pull!") + qdel(src) + +/obj/structure/ladder/proc/travel(going_up, mob/user, is_ghost, obj/structure/ladder/ladder) + if(!is_ghost) + show_fluff_message(going_up, user) + ladder.add_fingerprint(user) + + var/turf/T = get_turf(ladder) + var/atom/movable/AM + if(user.pulling) + AM = user.pulling + AM.forceMove(T) + user.forceMove(T) + if(AM) + user.start_pulling(AM) + +/obj/structure/ladder/proc/use(mob/user, is_ghost = FALSE) + if(!is_ghost && !in_range(src, user)) + return + if(up && down) - switch( alert("Go up or down the ladder?", "Ladder", "Up", "Down", "Cancel") ) + var/result = alert("Go up or down [src]?", "[name]", "Up", "Down", "Cancel") + if (!is_ghost && !in_range(src, user)) + return // nice try + switch(result) if("Up") - user.visible_message("[user] climbs up \the [src]!", \ - "You climb up \the [src]!") - user.forceMove(get_turf(up)) - up.add_fingerprint(user) + travel(TRUE, user, is_ghost, up) if("Down") - user.visible_message("[user] climbs down \the [src]!", \ - "You climb down \the [src]!") - user.forceMove(get_turf(down)) - down.add_fingerprint(user) + travel(FALSE, user, is_ghost, down) if("Cancel") return - else if(up) - user.visible_message("[user] climbs up \the [src]!", \ - "You climb up \the [src]!") - user.forceMove(get_turf(up)) - up.add_fingerprint(user) - + travel(TRUE, user, is_ghost, up) else if(down) - user.visible_message("[user] climbs down \the [src]!", \ - "You climb down \the [src]!") - user.forceMove(get_turf(down)) - down.add_fingerprint(user) + travel(FALSE, user, is_ghost, down) + else + to_chat(user, "[src] doesn't seem to lead anywhere!") - add_fingerprint(user) + if(!is_ghost) + add_fingerprint(user) -/obj/structure/ladder/attackby(obj/item/W, mob/user as mob, params) - return attack_hand(user) +/obj/structure/ladder/attack_hand(mob/user) + use(user) -/obj/structure/ladder/dive_point/buoy +/obj/structure/ladder/attackby(obj/item/W, mob/user, params) + return use(user) + +/obj/structure/ladder/attack_robot(mob/living/silicon/robot/R) + if(R.Adjacent(src)) + return use(R) + +//ATTACK GHOST IGNORING PARENT RETURN VALUE +/obj/structure/ladder/attack_ghost(mob/dead/observer/user) + use(user, TRUE) + +/obj/structure/ladder/proc/show_fluff_message(going_up, mob/user) + if(going_up) + user.visible_message("[user] climbs up [src].","You [use_verb] up [src].") + else + user.visible_message("[user] climbs down [src].","You [use_verb] down [src].") + + +// Indestructible away mission ladders which link based on a mapped ID and height value rather than X/Y/Z. +/obj/structure/ladder/unbreakable + name = "sturdy ladder" + desc = "An extremely sturdy metal ladder." + resistance_flags = INDESTRUCTIBLE + var/id + var/height = 0 // higher numbers are considered physically higher + +/obj/structure/ladder/unbreakable/Initialize(mapload) + GLOB.ladders += src + return ..() + +/obj/structure/ladder/unbreakable/Destroy() + . = ..() + if(. != QDEL_HINT_LETMELIVE) + GLOB.ladders -= src + +/obj/structure/ladder/unbreakable/LateInitialize() + // Override the parent to find ladders based on being height-linked + if(!id || (up && down)) + update_icon() + return + + for(var/O in GLOB.ladders) + var/obj/structure/ladder/unbreakable/L = O + if(L.id != id) + continue // not one of our pals + if(!down && L.height == height - 1) + down = L + L.up = src + L.update_icon() + if (up) + break // break if both our connections are filled + else if(!up && L.height == height + 1) + up = L + L.down = src + L.update_icon() + if (down) + break // break if both our connections are filled + + update_icon() + +/obj/structure/ladder/unbreakable/dive_point/buoy name = "diving point buoy" desc = "A buoy marking the location of an underwater dive area." icon = 'icons/misc/beach.dmi' icon_state = "buoy" id = "dive" height = 2 - use_verb = "dives" + use_verb = "swim" layer = MOB_LAYER + 0.2 //0.1 higher than the water overlay, this also means people can "swim" behind/under it -/obj/structure/ladder/dive_point/anchor +/obj/structure/ladder/unbreakable/dive_point/anchor name = "diving point anchor" desc = "An anchor tethered to the buoy at the surface, to keep the dive area marked." icon = 'icons/misc/beach.dmi' icon_state = "anchor" id = "dive" height = 1 - use_verb = "ascends" light_range = 5 -/obj/structure/ladder/dive_point/New() - ..() +/obj/structure/ladder/dive_point/Initialize(mapload) + . = ..() set_light(light_range, light_power) //magical glowing anchor /obj/structure/ladder/dive_point/update_icon() diff --git a/code/modules/mining/lavaland/loot/tendril_loot.dm b/code/modules/mining/lavaland/loot/tendril_loot.dm index c8579fbec65..3727c103a64 100644 --- a/code/modules/mining/lavaland/loot/tendril_loot.dm +++ b/code/modules/mining/lavaland/loot/tendril_loot.dm @@ -77,6 +77,21 @@ add_fingerprint(M) +//Book of Babel + +/obj/item/book_of_babel + name = "Book of Babel" + desc = "An ancient tome written in countless tongues." + icon = 'icons/obj/library.dmi' + icon_state = "book1" + w_class = 2 + +/obj/item/book_of_babel/attack_self(mob/user) + to_chat(user, "You flip through the pages of the book, quickly and conveniently learning every language in existence. Somewhat less conveniently, the aging book crumbles to dust in the process. Whoops.") + user.grant_all_languages() + new /obj/effect/decal/cleanable/ash(get_turf(user)) + qdel(src) + //Potion of Flight: as we do not have the "Angel" species this currently does not work. /obj/item/reagent_containers/glass/bottle/potion @@ -117,6 +132,30 @@ H.emote("scream") ..()*/ +/obj/item/jacobs_ladder + name = "jacob's ladder" + desc = "A celestial ladder that violates the laws of physics." + icon = 'icons/obj/structures.dmi' + icon_state = "ladder00" + +/obj/item/jacobs_ladder/attack_self(mob/user) + var/turf/T = get_turf(src) + var/ladder_x = T.x + var/ladder_y = T.y + to_chat(user, "You unfold the ladder. It extends much farther than you were expecting.") + var/last_ladder = null + for(var/i in 1 to world.maxz) + if(is_admin_level(i) || is_away_level(i)) + continue + var/turf/T2 = locate(ladder_x, ladder_y, i) + last_ladder = new /obj/structure/ladder/unbreakable/jacob(T2, null, last_ladder) + qdel(src) + +// Inherit from unbreakable but don't set ID, to suppress the default Z linkage +/obj/structure/ladder/unbreakable/jacob + name = "jacob's ladder" + desc = "An indestructible celestial ladder that violates the laws of physics." + //Boat /obj/vehicle/lavaboat diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index a2ee237c659..1a0a063e33f 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -18,7 +18,7 @@ if(!add_loot) return - var/loot = rand(1,25) + var/loot = rand(1, 26) switch(loot) if(1) new /obj/item/shared_storage/red(src) @@ -48,7 +48,7 @@ if(11) new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/berserker(src) if(12) - new /obj/item/sord(src) + new /obj/item/jacobs_ladder(src) if(13) new /obj/item/nullrod/scythe/talking(src) if(14) @@ -78,6 +78,8 @@ if(24) new /obj/item/spellbook/oneuse/summonitem(src) if(25) + new /obj/item/book_of_babel(src) + if(26) new /obj/item/borg/upgrade/modkit/lifesteal(src) new /obj/item/bedsheet/cult(src) diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm index 57725da8eb2..9391f24035a 100644 --- a/code/modules/mob/language.dm +++ b/code/modules/mob/language.dm @@ -762,6 +762,8 @@ desc = "Bark bark bark." key = "vu" - +/mob/proc/grant_all_languages() + for(var/la in GLOB.all_languages) + add_language(la) #undef SCRAMBLE_CACHE_LEN diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 42e710d0cf6..0a11a4397ad 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -65,3 +65,11 @@ . = ..() if(!S1.lock_shuttle_doors && id_tag == "s_docking_airlock") INVOKE_ASYNC(src, .proc/unlock) + +/obj/structure/ladder/onShuttleMove() + if(resistance_flags & INDESTRUCTIBLE) + // simply don't be moved + return FALSE + disconnect() + LateInitialize() + return ..() \ No newline at end of file