diff --git a/aurorastation.dme b/aurorastation.dme index b5aa22ab2ef..7637a5ccc7a 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -473,6 +473,7 @@ #include "code\datums\components\_component.dm" #include "code\datums\components\connect_mob_behalf.dm" #include "code\datums\components\drift.dm" +#include "code\datums\components\leanable.dm" #include "code\datums\components\local_network.dm" #include "code\datums\components\orbiter.dm" #include "code\datums\components\armor\armor.dm" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm index 35a19115667..a6f77c71fdb 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm @@ -39,3 +39,6 @@ /// Called when certain atoms are deconstructed, currently implemented on walls and floors #define COMSIG_ATOM_DECONSTRUCTED "atom_deconstructed" + +/// From /atom/proc/set_density(new_value) for when an atom changes density +#define COMSIG_ATOM_DENSITY_CHANGED "atom_density_change" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm index 6d53bff5edd..1a81389d5bf 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -1,2 +1,4 @@ ///from base of mob/living/death(): (gibbed) #define COMSIG_LIVING_DEATH "living_death" +/// From /mob/living/proc/stop_leaning() +#define COMSIG_LIVING_STOPPED_LEANING "living_stopped_leaning" diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index e375ba4b261..abea96f8a82 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -202,6 +202,7 @@ /// This trait is used for double shuttle seats in a single tile, used in handling occupant density. #define TRAIT_DOUBLE_SEATS "double_seats" + /// Apply this to make a mob passable by other mobs. #define TRAIT_UNDENSE "undense" @@ -216,3 +217,6 @@ /// Trait given when the mob lies down. #define TRAIT_SOURCE_LYING_DOWN "lying_down" + +/// A trait gained by leaning against something +#define TRAIT_LEANING "leaning" diff --git a/code/datums/components/leanable.dm b/code/datums/components/leanable.dm new file mode 100644 index 00000000000..ed0015cf9b5 --- /dev/null +++ b/code/datums/components/leanable.dm @@ -0,0 +1,135 @@ +/// Things with this component can be leaned onto +/datum/component/leanable + /// How much will mobs that lean onto this object be offset + var/leaning_offset = 11 + /// List of mobs currently leaning on our parent + var/list/leaning_mobs + /// Is this object currently leanable? + var/is_currently_leanable = TRUE + +/datum/component/leanable/Initialize(mob/living/leaner, leaning_offset = 11) + . = ..() + src.leaning_offset = leaning_offset + mousedrop_receive(parent, leaner, leaner) + +/datum/component/leanable/RegisterWithParent() + RegisterSignal(parent, COMSIG_MOUSEDROPPED_ONTO, PROC_REF(mousedrop_receive)) + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + RegisterSignal(parent, COMSIG_ATOM_DENSITY_CHANGED, PROC_REF(on_density_change)) + var/atom/leanable_atom = parent + is_currently_leanable = leanable_atom.density + +/datum/component/leanable/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, list( + COMSIG_MOVABLE_MOVED, + COMSIG_MOUSEDROPPED_ONTO, + COMSIG_ATOM_DENSITY_CHANGED, + )) + +/datum/component/leanable/Destroy(force) + stop_leaning_leaners() + return ..() + +/datum/component/leanable/proc/stop_leaning_leaners(fall) + for (var/mob/living/leaner as anything in leaning_mobs) + if(fall) + leaner.visible_message(SPAN_WARNING("[leaner] loses their balance!"), SPAN_DANGER("You lose balance!")) + leaner.Weaken(rand(3,5)) + leaner.stop_leaning() + LAZYCLEARLIST(leaning_mobs) + +/datum/component/leanable/proc/on_moved(datum/source) + SIGNAL_HANDLER + + for (var/mob/living/leaner as anything in leaning_mobs) + leaner.stop_leaning() + +/datum/component/leanable/proc/mousedrop_receive(atom/source, atom/movable/dropped, mob/user, params) + if (dropped != user) + return + if (!iscarbon(dropped)) + return + var/mob/living/leaner = dropped + var/can_lean = TRUE + + if (HAS_TRAIT_FROM(leaner, TRAIT_UNDENSE, TRAIT_LEANING)) + return + + if(istype(leaner.l_hand, /obj/item/grab) || istype(leaner.r_hand, /obj/item/grab)) + can_lean = FALSE + if(leaner.incapacitated()) + can_lean = FALSE + if(leaner.resting) + can_lean = FALSE + if(leaner.buckled_to) + can_lean = FALSE + + if(!is_currently_leanable || !can_lean) + return COMPONENT_CANCEL_MOUSEDROPPED_ONTO + leaner.start_leaning(source, leaning_offset) + LAZYADD(leaning_mobs, leaner) + RegisterSignals(leaner, list(COMSIG_LIVING_STOPPED_LEANING, COMSIG_QDELETING), PROC_REF(stopped_leaning), override = TRUE) + return COMPONENT_CANCEL_MOUSEDROPPED_ONTO + +/** + * Makes the mob lean on an atom + * Arguments + * + * * atom/lean_target - the target the mob is trying to lean on + * * leaning_offset - pixel offset to apply on the mob when leaning + */ +/mob/living/proc/start_leaning(atom/lean_target, leaning_offset) + var/direction = get_dir(lean_target, src) + var/shift_pixel_x = 0 + var/shift_pixel_y = 0 + set_dir(direction) + switch(dir) + if(NORTH) + shift_pixel_y = -10 + if(SOUTH) + shift_pixel_y = 16 + if(EAST) + shift_pixel_x = -10 + if(WEST) + shift_pixel_x = 10 + animate(src, pixel_x = shift_pixel_x, pixel_y = shift_pixel_y, time = 1) + if(direction == NORTH) + src.add_filter("cutout", 1, alpha_mask_filter(icon = icon('icons/effects/effects.dmi', "cutout"))) + set_density(FALSE) + ADD_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_WALL_LEANING) + ADD_TRAIT(src, TRAIT_LEANING, TRAIT_SOURCE_WALL_LEANING) + visible_message( + SPAN_NOTICE("[src] leans against [lean_target]."), + SPAN_NOTICE("You lean against [lean_target]."), + ) + RegisterSignals(src, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_RESISTED), PROC_REF(stop_leaning), src) + +/mob/living/proc/stop_leaning() + SIGNAL_HANDLER + set_density(FALSE) + pixel_x = initial(pixel_x) + pixel_y = initial(pixel_y) + layer = initial(layer) + UnregisterSignal(src, list( + COMSIG_MOVABLE_MOVED, + COMSIG_MOB_RESISTED, + )) + SEND_SIGNAL(src, COMSIG_LIVING_STOPPED_LEANING) + to_chat(src, SPAN_NOTICE("You stop leaning on the wall.")) + REMOVE_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_WALL_LEANING) + REMOVE_TRAIT(src, TRAIT_LEANING, TRAIT_SOURCE_WALL_LEANING) + remove_filter("cutout") + +/datum/component/leanable/proc/stopped_leaning(datum/source) + SIGNAL_HANDLER + LAZYREMOVE(leaning_mobs, source) + UnregisterSignal(source, list(COMSIG_LIVING_STOPPED_LEANING, COMSIG_QDELETING)) + +/datum/component/leanable/proc/on_density_change() + SIGNAL_HANDLER + is_currently_leanable = !is_currently_leanable + if(!is_currently_leanable) + stop_leaning_leaners(fall = TRUE) + return + stop_leaning_leaners() diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index f2bf645fb56..340aeb40347 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -288,3 +288,12 @@ if(pass_info.pass_flags & pass_flags_self) return TRUE . = !density + +///Setter for the `density` variable to append behavior related to its changing. +/atom/proc/set_density(new_value) + SHOULD_CALL_PARENT(TRUE) + if(density == new_value) + return + . = density + density = new_value + SEND_SIGNAL(src, COMSIG_ATOM_DENSITY_CHANGED) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 9ff666ced86..de1193ec7fb 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -643,12 +643,3 @@ /atom/proc/clear_bulletholes() for(var/obj/effect/overlay/bmark/bullet_mark in src) qdel(bullet_mark) - -// TODO make all atoms use set_density, do not rely on it at present -///Setter for the `density` variable to append behavior related to its changing. -/atom/proc/set_density(new_value) - SHOULD_CALL_PARENT(TRUE) - if(density == new_value) - return - . = density - density = new_value diff --git a/code/game/gamemodes/cult/runes/wall.dm b/code/game/gamemodes/cult/runes/wall.dm index 8e0516535a3..25edbce2f47 100644 --- a/code/game/gamemodes/cult/runes/wall.dm +++ b/code/game/gamemodes/cult/runes/wall.dm @@ -6,7 +6,7 @@ /datum/rune/wall/do_rune_action(mob/living/user, atom/movable/A) user.say("Khari'd! Eske'te tannin!") - A.density = !A.density + A.set_density(!A.density) user.take_organ_damage(2, 0) if(A.density) to_chat(user, SPAN_CULT("Your blood flows into the rune, and you feel that the very space over the rune thickens.")) diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 4624f885319..7eaa51ab246 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -6,9 +6,9 @@ desc = "It opens and closes." icon = 'icons/obj/doors/doorint.dmi' icon_state = "door_closed" - anchored = 1 - opacity = 1 - density = 1 + anchored = TRUE + opacity = TRUE + density = TRUE layer = CLOSED_DOOR_LAYER dir = SOUTH var/open_layer = OPEN_DOOR_LAYER @@ -70,6 +70,10 @@ else if(src.health < src.maxhealth * 3/4) . += SPAN_WARNING("\The [src] shows signs of damage!") +/obj/machinery/door/mouse_drop_receive(atom/dropping, mob/user, params) + //Adds the component only once. We do it here & not in Initialize() because there are tons of walls & we don't want to add to their init times + LoadComponent(/datum/component/leanable, dropping) + /obj/machinery/door/attack_generic(mob/user, damage, attack_message, environment_smash, armor_penetration, attack_flags, damage_type) if(damage >= 10) visible_message(SPAN_DANGER("\The [user] smashes into the [src]!")) @@ -132,7 +136,7 @@ playsound(src.loc, hatch_close_sound, 30, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE) /obj/machinery/door/Destroy() - density = 0 + set_density(FALSE) update_nearby_tiles() return ..() @@ -513,7 +517,7 @@ icon_state = "door_open" set_opacity(0) sleep(3) - src.density = 0 + set_density(FALSE) update_nearby_tiles() sleep(2) src.layer = open_layer @@ -549,7 +553,7 @@ do_animate("closing") sleep(3) - src.density = 1 + set_density(TRUE) explosion_resistance = initial(explosion_resistance) src.layer = closed_layer update_nearby_tiles() diff --git a/code/game/machinery/vending/_vending.dm b/code/game/machinery/vending/_vending.dm index e04b8702ff0..2091fa3c244 100644 --- a/code/game/machinery/vending/_vending.dm +++ b/code/game/machinery/vending/_vending.dm @@ -281,6 +281,10 @@ . = ..() v_asset = get_asset_datum(/datum/asset/spritesheet/vending) +/obj/machinery/vending/mouse_drop_receive(atom/dropping, mob/user, params) + //Adds the component only once. We do it here & not in Initialize() because there are tons of walls & we don't want to add to their init times + LoadComponent(/datum/component/leanable, dropping) + /obj/machinery/vending/proc/reset_light() set_light(initial(light_range), initial(light_power), initial(light_color)) diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 5e5da3e25c1..08def85c06c 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -103,28 +103,24 @@ return FALSE /obj/structure/mouse_drop_receive(atom/dropped, mob/user, params) - var/mob/living/H = user if(istype(H) && can_climb(H) && dropped == user) - do_climb(dropped) - else - return ..() + if(!do_climb(dropped)) + return ..() /obj/structure/proc/can_climb(var/mob/living/user, post_climb_check=0) if (!climbable) - to_chat(user, SPAN_WARNING("\The [src] cannot be climbed!")) return FALSE if (!can_touch(user) || (!post_climb_check && (user in climbers))) return FALSE if (!user.Adjacent(src)) - to_chat(user, SPAN_WARNING("You must be next to \the [src] to climb it.")) return FALSE var/obj/occupied = turf_is_crowded() if(occupied) - to_chat(user, SPAN_WARNING("There's \a [occupied] in the way.")) + to_chat(user, SPAN_WARNING("There's \a [occupied] in the way of climbing this.")) return FALSE return TRUE @@ -145,7 +141,7 @@ /obj/structure/proc/do_climb(var/mob/living/user) if (!can_climb(user)) - return + return FALSE user.visible_message(SPAN_WARNING("[user] starts [atom_flags & ATOM_FLAG_CHECKS_BORDER ? "leaping over" : "climbing onto"] \the [src]!")) LAZYADD(climbers, user) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 07402559e20..32ea921107f 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -781,6 +781,10 @@ qdel(src) update_nearby_icons() +/obj/structure/window/full/mouse_drop_receive(atom/dropping, mob/user, params) + //Adds the component only once. We do it here & not in Initialize() because there are tons of walls & we don't want to add to their init times + LoadComponent(/datum/component/leanable, dropping) + /********** Full Windows **********/ // Reinforced Window /obj/structure/window/full/reinforced diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 38a5f706e08..8395dabac88 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -44,9 +44,6 @@ var/tmp/image/fake_wall_image var/tmp/cached_adjacency - /// A lazylist of humans leaning on this wall. - var/list/hiding_humans - smoothing_flags = SMOOTH_MORE | SMOOTH_NO_CLEAR_ICON | SMOOTH_UNDERLAYS pathing_pass_method = TURF_PATHING_PASS_NO //Literally a wall, until we implement bots that can wallwarp, we might aswell save the processing @@ -94,6 +91,10 @@ if(locate(/obj/effect/overlay/wallrot) in src) . += SPAN_WARNING("There is fungus growing on [src].") +/turf/simulated/wall/mouse_drop_receive(atom/dropping, mob/user, params) + //Adds the component only once. We do it here & not in Initialize() because there are tons of walls & we don't want to add to their init times + LoadComponent(/datum/component/leanable, dropping) + // Walls always hide the stuff below them. /turf/simulated/wall/levelupdate(mapload) if (mapload) @@ -119,7 +120,6 @@ /turf/simulated/wall/Destroy() STOP_PROCESSING(SSprocessing, src) dismantle_wall(null, null, TRUE, TRUE) - LAZYNULL(hiding_humans) return ..() /turf/simulated/wall/process() @@ -313,81 +313,3 @@ /turf/simulated/wall/is_wall() return TRUE - -/turf/simulated/wall/mouse_drop_receive(atom/dropped, mob/user, params) - if(!ismob(dropped)) - return - - var/mob/living/carbon/human/current_mob = dropped - - // wall leaning by androbetel - if(!ishuman(current_mob)) - return - - if(current_mob != user) - return - - var/mob/living/carbon/hiding_human = current_mob - var/can_lean = TRUE - - if(istype(user.l_hand, /obj/item/grab) || istype(user.r_hand, /obj/item/grab)) - to_chat(user, SPAN_WARNING("You can't lean while grabbing someone!")) - can_lean = FALSE - if(current_mob.incapacitated()) - to_chat(user, SPAN_WARNING("You can't lean while incapacitated!")) - can_lean = FALSE - if(current_mob.resting) - to_chat(user, SPAN_WARNING("You can't lean while resting!")) - can_lean = FALSE - if(current_mob.buckled_to) - to_chat(user, SPAN_WARNING("You can't lean while buckled!")) - can_lean = FALSE - - var/direction = get_dir(src, current_mob) - var/shift_pixel_x = 0 - var/shift_pixel_y = 0 - - if(!can_lean) - return - switch(direction) - if(NORTH) - shift_pixel_y = -10 - if(SOUTH) - shift_pixel_y = 16 - if(WEST) - shift_pixel_x = 10 - if(EAST) - shift_pixel_x = -10 - else - return - - for(var/mob/living/carbon/human/hiding in hiding_humans) - if(hiding_humans[hiding] == direction) - return - - LAZYADD(hiding_humans, current_mob) - hiding_humans[current_mob] = direction - hiding_human.Moved() //just to be safe - hiding_human.set_dir(direction) - animate(hiding_human, pixel_x = shift_pixel_x, pixel_y = shift_pixel_y, time = 1) - if(direction == NORTH) - hiding_human.add_filter("cutout", 1, alpha_mask_filter(icon = icon('icons/effects/effects.dmi', "cutout"))) - hiding_human.density = FALSE - ADD_TRAIT(hiding_human, TRAIT_UNDENSE, TRAIT_SOURCE_WALL_LEANING) - RegisterSignals(hiding_human, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_RESISTED), PROC_REF(unhide_human), hiding_human) - ..() - -/turf/simulated/wall/proc/unhide_human(mob/living/carbon/human/to_unhide) - SIGNAL_HANDLER - if(!to_unhide) - return - - to_unhide.density = FALSE - to_unhide.pixel_x = initial(to_unhide.pixel_x) - to_unhide.pixel_y = initial(to_unhide.pixel_y) - to_unhide.layer = initial(to_unhide.layer) - LAZYREMOVE(hiding_humans, to_unhide) - UnregisterSignal(to_unhide, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_RESISTED)) - to_chat(to_unhide, SPAN_NOTICE("You stop leaning on the wall.")) - REMOVE_TRAIT(to_unhide, TRAIT_UNDENSE, TRAIT_SOURCE_WALL_LEANING) - to_unhide.remove_filter("cutout") diff --git a/html/changelogs/Bat-Leanable.yml b/html/changelogs/Bat-Leanable.yml new file mode 100644 index 00000000000..59c1ae0e835 --- /dev/null +++ b/html/changelogs/Bat-Leanable.yml @@ -0,0 +1,14 @@ +# Your name. +author: Batrachophrenoboocosmomachia + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - refactor: "Wall-leaning code refactored into new 'leanable' component." + - rscadd: "Full windows, doors (of all varieties), and vending machines can now be leaned against just like walls."