Skip to content

Commit

Permalink
Falling Falcons: Paratroopers Edition (#6150)
Browse files Browse the repository at this point in the history
# About the pull request
Replaces rappel system with paradrop system. You need a parachute on
your back to paradrop (successfully). You also can drop objects from the
above, but in most cases it's not a great idea (they can break). Crates
can be paradropped too, so this can be an interesting alternative to req
drops. In order for marines to paradrop, PO needs to choose a flare on
the console and lock the paradrop system to the signal. After that the
aft hatch will be open and you are free to jump! Just don't forget the
parachute. (SGs can wear parachutes with armor)

Credits to @SubjectD9341 for the sprites.

# Explain why it's good for the game
It's cooler than rappel.

# Testing Photographs and Procedure
<details>


https://github.com/cmss13-devs/cmss13/assets/115417687/53ec9ddb-5ff5-4af2-9df5-17835e5bc2bc


https://github.com/cmss13-devs/cmss13/assets/115417687/f296c1c7-1cfe-4f8f-89c0-233f612d8542


https://github.com/cmss13-devs/cmss13/assets/115417687/579dac1c-9248-4a7c-b0f7-c9466a131335


https://github.com/cmss13-devs/cmss13/assets/115417687/35728d57-7c83-4539-bf84-accb99127ad7

</details>


# Changelog
:cl: ihatethisengine
add: Added paradropping
del: Removed rappeling
/:cl:

---------

Co-authored-by: fira <[email protected]>
Co-authored-by: Drathek <[email protected]>
  • Loading branch information
3 people committed May 10, 2024
1 parent ba8c620 commit bec6653
Show file tree
Hide file tree
Showing 19 changed files with 363 additions and 164 deletions.
2 changes: 2 additions & 0 deletions code/__DEFINES/equipment.dm
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@
#define IGNITING_ITEM (1<<13)
/// Overrides NODROP in some cases (stripping)
#define FORCEDROP_CONDITIONAL (1<<14)
/// Overrides smartgunner not being able to wear backpacks
#define SMARTGUNNER_BACKPACK_OVERRIDE (1<<15)
//==========================================================================================


Expand Down
29 changes: 29 additions & 0 deletions code/datums/supply_packs/gear.dm
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,32 @@
containertype = /obj/structure/closet/crate/ammo
containername = "fulton recovery device crate"
group = "Gear"

/datum/supply_packs/parachute
name = "parachute crate (x20)"
contains = list(
/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/parachute,
/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/parachute,
/obj/item/parachute,
/obj/item/parachute,
)
cost = 40
containertype = /obj/structure/closet/crate/supply
containername = "parachute crate"
group = "Gear"
55 changes: 55 additions & 0 deletions code/game/machinery/computer/dropship_weapons.dm
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,61 @@

initiate_firemission(user, fm_tag, direction, text2num(offset_x_value), text2num(offset_y_value))
return TRUE
if("paradrop-lock")
var/obj/docking_port/mobile/marine_dropship/linked_shuttle = SSshuttle.getShuttle(shuttle_tag)
if(!linked_shuttle)
return FALSE
if(linked_shuttle.mode != SHUTTLE_CALL)
return FALSE
if(linked_shuttle.paradrop_signal)
clear_locked_turf_and_lock_aft()
return TRUE
var/datum/cas_signal/sig = get_cas_signal(camera_target_id)
if(!sig)
to_chat(user, SPAN_WARNING("No signal chosen."))
return FALSE
var/turf/location = get_turf(sig.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("Target is obscured."))
return FALSE
var/equipment_tag = params["equipment_id"]
for(var/obj/structure/dropship_equipment/equipment as anything in shuttle.equipments)
var/mount_point = equipment.ship_base.attach_id
if(mount_point != equipment_tag)
continue
if(istype(equipment, /obj/structure/dropship_equipment/paradrop_system))
var/obj/structure/dropship_equipment/paradrop_system/paradrop_system = equipment
if(paradrop_system.system_cooldown > world.time)
to_chat(user, SPAN_WARNING("You toggled the system too recently."))
return
paradrop_system.system_cooldown = world.time + 5 SECONDS
paradrop_system.visible_message(SPAN_NOTICE("[equipment] hums as it locks to a signal."))
break
linked_shuttle.paradrop_signal = sig
addtimer(CALLBACK(src, PROC_REF(open_aft_for_paradrop)), 2 SECONDS)
RegisterSignal(linked_shuttle.paradrop_signal, COMSIG_PARENT_QDELETING, PROC_REF(clear_locked_turf_and_lock_aft))
RegisterSignal(linked_shuttle, COMSIG_SHUTTLE_SETMODE, PROC_REF(clear_locked_turf_and_lock_aft))
return TRUE

/obj/structure/machinery/computer/dropship_weapons/proc/open_aft_for_paradrop()
var/obj/docking_port/mobile/marine_dropship/shuttle = SSshuttle.getShuttle(shuttle_tag)
if(!shuttle || !shuttle.paradrop_signal || shuttle.mode != SHUTTLE_CALL)
return
shuttle.door_control.control_doors("force-unlock", "aft", TRUE)

/obj/structure/machinery/computer/dropship_weapons/proc/clear_locked_turf_and_lock_aft()
SIGNAL_HANDLER
var/obj/docking_port/mobile/marine_dropship/shuttle = SSshuttle.getShuttle(shuttle_tag)
if(!shuttle)
return
shuttle.door_control.control_doors("force-lock", "aft", TRUE)
visible_message(SPAN_WARNING("[src] displays an alert as it loses the paradrop target."))
for(var/obj/structure/dropship_equipment/paradrop_system/parad in shuttle.equipments)
parad.visible_message(SPAN_WARNING("[parad] displays an alert as it loses the paradrop target."))
UnregisterSignal(shuttle.paradrop_signal, COMSIG_PARENT_QDELETING)
UnregisterSignal(shuttle, COMSIG_SHUTTLE_SETMODE)
shuttle.paradrop_signal = null

/obj/structure/machinery/computer/dropship_weapons/proc/get_weapon(eqp_tag)
var/obj/docking_port/mobile/marine_dropship/dropship = SSshuttle.getShuttle(shuttle_tag)
Expand Down
7 changes: 5 additions & 2 deletions code/game/machinery/doors/multi_tile.dm
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@
return
..()

/obj/structure/machinery/door/airlock/multi_tile/almayer/dropshiprear/unlock()
if(is_reserved_level(z))
/obj/structure/machinery/door/airlock/multi_tile/almayer/dropshiprear/unlock(forced=FALSE)
if(is_reserved_level(z) && !forced)
return // in orbit
..()

Expand All @@ -294,6 +294,9 @@
if(xeno.action_busy)
return

if(is_reserved_level(z)) //no prying in space even though it's funny
return

var/direction
switch(id)
if("starboard_door")
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/vending/vendor_types/requisitions.dm
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
list("Technician Welder-Satchel", round(scale * 5), /obj/item/storage/backpack/marine/engineerpack/satchel, VENDOR_ITEM_REGULAR),
list("IMP Ammo Rack", round(scale * 2), /obj/item/storage/backpack/marine/ammo_rack, VENDOR_ITEM_REGULAR),
list("Radio Telephone Pack", round(scale * 2), /obj/item/storage/backpack/marine/satchel/rto, VENDOR_ITEM_REGULAR),
list("Parachute", round(scale * 20), /obj/item/parachute, VENDOR_ITEM_REGULAR),

list("BELTS", -1, null, null),
list("G8-A General Utility Pouch", round(scale * 2), /obj/item/storage/backpack/general_belt, VENDOR_ITEM_REGULAR),
Expand All @@ -89,7 +90,6 @@
list("M276 M82F Holster Rig", round(scale * 2), /obj/item/storage/belt/gun/flaregun, VENDOR_ITEM_REGULAR),
list("M276 Shotgun Shell Loading Rig", round(scale * 10), /obj/item/storage/belt/shotgun, VENDOR_ITEM_REGULAR),
list("M276 Mortar Operator Belt", round(scale * 2), /obj/item/storage/belt/gun/mortarbelt, VENDOR_ITEM_REGULAR),
list("Rappel Harness", round(scale * 20), /obj/item/rappel_harness, VENDOR_ITEM_REGULAR),

list("POUCHES", -1, null, null),
list("Autoinjector Pouch", round(scale * 1), /obj/item/storage/pouch/autoinjector, VENDOR_ITEM_REGULAR),
Expand Down
15 changes: 8 additions & 7 deletions code/game/objects/items/misc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,12 @@
new /obj/item/evidencebag(src)
new /obj/item/evidencebag(src)

/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"
/obj/item/parachute
name = "parachute"
desc = "A surprisingly small yet bulky pack with just enough safety straps to make RnD pass health and safety. The label says the pack comes with two parachutes - main and reserve, but you doubt the pack can fit even one."
icon = 'icons/obj/items/clothing/backpacks.dmi'
icon_state = "parachute_pack"
item_state = "parachute_pack"
w_class = SIZE_MASSIVE
flags_equip_slot = SLOT_WAIST
flags_equip_slot = SLOT_BACK
flags_item = SMARTGUNNER_BACKPACK_OVERRIDE
190 changes: 154 additions & 36 deletions code/game/turfs/transit.dm
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
if(isobserver(crosser) || crosser.anchored)
return

if(!isitem(crosser) && !isliving(crosser))
if(!isobj(crosser) && !isliving(crosser))
return

if(!istype(old_loc, /turf/open/space))
Expand All @@ -24,6 +24,8 @@
/turf/open/space/transit/proc/handle_crosser(atom/movable/crosser)
if(QDELETED(crosser))
return
if(crosser.can_paradrop()) //let's not delete people who arent meant to be deleted... This shouldn't happen normally, but if it does, congratulations, you gamed the system
return
qdel(crosser)

/turf/open/space/transit/dropship
Expand All @@ -39,14 +41,42 @@
if(!istype(dropship) || dropship.mode != SHUTTLE_CALL)
return ..()

if(dropship.destination.id != DROPSHIP_LZ1 && dropship.destination.id != DROPSHIP_LZ2)
return ..() // we're not heading towards the LZs

// you just jumped out of a dropship heading towards the LZ, have fun living on the way down!
var/list/ground_z_levels = SSmapping.levels_by_trait(ZTRAIT_GROUND)
if(!length(ground_z_levels))
return ..()

if(dropship.paradrop_signal) //if dropship in paradrop mode, drop them near the signal. Whether they have a parachute or not
var/list/valid_turfs = list()
var/turf/location = get_turf(dropship.paradrop_signal.signal_loc)
for(var/turf/turf as anything in RANGE_TURFS(crosser.get_paradrop_scatter(), location))
var/area/turf_area = get_area(turf)
if(!turf_area || CEILING_IS_PROTECTED(turf_area.ceiling, CEILING_PROTECTION_TIER_1))
continue
if(turf.density)
continue
var/found_dense = FALSE
for(var/atom/turf_atom in turf)
if(turf_atom.density && turf_atom.can_block_movement)
found_dense = TRUE
break
if(found_dense)
continue
if(protected_by_pylon(TURF_PROTECTION_MORTAR, turf))
continue
valid_turfs += turf
var/turf/deploy_turf
if(length(valid_turfs)) //if we found a fitting place near the landing zone...
deploy_turf = pick(valid_turfs)
else //if we somehow did not. Drop them right on the signal then, there is nothing we can do
deploy_turf = location
if(crosser.can_paradrop())
INVOKE_ASYNC(crosser, TYPE_PROC_REF(/atom/movable, handle_paradrop), deploy_turf, dropship.name)
return
INVOKE_ASYNC(crosser, TYPE_PROC_REF(/atom/movable, handle_airdrop), deploy_turf, dropship.name)
return

//find a random spot to drop them
var/list/area/potential_areas = shuffle(SSmapping.areas_in_z["[ground_z_levels[1]]"])

for(var/area/maybe_this_area in potential_areas)
Expand All @@ -67,53 +97,141 @@
continue // couldnt find one in 10 loops, check another area

// we found a good turf, lets drop em
INVOKE_ASYNC(src, PROC_REF(handle_drop), crosser, possible_turf, dropship.name)
if(crosser.can_paradrop())
INVOKE_ASYNC(crosser, TYPE_PROC_REF(/atom/movable, handle_paradrop), possible_turf, dropship.name)
return
INVOKE_ASYNC(crosser, TYPE_PROC_REF(/atom/movable, handle_airdrop), possible_turf, dropship.name)
return

//we didn't find a turf to drop them... This shouldn't happen usually
if(crosser.can_paradrop()) //don't delete them if they were supposed to paradrop
to_chat(crosser, SPAN_BOLDWARNING("Your harness got stuck and you got thrown back in the dropship."))
var/turf/projected = get_ranged_target_turf(crosser.loc, turn(dir, 180), 15)
INVOKE_ASYNC(crosser, TYPE_PROC_REF(/atom/movable, throw_atom), projected, 50, SPEED_FAST, null, TRUE)
return
return ..() // they couldn't be dropped, just delete them

/turf/open/space/transit/dropship/proc/handle_drop(atom/movable/crosser, turf/target, dropship_name)
if(QDELETED(crosser))
return
ADD_TRAIT(crosser, TRAIT_IMMOBILIZED, TRAIT_SOURCE_DROPSHIP_INTERACTION)
/atom/movable/proc/can_paradrop()
return FALSE

/atom/movable/proc/get_paradrop_scatter()
return 7

/mob/living/carbon/human/can_paradrop()
if(istype(back, /obj/item/parachute))
return TRUE
return ..()

/obj/structure/closet/crate/can_paradrop() //for now all crates can be paradropped
return TRUE

/obj/structure/closet/crate/get_paradrop_scatter() //crates land closer to the signal
return 4

crosser.pixel_z = 360
crosser.forceMove(target)
crosser.visible_message(SPAN_WARNING("[crosser] falls out of the sky."), SPAN_HIGHDANGER("As you fall out of the [dropship_name], you plummet towards the ground."))
animate(crosser, time = 6, pixel_z = 0, flags = ANIMATION_PARALLEL)
/obj/structure/largecrate/can_paradrop()
return TRUE

REMOVE_TRAIT(crosser, TRAIT_IMMOBILIZED, TRAIT_SOURCE_DROPSHIP_INTERACTION)
if(isitem(crosser))
var/obj/item/item = crosser
item.explosion_throw(200) // give it a bit of a kick
/obj/structure/largecrate/get_paradrop_scatter()
return 4

/atom/movable/proc/handle_paradrop(turf/target, dropship_name)
clear_active_explosives()
ADD_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_SOURCE_DROPSHIP_INTERACTION)
ADD_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_DROPSHIP_INTERACTION)
var/image/cables = image('icons/obj/structures/droppod_32x64.dmi', src, "chute_cables_static")
overlays += cables
var/image/chute = image('icons/obj/structures/droppod_64x64.dmi', src, "chute_static")

chute.pixel_x -= 16
chute.pixel_y += 16

overlays += chute
pixel_z = 360
forceMove(target)
playsound(src, 'sound/items/fulton.ogg', 30, 1)
animate(src, time = 3.5 SECONDS, pixel_z = 0, flags = ANIMATION_PARALLEL)
addtimer(CALLBACK(target, TYPE_PROC_REF(/turf, ceiling_debris)), 2 SECONDS)
addtimer(CALLBACK(src, PROC_REF(clear_parachute), cables, chute), 3.5 SECONDS)

/mob/living/carbon/handle_paradrop(turf/target, dropship_name)
..()
if(client)
playsound_client(client, 'sound/items/fulton.ogg', src, 50, 1) //for some reason you don't hear the sound while dropping, maybe because of force move?

/atom/movable/proc/clear_parachute(image/cables, image/chute)
if(QDELETED(src))
return
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_SOURCE_DROPSHIP_INTERACTION)
REMOVE_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_DROPSHIP_INTERACTION)
overlays -= cables
overlays -= chute

/atom/movable/proc/clear_active_explosives()
for(var/obj/item/explosive/explosive in contents)
if(!explosive.active)
continue
explosive.deconstruct(FALSE)

/atom/movable/proc/handle_airdrop(turf/target, dropship_name)
clear_active_explosives()
ADD_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_SOURCE_DROPSHIP_INTERACTION)
pixel_z = 360
forceMove(target)
animate(src, time = 6, pixel_z = 0, flags = ANIMATION_PARALLEL)
INVOKE_ASYNC(target, TYPE_PROC_REF(/turf, ceiling_debris))
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, TRAIT_SOURCE_DROPSHIP_INTERACTION)

/obj/handle_airdrop(turf/target, dropship_name)
..()
if(!indestructible && prob(30)) // throwing objects from the air is not always a good idea
deconstruct(FALSE)

/obj/structure/closet/handle_airdrop(turf/target, dropship_name) // good idea but no
if(!opened)
for(var/atom/movable/content in src)
INVOKE_ASYNC(content, TYPE_PROC_REF(/atom/movable, handle_airdrop), target, dropship_name)
open()
. = ..()

if(!isliving(crosser))
return // don't know how you got here, but you shouldnt be here.
var/mob/living/fallen_mob = crosser
/obj/item/handle_airdrop(turf/target, dropship_name)
..()
if(QDELETED(src))
return
if(!indestructible && w_class < SIZE_MEDIUM) //tiny and small items will be lost, good riddance
deconstruct(FALSE)
return
explosion_throw(200) // give it a bit of a kick

/obj/item/explosive/handle_airdrop(turf/target, dropship_name)
if(active)
deconstruct(FALSE)
return
..()

/mob/living/handle_airdrop(turf/target, dropship_name)
..()
playsound(target, "punch", rand(20, 70), TRUE)
playsound(target, "punch", rand(20, 70), TRUE)
playsound(target, "bone_break", rand(20, 70), TRUE)
playsound(target, "bone_break", rand(20, 70), TRUE)

fallen_mob.KnockDown(10) // 10 seconds
fallen_mob.Stun(3) // 3 seconds


if(ishuman(fallen_mob))
var/mob/living/carbon/human/human = fallen_mob
human.last_damage_data = create_cause_data("falling from [dropship_name]", human)
// I'd say falling from space is pretty much like getting hit by an explosion
human.take_overall_armored_damage(300, ARMOR_BOMB, limb_damage_chance = 100)
// but just in case, you will still take a ton of damage.
human.take_overall_damage(200, used_weapon = "falling", limb_damage_chance = 100)
if(human.stat != DEAD)
human.death(human.last_damage_data)
fallen_mob.status_flags |= PERMANENTLY_DEAD
return
KnockDown(10)
Stun(3)
// take a little bit more damage otherwise
fallen_mob.take_overall_damage(400, used_weapon = "falling", limb_damage_chance = 100)
take_overall_damage(400, used_weapon = "falling", limb_damage_chance = 100)
visible_message(SPAN_WARNING("[src] falls out of the sky."), SPAN_HIGHDANGER("As you fall out of the [dropship_name], you plummet towards the ground."))

/mob/living/carbon/human/handle_airdrop(turf/target, dropship_name)
..()
last_damage_data = create_cause_data("falling from [dropship_name]", src)
// I'd say falling from space is pretty much like getting hit by an explosion
take_overall_armored_damage(300, ARMOR_BOMB, limb_damage_chance = 100)
// but just in case, you will still take a ton of damage.
take_overall_damage(200, used_weapon = "falling", limb_damage_chance = 100)
if(stat < DEAD)
death(last_damage_data)
status_flags |= PERMANENTLY_DEAD


/turf/open/space/transit/dropship/alamo
shuttle_tag = DROPSHIP_ALAMO
Expand Down
Loading

0 comments on commit bec6653

Please sign in to comment.