diff --git a/code/modules/unit_tests/interactions.dm b/code/modules/unit_tests/interactions.dm index 7e27b2b99d..fd75825bf4 100644 --- a/code/modules/unit_tests/interactions.dm +++ b/code/modules/unit_tests/interactions.dm @@ -3,4 +3,14 @@ SSinteractions.prepare_interactions() if(!SSinteractions.interactions || !length(SSinteractions.interactions)) Fail("make_interactions() was called but SSinteractions.interactions is empty.") + for(var/interaction_type in SSinteractions.interactions) + var/datum/interaction/interaction = SSinteractions.interactions[interaction_type] + if(!interaction.description) + Fail("Basetype [interaction.type] located within SSinteractions.interactions") + if(interaction.required_from_user_exposed && interaction.required_from_user_unexposed) + if(interaction.required_from_user_exposed != interaction.required_from_user_unexposed) + Fail("Interaction [interaction.description]([interaction.type]) has non-matching requirements for user exposed and unexposed, this is unsupported!") + if(interaction.required_from_target_exposed && interaction.required_from_target_unexposed) + if(interaction.required_from_target_exposed != interaction.required_from_target_unexposed) + Fail("Interaction [interaction.description]([interaction.type]) has non-matching requirements for target exposed and unexposed, this is unsupported!") return diff --git a/html/changelogs/archive/2023-12.yml b/html/changelogs/archive/2023-12.yml index 12f26133f8..4f5e672e25 100644 --- a/html/changelogs/archive/2023-12.yml +++ b/html/changelogs/archive/2023-12.yml @@ -59,3 +59,10 @@ Anonymous: - rscadd: 'Added ERP Tags for Character Directory: "Top-Pref", "Bottom-Pref", "Submissive Top", "Dominant Bottom".' + SandPoot: + - bugfix: Interactions that allow both exposed and unexposed can be done once again. + - code_imp: There are now unit tests for creating interactions incorrectly. +2023-12-17: + SandPoot: + - bugfix: required_from_user & target variables should check your requirements and + the interaction's reqs more precisely. diff --git a/modular_sand/code/controllers/subsystem/interactions.dm b/modular_sand/code/controllers/subsystem/interactions.dm index 9c4aa05006..57047c0347 100644 --- a/modular_sand/code/controllers/subsystem/interactions.dm +++ b/modular_sand/code/controllers/subsystem/interactions.dm @@ -40,9 +40,12 @@ SUBSYSTEM_DEF(interactions) /datum/controller/subsystem/interactions/proc/prepare_interactions() QDEL_NULL_LIST(interactions) interactions = list() - for(var/itype in subtypesof(/datum/interaction)) - var/datum/interaction/I = new itype() - interactions["[itype]"] = I + for(var/datum/interaction/interaction as anything in subtypesof(/datum/interaction)) + // Basetype, do not create + if(!initial(interaction.description)) + continue + interaction = new interaction() + interactions["[interaction.type]"] = interaction /// Blacklisting! /datum/controller/subsystem/interactions/proc/prepare_blacklisted_mobs() diff --git a/modular_sand/code/datums/components/interaction_menu_granter.dm b/modular_sand/code/datums/components/interaction_menu_granter.dm index 0790bb825e..7e68631555 100644 --- a/modular_sand/code/datums/components/interaction_menu_granter.dm +++ b/modular_sand/code/datums/components/interaction_menu_granter.dm @@ -431,6 +431,9 @@ var/list/sent_interactions = list() for(var/interaction_key in SSinteractions.interactions) var/datum/interaction/I = SSinteractions.interactions[interaction_key] + // THIS IS A BASETYPE, DO NOT SEND + if(!I.description) + continue var/list/interaction = list() interaction["key"] = I.type interaction["desc"] = I.description diff --git a/modular_sand/code/datums/interactions/_interaction.dm b/modular_sand/code/datums/interactions/_interaction.dm index fefb51e55f..9ea6ab6c3e 100644 --- a/modular_sand/code/datums/interactions/_interaction.dm +++ b/modular_sand/code/datums/interactions/_interaction.dm @@ -22,7 +22,7 @@ /// The base of all interactions /datum/interaction - var/description = "Interact with them." + var/description var/simple_message var/simple_style = "notice" var/write_log_user diff --git a/modular_sand/code/datums/interactions/interaction_datums/lewd/slap.dm b/modular_sand/code/datums/interactions/interaction_datums/lewd/slap.dm new file mode 100644 index 0000000000..a233587dee --- /dev/null +++ b/modular_sand/code/datums/interactions/interaction_datums/lewd/slap.dm @@ -0,0 +1,9 @@ +/datum/interaction/lewd/slap + description = "Slap their ass." + simple_message = "USER slaps TARGET right on the ass!" + simple_style = "danger" + interaction_sound = 'sound/weapons/slap.ogg' + required_from_user = INTERACTION_REQUIRE_HANDS + + write_log_user = "ass-slapped" + write_log_target = "was ass-slapped by" diff --git a/modular_sand/code/datums/interactions/lewd_interactions.dm b/modular_sand/code/datums/interactions/lewd_interactions.dm index bc628a89b2..617f65b36e 100644 --- a/modular_sand/code/datums/interactions/lewd_interactions.dm +++ b/modular_sand/code/datums/interactions/lewd_interactions.dm @@ -1,15 +1,9 @@ // If I could have gotten away with using a tilde in the type path, I would have. /datum/interaction/lewd - // Description can take in %COCK% as a wildcard to get replaced with a cock/strapon accordingly. - description = "Slap their ass." - simple_message = "USER slaps TARGET right on the ass!" - simple_style = "danger" - interaction_sound = 'sound/weapons/slap.ogg' + description = null + simple_style = "lewd" interaction_flags = INTERACTION_FLAG_ADJACENT | INTERACTION_FLAG_OOC_CONSENT - write_log_user = "ass-slapped" - write_log_target = "was ass-slapped by" - /// Use the number of required feet. var/require_user_num_feet var/require_target_num_feet @@ -47,15 +41,16 @@ return FALSE // Special behavior for things like non-humans and if both sides are allowed - if(!(has_penis == TRUE) || !(user_require_penis_exposed && user_require_penis_unexposed)) - if((user_require_penis_exposed) && has_penis == HAS_UNEXPOSED_GENITAL) - if(!silent) - to_chat(user, span_warning("Your penis need to be exposed.")) - return FALSE - if((user_require_penis_unexposed) && has_penis == HAS_EXPOSED_GENITAL) - if(!silent) - to_chat(user, span_warning("Your penis need to be unexposed.")) - return FALSE + if(!(user_require_penis_exposed && user_require_penis_unexposed)) + if(!(has_penis == TRUE)) + if((user_require_penis_exposed) && has_penis == HAS_UNEXPOSED_GENITAL) + if(!silent) + to_chat(user, span_warning("Your penis need to be exposed.")) + return FALSE + if((user_require_penis_unexposed) && has_penis == HAS_EXPOSED_GENITAL) + if(!silent) + to_chat(user, span_warning("Your penis need to be unexposed.")) + return FALSE var/user_require_balls_exposed = !!(required_from_user_exposed & INTERACTION_REQUIRE_BALLS) var/user_require_balls_unexposed = !!(required_from_user_unexposed & INTERACTION_REQUIRE_BALLS) @@ -66,15 +61,16 @@ to_chat(user, span_warning("You don't have balls.")) return FALSE - if(!(has_balls == TRUE) || !(user_require_balls_exposed && user_require_balls_unexposed)) - if((user_require_balls_exposed) && has_balls == HAS_UNEXPOSED_GENITAL) - if(!silent) - to_chat(user, span_warning("Your balls need to be exposed.")) - return FALSE - if((user_require_balls_unexposed) && has_balls == HAS_EXPOSED_GENITAL) - if(!silent) - to_chat(user, span_warning("Your balls need to be unexposed.")) - return FALSE + if(!(user_require_balls_exposed && user_require_balls_unexposed)) + if(!(has_balls == TRUE)) + if((user_require_balls_exposed) && has_balls == HAS_UNEXPOSED_GENITAL) + if(!silent) + to_chat(user, span_warning("Your balls need to be exposed.")) + return FALSE + if((user_require_balls_unexposed) && has_balls == HAS_EXPOSED_GENITAL) + if(!silent) + to_chat(user, span_warning("Your balls need to be unexposed.")) + return FALSE var/user_require_anus_exposed = !!(required_from_user_exposed & INTERACTION_REQUIRE_ANUS) var/user_require_anus_unexposed = !!(required_from_user_unexposed & INTERACTION_REQUIRE_ANUS) @@ -85,15 +81,16 @@ to_chat(user, span_warning("You don't have an anus.")) return FALSE - if(!(has_anus == TRUE) || !(user_require_anus_exposed && user_require_anus_unexposed)) - if(user_require_anus_exposed && has_anus == HAS_UNEXPOSED_GENITAL) - if(!silent) - to_chat(user, span_warning("Your anus needs to be exposed.")) - return FALSE - if(user_require_anus_unexposed && has_anus == HAS_EXPOSED_GENITAL) - if(!silent) - to_chat(user, span_warning("Your anus needs to be unexposed.")) - return FALSE + if(!(user_require_anus_exposed && user_require_anus_unexposed)) + if(!(has_anus == TRUE)) + if(user_require_anus_exposed && has_anus == HAS_UNEXPOSED_GENITAL) + if(!silent) + to_chat(user, span_warning("Your anus needs to be exposed.")) + return FALSE + if(user_require_anus_unexposed && has_anus == HAS_EXPOSED_GENITAL) + if(!silent) + to_chat(user, span_warning("Your anus needs to be unexposed.")) + return FALSE var/user_require_vagina_exposed = !!(required_from_user_exposed & INTERACTION_REQUIRE_VAGINA) var/user_require_vagina_unexposed = !!(required_from_user_unexposed & INTERACTION_REQUIRE_VAGINA) @@ -104,15 +101,16 @@ to_chat(user, span_warning("You don't have a vagina.")) return FALSE - if(!(has_vagina == TRUE) || !(user_require_vagina_exposed && user_require_vagina_unexposed)) - if(user_require_vagina_exposed && has_vagina == HAS_UNEXPOSED_GENITAL) - if(!silent) - to_chat(user, span_warning("Your vagina needs to be exposed.")) - return FALSE - if(user_require_vagina_unexposed && has_vagina == HAS_EXPOSED_GENITAL) - if(!silent) - to_chat(user, span_warning("Your vagina needs to be unexposed.")) - return FALSE + if(!(user_require_vagina_exposed && user_require_vagina_unexposed)) + if(!(has_vagina == TRUE)) + if(user_require_vagina_exposed && has_vagina == HAS_UNEXPOSED_GENITAL) + if(!silent) + to_chat(user, span_warning("Your vagina needs to be exposed.")) + return FALSE + if(user_require_vagina_unexposed && has_vagina == HAS_EXPOSED_GENITAL) + if(!silent) + to_chat(user, span_warning("Your vagina needs to be unexposed.")) + return FALSE var/user_require_breasts_exposed = !!(required_from_user_exposed & INTERACTION_REQUIRE_BREASTS) var/user_require_breasts_unexposed = !!(required_from_user_unexposed & INTERACTION_REQUIRE_BREASTS) @@ -123,15 +121,16 @@ to_chat(user, span_warning("You don't have breasts.")) return FALSE - if(!(has_breasts == TRUE) || !(user_require_breasts_exposed && user_require_breasts_unexposed)) - if(user_require_breasts_exposed && has_breasts == HAS_UNEXPOSED_GENITAL) - if(!silent) - to_chat(user, span_warning("Your breasts need to be exposed.")) - return FALSE - if(user_require_breasts_unexposed && has_breasts == HAS_EXPOSED_GENITAL) - if(!silent) - to_chat(user, span_warning("Your breasts need to be unexposed.")) - return FALSE + if(!(user_require_breasts_exposed && user_require_breasts_unexposed)) + if(!(has_breasts == TRUE)) + if(user_require_breasts_exposed && has_breasts == HAS_UNEXPOSED_GENITAL) + if(!silent) + to_chat(user, span_warning("Your breasts need to be exposed.")) + return FALSE + if(user_require_breasts_unexposed && has_breasts == HAS_EXPOSED_GENITAL) + if(!silent) + to_chat(user, span_warning("Your breasts need to be unexposed.")) + return FALSE var/user_require_feet_exposed = !!(required_from_user_exposed & INTERACTION_REQUIRE_FEET) var/user_require_feet_unexposed = !!(required_from_user_unexposed & INTERACTION_REQUIRE_FEET) @@ -276,15 +275,16 @@ to_chat(target, span_warning("They don't have a penis.")) return FALSE - if(!(has_penis == TRUE) || !(target_require_penis_exposed && target_require_penis_unexposed)) - if(target_require_penis_exposed && has_penis == HAS_UNEXPOSED_GENITAL) - if(!silent) - to_chat(target, span_warning("Their penis needs to be exposed.")) - return FALSE - if(target_require_penis_unexposed && has_penis == HAS_EXPOSED_GENITAL) - if(!silent) - to_chat(target, span_warning("Their penis needs to be unexposed.")) - return FALSE + if(!(target_require_penis_exposed && target_require_penis_unexposed)) + if(!(has_penis == TRUE)) + if(target_require_penis_exposed && has_penis == HAS_UNEXPOSED_GENITAL) + if(!silent) + to_chat(target, span_warning("Their penis needs to be exposed.")) + return FALSE + if(target_require_penis_unexposed && has_penis == HAS_EXPOSED_GENITAL) + if(!silent) + to_chat(target, span_warning("Their penis needs to be unexposed.")) + return FALSE var/target_require_balls_exposed = !!(required_from_target_exposed & INTERACTION_REQUIRE_BALLS) var/target_require_balls_unexposed = !!(required_from_target_unexposed & INTERACTION_REQUIRE_BALLS) @@ -295,15 +295,16 @@ to_chat(target, span_warning("They don't have balls.")) return FALSE - if(!(has_balls == TRUE) || !(target_require_balls_exposed && target_require_balls_unexposed)) - if(target_require_balls_exposed && has_balls == HAS_UNEXPOSED_GENITAL) - if(!silent) - to_chat(target, span_warning("Their balls need to be exposed.")) - return FALSE - if(target_require_balls_unexposed && has_balls == HAS_EXPOSED_GENITAL) - if(!silent) - to_chat(target, span_warning("Their balls need to be unexposed.")) - return FALSE + if(!(target_require_balls_exposed && target_require_balls_unexposed)) + if(!(has_balls == TRUE)) + if(target_require_balls_exposed && has_balls == HAS_UNEXPOSED_GENITAL) + if(!silent) + to_chat(target, span_warning("Their balls need to be exposed.")) + return FALSE + if(target_require_balls_unexposed && has_balls == HAS_EXPOSED_GENITAL) + if(!silent) + to_chat(target, span_warning("Their balls need to be unexposed.")) + return FALSE var/target_require_anus_exposed = !!(required_from_target_exposed & INTERACTION_REQUIRE_ANUS) var/target_require_anus_unexposed = !!(required_from_target_unexposed & INTERACTION_REQUIRE_ANUS) @@ -314,15 +315,16 @@ to_chat(target, span_warning("They don't have an anus.")) return FALSE - if(!(has_anus == TRUE) || !(target_require_anus_exposed && target_require_anus_unexposed)) - if(target_require_anus_exposed && has_anus == HAS_UNEXPOSED_GENITAL) - if(!silent) - to_chat(target, span_warning("Their anus needs to be exposed.")) - return FALSE - if(target_require_anus_unexposed && has_anus == HAS_EXPOSED_GENITAL) - if(!silent) - to_chat(target, span_warning("Their anus needs to be unexposed.")) - return FALSE + if(!(target_require_anus_exposed && target_require_anus_unexposed)) + if(!(has_anus == TRUE)) + if(target_require_anus_exposed && has_anus == HAS_UNEXPOSED_GENITAL) + if(!silent) + to_chat(target, span_warning("Their anus needs to be exposed.")) + return FALSE + if(target_require_anus_unexposed && has_anus == HAS_EXPOSED_GENITAL) + if(!silent) + to_chat(target, span_warning("Their anus needs to be unexposed.")) + return FALSE var/target_require_vagina_exposed = !!(required_from_target_exposed & INTERACTION_REQUIRE_VAGINA) var/target_require_vagina_unexposed = !!(required_from_target_unexposed & INTERACTION_REQUIRE_VAGINA) @@ -333,15 +335,16 @@ to_chat(target, span_warning("They don't have a vagina.")) return FALSE - if(!(has_vagina == TRUE) || !(target_require_vagina_exposed && target_require_vagina_unexposed)) - if(target_require_vagina_exposed && has_vagina == HAS_UNEXPOSED_GENITAL) - if(!silent) - to_chat(target, span_warning("Their vagina needs to be exposed.")) - return FALSE - if(target_require_vagina_unexposed && has_vagina == HAS_EXPOSED_GENITAL) - if(!silent) - to_chat(target, span_warning("Their vagina needs to be unexposed.")) - return FALSE + if(!(target_require_vagina_exposed && target_require_vagina_unexposed)) + if(!(has_vagina == TRUE)) + if(target_require_vagina_exposed && has_vagina == HAS_UNEXPOSED_GENITAL) + if(!silent) + to_chat(target, span_warning("Their vagina needs to be exposed.")) + return FALSE + if(target_require_vagina_unexposed && has_vagina == HAS_EXPOSED_GENITAL) + if(!silent) + to_chat(target, span_warning("Their vagina needs to be unexposed.")) + return FALSE var/target_require_breasts_exposed = !!(required_from_target_exposed & INTERACTION_REQUIRE_BREASTS) var/target_require_breasts_unexposed = !!(required_from_target_unexposed & INTERACTION_REQUIRE_BREASTS) @@ -352,15 +355,16 @@ to_chat(target, span_warning("They don't have breasts.")) return FALSE - if(!(has_breasts == TRUE) || !(target_require_breasts_exposed && target_require_breasts_unexposed)) - if(target_require_breasts_exposed && has_breasts == HAS_UNEXPOSED_GENITAL) - if(!silent) - to_chat(target, span_warning("Their breasts need to be exposed.")) - return FALSE - if(target_require_breasts_unexposed && has_breasts == HAS_EXPOSED_GENITAL) - if(!silent) - to_chat(target, span_warning("Their breasts need to be unexposed.")) - return FALSE + if(!(target_require_breasts_exposed && target_require_breasts_unexposed)) + if(!(has_breasts == TRUE)) + if(target_require_breasts_exposed && has_breasts == HAS_UNEXPOSED_GENITAL) + if(!silent) + to_chat(target, span_warning("Their breasts need to be exposed.")) + return FALSE + if(target_require_breasts_unexposed && has_breasts == HAS_EXPOSED_GENITAL) + if(!silent) + to_chat(target, span_warning("Their breasts need to be unexposed.")) + return FALSE var/target_require_feet_exposed = !!(required_from_target_exposed & INTERACTION_REQUIRE_FEET) var/target_require_feet_unexposed = !!(required_from_target_unexposed & INTERACTION_REQUIRE_FEET) diff --git a/tgstation.dme b/tgstation.dme index c88425a57d..7a78dcd046 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4077,6 +4077,7 @@ #include "modular_sand\code\datums\interactions\interaction_datums\lewd\nipsuck.dm" #include "modular_sand\code\datums\interactions\interaction_datums\lewd\nuts.dm" #include "modular_sand\code\datums\interactions\interaction_datums\lewd\oral.dm" +#include "modular_sand\code\datums\interactions\interaction_datums\lewd\slap.dm" #include "modular_sand\code\datums\interactions\interaction_datums\lewd\cursed\earfuck.dm" #include "modular_sand\code\datums\interactions\interaction_datums\lewd\cursed\eyefuck.dm" #include "modular_sand\code\datums\interactions\interaction_datums\lewd\self\breasts.dm" diff --git a/tgui/packages/tgui/interfaces/MobInteraction.tsx b/tgui/packages/tgui/interfaces/MobInteraction.tsx index 4dedb2348f..823723c83f 100644 --- a/tgui/packages/tgui/interfaces/MobInteraction.tsx +++ b/tgui/packages/tgui/interfaces/MobInteraction.tsx @@ -334,34 +334,62 @@ export const sortInteractions = (interactions, searchText = '', data) => { & interaction.interactionFlags) : true)), // Distance filter(interaction => - interaction.maxDistance >= max_distance), + max_distance <= interaction.maxDistance), // User requirements filter(interaction => interaction.required_from_user - ? !!(required_from_user & interaction.required_from_user) : true), - // User requires exposed - filter(interaction => interaction.required_from_user_exposed - ? !!(required_from_user_exposed - & interaction.required_from_user_exposed) : true), - // User requires unexposed - filter(interaction => interaction.required_from_user_unexposed - ? !!(required_from_user_unexposed - & interaction.required_from_user_unexposed) : true), + ? !!((required_from_user & interaction.required_from_user) + === interaction.required_from_user) : true), + + filter(interaction => { + // User requires exposed + const exposed = !interaction.required_from_user_exposed + || ((interaction.required_from_user_exposed + & required_from_user_exposed) + === interaction.required_from_user_exposed); + // User requires unexposed + const unexposed = !interaction.required_from_user_unexposed + || ((interaction.required_from_user_unexposed + & required_from_user_unexposed) + === interaction.required_from_user_unexposed); + + if (interaction.required_from_user_exposed + && interaction.required_from_user_unexposed) { + return exposed || unexposed; + } + else { + return exposed && unexposed; + } + }), + // User required feet amount filter(interaction => interaction.user_num_feet ? (interaction.user_num_feet <= user_num_feet) : true), // Target requirements filter(interaction => interaction.required_from_target - ? !!(required_from_target - & interaction.required_from_target) : true), - // Target requires exposed - filter(interaction => interaction.required_from_target_exposed - ? !!(required_from_target_exposed - & interaction.required_from_target_exposed) : true), - // Target requires unexposed - filter(interaction => interaction.required_from_target_unexposed - ? !!(required_from_target_unexposed - & interaction.required_from_target_unexposed) : true), + ? !!((required_from_target + & interaction.required_from_target) + === interaction.required_from_target) : true), + filter(interaction => { + // Target requires exposed + const exposed = !interaction.required_from_target_exposed + || ((interaction.required_from_target_exposed + & required_from_target_exposed) + === interaction.required_from_target_exposed); + // Target requires unexposed + const unexposed = !interaction.required_from_target_unexposed + || ((interaction.required_from_target_unexposed + & required_from_target_unexposed) + === interaction.required_from_target_unexposed); + + if (interaction.required_from_target_exposed + && interaction.required_from_target_unexposed) { + return exposed || unexposed; + } + else { + return exposed && unexposed; + } + }), // Target required feet amount filter(interaction => interaction.target_num_feet ? (interaction.target_num_feet <= target_num_feet) : true),