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 16 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 no being able to wear backpacks
ihatethisengine marked this conversation as resolved.
Show resolved Hide resolved
#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"
53 changes: 53 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,59 @@

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)
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))
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
170 changes: 134 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/T as anything in RANGE_TURFS(crosser.get_paradrop_scatter(), 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
ihatethisengine marked this conversation as resolved.
Show resolved Hide resolved
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,121 @@
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

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)
/mob/living/carbon/human/can_paradrop()
if(istype(back, /obj/item/parachute))
return TRUE
return ..()

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/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

/atom/movable/proc/handle_paradrop(turf/target, dropship_name)
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")
src.overlays += cables
ihatethisengine marked this conversation as resolved.
Show resolved Hide resolved
var/image/chute = image('icons/obj/structures/droppod_64x64.dmi', src, "chute_static")

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

src.overlays += chute
src.pixel_z = 360
src.forceMove(target)
ihatethisengine marked this conversation as resolved.
Show resolved Hide resolved
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/handle_airdrop(turf/target, dropship_name)
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_SMALL) //small items will be lost, good riddance
deconstruct(FALSE)
return
src.explosion_throw(200) // give it a bit of a kick
ihatethisengine marked this conversation as resolved.
Show resolved Hide resolved

/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
5 changes: 4 additions & 1 deletion code/modules/clothing/suits/marine_armor/smartgunner.dm
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
/obj/item/clothing/suit/storage/marine/smartgunner/mob_can_equip(mob/equipping_mob, slot, disable_warning = FALSE)
. = ..()

if(equipping_mob.back)
if(equipping_mob.back && !(equipping_mob.back.flags_item & SMARTGUNNER_BACKPACK_OVERRIDE))
to_chat(equipping_mob, SPAN_WARNING("You can't equip [src] while wearing a backpack."))
return FALSE

Expand All @@ -48,6 +48,9 @@
if(slot != WEAR_BACK)
return

if(equipping_item.flags_item & SMARTGUNNER_BACKPACK_OVERRIDE)
return

. = COMPONENT_HUMAN_CANCEL_ATTEMPT_EQUIP

if(equipping_item.flags_equip_slot == SLOT_BACK)
Expand Down
Loading
Loading