diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm index a1123ea4c4..76c61e9353 100644 --- a/code/game/objects/effects/step_triggers.dm +++ b/code/game/objects/effects/step_triggers.dm @@ -175,12 +175,9 @@ var/global/list/tele_landmarks = list() // Terrible, but the alternative is loop if(isliving(A)) // Someday, implement parachutes. For now, just turbomurder whoever falls. var/mob/living/L = A - for(var/i = 1 to 6) - L.adjustBruteLoss(100) + L.fall_impact(T, 42, 90, FALSE, TRUE) //You will not be defibbed from this. message_admins("\The [A] fell out of the sky.") - explosion(T, 0, 1, 2) A.forceMove(T) - T.visible_message("\A [A] falls out of the sky and crashes into \the [T]!") else message_admins("ERROR: planetary_fall step trigger lacks a planet to fall onto.") return diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index e7c5d9068a..d34bb3565d 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -5,6 +5,7 @@ /obj/item/weapon/storage/backpack name = "backpack" desc = "You wear this on your back and put items into it." + icon = 'icons/obj/clothing/backpack.dmi' icon_state = "backpack" sprite_sheets = list( "Teshari" = 'icons/mob/species/seromi/back.dmi' @@ -347,6 +348,8 @@ /obj/item/weapon/storage/backpack/messenger/black icon_state = "courierbagblk" + +//Purses /obj/item/weapon/storage/backpack/purse name = "purse" desc = "A small, fashionable bag typically worn over the shoulder." @@ -355,3 +358,69 @@ w_class = ITEMSIZE_LARGE max_w_class = ITEMSIZE_NORMAL max_storage_space = ITEMSIZE_COST_NORMAL * 5 + +//Parachutes +/obj/item/weapon/storage/backpack/parachute + name = "parachute" + desc = "A specially made backpack, designed to help one survive jumping from incredible heights. It sacrifices some storage space for that added functionality." + icon_state = "parachute" + item_state_slots = list(slot_r_hand_str = "backpack", slot_l_hand_str = "backpack") + max_storage_space = ITEMSIZE_COST_NORMAL * 5 + +/obj/item/weapon/storage/backpack/parachute/examine(mob/user) + var/msg = desc + if(get_dist(src, user) <= 1) + if(parachute) + msg += " It seems to be packed." + else + msg += " It seems to be unpacked." + to_chat(user, msg) + +/obj/item/weapon/storage/backpack/parachute/handleParachute() + parachute = FALSE //If you parachute in, the parachute has probably been used. + +/obj/item/weapon/storage/backpack/parachute/verb/pack_parachute() + + set name = "Pack/Unpack Parachute" + set category = "Object" + set src in usr + + if(!istype(src.loc, /mob/living)) + return + + var/mob/living/carbon/human/H = usr + + if(!istype(H)) + return + if(H.stat) + return + if(H.back == src) + to_chat(H, "How do you expect to work on \the [src] while it's on your back?") + return + + if(!parachute) //This packs the parachute + visible_message("\The [H] starts to pack \the [src]!", \ + "You start to pack \the [src]!", \ + "You hear the shuffling of cloth.") + if(do_after(H, 50)) + visible_message("\The [H] finishes packing \the [src]!", \ + "You finish packing \the [src]!", \ + "You hear the shuffling of cloth.") + parachute = TRUE + else + visible_message("\The [src] gives up on packing \the [src]!", \ + "You give up on packing \the [src]!") + return + else //This unpacks the parachute + visible_message("\The [src] starts to unpack \the [src]!", \ + "You start to unpack \the [src]!", \ + "You hear the shuffling of cloth.") + if(do_after(H, 25)) + visible_message("\The [src] finishes unpacking \the [src]!", \ + "You finish unpacking \the [src]!", \ + "You hear the shuffling of cloth.") + parachute = FALSE + else + visible_message("\The [src] decides not to unpack \the [src]!", \ + "You decide not to unpack \the [src]!") + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/animals/parrot.dm b/code/modules/mob/living/simple_animal/animals/parrot.dm index bf84d01e0e..59e04cf1d6 100644 --- a/code/modules/mob/living/simple_animal/animals/parrot.dm +++ b/code/modules/mob/living/simple_animal/animals/parrot.dm @@ -56,6 +56,10 @@ meat_type = /obj/item/weapon/reagent_containers/food/snacks/cracker + hovering = TRUE + softfall = TRUE + parachuting = TRUE + var/parrot_state = PARROT_WANDER //Hunt for a perch when created var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in life() being run every single tick. var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 089f629be5..9e9ca371ff 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -211,4 +211,9 @@ var/mob_size = MOB_MEDIUM var/forbid_seeing_deadchat = FALSE // Used for lings to not see deadchat, and to have ghosting behave as if they were not really dead. - var/seedarkness = 1 //Determines mob's ability to see shadows. 1 = Normal vision, 0 = darkvision \ No newline at end of file + var/seedarkness = 1 //Determines mob's ability to see shadows. 1 = Normal vision, 0 = darkvision + + // Falling things + var/hovering = FALSE // Is the mob floating or flying in some way? If so, don't fall normally. //Not implemented yet, idea is to let them ignore terrain slowdown and falling down floors + var/softfall = FALSE // Is the mob able to lessen their impact upon falling? + var/parachuting = FALSE // Is the mob able to jump out of planes and survive? Don't check this directly outside of CanParachute(). \ No newline at end of file diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index 0d24cea1e4..25d099155d 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -285,26 +285,141 @@ /obj/structure/stairs/CheckFall(var/atom/movable/falling_atom) return 1 -// Called by CheckFall when we actually hit something. Oof -/atom/movable/proc/fall_impact(var/atom/hit_atom) - visible_message("\The [src] falls from above and slams into \the [hit_atom]!", "You hear something slam into \the [hit_atom].") +// Can't fall onto ghosts +/mob/observer/dead/CheckFall() + return 0 + +// Called by CheckFall when we actually hit something. Various Vars will be described below +// hit_atom is the thing we fall on +// damage_min is the smallest amount of damage a thing (currently only mobs and mechs) will take from falling +// damage_max is the largest amount of damage a thing (currently only mobs and mechs) will take from falling. +// If silent is True, the proc won't play sound or give a message. +// If planetary is True, it's harder to stop the fall damage + +/atom/movable/proc/fall_impact(var/atom/hit_atom, var/damage_min = 0, var/damage_max = 10, var/silent = FALSE, var/planetary = FALSE) + if(!silent) + visible_message("\The [src] falls from above and slams into \the [hit_atom]!", "You hear something slam into \the [hit_atom].") // Take damage from falling and hitting the ground -/mob/living/carbon/human/fall_impact(var/turf/landing) - visible_message("\The [src] falls from above and slams into \the [landing]!", \ - "You fall off and hit \the [landing]!", \ - "You hear something slam into \the [landing].") - playsound(loc, "punch", 25, 1, -1) - var/damage = 15 // Because wounds heal rather quickly, 15 should be enough to discourage jumping off but not be enough to ruin you, at least for the first time. - apply_damage(rand(0, damage), BRUTE, BP_HEAD) - apply_damage(rand(0, damage), BRUTE, BP_TORSO) - apply_damage(rand(0, damage), BRUTE, BP_L_LEG) - apply_damage(rand(0, damage), BRUTE, BP_R_LEG) - apply_damage(rand(0, damage), BRUTE, BP_L_ARM) - apply_damage(rand(0, damage), BRUTE, BP_R_ARM) - Weaken(4) - updatehealth() +/mob/living/fall_impact(var/turf/landing, var/damage_min = 0, var/damage_max = 30, var/silent = FALSE, var/planetary = FALSE) + if(planetary && src.CanParachute()) + if(!silent) + visible_message("\The [src] glides in from above and lands on \the [landing]!", \ + "You land on \the [landing]!", \ + "You hear something land \the [landing].") + return + else if(!planetary && src.softfall) // Falling one floor and falling one atmosphere are very different things + if(!silent) + visible_message("\The [src] falls from above and lands on \the [landing]!", \ + "You land on \the [landing]!", \ + "You hear something land \the [landing].") + return + else + if(!silent) + if(planetary) + visible_message("\A [src] falls out of the sky and crashes into \the [landing]!", \ + " You fall out of the skiy and crash into \the [landing]!", \ + "You hear something slam into \the [landing].") + var/turf/T = get_turf(landing) + explosion(T, 0, 1, 2) + else + visible_message("\The [src] falls from above and slams into \the [landing]!", \ + "You fall off and hit \the [landing]!", \ + "You hear something slam into \the [landing].") + playsound(loc, "punch", 25, 1, -1) + if(planetary) //Since the planetary fall damage is calibrated for humans, we need to up this a bit + damage_min *= 2 + damage_max *= 2 + + adjustBruteLoss(rand(damage_min, damage_max)) + return + return + +/mob/living/carbon/human/fall_impact(var/turf/landing, var/damage_min = 0, var/damage_max = 10, var/silent = FALSE, var/planetary = FALSE) + if(planetary && src.CanParachute()) + if(!silent) + visible_message("\The [src] glides in from above and lands on \the [landing]!", \ + "You land on \the [landing]!", \ + "You hear something land \the [landing].") + return + else if(!planetary && src.softfall) // Falling one floor and falling one atmosphere are very different things + if(!silent) + visible_message("\The [src] falls from above and lands on \the [landing]!", \ + "You land on \the [landing]!", \ + "You hear something land \the [landing].") + return + else + if(!silent) + if(planetary) + visible_message("\A [src] falls out of the sky and crashes into \the [landing]!", \ + " You fall out of the skiy and crash into \the [landing]!", \ + "You hear something slam into \the [landing].") + var/turf/T = get_turf(landing) + explosion(T, 0, 1, 2) + else + visible_message("\The [src] falls from above and slams into \the [landing]!", \ + "You fall off and hit \the [landing]!", \ + "You hear something slam into \the [landing].") + playsound(loc, "punch", 25, 1, -1) + + // Because wounds heal rather quickly, 10 should be enough to discourage jumping off but not be enough to ruin you, at least for the first time. + apply_damage(rand(damage_min, damage_max), BRUTE, BP_HEAD) + apply_damage(rand(damage_min, damage_max), BRUTE, BP_TORSO) + apply_damage(rand(damage_min, damage_max), BRUTE, BP_GROIN) + apply_damage(rand(damage_min, damage_max), BRUTE, BP_L_LEG) + apply_damage(rand(damage_min, damage_max), BRUTE, BP_R_LEG) + apply_damage(rand(damage_min, damage_max), BRUTE, BP_L_FOOT) + apply_damage(rand(damage_min, damage_max), BRUTE, BP_R_FOOT) + apply_damage(rand(damage_min, damage_max), BRUTE, BP_L_ARM) + apply_damage(rand(damage_min, damage_max), BRUTE, BP_R_ARM) + apply_damage(rand(damage_min, damage_max), BRUTE, BP_L_HAND) + apply_damage(rand(damage_min, damage_max), BRUTE, BP_R_HAND) + Weaken(4) + updatehealth() + return + return + +//Checks if the mob is allowed to survive a fall from space +/mob/living/proc/CanParachute() + return parachuting + +//For humans, this needs to be a wee bit more complicated +/mob/living/carbon/human/CanParachute() + //Certain slots don't really need to be checked for parachute ability, i.e. pockets, ears, etc. If this changes, just add them to the loop, I guess? + //This is done in Priority Order, so items lower down the list don't call handleParachute() unless they're actually used. + if(back && back.isParachute()) + back.handleParachute() + return TRUE + if(s_store && s_store.isParachute()) + back.handleParachute() + return TRUE + if(belt && belt.isParachute()) + back.handleParachute() + return TRUE + if(wear_suit && wear_suit.isParachute()) + back.handleParachute() + return TRUE + if(w_uniform && w_uniform.isParachute()) + back.handleParachute() + return TRUE + else + return parachuting + +//For human falling code +//Using /obj instead of /obj/item because I'm not sure what all humans can pick up or wear +/obj + var/parachute = FALSE + +/obj/proc/isParachute() + return parachute + +//This is what makes the parachute items know they've been used. +//I made it /atom/movable so it can be retooled for other things (mobs, mechs, etc), though it's only currently called in human/CanParachute(). +/atom/movable/proc/handleParachute() + return + +//Mech Code /obj/mecha/handle_fall(var/turf/landing) // First things first, break any lattice var/obj/structure/lattice/lattice = locate(/obj/structure/lattice, loc) @@ -316,7 +431,7 @@ // Then call parent to have us actually fall return ..() -/obj/mecha/fall_impact(var/atom/hit_atom) +/obj/mecha/fall_impact(var/atom/hit_atom, var/damage_min = 15, var/damage_max = 30, var/silent = FALSE, var/planetary = FALSE) // Tell the pilot that they just dropped down with a superheavy mecha. if(occupant) to_chat(occupant, "\The [src] crashed down onto \the [hit_atom]!") @@ -328,9 +443,9 @@ L.Weaken(8) // Now to hurt the mech. - take_damage(rand(15, 30)) + take_damage(rand(damage_min, damage_max)) // And hurt the floor. if(istype(hit_atom, /turf/simulated/floor)) var/turf/simulated/floor/ground = hit_atom - ground.break_tile() + ground.break_tile() \ No newline at end of file diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi index ac581ad8e8..ced2f3683c 100644 Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ diff --git a/icons/obj/clothing/backpack.dmi b/icons/obj/clothing/backpack.dmi new file mode 100644 index 0000000000..a5b109af2b Binary files /dev/null and b/icons/obj/clothing/backpack.dmi differ diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi index d15a86e2f3..c9e19dd40b 100644 Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ