diff --git a/code/__DEFINES/misc_defines.dm b/code/__DEFINES/misc_defines.dm
index f344f422427..ba08edc8af7 100644
--- a/code/__DEFINES/misc_defines.dm
+++ b/code/__DEFINES/misc_defines.dm
@@ -745,3 +745,8 @@ do { \
//! The number of seconds between the start of the UNIX and BYOND epochs.
#define BYOND_EPOCH_UNIX 946702800
+
+#define BRIDGE_SPAWN_SUCCESS 0
+#define BRIDGE_SPAWN_TOO_WIDE 1
+#define BRIDGE_SPAWN_TOO_NARROW 2
+#define BRIDGE_SPAWN_BAD_TERRAIN 3
diff --git a/code/datums/ruins/bridges/bridges.dm b/code/datums/ruins/bridges/bridges.dm
index f8e1b51df56..35155716682 100644
--- a/code/datums/ruins/bridges/bridges.dm
+++ b/code/datums/ruins/bridges/bridges.dm
@@ -29,7 +29,7 @@
/obj/effect/spawner/dynamic_bridge
var/max_length = 8
var/min_length = 4
- var/bridge_theme = LONG_BRIDGE_THEME_CULT
+ var/bridge_theme
var/list/forwards_backwards
var/list/side_to_side
var/turf/forward_goal
@@ -37,20 +37,21 @@
/obj/effect/spawner/dynamic_bridge/Initialize(mapload)
. = ..()
- bridge_theme = pick(
- LONG_BRIDGE_THEME_CULT,
- LONG_BRIDGE_THEME_HIERO,
- LONG_BRIDGE_THEME_CLOCKWORK,
- LONG_BRIDGE_THEME_STONE,
- LONG_BRIDGE_THEME_WOOD,
- LONG_BRIDGE_THEME_CATWALK,
- )
+ if(!bridge_theme)
+ bridge_theme = pick(
+ LONG_BRIDGE_THEME_CULT,
+ LONG_BRIDGE_THEME_HIERO,
+ LONG_BRIDGE_THEME_CLOCKWORK,
+ LONG_BRIDGE_THEME_STONE,
+ LONG_BRIDGE_THEME_WOOD,
+ LONG_BRIDGE_THEME_CATWALK,
+ )
return INITIALIZE_HINT_LATELOAD
/obj/effect/spawner/dynamic_bridge/LateInitialize()
forwards_backwards = list(NORTH, SOUTH)
side_to_side = list(EAST, WEST)
- if(!attempt_bridge())
+ if(attempt_bridge() != BRIDGE_SPAWN_SUCCESS)
forward_goal = null
backward_goal = null
forwards_backwards = list(EAST, WEST)
@@ -85,6 +86,16 @@
return TRUE
+/obj/effect/spawner/dynamic_bridge/proc/bridgeable_turf(turf/T)
+ // Pre SSlatemapping, before we've replaced mapping turfs with their river theme
+ if(istype(T, /turf/simulated/floor/lava/mapping_lava))
+ return TRUE
+ // Everything else
+ if(istype(T, /turf/simulated/floor/chasm))
+ return TRUE
+ if(istype(T, /turf/simulated/floor/lava))
+ return TRUE
+
/// Returns whether the passed in turf is a valid "passage". A valid passage is
/// a lava tile that has lava on both sides of it. Invalid passage tiles do not
/// look good as bridge walkways and defeat the purpose of there is floor right
@@ -92,11 +103,11 @@
/obj/effect/spawner/dynamic_bridge/proc/valid_passage(turf/T)
if(T.flags & LAVA_BRIDGE)
return FALSE
- if(!istype(T, /turf/simulated/floor/lava/mapping_lava))
+ if(!bridgeable_turf(T))
return FALSE
- if(!istype(get_step(T, side_to_side[1]), /turf/simulated/floor/lava/mapping_lava))
+ if(!bridgeable_turf(get_step(T, side_to_side[1])))
return FALSE
- if(!istype(get_step(T, side_to_side[2]), /turf/simulated/floor/lava/mapping_lava))
+ if(!bridgeable_turf(get_step(T, side_to_side[2])))
return FALSE
var/area/A = get_area(T)
if(istype(A, /area/lavaland/surface/gulag_rock))
@@ -225,7 +236,7 @@
/// Make a tile safe for player passage, for use at the bridge entrance and exits
/obj/effect/spawner/dynamic_bridge/proc/cleanup_edge(turf/T)
- if(istype(T, /turf/simulated/floor/lava/mapping_lava))
+ if(bridgeable_turf(T))
T.ChangeTurf(/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface)
T.icon_state = "basalt" // hate
@@ -260,6 +271,13 @@
count++
walk_dir = forwards_backwards[1]
+ if(bad_passage)
+ return BRIDGE_SPAWN_BAD_TERRAIN
+ if(count < min_length)
+ return BRIDGE_SPAWN_TOO_NARROW
+ if(count > max_length)
+ return BRIDGE_SPAWN_TOO_WIDE
+
if(!bad_passage && count >= min_length && forward_goal && backward_goal)
for(var/turf/T in get_line(forward_goal, backward_goal))
make_walkway(T)
@@ -278,7 +296,7 @@
get_step(get_step(backward_goal, side_to_side[2]), forwards_backwards[2])))
cleanup_edge(T)
- return TRUE
+ return BRIDGE_SPAWN_SUCCESS
/// Checks if we are going out of bounds. Returns TRUE if we are close (less than or equal to 2 turfs) to a border
/obj/effect/spawner/dynamic_bridge/proc/out_of_bounds(direction, turf/current_turf)
@@ -296,6 +314,20 @@
return current_turf.x <= 2
return TRUE
+/obj/effect/spawner/dynamic_bridge/capsule
+ bridge_theme = LONG_BRIDGE_THEME_CATWALK
+
+/obj/effect/spawner/dynamic_bridge/capsule/Initialize(mapload, thrown_dir)
+ . = ..()
+ if(thrown_dir in list(EAST, WEST))
+ forwards_backwards = list(EAST, WEST)
+ side_to_side = list(NORTH, SOUTH)
+ else
+ forwards_backwards = list(NORTH, SOUTH)
+ side_to_side = list(EAST, WEST)
+
+ return INITIALIZE_HINT_NORMAL
+
/datum/map_template/ruin/lavaland/zlvl_bridge
prefix = "_maps/map_files/RandomRuins/LavaRuins/zlvl_bridges/"
ci_exclude = /datum/map_template/ruin/lavaland/zlvl_bridge
diff --git a/code/game/turfs/simulated/floor/chasm.dm b/code/game/turfs/simulated/floor/chasm.dm
index 2a41fb32514..1f34b58e429 100644
--- a/code/game/turfs/simulated/floor/chasm.dm
+++ b/code/game/turfs/simulated/floor/chasm.dm
@@ -146,9 +146,11 @@
if(!AM.simulated || is_type_in_typecache(AM, forbidden_types) || AM.throwing)
return FALSE
//Flies right over the chasm
+ if(HAS_TRAIT(AM, TRAIT_FLYING))
+ return FALSE
if(isliving(AM))
var/mob/living/M = AM
- if(HAS_TRAIT(M, TRAIT_FLYING) || M.floating)
+ if(M.floating)
return FALSE
if(istype(M.buckled, /obj/tgvehicle/scooter/skateboard/hoverboard))
return FALSE
diff --git a/code/game/turfs/simulated/floor/lava.dm b/code/game/turfs/simulated/floor/lava.dm
index 9d8393db817..bb75c9f9147 100644
--- a/code/game/turfs/simulated/floor/lava.dm
+++ b/code/game/turfs/simulated/floor/lava.dm
@@ -76,7 +76,10 @@
var/thing_to_check = src
if(AM)
thing_to_check = list(AM)
- for(var/thing in thing_to_check)
+ for(var/atom/thing in thing_to_check)
+ if(HAS_TRAIT(thing, TRAIT_FLYING))
+ continue //YOU'RE FLYING OVER IT
+
if(isobj(thing))
var/obj/O = thing
if(!O.simulated)
@@ -97,8 +100,6 @@
else if(isliving(thing))
. = TRUE
var/mob/living/L = thing
- if(HAS_TRAIT(L, TRAIT_FLYING))
- continue //YOU'RE FLYING OVER IT
var/buckle_check = L.buckling
if(!buckle_check)
buckle_check = L.buckled
@@ -206,7 +207,10 @@
var/thing_to_check = src
if(AM)
thing_to_check = list(AM)
- for(var/thing in thing_to_check)
+ for(var/atom/thing in thing_to_check)
+ if(HAS_TRAIT(thing, TRAIT_FLYING))
+ continue //YOU'RE FLYING OVER IT
+
if(isobj(thing))
var/obj/O = thing
if(!O.simulated)
diff --git a/code/modules/mining/bridge_capsule.dm b/code/modules/mining/bridge_capsule.dm
new file mode 100644
index 00000000000..454bc862b8d
--- /dev/null
+++ b/code/modules/mining/bridge_capsule.dm
@@ -0,0 +1,70 @@
+/obj/item/bridge_capsule
+ name = "bluespace bridge capsule"
+ desc = "A bridge stored within a pocket of bluespace."
+ icon_state = "bridge_capsule"
+ icon = 'icons/obj/mining.dmi'
+ w_class = WEIGHT_CLASS_TINY
+ origin_tech = "engineering=3;bluespace=3"
+ new_attack_chain = TRUE
+ var/used = FALSE
+ var/thrown_dir
+
+/obj/item/bridge_capsule/examine(mob/user)
+ . = ..()
+ . += "To use, activate it in-hand and throw it between the middle of the two areas you are trying to bridge."
+ . += "For best results, ensure the land on both sides of the span are smooth."
+
+/obj/item/bridge_capsule/throw_at(atom/target, range, speed, mob/thrower, spin, diagonals_first, datum/callback/callback, force, dodgeable)
+ . = ..()
+ if(used)
+ ADD_TRAIT(src, TRAIT_FLYING, "[UID()]")
+ thrown_dir = get_dir(thrower, target)
+ to_chat(thrower, "[src] hovers above the ground as it prepares to deploy...")
+
+/obj/item/bridge_capsule/activate_self(mob/user)
+ . = ..()
+ if(!used)
+ loc.visible_message("[src] begins to shake. Stand back!")
+ used = TRUE
+ addtimer(CALLBACK(src, PROC_REF(deploy)), 5 SECONDS)
+
+/obj/item/bridge_capsule/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
+ . = ..()
+ thrown_dir = throwingdatum.init_dir
+
+/obj/item/bridge_capsule/proc/deploy()
+ if(ismob(loc))
+ to_chat(loc, "[src] stops shaking. Looks like it needs to be thrown to deploy.")
+ used = FALSE
+ return
+
+ if(istype(loc, SSmapping.lavaland_theme?.primary_turf_type))
+ loc.visible_message("[src] suddenly bursts!")
+ var/obj/effect/spawner/dynamic_bridge/capsule/spawner = new(loc, thrown_dir)
+ var/result = spawner.attempt_bridge()
+ var/fail_message = "[src] buzzes loudly and falls to the ground!"
+ switch(result)
+ if(BRIDGE_SPAWN_SUCCESS)
+ qdel(src)
+ if(BRIDGE_SPAWN_BAD_TERRAIN)
+ loc.visible_message("[fail_message] It looks like the terrain here is too uneven for a bridge.")
+ stop_flying()
+ if(BRIDGE_SPAWN_TOO_NARROW)
+ loc.visible_message("[fail_message] It looks like the span here is too narrow.")
+ stop_flying()
+ if(BRIDGE_SPAWN_TOO_WIDE)
+ loc.visible_message("[fail_message] It looks like the span here is too wide.")
+ stop_flying()
+
+ qdel(spawner)
+ return
+
+ stop_flying()
+ to_chat(loc, "[src] flutters to the ground, refusing to deploy. Maybe you can't do that here?")
+
+/obj/item/bridge_capsule/proc/stop_flying()
+ REMOVE_TRAIT(src, TRAIT_FLYING, "[UID()]")
+ // Easiest/fastest way to trigger chasms/lava underneath us to
+ // determine if something needs to happen when we lose our trait
+ if(isturf(loc))
+ loc.process()
diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm
index f53b72938ab..1998a12f84b 100644
--- a/code/modules/mining/machine_vending.dm
+++ b/code/modules/mining/machine_vending.dm
@@ -59,6 +59,7 @@
EQUIPMENT("Mining Charge Detonator", /obj/item/detonator, 150),
EQUIPMENT("Shelter Capsule", /obj/item/survivalcapsule, 400),
EQUIPMENT("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000),
+ EQUIPMENT("Bridge Capsule", /obj/item/bridge_capsule, 300),
EQUIPMENT("Stabilizing Serum", /obj/item/hivelordstabilizer, 400),
EQUIPMENT("Survival Medipen", /obj/item/reagent_containers/hypospray/autoinjector/survival, 500),
)
diff --git a/icons/obj/mining.dmi b/icons/obj/mining.dmi
index adf10d0a2d6..d869254834e 100644
Binary files a/icons/obj/mining.dmi and b/icons/obj/mining.dmi differ
diff --git a/paradise.dme b/paradise.dme
index a4a18400f65..efdd4e56593 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -2260,6 +2260,7 @@
#include "code\modules\maze_generation\loot_modules\_loot_module_base.dm"
#include "code\modules\maze_generation\loot_modules\turf_painter.dm"
#include "code\modules\mining\abandonedcrates.dm"
+#include "code\modules\mining\bridge_capsule.dm"
#include "code\modules\mining\fulton.dm"
#include "code\modules\mining\machine_processing.dm"
#include "code\modules\mining\machine_redemption.dm"