From eaec2dc3b78eee255aec5ccee0bf28fb37bacac2 Mon Sep 17 00:00:00 2001 From: Sealed101 Date: Thu, 29 Jun 2023 04:00:33 +0300 Subject: [PATCH] RPD mouse wheel layer change checks for actually holding the RPD (#76319) ## About The Pull Request Fixes mousewheel changing the selected piping layer when the RCD is in-hands, but not in the active hand. Also dmdocs, changes some var names, grammar checks some stuff and adds balloon alerts when installing the wrench upgrade and reprogramming a pipe. Removes `activate()` proc that was just a `playsound()`, makes the sound used for that a define here so it's obvious why's the sound being played ## Why It's Good For The Game Closes #54844 (it was fixed at some point actually so you couldn't mousewheel to change layers on an RPD on your belt slot, but the in-hand inconsistency remained) Code's a little cleaner Installing an upgrade's a little more obvious ## Changelog :cl: fix: fixed RPD switching layers on mousescroll when not in active hand qol: installing an RPD wrench upgrade & reprogramming a pipe via RMB produces a balloon alert /:cl: --- code/game/objects/items/rcd/RPD.dm | 177 +++++++++++++++-------------- 1 file changed, 94 insertions(+), 83 deletions(-) diff --git a/code/game/objects/items/rcd/RPD.dm b/code/game/objects/items/rcd/RPD.dm index 474017e4243..20ac5471ab8 100644 --- a/code/game/objects/items/rcd/RPD.dm +++ b/code/game/objects/items/rcd/RPD.dm @@ -9,6 +9,9 @@ #define DESTROY_MODE (1<<2) #define REPROGRAM_MODE (1<<3) +///Sound to make when we use the item to build/destroy something +#define RPD_USE_SOUND 'sound/items/deconstruct.ogg' + GLOBAL_LIST_INIT(atmos_pipe_recipes, list( "Pipes" = list( new /datum/pipe_info/pipe("Pipe", /obj/machinery/atmospherics/pipe/smart, TRUE), @@ -239,8 +242,8 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( /obj/item/pipe_dispenser/examine(mob/user) . = ..() - . += "You can scroll your mouse wheel to change the piping layer." - . += "You can right click a pipe to set the RPD to its color and layer." + . += span_notice("You can scroll your mouse wheel to change the piping layer.") + . += span_notice("You can right click a pipe to set the RPD to its color and layer.") /obj/item/pipe_dispenser/equipped(mob/user, slot, initial) . = ..() @@ -266,6 +269,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( if(target.pipe_color && target.piping_layer) paint_color = GLOB.pipe_color_name[target.pipe_color] piping_layer = target.piping_layer + balloon_alert(user, "color/layer copied") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN /obj/item/pipe_dispenser/add_item_context(obj/item/source, list/context, atom/target, mob/living/user) @@ -280,7 +284,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( /obj/item/pipe_dispenser/suicide_act(mob/living/user) user.visible_message(span_suicide("[user] points the end of the RPD down [user.p_their()] throat and presses a button! It looks like [user.p_theyre()] trying to commit suicide...")) playsound(get_turf(user), 'sound/machines/click.ogg', 50, TRUE) - playsound(get_turf(user), 'sound/items/deconstruct.ogg', 50, TRUE) + playsound(get_turf(user), RPD_USE_SOUND, 50, TRUE) return BRUTELOSS /obj/item/pipe_dispenser/ui_assets(mob/user) @@ -309,29 +313,31 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( "mode" = mode, ) - var/list/recipes + //currently selected category (atmos, disposal or transit) + var/list/selected_major_category switch(category) if(ATMOS_CATEGORY) - recipes = GLOB.atmos_pipe_recipes + selected_major_category = GLOB.atmos_pipe_recipes if(DISPOSALS_CATEGORY) - recipes = GLOB.disposal_pipe_recipes + selected_major_category = GLOB.disposal_pipe_recipes if(TRANSIT_CATEGORY) - recipes = GLOB.transit_tube_recipes - for(var/c in recipes) - var/list/cat = recipes[c] - var/list/r = list() - for(var/i in 1 to cat.len) - var/datum/pipe_info/info = cat[i] + selected_major_category = GLOB.transit_tube_recipes + //selected subcategory (e.g. pipes/binary/devices/heat exchange for atmos) + for(var/subcategory in selected_major_category) + var/list/subcategory_recipes = selected_major_category[subcategory] + var/list/available_recipe = list() + for(var/i in 1 to subcategory_recipes.len) + var/datum/pipe_info/info = subcategory_recipes[i] - r += list(list( + available_recipe += list(list( "pipe_name" = info.name, "pipe_index" = i, "previews" = info.get_preview(p_dir, info == recipe) )) if(info == recipe) - data["selected_category"] = c + data["selected_category"] = subcategory - data["categories"] += list(list("cat_name" = c, "recipes" = r)) + data["categories"] += list(list("cat_name" = subcategory, "recipes" = available_recipe)) var/list/init_directions = list("north" = FALSE, "south" = FALSE, "east" = FALSE, "west" = FALSE) for(var/direction in GLOB.cardinals) @@ -377,8 +383,8 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( p_flipped = text2num(params["flipped"]) playeffect = FALSE if("mode") - var/n = text2num(params["mode"]) - mode ^= n + var/selected_mode = text2num(params["mode"]) + mode ^= selected_mode if("init_dir_setting") var/target_dir = p_init_dir ^ text2dir(params["dir_flag"]) // Refuse to create a smart pipe that can only connect in one direction (it would act weirdly and lack an icon) @@ -394,12 +400,12 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( playsound(get_turf(src), 'sound/effects/pop.ogg', 50, FALSE) return TRUE -/obj/item/pipe_dispenser/pre_attack(atom/A, mob/user, params) - if(!ISADVANCEDTOOLUSER(user) || istype(A, /turf/open/space/transit)) +/obj/item/pipe_dispenser/pre_attack(atom/atom_to_attack, mob/user, params) + if(!ISADVANCEDTOOLUSER(user) || istype(atom_to_attack, /turf/open/space/transit)) return ..() - if(istype(A, /obj/item/rpd_upgrade)) - var/obj/item/rpd_upgrade/rpd_up = A + if(istype(atom_to_attack, /obj/item/rpd_upgrade)) + var/obj/item/rpd_upgrade/rpd_up = atom_to_attack //already installed if(rpd_up.upgrade_flags & upgrade_flags) @@ -409,10 +415,11 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( //install & delete upgrade upgrade_flags |= rpd_up.upgrade_flags playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) + balloon_alert(user, "upgrade installed") qdel(rpd_up) return TRUE - var/atom/attack_target = A + var/atom/attack_target = atom_to_attack //So that changing the menu settings doesn't affect the pipes already being built. var/queued_p_type = recipe.id @@ -438,79 +445,79 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( if((mode & DESTROY_MODE) && istype(attack_target, /obj/item/pipe) || istype(attack_target, /obj/structure/disposalconstruct) || istype(attack_target, /obj/structure/c_transit_tube) || istype(attack_target, /obj/structure/c_transit_tube_pod) || istype(attack_target, /obj/item/pipe_meter) || istype(attack_target, /obj/structure/disposalpipe/broken)) playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) if(do_after(user, destroy_speed, target = attack_target)) - activate() + playsound(get_turf(src), RPD_USE_SOUND, 50, TRUE) qdel(attack_target) return if(mode & REPROGRAM_MODE) // If this is a placed smart pipe, try to reprogram it - var/obj/machinery/atmospherics/pipe/smart/S = attack_target - if(istype(S)) - if (S.dir == ALL_CARDINALS) + var/obj/machinery/atmospherics/pipe/smart/target_smart_pipe = attack_target + if(istype(target_smart_pipe)) + if(target_smart_pipe.dir == ALL_CARDINALS) balloon_alert(user, "has no unconnected directions!") return - var/old_init_dir = S.get_init_directions() - if (old_init_dir == p_init_dir) + var/old_init_dir = target_smart_pipe.get_init_directions() + if(old_init_dir == p_init_dir) balloon_alert(user, "already configured!") return // Check for differences in unconnected directions - var/target_differences = (p_init_dir ^ old_init_dir) & ~S.connections - if (!target_differences) + var/target_differences = (p_init_dir ^ old_init_dir) & ~target_smart_pipe.connections + if(!target_differences) balloon_alert(user, "already configured for its directions!") return playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) - if(!do_after(user, reprogram_speed, target = S)) + if(!do_after(user, reprogram_speed, target = target_smart_pipe)) return // Something else could have changed the target's state while we were waiting in do_after // Most of the edge cases don't matter, but atmos components being able to have live connections not described by initializable directions sounds like a headache at best and an exploit at worst // Double check to make sure that nothing has changed. If anything we were about to change was connected during do_after, abort - if (target_differences & S.connections) - balloon_alert(user, "cant configure for its direction!") + if(target_differences & target_smart_pipe.connections) + balloon_alert(user, "can't configure for its direction!") return // Grab the current initializable directions, which may differ from old_init_dir if someone else was working on the same pipe at the same time - var/current_init_dir = S.get_init_directions() + var/current_init_dir = target_smart_pipe.get_init_directions() // Access p_init_dir directly. The RPD can change target layer and initializable directions (though not pipe type or dir) while working to dispense and connect a component, // and have it reflected in the final result. Reprogramming should be similarly consistent. var/new_init_dir = (current_init_dir & ~target_differences) | (p_init_dir & target_differences) // Don't make a smart pipe with only one connection - if (ISSTUB(new_init_dir)) + if(ISSTUB(new_init_dir)) balloon_alert(user, "no one directional pipes allowed!") return - S.set_init_directions(new_init_dir) + target_smart_pipe.set_init_directions(new_init_dir) // We're now reconfigured. // We can never disconnect from existing connections, but we can connect to previously unconnected directions, and should immediately do so var/newly_permitted_connections = new_init_dir & ~current_init_dir if(newly_permitted_connections) // We're allowed to connect in new directions. Recompute our nodes // Disconnect from everything that is currently connected - for (var/i in 1 to S.device_type) + for(var/i in 1 to target_smart_pipe.device_type) // This is basically pipe.nullifyNode, but using it here would create a pitfall for others attempting to // copy and paste disconnection code for other components. Welcome to the atmospherics subsystem - var/obj/machinery/atmospherics/node = S.nodes[i] - if (!node) + var/obj/machinery/atmospherics/node = target_smart_pipe.nodes[i] + if(!node) continue - node.disconnect(S) - S.nodes[i] = null + node.disconnect(target_smart_pipe) + target_smart_pipe.nodes[i] = null // Get our new connections - S.atmos_init() + target_smart_pipe.atmos_init() // Connect to our new connections - for (var/obj/machinery/atmospherics/O in S.nodes) - O.atmos_init() - O.add_member(src) - SSair.add_to_rebuild_queue(S) + for(var/obj/machinery/atmospherics/connected_device in target_smart_pipe.nodes) + connected_device.atmos_init() + connected_device.add_member(target_smart_pipe) + SSair.add_to_rebuild_queue(target_smart_pipe) // Finally, update our internal state - update_pipe_icon also updates dir and connections - S.update_pipe_icon() - user.visible_message(span_notice("[user] reprograms the \the [S]."),span_notice("You reprogram \the [S].")) + target_smart_pipe.update_pipe_icon() + user.visible_message(span_notice("[user] reprograms \the [target_smart_pipe]."), span_notice("You reprogram \the [target_smart_pipe].")) return // If this is an unplaced smart pipe, try to reprogram it - var/obj/item/pipe/quaternary/I = attack_target - if(istype(I) && ispath(I.pipe_type, /obj/machinery/atmospherics/pipe/smart)) + var/obj/item/pipe/quaternary/target_unsecured_pipe = attack_target + if(istype(target_unsecured_pipe) && ispath(target_unsecured_pipe.pipe_type, /obj/machinery/atmospherics/pipe/smart)) // An unplaced pipe never has any existing connections, so just directly assign the new configuration - I.p_init_dir = p_init_dir - I.update() + target_unsecured_pipe.p_init_dir = p_init_dir + target_unsecured_pipe.update() if(mode & BUILD_MODE) switch(category) //if we've gotten this var, the target is valid @@ -520,20 +527,20 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) if (recipe.type == /datum/pipe_info/meter) if(do_after(user, atmos_build_speed, target = attack_target)) - activate() - var/obj/item/pipe_meter/PM = new /obj/item/pipe_meter(get_turf(attack_target)) - PM.setAttachLayer(piping_layer) + playsound(get_turf(src), RPD_USE_SOUND, 50, TRUE) + var/obj/item/pipe_meter/new_meter = new /obj/item/pipe_meter(get_turf(attack_target)) + new_meter.setAttachLayer(piping_layer) if(mode & WRENCH_MODE) - PM.wrench_act(user, src) + new_meter.wrench_act(user, src) else if(recipe.all_layers == FALSE && (piping_layer == 1 || piping_layer == 5)) - balloon_alert(user, "cant build on this layer!") + balloon_alert(user, "can't build on this layer!") return ..() if(do_after(user, atmos_build_speed, target = attack_target)) if(recipe.all_layers == FALSE && (piping_layer == 1 || piping_layer == 5))//double check to stop cheaters (and to not waste time waiting for something that can't be placed) - balloon_alert(user, "cant build on this layer!") + balloon_alert(user, "can't build on this layer!") return ..() - activate() + playsound(get_turf(src), RPD_USE_SOUND, 50, TRUE) var/obj/machinery/atmospherics/path = queued_p_type var/pipe_item_type = initial(path.construction_type) || /obj/item/pipe var/obj/item/pipe/pipe_type = new pipe_item_type( @@ -545,8 +552,8 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( ispath(queued_p_type, /obj/machinery/atmospherics/pipe/smart) ? p_init_dir : null, ) if(queued_p_flipped && istype(pipe_type, /obj/item/pipe/trinary/flippable)) - var/obj/item/pipe/trinary/flippable/F = pipe_type - F.flipped = queued_p_flipped + var/obj/item/pipe/trinary/flippable/new_flippable_pipe = pipe_type + new_flippable_pipe.flipped = queued_p_flipped pipe_type.update() pipe_type.add_fingerprint(usr) @@ -565,19 +572,19 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( return playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) if(do_after(user, disposal_build_speed, target = attack_target)) - var/obj/structure/disposalconstruct/C = new (attack_target, queued_p_type, queued_p_dir, queued_p_flipped) + var/obj/structure/disposalconstruct/new_disposals_segment = new (attack_target, queued_p_type, queued_p_dir, queued_p_flipped) - if(!C.can_place()) + if(!new_disposals_segment.can_place()) balloon_alert(user, "not enough room!") - qdel(C) + qdel(new_disposals_segment) return - activate() + playsound(get_turf(src), RPD_USE_SOUND, 50, TRUE) - C.add_fingerprint(usr) - C.update_appearance() + new_disposals_segment.add_fingerprint(usr) + new_disposals_segment.update_appearance() if(mode & WRENCH_MODE) - C.wrench_act(user, src) + new_disposals_segment.wrench_act(user, src) return if(TRANSIT_CATEGORY) //Making transit tubes @@ -595,7 +602,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) if(do_after(user, transit_build_speed, target = attack_target)) - activate() + playsound(get_turf(src), RPD_USE_SOUND, 50, TRUE) if(queued_p_type == /obj/structure/c_transit_tube_pod) var/obj/structure/c_transit_tube_pod/pod = new /obj/structure/c_transit_tube_pod(attack_target) pod.add_fingerprint(usr) @@ -617,31 +624,24 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( else return ..() -/obj/item/pipe_dispenser/proc/activate() - playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, TRUE) -/obj/item/pipe_dispenser/proc/mouse_wheeled(mob/source, atom/A, delta_x, delta_y, params) +///Changes the piping layer when the mousewheel is scrolled up or down. +/obj/item/pipe_dispenser/proc/mouse_wheeled(mob/source_mob, atom/A, delta_x, delta_y, params) SIGNAL_HANDLER - if(source.incapacitated(IGNORE_RESTRAINTS|IGNORE_STASIS)) + if(source_mob.incapacitated(IGNORE_RESTRAINTS|IGNORE_STASIS)) + return + if(source_mob.get_active_held_item() != src) return if(delta_y < 0) piping_layer = min(PIPING_LAYER_MAX, piping_layer + 1) else if(delta_y > 0) piping_layer = max(PIPING_LAYER_MIN, piping_layer - 1) - else + else //mice with side-scrolling wheels are apparently a thing and fuck this up return SStgui.update_uis(src) - to_chat(source, span_notice("You set the layer to [piping_layer].")) + to_chat(source_mob, span_notice("You set the layer to [piping_layer].")) -#undef ATMOS_CATEGORY -#undef DISPOSALS_CATEGORY -#undef TRANSIT_CATEGORY - -#undef BUILD_MODE -#undef DESTROY_MODE -#undef WRENCH_MODE -#undef REPROGRAM_MODE /obj/item/rpd_upgrade name = "RPD advanced design disk" @@ -654,3 +654,14 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( /obj/item/rpd_upgrade/unwrench desc = "Adds reverse wrench mode to the RPD. Attention, due to budget cuts, the mode is hard linked to the destroy mode control button." upgrade_flags = RPD_UPGRADE_UNWRENCH + +#undef ATMOS_CATEGORY +#undef DISPOSALS_CATEGORY +#undef TRANSIT_CATEGORY + +#undef BUILD_MODE +#undef DESTROY_MODE +#undef WRENCH_MODE +#undef REPROGRAM_MODE + +#undef RPD_USE_SOUND