diff --git a/code/datums/elements/climbable.dm b/code/datums/elements/climbable.dm new file mode 100644 index 00000000000..d3ba2def7e7 --- /dev/null +++ b/code/datums/elements/climbable.dm @@ -0,0 +1,104 @@ +/datum/element/climbable + element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH + id_arg_index = 2 + ///Time it takes to climb onto the object + var/climb_time = (2 SECONDS) + ///Stun duration for when you get onto the object + var/climb_stun = (2 SECONDS) + ///Assoc list of object being climbed on - climbers. This allows us to check who needs to be shoved off a climbable object when its clicked on. + var/list/current_climbers + +/datum/element/climbable/Attach(datum/target, climb_time, climb_stun) + . = ..() + + if(!isatom(target) || isarea(target)) + return ELEMENT_INCOMPATIBLE + if(climb_time) + src.climb_time = climb_time + if(climb_stun) + src.climb_stun = climb_stun + + RegisterSignal(target, COMSIG_ATOM_ATTACK_HAND, .proc/attack_hand) + RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine) + RegisterSignal(target, COMSIG_MOUSEDROPPED_ONTO, .proc/mousedrop_receive) + RegisterSignal(target, COMSIG_ATOM_BUMPED, .proc/try_speedrun) + +/datum/element/climbable/Detach(datum/target) + UnregisterSignal(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_PARENT_EXAMINE, COMSIG_MOUSEDROPPED_ONTO, COMSIG_ATOM_BUMPED)) + return ..() + +/datum/element/climbable/proc/on_examine(atom/source, mob/user, list/examine_texts) + SIGNAL_HANDLER + + if(can_climb(source, user)) + examine_texts += "[source] looks climbable" + +/datum/element/climbable/proc/can_climb(atom/source, mob/user) + return TRUE + +/datum/element/climbable/proc/attack_hand(atom/climbed_thing, mob/user) + SIGNAL_HANDLER + var/list/climbers = LAZYACCESS(current_climbers, climbed_thing) + for(var/i in climbers) + var/mob/living/structure_climber = i + if(structure_climber == user) + return + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(climbed_thing) + structure_climber.Paralyze(40) + structure_climber.visible_message("[structure_climber] is knocked off [climbed_thing].", "You're knocked off [climbed_thing]!", "You see [structure_climber] get knocked off [climbed_thing].") + + +/datum/element/climbable/proc/climb_structure(atom/climbed_thing, mob/living/user) + if(!can_climb(climbed_thing, user)) + return + climbed_thing.add_fingerprint(user) + user.visible_message("[user] starts climbing onto [climbed_thing].", \ + "You start climbing onto [climbed_thing]...") + var/adjusted_climb_time = climb_time + if(HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //climbing takes twice as long without help from the hands. + adjusted_climb_time *= 2 + if(isalien(user)) + adjusted_climb_time *= 0.25 //aliens are terrifyingly fast + if(HAS_TRAIT(user, TRAIT_FREERUNNING)) //do you have any idea how fast I am??? + adjusted_climb_time *= 0.8 + LAZYADDASSOC(current_climbers, climbed_thing, user) + if(do_after(user, adjusted_climb_time, climbed_thing)) + if(QDELETED(climbed_thing)) //Checking if structure has been destroyed + return + if(do_climb(climbed_thing, user)) + user.visible_message("[user] climbs onto [climbed_thing].", \ + "You climb onto [climbed_thing].") + log_combat(user, climbed_thing, "climbed onto") + if(climb_stun) + user.Stun(climb_stun) + else + to_chat(user, "You fail to climb onto [climbed_thing].") + LAZYREMOVEASSOC(current_climbers, climbed_thing, user) + + +/datum/element/climbable/proc/do_climb(atom/climbed_thing, mob/living/user) + climbed_thing.density = FALSE + . = step(user, get_dir(user,climbed_thing.loc)) + climbed_thing.density = TRUE + +///Handles climbing onto the atom when you click-drag +/datum/element/climbable/proc/mousedrop_receive(atom/climbed_thing, atom/movable/dropped_atom, mob/user) + SIGNAL_HANDLER + if(user == dropped_atom && isliving(dropped_atom)) + var/mob/living/living_target = dropped_atom + if(isanimal(living_target)) + var/mob/living/simple_animal/animal = dropped_atom + if (!animal.dextrous) + return + if(living_target.mobility_flags & MOBILITY_MOVE) + INVOKE_ASYNC(src, .proc/climb_structure, climbed_thing, living_target) + return + +///Tries to climb onto the target if the forced movement of the mob allows it +/datum/element/climbable/proc/try_speedrun(datum/source, mob/bumpee) + SIGNAL_HANDLER + if(!istype(bumpee)) + return + if(bumpee.force_moving?.allow_climbing) + do_climb(source, bumpee) diff --git a/code/datums/forced_movement.dm b/code/datums/forced_movement.dm index 2252bb6c665..14aa43364ce 100644 --- a/code/datums/forced_movement.dm +++ b/code/datums/forced_movement.dm @@ -84,10 +84,3 @@ . = TryMove(TRUE) . = . && (vic.loc != tar.loc) - -/mob/Bump(atom/A) - . = ..() - if(force_moving?.allow_climbing && isstructure(A)) - var/obj/structure/S = A - if(S.climbable) - S.do_climb(src) diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index 99d64b00294..fe6f0cb6d20 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -106,11 +106,14 @@ proj_pass_rate = 20 pass_flags_self = LETPASSTHROW bar_material = SAND - climbable = TRUE smoothing_flags = SMOOTH_BITMASK smoothing_groups = list(SMOOTH_GROUP_SANDBAGS) canSmoothWith = list(SMOOTH_GROUP_SANDBAGS, SMOOTH_GROUP_WALLS, SMOOTH_GROUP_SECURITY_BARRICADE) +/obj/structure/barricade/sandbags/Initialize() + . = ..() + AddElement(/datum/element/climbable) + /obj/structure/barricade/security name = "security barrier" desc = "A deployable barrier. Provides good cover in fire fights." diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 08d3fda3597..f5ed1357ba6 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -8,13 +8,7 @@ flags_ricochet = RICOCHET_HARD receive_ricochet_chance_mod = 0.6 pass_flags_self = PASSSTRUCTURE - - var/climb_time = 20 - var/climb_stun = 20 - var/climbable = FALSE - var/mob/living/structureclimber - var/broken = 0 //similar to machinery's stat BROKEN - + var/broken = FALSE /obj/structure/Initialize() if (!armor) @@ -33,78 +27,10 @@ QUEUE_SMOOTH_NEIGHBORS(src) return ..() -/obj/structure/attack_hand(mob/user) - . = ..() - if(.) - return - if(structureclimber && structureclimber != user) - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src) - structureclimber.Paralyze(40) - structureclimber.visible_message("[structureclimber] is knocked off [src].", "You're knocked off [src]!", "You see [structureclimber] get knocked off [src].") - /obj/structure/ui_act(action, params) add_fingerprint(usr) return ..() -/obj/structure/MouseDrop_T(atom/movable/O, mob/user) - . = ..() - if(!climbable) - return - if(user == O && isliving(O)) - var/mob/living/L = O - if(isanimal(L)) - var/mob/living/simple_animal/A = L - if (!A.dextrous) - return - if(L.mobility_flags & MOBILITY_MOVE) - climb_structure(user) - return - if(!istype(O, /obj/item) || user.get_active_held_item() != O) - return - if(iscyborg(user)) - return - if(!user.dropItemToGround(O)) - return - if (O.loc != src.loc) - step(O, get_dir(O, src)) - -/obj/structure/proc/do_climb(atom/movable/A) - if(climbable) - if(A.loc == src.loc) - var/turf/where_to_climb = get_step(A,dir) - if(!where_to_climb.is_blocked_turf()) - A.forceMove(where_to_climb) - return TRUE - density = FALSE - . = step(A,get_dir(A,src.loc)) - density = TRUE - -/obj/structure/proc/climb_structure(mob/living/user) - src.add_fingerprint(user) - user.visible_message("[user] starts climbing onto [src].", \ - "You start climbing onto [src]...") - var/adjusted_climb_time = climb_time - if(HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) //climbing takes twice as long without help from the hands. - adjusted_climb_time *= 2 - if(isalien(user)) - adjusted_climb_time *= 0.25 //aliens are terrifyingly fast - if(HAS_TRAIT(user, TRAIT_FREERUNNING)) //do you have any idea how fast I am??? - adjusted_climb_time *= 0.8 - structureclimber = user - if(do_mob(user, user, adjusted_climb_time)) - if(src.loc) //Checking if structure has been destroyed - if(do_climb(user)) - user.visible_message("[user] climbs onto [src].", \ - "You climb onto [src].") - log_combat(user, src, "climbed onto") - if(climb_stun) - user.Stun(climb_stun) - . = 1 - else - to_chat(user, "You fail to climb onto [src].") - structureclimber = null - /obj/structure/examine(mob/user) . = ..() if(!(resistance_flags & INDESTRUCTIBLE)) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index e4d60c849a4..df26a03e182 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -167,11 +167,15 @@ opened = TRUE if(!dense_when_open) density = FALSE - climb_time *= 0.5 //it's faster to climb onto an open thing dump_contents() update_icon() + after_open(user, force) return TRUE +///Proc to override for effects after opening a door +/obj/structure/closet/proc/after_open(mob/living/user, force = FALSE) + return + /obj/structure/closet/proc/insert(atom/movable/AM) if(contents.len >= storage_capacity) return -1 @@ -218,12 +222,17 @@ return FALSE take_contents() playsound(loc, close_sound, close_sound_volume, TRUE, -3) - climb_time = initial(climb_time) opened = FALSE density = TRUE update_icon() + after_close(user) return TRUE +///Proc to override for effects after closing a door +/obj/structure/closet/proc/after_close(mob/living/user) + return + + /obj/structure/closet/proc/toggle(mob/living/user) if(opened) return close(user) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 4c92ce90129..8b14ced6df2 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -9,15 +9,13 @@ allow_objects = TRUE allow_dense = TRUE dense_when_open = TRUE - climbable = TRUE - climb_time = 10 //real fast, because let's be honest stepping into or onto a crate is easy - climb_stun = 0 //climbing onto crates isn't hard, guys delivery_icon = "deliverycrate" open_sound = 'sound/machines/crate_open.ogg' close_sound = 'sound/machines/crate_close.ogg' open_sound_volume = 35 close_sound_volume = 50 drag_slowdown = 0 + var/climb_time = 20 var/obj/item/paper/fluff/jobs/cargo/manifest/manifest /obj/structure/closet/crate/Initialize() @@ -25,6 +23,7 @@ if(icon_state == "[initial(icon_state)]open") opened = TRUE update_icon() + AddElement(/datum/element/climbable, climb_time = opened ? climb_time : climb_time * 0.5, climb_stun = 0) /obj/structure/closet/crate/CanAllowThrough(atom/movable/mover, turf/target) . = ..() @@ -51,6 +50,17 @@ if(manifest) tear_manifest(user) +/obj/structure/closet/crate/after_open(mob/living/user, force) + . = ..() + RemoveElement(/datum/element/climbable) + AddElement(/datum/element/climbable, climb_time = climb_time * 0.5, climb_stun = 0) + +/obj/structure/closet/crate/after_close(mob/living/user) + . = ..() + RemoveElement(/datum/element/climbable) + AddElement(/datum/element/climbable, climb_time = climb_time, climb_stun = 0) + + /obj/structure/closet/crate/open(mob/living/user, force = FALSE) . = ..() if(. && manifest) diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm index 8697662ed09..87ba54103d8 100644 --- a/code/game/objects/structures/fence.dm +++ b/code/game/objects/structures/fence.dm @@ -79,11 +79,11 @@ if(MEDIUM_HOLE) visible_message("\The [user] cuts into \the [src] some more.") to_chat(user, "You could probably fit yourself through that hole now. Although climbing through would be much faster if you made it even bigger.") - climbable = TRUE + AddElement(/datum/element/climbable) if(LARGE_HOLE) visible_message("\The [user] completely cuts through \the [src].") to_chat(user, "The hole in \the [src] is now big enough to walk through.") - climbable = FALSE + RemoveElement(/datum/element/climbable) update_cut_status() diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm index 00ed93933aa..b67e5f07a2c 100644 --- a/code/game/objects/structures/railings.dm +++ b/code/game/objects/structures/railings.dm @@ -5,7 +5,10 @@ icon_state = "railing" density = TRUE anchored = TRUE - climbable = TRUE + + var/climbable = TRUE + ///Initial direction of the railing. + var/ini_dir /obj/structure/railing/corner //aesthetic corner sharp edges hurt oof ouch icon_state = "railing_corner" @@ -16,6 +19,13 @@ . = ..() AddComponent(/datum/component/simple_rotation,ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS ,null,CALLBACK(src, .proc/can_be_rotated),CALLBACK(src,.proc/after_rotation)) + +/obj/structure/railing/Initialize() + . = ..() + ini_dir = dir + if(climbable) + AddElement(/datum/element/climbable) + /obj/structure/railing/attackby(obj/item/I, mob/living/user, params) ..() add_fingerprint(user) diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 2e60dcaf8cd..34358041ad4 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -22,7 +22,6 @@ anchored = TRUE pass_flags_self = PASSTABLE | LETPASSTHROW layer = TABLE_LAYER - climbable = TRUE var/frame = /obj/structure/table_frame var/framestack = /obj/item/stack/rods var/buildstack = /obj/item/stack/sheet/metal @@ -37,6 +36,12 @@ smoothing_groups = list(SMOOTH_GROUP_TABLES) canSmoothWith = list(SMOOTH_GROUP_TABLES) +/obj/structure/table/Initialize(mapload, _buildstack) + . = ..() + if(_buildstack) + buildstack = _buildstack + AddElement(/datum/element/climbable) + /obj/structure/table/examine(mob/user) . = ..() . += deconstruction_hints(user) diff --git a/code/game/objects/structures/transit_tubes/transit_tube.dm b/code/game/objects/structures/transit_tubes/transit_tube.dm index 223d690c2bc..e3a8e2e008a 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube.dm @@ -7,7 +7,6 @@ density = TRUE layer = LOW_ITEM_LAYER anchored = TRUE - climbable = TRUE pass_flags_self = PASSGLASS var/tube_construction = /obj/structure/c_transit_tube var/list/tube_dirs //list of directions this tube section can connect to. @@ -20,6 +19,7 @@ setDir(newdirection) init_tube_dirs() generate_tube_overlays() + AddElement(/datum/element/climbable) /obj/structure/transit_tube/Destroy() for(var/obj/structure/transit_tube_pod/P in loc) diff --git a/code/modules/religion/religion_structures.dm b/code/modules/religion/religion_structures.dm index 061d4285ec4..38e1bf0e58d 100644 --- a/code/modules/religion/religion_structures.dm +++ b/code/modules/religion/religion_structures.dm @@ -6,7 +6,6 @@ density = TRUE anchored = TRUE layer = TABLE_LAYER - climbable = TRUE pass_flags_self = LETPASSTHROW can_buckle = TRUE buckle_lying = 90 //we turn to you! @@ -16,6 +15,7 @@ /obj/structure/altar_of_gods/Initialize(mapload) . = ..() reflect_sect_in_icons() + AddElement(/datum/element/climbable) /obj/structure/altar_of_gods/ComponentInitialize() . = ..() diff --git a/tgstation.dme b/tgstation.dme index 9487526362c..1f076211243 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -594,6 +594,7 @@ #include "code\datums\elements\bsa_blocker.dm" #include "code\datums\elements\caltrop.dm" #include "code\datums\elements\cleaning.dm" +#include "code\datums\elements\climbable.dm" #include "code\datums\elements\decal.dm" #include "code\datums\elements\digitalcamo.dm" #include "code\datums\elements\dryable.dm"