diff --git a/code/game/objects/items/misc.dm b/code/game/objects/items/misc.dm index e5dcb3966b..1bdcab2ac6 100644 --- a/code/game/objects/items/misc.dm +++ b/code/game/objects/items/misc.dm @@ -301,3 +301,105 @@ w_class = SIZE_MASSIVE flags_equip_slot = SLOT_BACK flags_item = SMARTGUNNER_BACKPACK_OVERRIDE + +/obj/item/rappel_harness + name = "rappel harness" + desc = "A simple, uncomfortable rappel harness with just enough safety straps to make RnD pass health and safety. It comes with an in-built descender, but has no pouches for ammunition." + icon = 'icons/obj/items/clothing/belts.dmi' + icon_state = "rappel_harness" + item_state = "rappel_harness" + w_class = SIZE_MASSIVE + flags_equip_slot = SLOT_WAIST + +/obj/item/rappel_harness/extract + name = "dual purpose rappel-fulton harness" + desc = "A fulton Surface To Air Recovery System (STARS). Special latch/hook assembly allows for aircraft on flyby equipped with a rappel system to pick up the attached item or person. The complex assembly of venlar rigging and secured buckles takes some time to set up though." + icon_state = "rappel_harness_adv" + var/shuttle_id = DROPSHIP_MIDWAY + actions_types = list(/datum/action/item_action/STARS) + +/obj/item/rappel_harness/extract/proc/try_extract(mob/living/carbon/human/user) + var/obj/docking_port/mobile/marine_dropship/shuttle = SSshuttle.getShuttle(shuttle_id) + + if(!shuttle) + return + + if(!shuttle.in_flyby) + to_chat(user, SPAN_WARNING("No shuttle detected in lower orbit, aborting extraction.")) + return + + var/obj/structure/dropship_equipment/paradrop_system/rapsys = locate() in shuttle.equipments + if(!rapsys) + to_chat(user, SPAN_WARNING("No rappel system detected in shuttle, aborting extraction.")) + return + + user.visible_message(SPAN_DANGER("[user] gets snatched off the ground with a tremendous force!")) + if(prob(50)) + user.emote("scream") + user.apply_effect(10, STUN) + animate(user, time = 2, pixel_x = 360, pixel_y = 360, flags = ANIMATION_PARALLEL) + playsound(user,'sound/effects/bamf.ogg', 50, 1) + addtimer(CALLBACK(src, PROC_REF(on_extract), user, rapsys), 1.5 SECONDS) + +/obj/item/rappel_harness/extract/attack(mob/living/carbon/human/H, mob/living/carbon/human/user) + if(H.belt) + to_chat(user, SPAN_WARNING("Remove their belt first!")) + return + + if(!is_ground_level(H.z)) + return + + var/area/location_area = get_area(H) + if(CEILING_IS_PROTECTED(location_area.ceiling, CEILING_PROTECTION_TIER_1)) + to_chat(H, SPAN_WARNING("There's no space for fulton balloon to fly in this area.")) + return + + user.visible_message(SPAN_DANGER("[user] begins to adjust the fulton device on [H] for extraction!")) + playsound(H, 'sound/items/fulton.ogg', 50, 1) + if(!do_after(user, (10 SECONDS), INTERRUPT_ALL, BUSY_ICON_HOSTILE, H)) + return + + if(H.belt) + to_chat(user, SPAN_WARNING("Remove their belt!")) + return + user.drop_inv_item_to_loc(src, H) + H.equip_to_slot_if_possible(src, WEAR_WAIST) + try_extract(H) + +/obj/item/rappel_harness/extract/proc/on_extract(mob/living/carbon/human/user, obj/structure/dropship_equipment/paradrop_system/system) + flick("rappel_hatch_opening", system) + user.pixel_x = 0 + user.pixel_y = 0 + user.forceMove(get_turf(system)) + user.apply_effect(5, WEAKEN) + if(prob(25)) + user.do_vomit() + +/datum/action/item_action/STARS/New(Target, obj/item/holder) + . = ..() + name = "Attempt Extraction" + action_icon_state = "extract" + button.name = name + button.overlays.Cut() + button.overlays += image('icons/mob/hud/actions.dmi', button, action_icon_state) + +/datum/action/item_action/STARS/action_activate() + . = ..() + var/obj/item/rappel_harness/extract/harness = holder_item + var/mob/living/carbon/human/H = usr + if(H.belt != harness) + to_chat(H, SPAN_WARNING("You have to equip the harness on your belt!")) + return + if(!is_ground_level(H.z)) + return + + var/area/location_area = get_area(H) + if(CEILING_IS_PROTECTED(location_area.ceiling, CEILING_PROTECTION_TIER_1)) + to_chat(H, SPAN_WARNING("There's no space for fulton balloon to fly in this area.")) + return + H.visible_message(SPAN_DANGER("[H] begins to adjust the fulton device for self-extraction!")) + playsound(H, 'sound/items/fulton.ogg', 50, 1) + if(!do_after(H, (10 SECONDS), INTERRUPT_ALL, BUSY_ICON_HOSTILE, harness)) + return + + harness.try_extract(H) diff --git a/code/modules/cm_marines/dropship_equipment.dm b/code/modules/cm_marines/dropship_equipment.dm index c244c41173..17d612b3ba 100644 --- a/code/modules/cm_marines/dropship_equipment.dm +++ b/code/modules/cm_marines/dropship_equipment.dm @@ -1298,6 +1298,7 @@ point_cost = 50 combat_equipment = FALSE var/system_cooldown + var/harness = /obj/item/rappel_harness /obj/structure/dropship_equipment/paradrop_system/ui_data(mob/user) . = list() @@ -1311,8 +1312,113 @@ else icon_state = "rappel_module_packaged" +/obj/effect/warning/rappel + color = "#17d17a" + /obj/structure/dropship_equipment/paradrop_system/attack_hand(mob/living/carbon/human/user) - return + var/datum/cas_iff_group/cas_group = cas_groups[FACTION_MARINE] + var/list/targets = cas_group.cas_signals + + if(!LAZYLEN(targets)) + to_chat(user, SPAN_NOTICE("No CAS signals found.")) + return + + if(!can_use(user)) + return + + var/user_input = tgui_input_list(user, "Choose a target to jump to.", name, targets) + if(!user_input) + return + + if(!can_use(user)) + return + + var/datum/cas_signal/LT = user_input + if(!istype(LT) || !LT.valid_signal()) + return + + var/turf/location = get_turf(LT.signal_loc) + var/area/location_area = get_area(location) + if(CEILING_IS_PROTECTED(location_area.ceiling, CEILING_PROTECTION_TIER_1)) + to_chat(user, SPAN_WARNING("You cannot jump to the target. It is probably underground.")) + return + + var/list/valid_turfs = list() + for(var/turf/T as anything in RANGE_TURFS(2, location)) + var/area/t_area = get_area(T) + if(!t_area || CEILING_IS_PROTECTED(t_area.ceiling, CEILING_PROTECTION_TIER_1)) + continue + if(T.density) + continue + var/found_dense = FALSE + for(var/atom/A in T) + if(A.density && A.can_block_movement) + found_dense = TRUE + break + if(found_dense) + continue + if(protected_by_pylon(TURF_PROTECTION_MORTAR, T)) + continue + valid_turfs += T + + if(!length(valid_turfs)) + to_chat(user, SPAN_WARNING("There's nowhere safe for you to land, the landing zone is too congested.")) + return + + var/turf/deploy_turf = pick(valid_turfs) + + var/obj/effect/warning/rappel/warning_zone = new(deploy_turf) + flick("rappel_hatch_opening", src) + icon_state = "rappel_hatch_open" + user.forceMove(loc) + user.client?.perspective = EYE_PERSPECTIVE + user.client?.eye = deploy_turf + + if(!do_after(user, 4 SECONDS, INTERRUPT_NO_NEEDHAND, BUSY_ICON_FRIENDLY, user, INTERRUPT_MOVED) || !can_use(user) || protected_by_pylon(TURF_PROTECTION_MORTAR, deploy_turf)) + qdel(warning_zone) + flick("rappel_hatch_closing", src) + icon_state = "rappel_hatch_closed" + user.client?.perspective = MOB_PERSPECTIVE + user.client?.eye = user + return + + new /obj/effect/rappel_rope(deploy_turf) + user.forceMove(deploy_turf) + INVOKE_ASYNC(user, TYPE_PROC_REF(/mob/living/carbon/human, animation_rappel)) + user.client?.perspective = MOB_PERSPECTIVE + user.client?.eye = user + deploy_turf.ceiling_debris_check(2) + playsound(deploy_turf, 'sound/items/rappel.ogg', 50, TRUE) + + flick("rappel_hatch_closing", src) + icon_state = "rappel_hatch_closed" + qdel(warning_zone) + +/obj/structure/dropship_equipment/paradrop_system/proc/can_use(mob/living/carbon/human/user) + if(linked_shuttle.mode != SHUTTLE_CALL) + to_chat(user, SPAN_WARNING("\The [src] can only be used while in flight.")) + return FALSE + + if(!linked_shuttle.in_flyby) + to_chat(user, SPAN_WARNING("\The [src] requires a flyby flight to be used.")) + return FALSE + + if(user.buckled) + to_chat(user, SPAN_WARNING("You cannot rappel while buckled!")) + return FALSE + + if(user.is_mob_incapacitated()) + to_chat(user, SPAN_WARNING("You are in no state to do that!")) + return FALSE + + if(!istype(user.belt, harness)) + to_chat(user, SPAN_WARNING("You must have a rappel harness equipped in order to use \the [src]!")) + return FALSE + + if(user.action_busy) + return FALSE + + return TRUE // used in the simulation room for cas runs, removed the sound and ammo depletion methods. // copying code is definitely bad, but adding an unnecessary sim or not sim boolean check in the open_fire_firemission just doesn't seem right. diff --git a/maps/map_files/rover/rover.dmm b/maps/map_files/rover/rover.dmm index f933a15ae9..e672e1b887 100644 --- a/maps/map_files/rover/rover.dmm +++ b/maps/map_files/rover/rover.dmm @@ -2825,14 +2825,30 @@ /area/golden_arrow/dorms) "As" = ( /obj/structure/closet/crate/green, -/obj/item/parachute, -/obj/item/parachute, -/obj/item/parachute, -/obj/item/parachute, -/obj/item/parachute, -/obj/item/parachute, -/obj/item/parachute, -/obj/item/parachute, +/obj/item/rappel_harness/extract{ + shuttle_id = "dropship_cyclone" + }, +/obj/item/rappel_harness/extract{ + shuttle_id = "dropship_cyclone" + }, +/obj/item/rappel_harness/extract{ + shuttle_id = "dropship_cyclone" + }, +/obj/item/rappel_harness/extract{ + shuttle_id = "dropship_cyclone" + }, +/obj/item/rappel_harness/extract{ + shuttle_id = "dropship_cyclone" + }, +/obj/item/rappel_harness/extract{ + shuttle_id = "dropship_cyclone" + }, +/obj/item/rappel_harness/extract{ + shuttle_id = "dropship_cyclone" + }, +/obj/item/rappel_harness/extract{ + shuttle_id = "dropship_cyclone" + }, /turf/open/floor/almayer{ icon_state = "cargo" },