From d9329f3e8f220850fa482be0acd87f24ded0808a Mon Sep 17 00:00:00 2001 From: tralezab <40974010+tralezab@users.noreply.github.com> Date: Sat, 2 Oct 2021 00:11:55 -0700 Subject: [PATCH] Fixes spirit holding component not working on null rods by adding the subtype picker component to null rods (#60767) Null rods were not calling parent on attack_self and thus not sending signals to the component. DUMB! STINKY!!!! I have elected to make subtype picking a component itself so this mistake will never be remade fixes #60712 (possessed blades cannot awaken a spirit) also, fixes spirit holder not making sounds --- code/datums/components/spirit_holding.dm | 4 +- code/datums/components/subtype_picker.dm | 88 ++++++++++++++++++++++++ code/game/objects/items/holy_weapons.dm | 75 ++++---------------- tgstation.dme | 1 + 4 files changed, 105 insertions(+), 63 deletions(-) create mode 100644 code/datums/components/subtype_picker.dm diff --git a/code/datums/components/spirit_holding.dm b/code/datums/components/spirit_holding.dm index 74c34024209..41c5fd9362c 100644 --- a/code/datums/components/spirit_holding.dm +++ b/code/datums/components/spirit_holding.dm @@ -101,10 +101,10 @@ /datum/component/spirit_holding/proc/attempt_exorcism(mob/exorcist) var/atom/movable/exorcised_movable = parent to_chat(exorcist, span_notice("You begin to exorcise [parent]...")) - playsound(src,'sound/hallucinations/veryfar_noise.ogg',40,TRUE) + playsound(parent, 'sound/hallucinations/veryfar_noise.ogg',40,TRUE) if(!do_after(exorcist, 4 SECONDS, target = exorcised_movable)) return - playsound(src,'sound/effects/pray_chaplain.ogg',60,TRUE) + playsound(parent, 'sound/effects/pray_chaplain.ogg',60,TRUE) UnregisterSignal(exorcised_movable, list(COMSIG_ATOM_RELAYMOVE, COMSIG_BIBLE_SMACKED)) RegisterSignal(exorcised_movable, COMSIG_ITEM_ATTACK_SELF, .proc/on_attack_self) to_chat(bound_spirit, span_userdanger("You were exorcised!")) diff --git a/code/datums/components/subtype_picker.dm b/code/datums/components/subtype_picker.dm new file mode 100644 index 00000000000..6660e20c458 --- /dev/null +++ b/code/datums/components/subtype_picker.dm @@ -0,0 +1,88 @@ +/** + * subtype picker component allows for an item to transform into its subtypes (this is not enforced and you can turn in whatever types, but + * i used this name as it was incredibly accurate for current usage of the behavior) + * + * Used for the null rod to pick the other holy weapons. + */ +/datum/component/subtype_picker + ///A list of types and their menu descriptions + var/list/subtype2descriptions + ///list given to the radial menu to display, built after init + var/list/built_radial_list + ///the radial will return a name of the wanted subtype, this is a list of names back to the type, built after init + var/list/name2subtype + ///optional proc to callback to when the weapon is picked + var/datum/callback/on_picked_callback + +/datum/component/subtype_picker/Initialize(subtype2descriptions, on_picked_callback) + . = ..() + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + src.subtype2descriptions = subtype2descriptions + src.on_picked_callback = on_picked_callback + build_radial_list() + +/datum/component/subtype_picker/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/on_attack_self) + +/datum/component/subtype_picker/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, COMSIG_ITEM_ATTACK_SELF) + +///signal called by the stat of the target changing +/datum/component/subtype_picker/proc/on_attack_self(datum/target, mob/user) + SIGNAL_HANDLER + INVOKE_ASYNC(src, .proc/pick_subtype, target, user) + +/** + * pick_subtype: turns the list of types to their description into all the data radial menus need + */ +/datum/component/subtype_picker/proc/build_radial_list() + built_radial_list = list() + name2subtype = list() + for(var/obj/item/subtype as anything in subtype2descriptions) + + var/datum/radial_menu_choice/option = new + option.image = image(icon = initial(subtype.icon), icon_state = initial(subtype.icon_state)) + option.info = span_boldnotice(subtype2descriptions[subtype]) + + name2subtype[initial(subtype.name)] = subtype + built_radial_list += list(initial(subtype.name) = option) + built_radial_list = sortList(built_radial_list) + +/** + * pick_subtype: called from on_attack_self, shows a user a radial menu of all available null rod reskins and replaces the current null rod with the user's chosen reskinned variant + * + * Arguments: + * * target: parent this element is attached to that is being activated + * * picker: user who interacted with the item + */ +/datum/component/subtype_picker/proc/pick_subtype(datum/target, mob/picker) + + var/name_of_type = show_radial_menu(picker, target, built_radial_list, custom_check = CALLBACK(src, .proc/check_menu, target, picker), radius = 42, require_near = TRUE) + if(!name_of_type || !check_menu(target, picker)) + return + + var/picked_subtype = name2subtype[name_of_type] + on_picked_callback?.Invoke(picked_subtype) + picked_subtype = new picked_subtype(picker.drop_location()) + + qdel(target) + picker.put_in_hands(picked_subtype) + +/** + * Checks if we are allowed to interact with the radial menu + * + * Arguments: + * * target: parent the radial menu is from + * * user: the mob interacting with the menu + */ +/datum/component/subtype_picker/proc/check_menu(datum/target, mob/user) + if(!istype(user)) + return FALSE + if(QDELETED(target)) + return FALSE + if(user.incapacitated() || !user.is_holding(target)) + return FALSE + return TRUE diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm index 3b30066a7e9..4541edae392 100644 --- a/code/game/objects/items/holy_weapons.dm +++ b/code/game/objects/items/holy_weapons.dm @@ -143,77 +143,31 @@ w_class = WEIGHT_CLASS_TINY obj_flags = UNIQUE_RENAME wound_bonus = -10 - /// If this item has already been reskinned - var/reskinned = FALSE - /// If this item can be used in a reskin variant selection + /// boolean on whether it's allowed to be picked from the nullrod's transformation ability var/chaplain_spawnable = TRUE - /// Short description of what this item is capable of, for radial menu uses + /// Short description of what this item is capable of, for radial menu uses. var/menu_description = "A standard chaplain's weapon. Fits in pockets. Can be worn on the belt." /obj/item/nullrod/Initialize(mapload) . = ..() AddComponent(/datum/component/anti_magic, TRUE, TRUE, FALSE, null, null, FALSE) AddElement(/datum/element/bane, /mob/living/simple_animal/revenant, 0, 25, FALSE) + if(!GLOB.holy_weapon_type && istype(src, /obj/item/nullrod)) + var/list/rods = list() + for(var/obj/item/nullrod/nullrod_type as anything in typesof(/obj/item/nullrod)) + if(!chaplain_spawnable) + continue + rods[nullrod_type] = initial(nullrod_type.menu_description) + AddComponent(/datum/component/subtype_picker, rods, CALLBACK(src, .proc/on_holy_weapon_picked)) + +/obj/item/nullrod/proc/on_holy_weapon_picked(obj/item/nullrod/holy_weapon_type) + GLOB.holy_weapon_type = holy_weapon_type + SSblackbox.record_feedback("tally", "chaplain_weapon", 1, "[initial(holy_weapon_type.name)]") /obj/item/nullrod/suicide_act(mob/user) user.visible_message(span_suicide("[user] is killing [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to get closer to god!")) return (BRUTELOSS|FIRELOSS) -/obj/item/nullrod/attack_self(mob/user) - if(user.mind && (user.mind.holy_role) && !reskinned) - reskin_holy_weapon(user) - -/** - * Shows a user a radial menu of all available null rod reskins and replaces the current null rod with the user's chosen reskinned variant - * - * Arguments: - * * user The mob choosing a null rod reskin variant - */ -/obj/item/nullrod/proc/reskin_holy_weapon(mob/user) - if(GLOB.holy_weapon_type) - return - var/list/display_names = list() - var/list/nullrod_icons = list() - for(var/rod in typesof(/obj/item/nullrod)) - var/obj/item/nullrod/rodtype = rod - if(initial(rodtype.chaplain_spawnable)) - var/datum/radial_menu_choice/option = new - option.image = image(icon = initial(rodtype.icon), icon_state = initial(rodtype.icon_state)) - option.info = span_boldnotice("[initial(rodtype.menu_description)]") - display_names[initial(rodtype.name)] = rodtype - nullrod_icons += list(initial(rodtype.name) = option) - - nullrod_icons = sortList(nullrod_icons) - var/choice = show_radial_menu(user, src , nullrod_icons, custom_check = CALLBACK(src, .proc/check_menu, user), radius = 42, require_near = TRUE) - if(!choice || !check_menu(user)) - return - - var/picked_rod_type = display_names[choice] // This needs to be on a separate var as list member access is not allowed for new - var/obj/item/nullrod/holy_weapon = new picked_rod_type(user.drop_location()) - GLOB.holy_weapon_type = holy_weapon.type - - SSblackbox.record_feedback("tally", "chaplain_weapon", 1, "[choice]") - - if(holy_weapon) - holy_weapon.reskinned = TRUE - qdel(src) - user.put_in_hands(holy_weapon) - -/** - * Checks if we are allowed to interact with a radial menu - * - * Arguments: - * * user The mob interacting with the menu - */ -/obj/item/nullrod/proc/check_menu(mob/user) - if(!istype(user)) - return FALSE - if(QDELETED(src) || reskinned) - return FALSE - if(user.incapacitated() || !user.is_holding(src)) - return FALSE - return TRUE - /obj/item/nullrod/godhand name = "god hand" desc = "This hand of yours glows with an awesome power!" @@ -462,8 +416,7 @@ hitsound = 'sound/weapons/chainsawhit.ogg' tool_behaviour = TOOL_SAW toolspeed = 0.5 //faster than normal saw - chaplain_spawnable = FALSE - menu_description = "A sharp chainsaw sword dealing a very high amount of damage which partially penetrates armor. Able to awaken a friendly spirit providing guidance. Can be used as a faster saw tool. Very effective at butchering bodies. Can be worn on the belt." + chaplain_spawnable = FALSE //prevents being pickable as a chaplain weapon (it has 30 force) /obj/item/nullrod/hammer name = "relic war hammer" diff --git a/tgstation.dme b/tgstation.dme index c491284ca40..da8ec9f0380 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -626,6 +626,7 @@ #include "code\datums\components\stationloving.dm" #include "code\datums\components\stationstuck.dm" #include "code\datums\components\strong_pull.dm" +#include "code\datums\components\subtype_picker.dm" #include "code\datums\components\summoning.dm" #include "code\datums\components\swabbing.dm" #include "code\datums\components\swarming.dm"