From bedf896c3ca2ddf85238c87d861dcba107d7e52a Mon Sep 17 00:00:00 2001 From: Tim Date: Mon, 23 Jan 2023 08:09:34 -0600 Subject: [PATCH] Chisel and Statue Improvements (#72638) ## About The Pull Request Resolves #72607 Resolves #65701 Chisels and statues had quite a few bugs and they lacked any kind of feedback system to inform the user on it's proper usage. My improvements do the following: - Chisels now give balloon alerts when selecting a target, cancelling, or sculpting - Chisels used on carving blocks that were interrupted will now continue - Fixed abstract statues not letting people use the radial menu - Fixed chisel targets to have no range requirement (so you can use binoculars, cameras, etc.) - Refactor a lot of the chisel and statue code to be more robust - Add sculpting sounds when chisels are used on carving blocks ## Why It's Good For The Game Less bugs and now people can now sculpt easier. ## Changelog :cl: soundadd: Chisels now make sculpting sounds when used on carving blocks qol: Chisels now give balloon alerts when selecting a target, cancelling, or sculpting qol: Chisels used on carving blocks that were interrupted will now continue fix: Fix chisel targets to have no range requirement (so you can use binoculars, cameras, etc.) fix: Fix standard mineral blocks (uranium, diamond, plasma, etc.) not letting people use the radial menu to craft abstract statues properly. This does not work with custom mineral blocks. (pizza, glass, etc.) code: Change chisel and statue code to be more readable /:cl: Co-authored-by: Time-Green --- code/game/atoms.dm | 7 ++- code/modules/art/statues.dm | 103 ++++++++++++++++++++++-------------- 2 files changed, 66 insertions(+), 44 deletions(-) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 584e24b23ba..9ac44b4460d 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1648,11 +1648,10 @@ * Fetches a list of all of the materials this object has of the desired type. Returns null if there is no valid materials of the type * * Arguments: - * - [mat_type][/datum/material]: The type of material we are checking for - * - exact: Whether to search for the _exact_ material type + * - [required_material][/datum/material]: The type of material we are checking for * - mat_amount: The minimum required amount of material */ -/atom/proc/has_material_type(datum/material/mat_type, exact=FALSE, mat_amount=0) +/atom/proc/has_material_type(datum/material/required_material, mat_amount = 0) var/list/cached_materials = custom_materials if(!length(cached_materials)) return null @@ -1662,7 +1661,7 @@ if(cached_materials[current_material] < mat_amount) continue var/datum/material/material = GET_MATERIAL_REF(current_material) - if(exact ? material.type != current_material : !istype(material, mat_type)) + if(!istype(material, required_material)) continue LAZYSET(materials_of_type, material, cached_materials[current_material]) diff --git a/code/modules/art/statues.dm b/code/modules/art/statues.dm index ad4f3ccc933..c29c9957774 100644 --- a/code/modules/art/statues.dm +++ b/code/modules/art/statues.dm @@ -1,3 +1,7 @@ +/// This controls the delay for the sculpt rock breaking sound +/// Every 4th iterator while sculpting will emit a sound (rougly every couple of seconds) +#define SCULPT_SOUND_INCREMENT 4 + /obj/structure/statue name = "statue" desc = "Placeholder. Yell at Firecage if you SOMEHOW see this." @@ -36,12 +40,8 @@ if(W.tool_behaviour == TOOL_WELDER) if(!W.tool_start_check(user, amount=0)) return FALSE - - user.visible_message(span_notice("[user] is slicing apart the [name]."), \ - span_notice("You are slicing apart the [name]...")) + user.balloon_alert(user, "slicing apart...") if(W.use_tool(src, user, 40, volume=50)) - user.visible_message(span_notice("[user] slices apart the [name]."), \ - span_notice("You slice apart the [name]!")) deconstruct(TRUE) return return ..() @@ -56,7 +56,7 @@ var/datum/material/custom_material = GET_MATERIAL_REF(mat) var/amount = max(0,round(custom_materials[mat]/MINERAL_MATERIAL_AMOUNT) + amount_mod) if(amount > 0) - new custom_material.sheet_type(drop_location(),amount) + new custom_material.sheet_type(drop_location(), amount) qdel(src) //////////////////////////////////////STATUES///////////////////////////////////////////////////////////// @@ -279,7 +279,7 @@ attack_verb_continuous = list("stabs") attack_verb_simple = list("stab") hitsound = 'sound/weapons/bladeslice.ogg' - usesound = list('sound/items/screwdriver.ogg', 'sound/items/screwdriver2.ogg') + usesound = list('sound/effects/picaxe1.ogg', 'sound/effects/picaxe2.ogg', 'sound/effects/picaxe3.ogg') drop_sound = 'sound/items/handling/screwdriver_drop.ogg' pickup_sound = 'sound/items/handling/screwdriver_pickup.ogg' sharpness = SHARP_POINTY @@ -311,35 +311,40 @@ Point with the chisel at the target to choose what to sculpt or hit block to cho Hit block again to start sculpting. Moving interrupts */ -/obj/item/chisel/pre_attack(atom/A, mob/living/user, params) +/obj/item/chisel/pre_attack(atom/target, mob/living/user, params) . = ..() if(sculpting) - return - if(istype(A,/obj/structure/carving_block)) - if(A == prepared_block && (prepared_block.current_target || prepared_block.current_preset_type)) + return TRUE + if(istype(target, /obj/structure/carving_block)) + var/obj/structure/carving_block/sculpt_block = target + + if(sculpt_block.completion) // someone already started sculpting this so just finish + set_block(sculpt_block, user, silent = TRUE) + start_sculpting(user) + else if(sculpt_block == prepared_block && (prepared_block.current_target || prepared_block.current_preset_type)) start_sculpting(user) else if(!prepared_block) - set_block(A,user) - else if(A == prepared_block) + set_block(sculpt_block, user) + else if(sculpt_block == prepared_block) show_generic_statues_prompt(user) return TRUE else if(prepared_block) //We're aiming at something next to us with block prepared - prepared_block.set_target(A,user) + prepared_block.set_target(target, user) return TRUE // We aim at something distant. /obj/item/chisel/afterattack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() - if(proximity_flag) - return . if (!sculpting && prepared_block && ismovable(target) && prepared_block.completion == 0) prepared_block.set_target(target,user) return . | AFTERATTACK_PROCESSED_ITEM +/// Starts or continues the sculpting action on the carving block material /obj/item/chisel/proc/start_sculpting(mob/living/user) - to_chat(user,span_notice("You start sculpting [prepared_block]."),type=MESSAGE_TYPE_INFO) + user.balloon_alert(user, "sculpting block...") + playsound(src, pick(usesound), 75, TRUE) sculpting = TRUE //How long whole process takes var/sculpting_time = 30 SECONDS @@ -348,9 +353,12 @@ Moving interrupts var/interrupted = FALSE var/remaining_time = sculpting_time - (prepared_block.completion * sculpting_time) - var/datum/progressbar/total_progress_bar = new(user, sculpting_time, prepared_block ) + var/datum/progressbar/total_progress_bar = new(user, sculpting_time, prepared_block) while(remaining_time > 0 && !interrupted) - if(do_after(user,sculpting_period, target = prepared_block, progress = FALSE)) + if(do_after(user, sculpting_period, target = prepared_block, progress = FALSE)) + var/time_delay = !(remaining_time % SCULPT_SOUND_INCREMENT) + if(time_delay) + playsound(src, 'sound/effects/break_stone.ogg', 50, TRUE) remaining_time -= sculpting_period prepared_block.set_completion((sculpting_time - remaining_time)/sculpting_time) total_progress_bar.update(sculpting_time - remaining_time) @@ -359,42 +367,54 @@ Moving interrupts total_progress_bar.end_progress() if(!interrupted && !QDELETED(prepared_block)) prepared_block.create_statue() - to_chat(user,span_notice("The statue is finished!"),type=MESSAGE_TYPE_INFO) - break_sculpting() + user.balloon_alert(user, "statue finished") + stop_sculpting(silent = !interrupted) -/obj/item/chisel/proc/set_block(obj/structure/carving_block/B,mob/living/user) +/// To setup the sculpting target for the carving block +/obj/item/chisel/proc/set_block(obj/structure/carving_block/B, mob/living/user, silent = FALSE) prepared_block = B tracked_user = user - RegisterSignal(tracked_user,COMSIG_MOVABLE_MOVED, PROC_REF(break_sculpting)) - to_chat(user,span_notice("You prepare to work on [B]."),type=MESSAGE_TYPE_INFO) + RegisterSignal(tracked_user, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + if(!silent) + user.balloon_alert(user, "select sculpt target") /obj/item/chisel/dropped(mob/user, silent) . = ..() - break_sculpting() + stop_sculpting() -/obj/item/chisel/proc/break_sculpting() - SIGNAL_HANDLER +/// Cancel the sculpting action +/obj/item/chisel/proc/stop_sculpting(silent = FALSE) sculpting = FALSE if(prepared_block && prepared_block.completion == 0) prepared_block.reset_target() prepared_block = null + + if(!silent && tracked_user) + tracked_user.balloon_alert(tracked_user, "sculpting cancelled!") + if(tracked_user) - UnregisterSignal(tracked_user,COMSIG_MOVABLE_MOVED) + UnregisterSignal(tracked_user, COMSIG_MOVABLE_MOVED) tracked_user = null +/obj/item/chisel/proc/on_moved() + SIGNAL_HANDLER + + stop_sculpting() + /obj/item/chisel/proc/show_generic_statues_prompt(mob/living/user) var/list/choices = list() for(var/statue_path in prepared_block.get_possible_statues()) - var/obj/structure/statue/S = statue_path - choices[statue_path] = image(icon=initial(S.icon),icon_state=initial(S.icon_state)) - var/choice = show_radial_menu(user, prepared_block , choices, require_near = TRUE) + var/obj/structure/statue/abstract_statue = statue_path + choices[statue_path] = image(icon = initial(abstract_statue.icon), icon_state = initial(abstract_statue.icon_state)) + if(!choices.len) + user.balloon_alert(user, "no abstract statues for material!") + + var/choice = show_radial_menu(user, prepared_block, choices, require_near = TRUE) if(choice) prepared_block.current_preset_type = choice var/image/chosen_looks = choices[choice] prepared_block.current_target = chosen_looks.appearance - var/obj/structure/statue/S = choice - to_chat(user,span_notice("You decide to sculpt [prepared_block] into [initial(S.name)]."),type=MESSAGE_TYPE_INFO) - + user.balloon_alert(user, "abstract statue selected") /obj/structure/carving_block name = "block" @@ -423,9 +443,8 @@ Moving interrupts target_appearance_with_filters = null return ..() -/obj/structure/carving_block/proc/set_target(atom/movable/target,mob/living/user) - if(!is_viable_target(target)) - to_chat(user,"You won't be able to carve that.") +/obj/structure/carving_block/proc/set_target(atom/movable/target, mob/living/user) + if(!is_viable_target(user, target)) return if(istype(target,/obj/structure/statue/custom)) var/obj/structure/statue/custom/original = target @@ -433,7 +452,7 @@ Moving interrupts else current_target = target.appearance var/mutable_appearance/ma = current_target - to_chat(user,span_notice("You decide to sculpt [src] into [ma.name]."),type=MESSAGE_TYPE_INFO) + user.balloon_alert(user, "sculpt target is [ma.name]") /obj/structure/carving_block/proc/reset_target() current_target = null @@ -448,13 +467,15 @@ Moving interrupts var/mutable_appearance/clone = new(target_appearance_with_filters) . += clone -/obj/structure/carving_block/proc/is_viable_target(atom/movable/target) +/obj/structure/carving_block/proc/is_viable_target(mob/living/user, atom/movable/target) //Only things on turfs if(!isturf(target.loc)) + user.balloon_alert(user, "no sculpt target!") return FALSE //No big icon things var/icon/thing_icon = icon(target.icon, target.icon_state) if(thing_icon.Height() != world.icon_size || thing_icon.Width() != world.icon_size) + user.balloon_alert(user, "sculpt target is too big!") return FALSE return TRUE @@ -505,7 +526,7 @@ Moving interrupts var/list/carving_cost = statue_costs[statue_path] var/enough_materials = TRUE for(var/required_material in carving_cost) - if(!has_material_type(required_material, TRUE, carving_cost[required_material])) + if(!has_material_type(required_material, carving_cost[required_material])) enough_materials = FALSE break if(enough_materials) @@ -588,3 +609,5 @@ Moving interrupts . = ..() if(content_ma) . += content_ma + +#undef SCULPT_SOUND_INCREMENT