mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-30 08:37:43 +01:00
## About The Pull Request the arguments for `SEND_SOUND` were switched, so even if a sound for an interaction was set it would not work. I have also changed it to use playsound instead (similarly to the existing sound emotes see [code/datums/emotes.dm:141](https://github.com/Bubberstation/Bubberstation/blob/master/code/datums/emotes.dm#L141)) that way it doesn't play at the default full volume (ouch) and would sound as you expect it. Only downside to this is that this would be audible to ghosts as well. I can just revert that change and just leave it at `SEND_SOUND(mob, sound(sound_cache, volume = 50))` if necessary. while writing this pr I was thinking, maybe I could've also set vary to true so the pitch changes as well, but I've not tested that yet and maybe it actually sounds horrible. ## Why It's Good For The Game If sounds are added to interactions, they would actually work. ## Proof Of Testing <details> <summary>Screenshots/Videos</summary> https://github.com/user-attachments/assets/2cc8fe04-b6b5-4f21-8cc3-bac1cf6309e0 </details> ## Changelog 🆑 fix: fixed interaction sounds not working (if there was one) /🆑
276 lines
11 KiB
Plaintext
276 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 receives from this interaciton.
|
|
var/target_pleasure = 0
|
|
/// The amount of arousal the target receives from this interaction.
|
|
var/target_arousal = 0
|
|
/// The amount of pain the target receives.
|
|
var/target_pain = 0
|
|
/// The amount of pleasure the user receives.
|
|
var/user_pleasure = 0
|
|
/// The amount of arousal the user receives.
|
|
var/user_arousal = 0
|
|
/// The amount of pain the user receives.
|
|
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/genital/required_part = user.get_organ_slot(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/genital/required_part = target.get_organ_slot(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, obj/body_relay = null)
|
|
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)
|
|
if(!isnull(body_relay))
|
|
msg = replacetext(msg, "%TARGET%", "\the [body_relay.name]")
|
|
// 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("subtle", null, msg, TRUE)
|
|
else
|
|
user.manual_emote(msg)
|
|
if(user_messages.len)
|
|
var/user_msg = pick(user_messages)
|
|
if(!isnull(body_relay))
|
|
user_msg = replacetext(user_msg, "%TARGET%", "\the [body_relay.name]")
|
|
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)
|
|
if(!isnull(body_relay))
|
|
target_msg = replacetext(target_msg, "%USER%", "Unknown")
|
|
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)
|
|
// playsound()'s range is always SOUND_RANGE (15) + extrarange, so we offset by -SOUND_RANGE to make
|
|
// extrarange effectively equal to the JSON-defined sound_range instead of stacking on top of it.
|
|
playsound(source = user, soundin = sound_cache, vol = 50, vary = FALSE, extrarange = sound_range - SOUND_RANGE, ignore_walls = FALSE, volume_preference = /datum/preference/numeric/volume/sound_emote)
|
|
|
|
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)
|
|
if(body_relay)
|
|
var/obj/lewd_portal_relay/body_portal_relay = body_relay
|
|
body_portal_relay.update_visuals()
|
|
|
|
/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
|
|
|
|
ADMIN_VERB(reload_interactions, R_DEBUG, "Reload Interactions", "Force reload interactions.", ADMIN_CATEGORY_DEBUG)
|
|
populate_interaction_instances()
|