Files
Bubberstation/modular_skyrat/modules/interaction_menu/code/interaction_datum.dm
T
f58cf51ae4 [MODULARIZING] Partial Refactoring of Organic Interface Code (#16019)
This took a long time to review, but at long last, it's done.

* HUD clutter, good bye, you will not be missed.

* Oh my god so much pain

* No commented code. Bad.

* Uwaaaa

* Add a couple of warning messages for common scenarios, use defines

* AAAAA

* AAAAAAAAAAA

* UWAAAA

* UUUUWAAAA, DOCUMENTED SNOWFLAKE CODE

* FUCK

* Apply suggestions!

* Apply suggestions part one!

Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com>

* Apply suggestions part two!

* Static list

I think this is how it works, it compiles, sooo

* Split the giga monolith that's known as technical_stuff_for_lewd into two parts, human_helpers, and whatever else isn't a human helper. Also add a helper proc for mass update_inv on organic interface slots.

* I can't believe you did this

* Did you know that organic interface code didn't clean up it's defines? Neither did I!

* Less ifs

* Apply last suggestion that I know how to

* That was a lie

* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

I HAVEN'T EVEN TOUCHED THE WORST CODE

* Oops

* Pain

* No copypaste

* Proc is 🦀

* No, bad, no commenting code.

* List prittification for chems, reduces the sheer amount of update_body calls

* Allow non-lewd empath users to not have to suffer raunchy inspect messages

* Oop

* DEEEEEEEEEEEEFINES

* Oh yea, that review. Definitely remembered.

* Formatting

* Breast defines

* MUCH cleaner and MUCH MUCH EASIER to expand incubus draft code

* Credit myself if I'm gonna spend days of my time combing through this

* Oops

* Chems cleanup

* Whyyyyyyy

* Climax rework, suffering.

* Wtf is this name

* Oh yea, arousal clamping.

* Haha, oops

* Bad, this is stupid easy to modularize

* Why is body_size in here

* List formatting, grammar stuff, and also breast size is now letters

* This took too long for me to figure out and I'm not proud

* Even I can make shitcode!

* Oops, typo

* You know, this makes more sense.

* Mhhh, this shouldn't be named like this

* Wrong file

* Rename `adjustPain`

* Dead code go yeet

* Y U NO READ DOCS

* Doc proc, minor optimisation

* Reviews part 1

* Whoever made this should read good coding practices. For any language.

* DEEEEFINNNEEEEESSSS

* SUFFER

* No more fluids

* EEEEEEEEEEE

* AAAAAAAAAAAAAAA

* MOB IS 🦀

* AAAAAAAAAAAAAAAAAA

* AAAAAA

* UWA

* And also solve tastfish's reviews!

* Partial revert, cause DM hates that

* I am very smart

* EEE

* AAAAAA

* Haha keyboard go click clack

* CODE

* FUCK

* FUUUUUCK

* PERFORMANCE

* Reset horny to 0 if erp disabled

* Arousal minimum is now 0, cause negatives are dumb and I hate them

Also cuts out some code that isn't needed as far as I can tell.

* Apply suggestions!

Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com>

* AAAAA

* AAAAAAAAAA I CAN'T CODE

* Aaaaa

* WHY GIT

* I am very smart part... uh, four?

* Minor formatting changes

* Oops, fix potential cause of multiple climax attempts

* WAIT, WHY AREN'T YOU USING THE DEFINE?!

* Magic number slightly less magicky, sorta.

Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com>
2022-10-04 11:06:12 -04:00

272 lines
11 KiB
Plaintext

GLOBAL_LIST_EMPTY_TYPED(interaction_instances, /datum/interaction)
/datum/interaction
/// The name to be displayed in the interaction menu for this interaction
var/name = "broken interaction"
/// The description of the interacton.
var/description = "broken"
/// If it can be done at a distance.
var/distance_allowed = FALSE
/// A list of possible messages displayed loaded by the JSON.
var/list/message = list()
/// A list of possible messages displayed directly to the USER.
var/list/user_messages = list()
/// A list of possible messages displayed directly to the TARGET.
var/list/target_messages = list()
/// What category this interaction will fall under in the menu.
var/category = INTERACTION_CAT_HIDE
/// Defines how we interact with ourselves or others.
var/usage = INTERACTION_OTHER
/// Does this interaction play a sound?
var/sound_use = FALSE
/// If it plays a sound, how far does it travel?
var/sound_range = 1
/// Stores the sound for later.
var/sound_cache = null
/// Is this lewd?
var/lewd = FALSE
/// What parts do WE need(IMPORTANT TO GET IT TO THE CORRECT DEFINE, ORGAN SLOT)?
var/list/user_required_parts = list()
/// What parts do they need(IMPORTANT TO GET IT TO THE CORRECT DEFINE, ORGAN SLOT)?
var/list/target_required_parts = list()
/// The amount of pleasure the target recieves from this interaciton.
var/target_pleasure = 0
/// The amount of arousal the target recieves from this interaction.
var/target_arousal = 0
/// The amount of pain the target recieves.
var/target_pain = 0
/// The amount of pleasure the user recieves.
var/user_pleasure = 0
/// The amount of arousal the user recieves.
var/user_arousal = 0
/// The amount of pain the user recieves.
var/user_pain = 0
/// A list of possible sounds.
var/list/sound_possible = list()
/// What requirements does this interaction have? See defines.
var/list/interaction_requires = list()
/// What color should the interaction button be?
var/color = "blue"
/// What sexuality preference do we display for.
var/sexuality = ""
/datum/interaction/proc/allow_act(mob/living/carbon/human/user, mob/living/carbon/human/target)
if(target == user && usage == INTERACTION_OTHER)
return FALSE
if(user_required_parts.len)
for(var/thing in user_required_parts)
var/obj/item/organ/external/genital/required_part = user.getorganslot(thing)
if(isnull(required_part))
return FALSE
if(!required_part.is_exposed())
return FALSE
if(target_required_parts.len)
for(var/thing in target_required_parts)
var/obj/item/organ/external/genital/required_part = target.getorganslot(thing)
if(isnull(required_part))
return FALSE
if(!required_part.is_exposed())
return FALSE
for(var/requirement in interaction_requires)
switch(requirement)
if(INTERACTION_REQUIRE_SELF_HAND)
if(!user.get_active_hand())
return FALSE
if(INTERACTION_REQUIRE_TARGET_HAND)
if(!target.get_active_hand())
return FALSE
else
CRASH("Unimplemented interaction requirement '[requirement]'")
return TRUE
/datum/interaction/proc/act(mob/living/carbon/human/user, mob/living/carbon/human/target)
if(!allow_act(user, target))
return
if(!message)
message_admins("Interaction had a null message list. '[name]'")
return
if(!islist(message) && istext(message))
message_admins("Deprecated message handling for '[name]'. Correct format is a list with one entry. This message will only show once.")
message = list(message)
var/msg = pick(message)
// We replace %USER% with nothing because manual_emote already prepends it.
msg = trim(replacetext(replacetext(msg, "%TARGET%", "[target]"), "%USER%", ""), INTERACTION_MAX_CHAR)
if(lewd)
user.emote("subtler", null, msg, TRUE)
else
user.manual_emote(msg)
if(user_messages.len)
var/user_msg = pick(user_messages)
user_msg = replacetext(replacetext(user_msg, "%TARGET%", "[target]"), "%USER%", "[user]")
to_chat(user, user_msg)
if(target_messages.len)
var/target_msg = pick(target_messages)
target_msg = replacetext(replacetext(target_msg, "%TARGET%", "[target]"), "%USER%", "[user]")
to_chat(target, target_msg)
if(sound_use)
if(!sound_possible)
message_admins("Interaction has sound_use set to TRUE but does not set sound! '[name]'")
return
if(!islist(sound_possible) && istext(sound_possible))
message_admins("Deprecated sound handling for '[name]'. Correct format is a list with one entry. This message will only show once.")
sound_possible = list(sound_possible)
sound_cache = pick(sound_possible)
for(var/mob/mob in view(sound_range, user))
SEND_SOUND(sound_cache, mob)
if(lewd)
user.adjust_pleasure(user_pleasure)
user.adjust_arousal(user_arousal)
user.adjust_pain(user_pain)
target.adjust_pleasure(target_pleasure)
target.adjust_arousal(target_arousal)
target.adjust_pain(target_pain)
/datum/interaction/proc/load_from_json(path)
var/fpath = path
if(!fexists(fpath))
message_admins("Attempted to load an interaction from json and the file does not exist")
qdel(src)
return FALSE
var/file = file(fpath)
var/list/json = json_load(file)
name = sanitize_text(json["name"])
description = sanitize_text(json["description"])
distance_allowed = sanitize_integer(json["distance_allowed"], 0, 1, 0)
message = sanitize_islist(json["message"], list("json error"))
category = sanitize_text(json["category"])
usage = sanitize_text(json["usage"])
sound_use = sanitize_integer(json["sound_use"], 0, 1, 0)
sound_range = sanitize_integer(json["sound_range"], 1, 7, 1)
sound_possible = sanitize_islist(json["sound_possible"], list("json error"))
interaction_requires = sanitize_islist(json["interaction_requires"], list())
color = sanitize_text(json["color"])
user_messages = sanitize_islist(json["user_messages"], list())
user_required_parts = sanitize_islist(json["user_required_parts"], list())
user_arousal = sanitize_integer(json["user_arousal"], 0, 100, 0)
user_pleasure = sanitize_integer(json["user_pleasure"], 0, 100, 0)
user_pain = sanitize_integer(json["user_pain"], 0, 100, 0)
target_messages = sanitize_islist(json["target_messages"], list())
target_required_parts = sanitize_islist(json["target_required_parts"], list())
target_arousal = sanitize_integer(json["target_arousal"], 0, 100, 0)
target_pleasure = sanitize_integer(json["target_pleasure"], 0, 100, 0)
target_pain = sanitize_integer(json["target_pain"], 0, 100, 0)
lewd = sanitize_integer(json["lewd"], 0, 1, 0)
sexuality = sanitize_text(json["sexuality"])
return TRUE
/datum/interaction/proc/json_save(path)
var/fpath = path
if(fexists(fpath))
fdel(fpath)
var/list/json = list(
"name" = name,
"description" = description,
"distance_allowed" = distance_allowed,
"message" = message,
"category" = category,
"usage" = usage,
"sound_use" = sound_use,
"sound_range" = sound_range,
"sound_possible" = sound_possible,
"interaction_requires" = interaction_requires,
"color" = color,
"user_messages" = user_messages,
"user_required_parts" = user_required_parts,
"user_arousal" = user_arousal,
"user_pleasure" = user_pleasure,
"user_pain" = user_pain,
"target_messages" = target_messages,
"target_required_parts" = target_required_parts,
"target_arousal" = target_arousal,
"target_pleasure" = target_pleasure,
"target_pain" = target_pain,
"lewd" = lewd,
"sexuality" = sexuality,
)
var/file = file(fpath)
WRITE_FILE(file, json_encode(json))
return TRUE
/mob/living/carbon/human/Initialize(mapload)
. = ..()
AddComponent(/datum/component/interactable)
/// Global loading procs
/proc/populate_interaction_instances()
for(var/spath in subtypesof(/datum/interaction))
var/datum/interaction/interaction = new spath()
GLOB.interaction_instances[interaction.name] = interaction
populate_interaction_jsons(INTERACTION_JSON_FOLDER)
/proc/populate_interaction_jsons(directory)
for(var/file in flist(directory))
if(flist(directory + file) && !findlasttext(directory + file, ".json"))
populate_interaction_instances(directory + file)
continue
if(findlasttext(directory + file, ".master.json")) // This is a master json which has special handling
populate_interaction_jsons_master(directory + file)
continue
var/datum/interaction/interaction = new()
if(interaction.load_from_json(directory + file))
GLOB.interaction_instances[interaction.name] = interaction
else message_admins("Error loading interaction from file: '[directory + file]'. Inform coders.")
/proc/populate_interaction_jsons_master(path)
if(!fexists(path))
message_admins("We are attempting to load an interaction master without the file existing! '[path]'")
return
var/file = file(path)
var/list/json = json_load(file)
for(var/iname in json)
if(GLOB.interaction_instances[iname])
message_admins("Interaction Master '[path]' contained a duplicate interaction! '[iname]'")
continue
var/list/ijson = json[iname]
if(ijson["name"] != iname)
message_admins("Interaction Master '[path]' contained an invalid interaction! '[iname]'")
continue
var/datum/interaction/interaction = new()
interaction.distance_allowed = sanitize_integer(ijson["distance_allowed"], 0, 1, 0)
interaction.message = sanitize_islist(ijson["message"], list("json error"))
interaction.category = sanitize_text(ijson["category"])
interaction.usage = sanitize_text(ijson["usage"])
interaction.sound_use = sanitize_integer(ijson["sound_use"], 0, 1, 0)
interaction.sound_range = sanitize_integer(ijson["sound_range"], 1, 7, 1)
interaction.sound_possible = sanitize_islist(ijson["sound_possible"], list("json error"))
interaction.interaction_requires = sanitize_islist(ijson["interaction_requires"], list())
interaction.color = sanitize_text(ijson["color"])
interaction.user_messages = sanitize_islist(ijson["user_messages"], list())
interaction.user_required_parts = sanitize_islist(ijson["user_required_parts"], list())
interaction.user_arousal = sanitize_integer(ijson["user_arousal"], 0, 100, 0)
interaction.user_pleasure = sanitize_integer(ijson["user_pleasure"], 0, 100, 0)
interaction.user_pain = sanitize_integer(ijson["user_pain"], 0, 100, 0)
interaction.target_messages = sanitize_islist(ijson["target_messages"], list())
interaction.target_required_parts = sanitize_islist(ijson["target_required_parts"], list())
interaction.target_arousal = sanitize_integer(ijson["target_arousal"], 0, 100, 0)
interaction.target_pleasure = sanitize_integer(ijson["target_pleasure"], 0, 100, 0)
interaction.target_pain = sanitize_integer(ijson["target_pain"], 0, 100, 0)
interaction.lewd = sanitize_integer(ijson["lewd"], 0, 1, 0)
interaction.sexuality = sanitize_text(ijson["sexuality"])
GLOB.interaction_instances[iname] = interaction
/client/proc/reload_interactions()
set category = "Debug"
set name = "Reload Interactions"
set desc = "Force reload interactions"
if(!check_rights(R_DEBUG))
return
populate_interaction_instances()