diff --git a/GainStation13/code/mechanics/transformation/mob_procs.dm b/GainStation13/code/mechanics/transformation/mob_procs.dm new file mode 100644 index 0000000000..bd28328b30 --- /dev/null +++ b/GainStation13/code/mechanics/transformation/mob_procs.dm @@ -0,0 +1,24 @@ +/mob/living/proc/get_tf_component() + var/obj/item/item_loc = loc + if(!istype(item_loc)) + return FALSE + + var/datum/component/transformation_item/transformation_component = item_loc.GetComponent(/datum/component/transformation_item) + if(!istype(transformation_component) || (transformation_component.transformed_mob != src)) + return FALSE + + return transformation_component + +/mob/living/proc/handle_transformation_ooc_escape() + var/datum/component/transformation_item/transformation_component = get_tf_component() + if(!transformation_component) + return FALSE + + qdel(transformation_component) + return TRUE + +/mob/living/proc/attempt_to_escape_tf() + var/datum/component/transformation_item/transformation_component = get_tf_component() + if(!transformation_component) + return FALSE + diff --git a/GainStation13/code/mechanics/transformation/transformation_component.dm b/GainStation13/code/mechanics/transformation/transformation_component.dm new file mode 100644 index 0000000000..63909f01db --- /dev/null +++ b/GainStation13/code/mechanics/transformation/transformation_component.dm @@ -0,0 +1,103 @@ +/datum/component/transformation_item + /// What mob do we currently have stuck in us? + var/mob/living/transformed_mob + /// Do we release the mob when the parent item is destroyed? + var/release_mob = TRUE + /// Is the mob able to speak as the item? + var/able_to_speak = FALSE + /// Is the mob able to emote as the item? + var/able_to_emote = FALSE + /// Does the object scale with the sprite size of the mob inside of it? + var/scale_object = TRUE + /// Show custom description. + var/show_that_object_is_tf = TRUE + /// Stored real name + var/stored_real_name = "" + /// Stored name + var/stored_name = "" + /// Is the component able to be removed? + var/stuck_on_item = FALSE + /// Is the person able to struggle out? + var/able_to_struggle_out = TRUE + +/datum/component/transformation_item/Initialize() + RegisterSignal(parent, COMSIG_PARENT_EXAMINE, PROC_REF(examine)) + RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(remove_mob)) + +/datum/component/transformation_item/Destroy(force, silent) + remove_mob() + return ..() + +/datum/component/transformation_item/proc/examine(datum/source, mob/user, list/examine_list) + if(show_that_object_is_tf) + examine_list += span_notice("Something about [source] seems lifelike.") + +/datum/component/transformation_item/proc/register_mob(mob/living/mob_to_register) + var/atom/parent_atom = parent + if(!istype(mob_to_register)) + return FALSE + + transformed_mob = mob_to_register + stored_real_name = mob_to_register.real_name + stored_name = mob_to_register.name + + // This is really dumb, but if it works, then maybe it is not dumb. + mob_to_register.real_name = parent_atom.name + mob_to_register.name = parent_atom.name + + ADD_TRAIT(mob_to_register, TRAIT_TRANSFORMED, src) + ADD_TRAIT(mob_to_register, TRAIT_RESISTCOLD, src) + ADD_TRAIT(mob_to_register, TRAIT_RESISTLOWPRESSURE, src) + ADD_TRAIT(mob_to_register, TRAIT_LOWPRESSURECOOLING, src) + + if(!able_to_speak) + ADD_TRAIT(mob_to_register, TRAIT_MUTE, src) + + if(!able_to_emote) + ADD_TRAIT(mob_to_register, TRAIT_EMOTEMUTE, src) + + // need to stop them from using radio headsets. + var/mob/living/carbon/human/human_mob = mob_to_register + if(istype(human_mob)) + ADD_TRAIT(mob_to_register, TRAIT_PARALYSIS_L_ARM, src) + ADD_TRAIT(mob_to_register, TRAIT_PARALYSIS_R_ARM, src) + human_mob.update_disabled_bodyparts() + + mob_to_register.forceMove(parent_atom) + if(scale_object) + var/target_size = mob_to_register.size_multiplier + var/matrix/new_matrix = new + + new_matrix.Scale(target_size) + new_matrix.Translate(0,16 * (target_size-1)) + parent_atom.transform = new_matrix + +/datum/component/transformation_item/proc/remove_mob() + if(!release_mob || !transformed_mob) + return FALSE + + transformed_mob.real_name = stored_real_name + transformed_mob.name = stored_name + + if(!able_to_speak) + REMOVE_TRAIT(transformed_mob, TRAIT_MUTE, src) + + if(!able_to_emote) + REMOVE_TRAIT(transformed_mob, TRAIT_EMOTEMUTE, src) + + REMOVE_TRAIT(transformed_mob, TRAIT_TRANSFORMED, src) + REMOVE_TRAIT(transformed_mob, TRAIT_RESISTCOLD, src) + REMOVE_TRAIT(transformed_mob, TRAIT_RESISTLOWPRESSURE, src) + REMOVE_TRAIT(transformed_mob, TRAIT_LOWPRESSURECOOLING, src) + + var/mob/living/carbon/human/human_mob = transformed_mob + if(istype(human_mob)) + REMOVE_TRAIT(human_mob, TRAIT_PARALYSIS_L_ARM, src) + REMOVE_TRAIT(human_mob, TRAIT_PARALYSIS_R_ARM, src) + human_mob.update_disabled_bodyparts() + + var/atom/parent_atom = parent + transformed_mob.forceMove(parent_atom.loc) + if(scale_object) + parent_atom.transform = null + diff --git a/GainStation13/code/mechanics/transformation/transformation_item.dm b/GainStation13/code/mechanics/transformation/transformation_item.dm new file mode 100644 index 0000000000..a45889e7f1 --- /dev/null +++ b/GainStation13/code/mechanics/transformation/transformation_item.dm @@ -0,0 +1,113 @@ +/obj/item/transformation_item + name = "Handheld Transmogrifier" + desc = "a handheld device that is mysteriously able to turn people into objects. It can also be used to remove said transformations." + icon = 'icons/obj/abductor.dmi' + icon_state = "gizmo_scan" + item_state = "silencer" + lefthand_file = 'icons/mob/inhands/antag/abductor_lefthand.dmi' + righthand_file = 'icons/mob/inhands/antag/abductor_righthand.dmi' + + /// What item are we wanting to TF people into? + var/obj/item/target_item + /// Do we want our transformed person to be able to speak as the object? + var/able_to_speak = TRUE + /// Do we want our transformed person to be able to emote as the object? + var/able_to_emote = TRUE + /// Do we want the item to scale? + var/scale_object = FALSE + /// Do we want to show that the object was once a person? + var/show_that_object_is_tf = TRUE + /// Is our captured person able to struggle out? + var/able_to_struggle_out = TRUE + /// Do we have any items we can't turn people into? + var/list/object_blacklist = list() + +/obj/item/transformation_item/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + var/obj/item/attacked_item = target + if(!proximity_flag || !istype(attacked_item)) + return FALSE + + var/datum/component/transformation_item/transformation_component = attacked_item.GetComponent(/datum/component/transformation_item) + if(istype(transformation_component) && !transformation_component.stuck_on_item) + qdel(transformation_component) + to_chat(user, span_notice("You dispel the current transformation for [attacked_item].")) + return + + target_item = attacked_item + to_chat(user, span_notice("The next time someone is transformed, they will be transformed into [attacked_item].")) + return + +/obj/item/transformation_item/attack(mob/living/M, mob/living/user) + if(!target_item) + to_chat(user, span_warning("You need to have an item linked to transform someone.")) + return + + perform_transfomration(M, user) + return + +/obj/item/transformation_item/proc/perform_transfomration(mob/living/target_mob, mob/living/user) + if(!istype(target_mob)) + return FALSE + + if(!target_item.Adjacent(target_mob)) + to_chat(user, span_warning("The [target_item] isn't close enough to [target_mob]")) + return FALSE + + if(target_item in target_mob.get_contents()) + return FALSE // Don't TF someone into something they are holding. + + // You'll want to check prefs here. + var/datum/component/transformation_item/transformation_component = target_item.AddComponent(/datum/component/transformation_item) + // Make sure that we apply our variables before we actually put the mob in the item. + transformation_component.able_to_speak = able_to_speak + transformation_component.able_to_emote = able_to_emote + transformation_component.scale_object = scale_object + transformation_component.show_that_object_is_tf = show_that_object_is_tf + transformation_component.able_to_struggle_out = able_to_struggle_out + + transformation_component.register_mob(target_mob) + target_item = null + return TRUE + +/obj/item/transformation_item/ui_data(mob/user) + var/list/data = list() + data["able_to_speak"] = able_to_speak + data["able_to_emote"] = able_to_emote + data["scale_object"] = scale_object + data["show_that_object_is_tf"] = show_that_object_is_tf + data["linked_item_name"] = target_item + data["able_to_struggle_out"] = able_to_struggle_out + + return data + +/obj/item/transformation_item/ui_act(action, params) + if(..()) + return + + switch(action) + if("set_speaking") + able_to_speak = !able_to_speak + . = TRUE + + if("set_emote") + able_to_emote = !able_to_emote + . = TRUE + + if("set_scale") + scale_object = !scale_object + . = TRUE + + if("toggle_struggle") + able_to_struggle_out = !able_to_struggle_out + . = TRUE + + if("set_show_desc") + show_that_object_is_tf = !show_that_object_is_tf + . = TRUE + +/obj/item/transformation_item/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TransformTool", name) + ui.open() diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 2bc634bb61..f2c3c13848 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -295,6 +295,7 @@ #define TRAIT_HEAT "heat" #define TRAIT_DISTANT "headpat_hater" #define TRAIT_CUM_PLUS "cum_plus" +#define TRAIT_TRANSFORMED "transformed" // mobility flag traits // IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index cd69b5ee52..863b087361 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -820,6 +820,10 @@ //Sure, give clickdelay for anti spam. shouldn't be combat voring anyways. return TRUE + // GS13 Escape Transformation + if(attempt_to_escape_tf()) + return TRUE + //Breaking out of a container (Locker, sleeper, cryo...) if(isobj(loc)) var/obj/C = loc diff --git a/code/modules/vore/eating/living.dm b/code/modules/vore/eating/living.dm index d5a3627684..96aff93e7f 100644 --- a/code/modules/vore/eating/living.dm +++ b/code/modules/vore/eating/living.dm @@ -221,7 +221,12 @@ // /mob/living/proc/escapeOOC() set name = "OOC Escape" - set category = "Vore" + set category = "ERP" // GS13 Change + + //GS13 EDIT START + handle_transformation_ooc_escape() + //GS13 EDIT END + //You're in a belly! if(isbelly(loc)) diff --git a/tgstation.dme b/tgstation.dme index 7ba4d39826..883a4fce8f 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3972,6 +3972,9 @@ #include "GainStation13\code\mechanics\web_weaving.dm" #include "GainStation13\code\mechanics\den abductors\console.dm" #include "GainStation13\code\mechanics\den abductors\purchaseble_goodies.dm" +#include "GainStation13\code\mechanics\transformation\mob_procs.dm" +#include "GainStation13\code\mechanics\transformation\transformation_component.dm" +#include "GainStation13\code\mechanics\transformation\transformation_item.dm" #include "GainStation13\code\mobs\cakegolem.dm" #include "GainStation13\code\mobs\chocoslime.dm" #include "GainStation13\code\mobs\eat_trash.dm" diff --git a/tgui/packages/tgui/interfaces/TransformTool.js b/tgui/packages/tgui/interfaces/TransformTool.js new file mode 100644 index 0000000000..fb06cc0c29 --- /dev/null +++ b/tgui/packages/tgui/interfaces/TransformTool.js @@ -0,0 +1,66 @@ +import { useBackend } from '../backend'; +import { Box, Button, LabeledList, NumberInput, Section } from '../components'; +import { Window } from '../layouts'; + +export const TransformTool = (props, context) => { + const { act, data } = useBackend(context); + const { + able_to_speak, + able_to_emote, + scale_object, + show_that_object_is_tf, + linked_item_name, + able_to_struggle_out, + } = data; + + return ( + + +
+ + Current item: {linked_item_name ? linked_item_name : "None"} + +
+ + + + + + + + + + + +
+
+
+ ); +};