Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Falling Falcons: Paratroopers Edition #6150

Merged
merged 20 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions code/__DEFINES/equipment.dm
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,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
ihatethisengine marked this conversation as resolved.
Show resolved Hide resolved

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)
Drulikar marked this conversation as resolved.
Show resolved Hide resolved
..()
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
Loading