Merge remote-tracking branch 'origin/master' into upstream-feb12-2026

# Conflicts:
#	_maps/map_files/moonstation/moonstation.dmm
#	tgstation.dme
This commit is contained in:
nevimer
2026-03-10 17:10:57 -04:00
126 changed files with 57368 additions and 3868 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,2 +0,0 @@
GLOBAL_LIST_EMPTY(blooper_list)
GLOBAL_LIST_EMPTY(blooper_random_list)
@@ -1,4 +1,3 @@
//BLOOPER defines
#define BLOOPER_DEFAULT_MINPITCH 0.4
#define BLOOPER_DEFAULT_MAXPITCH 2
#define BLOOPER_DEFAULT_MINVARY 0.1
@@ -8,12 +7,10 @@
#define BLOOPER_SPEED_BASELINE 4 //Used to calculate delay between BLOOPERs, any BLOOPER speeds below this feature higher BLOOPER density, any speeds above feature lower BLOOPER density. Keeps BLOOPERing length consistent
#define BLOOPER_TRANSMIT_VOLUME 55
#define BLOOPER_MAX_BLOOPERS 24
#define BLOOPER_MAX_TIME (1.5 SECONDS) // More or less the amount of time the above takes to process through with a BLOOPER speed of 2.
#define BLOOPER_PITCH_RAND(gend) ((gend == MALE ? rand(60, 120) : (gend == FEMALE ? rand(80, 140) : rand(60,140))) / 100) //Macro for determining random pitch based off gender
#define BLOOPER_VARIANCE_RAND (rand(BLOOPER_DEFAULT_MINVARY * 100, BLOOPER_DEFAULT_MAXVARY * 100) / 100) //Macro for randomizing BLOOPER variance to reduce the amount of copy-pasta necessary for that
#define BLOOPER_DO_VARY(pitch, variance) (rand(((pitch * 100) - (variance*50)), ((pitch*100) + (variance*50))) / 100)
#define BLOOPER_SOUND_FALLOFF_EXPONENT 0.5 //At lower ranges, we want the exponent to be below 1 so that whispers don't sound too awkward. At higher ranges, we want the exponent fairly high to make yelling less obnoxious
+1
View File
@@ -1,3 +1,4 @@
// boolean - weather types that occur on the level
#define ZTRAIT_SANDSTORM_MOON "Weather_Sandstorm_Moon"
#define AWAYSTART_MAINTSROOM "AWAYSTART_MAINTSROOM"
+1
View File
@@ -93,6 +93,7 @@ GLOBAL_LIST_INIT(organ_choice, list(
"Lungs" = ORGAN_SLOT_LUNGS,
"Liver" = ORGAN_SLOT_LIVER,
"Stomach" = ORGAN_SLOT_STOMACH,
"Brain" = ORGAN_SLOT_BRAIN, //BUBBER EDIT - Added line
))
///Paraplegic Quirk
@@ -10,13 +10,16 @@
/// whether the removal needs to be surgical for it to explode. If you're adding more modes, just pass the signal directly instead
var/surgical
/// whether the removal causes the organ to qdel
var/annihilate
/datum/element/dangerous_organ_removal/Attach(datum/target, surgical = FALSE)
/datum/element/dangerous_organ_removal/Attach(datum/target, surgical = FALSE, annihilate = TRUE) //BUBBER EDIT - Adds new variable to make organ not qdel
. = ..()
if(!isorgan(target))
return ELEMENT_INCOMPATIBLE
src.surgical = surgical
src.annihilate = annihilate
if(surgical)
RegisterSignal(target, COMSIG_ORGAN_SURGICALLY_REMOVED, PROC_REF(on_removal))
@@ -40,4 +43,10 @@
source.audible_message("[source] explodes into tiny pieces!")
explosion(source, light_impact_range = 1, explosion_cause = source)
qdel(source)
//BUBBER EDIT - if it doesn't qdel, set it to take all its health in damage
//qdel(source)
if(annihilate)
qdel(source)
else
source.set_organ_damage(source.maxHealth)
//EDIT END
@@ -1,4 +1,5 @@
/datum/quirk/prosthetic_organ
// BUBBER EDIT: moved to modular_zubbers\code\datums\quirks\negative_quirks\prosthetic_organ_bubber.dm
/* /datum/quirk/prosthetic_organ
name = "Prosthetic Organ"
desc = "An accident caused you to lose one of your organs. Because of this, you now have a surplus prosthetic!"
icon = FA_ICON_LUNGS
@@ -72,3 +73,4 @@
if(old_organ)
old_organ.Insert(quirk_holder, special = TRUE)
old_organ = null
*/
-6
View File
@@ -515,12 +515,6 @@
if(NAMEOF(src, glide_size))
set_glide_size(var_value)
. = TRUE
// THE BUBBER EDIT ADDITION BEGIN - BLOOPER
if(NAMEOF(src, blooper)) // Sorry, Vishenka.
if(isfile(var_value))
blooper = sound(var_value) //bark() expects vocal_bark to already be a sound datum, for performance reasons. adminbus QoL!
. = TRUE
// THE BUBBER EDIT ADDITION END
if(!isnull(.))
datum_flags |= DF_VAR_EDITED
return
-6
View File
@@ -22,12 +22,6 @@
// Snowflake for Ethereals
human.updatehealth()
human.updateappearance(mutcolor_update = TRUE)
// BUBBER EDIT ADDITION BEGIN - Bloopers
human.set_blooper(pick(GLOB.blooper_list))
human.blooper_pitch = BLOOPER_PITCH_RAND(human.gender)
human.blooper_pitch_range = BLOOPER_VARIANCE_RAND
human.blooper_speed = rand(BLOOPER_DEFAULT_MINSPEED, BLOOPER_DEFAULT_MAXSPEED)
// BUBBER EDIT ADDITION END - Bloopers
/**
* Randomizes a human, but produces someone who looks exceedingly average (by most standards).
@@ -575,6 +575,10 @@
new_profile.undershirt_color = target.undershirt_color
new_profile.socks_color = target.socks_color
new_profile.bra_color = target.bra_color
new_profile.blooper = target.blooper
new_profile.blooper_speed = target.blooper_speed
new_profile.blooper_pitch = target.blooper_pitch
new_profile.blooper_pitch_range = target.blooper_pitch_range
new_profile.eye_color_left = target.eye_color_left
new_profile.eye_color_right = target.eye_color_right
new_profile.emissive_eyes = target.emissive_eyes
@@ -584,12 +588,6 @@
new_profile.target_size = target.mob_size
//SKYRAT EDIT ADDITION END
// Hair and facial hair gradients, alongside their colours.
//THE BUBBER EDIT ADDITION BEGIN - Voice Bark
new_profile.blooper_id = target.blooper_id
new_profile.blooper_pitch = target.blooper_pitch
new_profile.blooper_speed = target.blooper_speed
new_profile.blooper_pitch_range = target.blooper_pitch_range
//THE BUBBER EDIT END
// Grab skillchips they have
new_profile.skillchips = target.clone_skillchip_list(TRUE)
@@ -829,6 +827,10 @@
user.undershirt_color = chosen_profile.undershirt_color
user.socks_color = chosen_profile.socks_color
user.bra_color = chosen_profile.bra_color
user.blooper = chosen_profile.blooper
user.blooper_speed = chosen_profile.blooper_speed
user.blooper_pitch = chosen_profile.blooper_pitch
user.blooper_pitch_range = chosen_profile.blooper_pitch_range
user.emissive_eyes = chosen_profile.emissive_eyes
user.dna.mutant_bodyparts = chosen_dna.mutant_bodyparts.Copy()
user.dna.body_markings = chosen_dna.body_markings.Copy()
@@ -970,13 +972,6 @@
//this has to be at the end of the proc or it breaks everything below it, womp womp
user.set_mob_height(chosen_profile.target_body_scaling)
// SKYRAT EDIT END
//THE BUBBER EDIT ADDITION BEGIN - Voice Bark
user.blooper = null
user.blooper_id = chosen_profile.blooper_id
user.blooper_pitch = chosen_profile.blooper_pitch
user.blooper_speed = chosen_profile.blooper_speed
user.blooper_pitch_range = chosen_profile.blooper_pitch_range
//THE BUBBER EDIT END
// Changeling profile themselves. Store a data to store what every DNA instance looked like.
/datum/changeling_profile
/// The name of the profile / the name of whoever this profile source.
@@ -1075,6 +1070,10 @@
new_profile.socks_color = socks_color
new_profile.bra = bra
new_profile.bra_color = bra_color
new_profile.blooper = blooper
new_profile.blooper_speed = blooper_speed
new_profile.blooper_pitch = blooper_pitch
new_profile.blooper_pitch_range = blooper_pitch_range
new_profile.eye_color_left = eye_color_left
new_profile.eye_color_right = eye_color_right
new_profile.emissive_eyes = emissive_eyes
@@ -2,13 +2,13 @@
name = "cybernetic brain"
desc = "A mechanical brain found inside of androids. Not to be confused with a positronic brain."
icon_state = "brain-c"
organ_flags = ORGAN_ROBOTIC | ORGAN_VITAL
organ_flags = ORGAN_ROBOTIC | ORGAN_VITAL | ORGAN_PROMINENT //BUBBER EDIT - Added ORGAN_PROMINENT
failing_desc = "seems to be broken, and will not work without repairs."
shade_color = null
var/emp_dmg_mult = 1 //BUBBER EDIT - Variable multiplier for damage from EMPs. Note the base damage is 20.
var/emp_dmg_max = 999 ///BUBBER EDIT - Threshold before the organ simply stops taking damage from EMPs. Defaults to kill
/* BUBBER EDIT - Reformatted this to change text based on flags
/obj/item/organ/brain/cybernetic/brain_damage_examine()
if(suicided)
return span_info("Its circuitry is smoking slightly. They must not have been able to handle the stress of it all.")
@@ -21,9 +21,57 @@
return span_info("You can feel the small spark of life still left in this one.")
else
return span_info("This one is completely devoid of life.")
*/
//BUBBER EDIT - See above
/obj/item/organ/brain/cybernetic/brain_damage_examine()
var/is_organic = (organ_flags & ORGAN_ORGANIC) //this determines if it'll print the organic or cybernetic texts
if(suicided)
return span_info("Its circuitry is smoking slightly. They must not have been able to handle the stress of it all.")
if(brainmob && (decoy_override || brainmob.client || brainmob.get_ghost()))
if(organ_flags & ORGAN_FAILING)
return span_info("It seems to still have a bit of energy within it, but it's rather damaged... You may be able to repair it with [is_organic ? "some <b>mannitol</b> or " : ""]a <b>multitool</b>.")
else if(damage >= BRAIN_DAMAGE_DEATH*0.5)
return span_info("You can feel the small spark of life still left in this one, but it's got some dents. You may be able to restore it with [is_organic ? "some <b>mannitol</b> or " : ""]a <b>multitool</b>.")
else
return span_info("You can feel the small spark of life still left in this one.")
else
return span_info("This one is completely devoid of life.")
// EDIT END
/* BUBBER EDIT - Expands this proc for hybrid brain_size.
ORIGINAL:
/obj/item/organ/brain/cybernetic/check_for_repair(obj/item/item, mob/user)
if (item.tool_behaviour == TOOL_MULTITOOL) //attempt to repair the brain
if (brainmob?.health <= HEALTH_THRESHOLD_DEAD) //if the brain is fucked anyway, do nothing
to_chat(user, span_warning("[src] is far too damaged, there's nothing else we can do for it!"))
return TRUE
if (DOING_INTERACTION(user, src))
to_chat(user, span_warning("you're already repairing [src]!"))
return TRUE
user.visible_message(span_notice("[user] slowly starts to repair [src] with [item]."), span_notice("You slowly start to repair [src] with [item]."))
var/did_repair = FALSE
while(damage > 0)
if(item.use_tool(src, user, 3 SECONDS, volume = 50))
did_repair = TRUE
set_organ_damage(max(0, damage - 20))
else
break
if (did_repair)
if (damage > 0)
user.visible_message(span_notice("[user] partially repairs [src] with [item]."), span_notice("You partially repair [src] with [item]."))
else
user.visible_message(span_notice("[user] fully repairs [src] with [item], causing its warning light to stop flashing."), span_notice("You fully repair [src] with [item], causing its warning light to stop flashing."))
else
to_chat(user, span_warning("You failed to repair [src] with [item]!"))
return TRUE
return FALSE
EDIT END */
// EDITED:
/obj/item/organ/brain/cybernetic/check_for_repair(obj/item/item, mob/user)
if (item.tool_behaviour == TOOL_MULTITOOL && (organ_flags & ORGAN_ROBOTIC)) //robotic repair option
if (brainmob?.health <= HEALTH_THRESHOLD_DEAD) //if the brain is fucked anyway, do nothing
to_chat(user, span_warning("[src] is far too damaged, there's nothing else we can do for it!"))
return TRUE
@@ -50,6 +98,23 @@
to_chat(user, span_warning("You failed to repair [src] with [item]!"))
return TRUE
else if(damage && item.is_drainable() && item.reagents.has_reagent(/datum/reagent/medicine/mannitol) && (organ_flags & ORGAN_ORGANIC)) //ganic repair option (this is for the cortical children)
if(brainmob?.health <= HEALTH_THRESHOLD_DEAD) //if the brain is fucked anyway, do nothing
to_chat(user, span_warning("[src] is far too damaged, there's nothing else we can do for it!"))
return TRUE
user.visible_message(span_notice("[user] starts to slowly pour the contents of [item] onto [src]."), span_notice("You start to slowly pour the contents of [item] onto [src]."))
if(!do_after(user, 3 SECONDS, src))
to_chat(user, span_warning("You failed to pour the contents of [item] onto [src]!"))
return TRUE
var/and_bright_shade = !shade_color ? "" : " and turn a slightly brighter shade of [shade_color]"
user.visible_message(span_notice("[user] pours the contents of [item] onto [src], causing it to reform its original shape[and_bright_shade]."), span_notice("You pour the contents of [item] onto [src], causing it to reform its original shape[and_bright_shade]."))
var/amount = item.reagents.get_reagent_amount(/datum/reagent/medicine/mannitol)
var/healto = max(0, damage - amount * 2)
item.reagents.remove_all(ROUND_UP(item.reagents.total_volume / amount * (damage - healto) * 0.5)) //only removes however much solution is needed while also taking into account how much of the solution is mannitol
set_organ_damage(healto) //heals 2 damage per unit of mannitol, and by using "set_organ_damage", we clear the failing variable if that was up
return TRUE
return FALSE
/obj/item/organ/brain/cybernetic/emp_act(severity)
@@ -1895,6 +1895,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
to_add += create_pref_liver_perks()
to_add += create_pref_lung_perks()
to_add += create_pref_brain_perks() //BUBBER EDIT - We got brain stuff
return to_add
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

+17
View File
@@ -0,0 +1,17 @@
# Vocal Bloopers
By default, no vocal bloopers are included in the repository, a sample config file is provided to demonstrate the format used.
To set things up, copy `blooper_config.json.example` and rename it to `blooper_config.json`, then add an entry for each blooper.
## Config Format
- name: Required, the user-friendly name of the voice, will be shown in the config menu
- id: Required, an internal ID for the voice, must be unique
- files: Required, a JSON array of audio file names, relative to the bloopers directory.
If there's more than one entry, a file is randomly chosen each time the user speaks
- min_speed: Optional, the minimum value the user can set for the voice speed
- max_speed: Optional, the maximum value the user can set for the voice speed
- min_pitch: Optional, the minimum value the user can set for the voice pitch
- max_pitch: Optional, the maximum value the user can set for the voice pitch
- min_vary: Optional, the minimum value the user can set for the voice pitch variance
- max_vary: Optional, the maximum value the user can set for the voice pitch variance
@@ -0,0 +1,13 @@
[
{
"name": "Example Blooper",
"id": "example",
"files": ["example.ogg"],
"min_speed": 2,
"max_speed": 16,
"min_pitch": 0.4,
"max_pitch": 2,
"min_vary": 0.1,
"max_vary": 0.8
}
]
@@ -0,0 +1,12 @@
author: "aKromatopzia"
delete-after: True
changes:
- rscadd: "Previously-unobtainable surplus-cybernetic brain added to Prosthetic Organ quirk"
- rscadd: "Cybernetic brains given the ORGAN_PROMINENT trait, following normal brains."
- rscadd: "Vox Primalis unique cybernetic brain has been finally added to their species descriptions. (Bet you didn't know they had one)"
- balance: "(Roundstart) Cortical brains given ORGAN_ORGANIC, so they can be fixed with both mannitol and a multitool. Vox brains are excluded"
- balance: "Brain options in character prefs have been given a 1 quirkiness-point cost."
- bugfix: "Cybernetic/Cortical brains have their repair hints updated to reflect what they can actually be repaired by"
- code_imp: "Prosthetic Organ quirk moved to modular_zubbers"
- code_imp: "'Vox Primalis' now considered a collective noun"
- code_imp: "dangerous_organ_removal has a new variable to toggle between organ qdel or having its damage set to max health."
@@ -1,4 +0,0 @@
author: "BingusTheClown"
delete-after: True
changes:
- bugfix: "IPC's no longer have an error when customizing their look with a screen."
@@ -24,3 +24,28 @@
shayoki:
- qol: Non-evolved dense organ tissues in Medical Cytology require Saline-Glucose
Solution instead of Organ Tissue.
2026-03-07:
BingusTheClown:
- bugfix: IPC's no longer have an error when customizing their look with a screen.
MrGloopy:
- rscadd: Recolouring for moth eyes. You will have to set new colours in character
preferences.
- bugfix: HUD implants not displaying on moth eyes
- bugfix: De-spookified moths that have had their eyes replaced. (Fixed moth eyes
disappearing when replaced with anything else, rendering your sprite eyeless
until your original eyes were put back in).
Yobrocharlie:
- rscadd: Added a new gateway for admins to fuck around with.
2026-03-08:
xPokee:
- rscadd: Added new zombie markings.
xPokee, LT3, TealSeer:
- refactor: Refactored vocal bloopers to be a config based subsystem.
- config: Added all the previous vocal bloopers to the new config, as well as some
new ones, look for them!
2026-03-09:
BurgerBB:
- map: Makes Moon Station Botany better.
ibexgoetia:
- rscadd: Nanotrasen Consultants and Heads of Personnel may rejoice the presence
of a Paperwork Implant autosurgeon in their lockers.
@@ -1,6 +1,5 @@
/proc/make_skyrat_datum_references()
init_prefs_emotes()
make_bloopers()
make_default_mutant_bodypart_references()
make_body_marking_references()
make_body_marking_set_references()
@@ -181,12 +180,3 @@
continue
SSaccessories.bra_m -= sprite_name
// Text bloopers
/proc/make_bloopers()
GLOB.blooper_list = list()
for(var/sound_blooper_path in subtypesof(/datum/blooper))
var/datum/blooper/bloop = new sound_blooper_path()
GLOB.blooper_list[bloop.id] = sound_blooper_path
if(bloop.allow_random)
GLOB.blooper_random_list[bloop.id] = sound_blooper_path
@@ -36,11 +36,14 @@
//BRAINS
/datum/augment_item/organ/brain
slot = AUGMENT_SLOT_BRAIN
cost = 1 // while a sidegrade, there are genuine applications for having a vanilla brain
/datum/augment_item/organ/brain/cortical
name = "Cortically-Augmented Brain"
slot = AUGMENT_SLOT_BRAIN
path = /obj/item/organ/brain/cybernetic/cortical
cost = 1 // technically a downgrade in most applications, but has uses.
//EDIT END
//HEARTS
@@ -1,6 +1,7 @@
/datum/species/vox
// Bird-like humanoids
name = "Vox Primalis"
plural_form = "Vox Primalis"
id = SPECIES_VOX
can_augment = FALSE
inherent_traits = list(
+16 -22
View File
@@ -3,9 +3,10 @@
//The accessible cybernetic brain
/obj/item/organ/brain/cybernetic/cortical
name = "cortically-augmented brain"
desc = "A brain which has been in some part mechanized."
desc = "A brain which has been, in some part, mechanized."
icon = 'modular_skyrat/master_files/icons/obj/medical/organs/organs.dmi'
icon_state = "brain-c"
organ_flags = ORGAN_ORGANIC | ORGAN_ROBOTIC | ORGAN_VITAL | ORGAN_PROMINENT //it's a bit weird to be both organic and robotic, but yk
emp_dmg_mult = 1.5 //Note that the base damage is 20/10
emp_dmg_max = 150
@@ -26,30 +27,23 @@
owner.adjust_stutter(15 SECONDS)
owner.adjust_confusion(3 SECONDS)
// It's still organic
/obj/item/organ/brain/cybernetic/cortical/brain_damage_examine()
if(suicided)
return span_info("Its circuitry is smoking slightly. They must not have been able to handle the stress of it all.")
if(brainmob && (decoy_override || brainmob.client || brainmob.get_ghost()))
if(organ_flags & ORGAN_FAILING)
return span_info("It seems to still have a bit of energy within it, but it's rather damaged... You may be able to restore it with some <b>mannitol</b>.")
else if(damage >= BRAIN_DAMAGE_DEATH*0.5)
return span_info("You can feel the small spark of life still left in this one, but it's got some bruises. You may be able to restore it with some <b>mannitol</b>.")
else
return span_info("You can feel the small spark of life still left in this one.")
else
return span_info("This one is completely devoid of life.")
//New vox Brain
/obj/item/organ/brain/cybernetic/cortical/vox
name = "vox-augmented brain"
desc = "A brain which has been in some part mechanized. The components are seamlessly integrated into the flesh."
emp_dmg_mult = 1 //Nobody notices the brain damage anyways wink wink
desc = "A brain which has been in some part mechanized. The components are seamlessly integrated into the flesh, which protect that grey matter from biological processes."
organ_flags = ORGAN_ROBOTIC | ORGAN_VITAL | ORGAN_PROMINENT //Vox brains weren't organic originally. See no reason to change it
emp_dmg_mult = 1 //20/10 is the voxs' original damage factor
//surplus; TBI to prosthetic organ quirk
/obj/item/organ/brain/cybernetic/cortical/surplus
//surplus; Used in prosthetic organ quirk
//note that this is a direct child of the android brain, and is thus not organic
/obj/item/organ/brain/cybernetic/surplus
name = "surplus augmented brain"
desc = "A brain which has been in some part mechanized. It looks a bit cheap."
maxHealth = BRAIN_DAMAGE_DEATH*0.5 //200 -> 100, at time of creation
desc = "A mostly-mechanized brain. Not much of the flesh remains. Does this make them an IPC? "
maxHealth = BRAIN_DAMAGE_DEATH*0.75 //200 -> 150, per original intention
emp_dmg_mult = 1.5 //Note that the base damage is 20/10
emp_dmg_max = 999
/obj/item/organ/brain/cybernetic/surplus/Initialize(mapload)
. = ..()
AddElement(/datum/element/dangerous_organ_removal, /*surgical = */ TRUE, /*annihilate = */ FALSE) //annihilate just means it wont qdel when removed
@@ -0,0 +1,40 @@
#define BLOOPER_CONFIG_PATH "[global.config.directory]/bloopers"
SUBSYSTEM_DEF(blooper)
name = "Blooper"
flags = SS_NO_FIRE | SS_NO_INIT
var/list/blooper_list
/datum/controller/subsystem/blooper/OnConfigLoad()
blooper_list = initialize_blooper_datums()
/datum/controller/subsystem/blooper/proc/initialize_blooper_datums()
var/list/blooper_datums = list()
if(!rustg_file_exists("[BLOOPER_CONFIG_PATH]/blooper_config.json"))
logger.Log(LOG_CATEGORY_DEBUG, "blooper_config.json not found.")
return blooper_datums
var/list/blooper_entries = safe_json_decode(rustg_file_read("[BLOOPER_CONFIG_PATH]/blooper_config.json"))
if(isnull(blooper_entries))
stack_trace("Blooper config is malformed!")
return blooper_datums
for(var/entry in blooper_entries)
// These fields are required
if(isnull(entry["name"]) || isnull(entry["id"]) || isnull(entry["files"]) || !length(entry["files"]))
stack_trace("Blooper config entry was missing required field!")
continue
var/datum/blooper/new_blooper = new()
new_blooper.name = entry["name"]
new_blooper.id = entry["id"]
for(var/file in entry["files"])
new_blooper.soundpath_list += sound("[BLOOPER_CONFIG_PATH]/[file]")
new_blooper.min_pitch = entry["min_pitch"] || BLOOPER_DEFAULT_MINPITCH
new_blooper.max_pitch = entry["max_pitch"] || BLOOPER_DEFAULT_MAXPITCH
new_blooper.min_vary = entry["min_vary"] || BLOOPER_DEFAULT_MINVARY
new_blooper.max_vary = entry["max_vary"] || BLOOPER_DEFAULT_MAXVARY
new_blooper.min_speed = entry["min_speed"] || BLOOPER_DEFAULT_MINSPEED
new_blooper.max_speed = entry["max_speed"] || BLOOPER_DEFAULT_MAXSPEED
blooper_datums[new_blooper.id] = new_blooper
return blooper_datums
#undef BLOOPER_CONFIG_PATH
+63
View File
@@ -0,0 +1,63 @@
/datum/blooper
/// User friendly name of the blooper, displayed in preferences menu
var/name = null
/// Internal ID of blooper, must be unique
var/id = null
/// List of sounds, one is chosen randomly each time the blooper is played
var/list/sound/soundpath_list = list()
var/min_pitch = BLOOPER_DEFAULT_MINPITCH
var/max_pitch = BLOOPER_DEFAULT_MAXPITCH
var/min_vary = BLOOPER_DEFAULT_MINVARY
var/max_vary = BLOOPER_DEFAULT_MAXVARY
// Speed vars. Speed determines the number of characters required for each blooper, with lower speeds being faster with higher blooper density
var/min_speed = BLOOPER_DEFAULT_MINSPEED
var/max_speed = BLOOPER_DEFAULT_MAXSPEED
/**
* Plays the vocal blooper for all listeners, duration is controlled by length of the message argument
* * speaker - Origin source of the blooper, used for 3D audio calculations and to set the cooldown
* * listeners - List of mobs that will hear the blooper
* * message - Chat message being sent, used to determine how long bloopers play for (longer message, more sounds)
* * distance - Range at which the sounds will be heard
* * volume - Volume the sounds will play at
* * speed - How fast the bloopers will be, percentage between 0 - 100, converted and clamped to datum's min/max values
* * pitch - How high-pitched the bloopers will be, percentage between 0 - 100, converted and clamped to datum's min/max values
* * pitch_range - Amount of pitch variance for each blooper, percentage between 0 - 100, converted and clamped to datum's max/min values
*/
/datum/blooper/proc/play_bloop(atom/movable/speaker, list/listeners, message, distance, volume, speed, pitch, pitch_range)
if(!COOLDOWN_FINISHED(speaker, blooper_cooldown))
return
volume = min(volume, 100)
// convert passed values (which are percentages) into value clamped between min and max of blooper datum
speed = round(min_speed + ((max_speed - min_speed) * ((100 - speed) / 100)), 0.01) // this one gets inverted because lower % = faster isn't intuitive
pitch = round(min_pitch + ((max_pitch - min_pitch) * (pitch / 100)), 0.01)
pitch_range = round(min_vary + ((max_vary - min_vary) * (pitch_range / 100)), 0.01)
for(var/mob/target_mob in listeners)
if(target_mob.client && !(target_mob.client.prefs.read_preference(/datum/preference/toggle/hear_blooper)))
listeners -= target_mob
var/sound/blooper_pick = pick(soundpath_list)
var/num_bloopers = min(round(length(message) / speed, 1) + 1, BLOOPER_MAX_BLOOPERS)
var/total_delay = 0
for(var/i in 1 to num_bloopers)
if(total_delay > BLOOPER_MAX_TIME)
break
addtimer(CALLBACK(src, PROC_REF(play_callback), speaker, listeners, distance, volume, BLOOPER_DO_VARY(pitch, pitch_range), blooper_pick), total_delay)
total_delay += rand(DS2TICKS(speed / BLOOPER_SPEED_BASELINE), DS2TICKS(speed / BLOOPER_SPEED_BASELINE) + DS2TICKS(speed / BLOOPER_SPEED_BASELINE)) TICKS
COOLDOWN_START(speaker, blooper_cooldown, total_delay)
/// Private callback function to actually play the sound, used for timers scheduled in play_bloop
/datum/blooper/proc/play_callback(atom/movable/speaker, list/listeners, distance, volume, pitch, sound/voice)
PRIVATE_PROC(TRUE)
for(var/mob/target_mob in listeners)
target_mob.playsound_local(
turf_source = get_turf(speaker),
soundin = voice,
vol = volume,
vary = TRUE,
frequency = pitch,
falloff_distance = 0,
falloff_exponent = BLOOPER_SOUND_FALLOFF_EXPONENT,
max_distance = distance,
)
@@ -0,0 +1,102 @@
/datum/quirk/prosthetic_organ
name = "Prosthetic Organ"
desc = "An accident caused you to lose one of your organs. Because of this, you now have a surplus prosthetic!"
icon = FA_ICON_LUNGS
value = -3
medical_record_text = "During physical examination, patient was found to have a low-budget prosthetic organ. \
<b>Removal of these organs is known to be dangerous to the patient as well as the practitioner.</b>"
hardcore_value = 3
mail_goodies = list(/obj/item/storage/organbox)
/// The slot to replace, in string form
var/slot_string = "organ"
/// The original organ from before the prosthetic was applied
var/obj/item/organ/old_organ
/datum/quirk_constant_data/prosthetic_organ
associated_typepath = /datum/quirk/prosthetic_organ
customization_options = list(/datum/preference/choiced/prosthetic_organ)
/datum/quirk/prosthetic_organ/add_unique(client/client_source)
var/mob/living/carbon/human/human_holder = quirk_holder
var/static/list/organ_slots = list(
ORGAN_SLOT_HEART,
ORGAN_SLOT_LUNGS,
ORGAN_SLOT_LIVER,
ORGAN_SLOT_STOMACH,
ORGAN_SLOT_BRAIN,
)
var/preferred_organ = GLOB.organ_choice[client_source?.prefs?.read_preference(/datum/preference/choiced/prosthetic_organ)]
if(isnull(preferred_organ)) //Client is gone or they chose a random prosthetic
preferred_organ = GLOB.organ_choice[pick(GLOB.organ_choice)]
var/list/possible_organ_slots = organ_slots.Copy()
if(!CAN_HAVE_BLOOD(human_holder))
possible_organ_slots -= ORGAN_SLOT_HEART
if(HAS_TRAIT(human_holder, TRAIT_NOBREATH))
possible_organ_slots -= ORGAN_SLOT_LUNGS
if(HAS_TRAIT(human_holder, TRAIT_LIVERLESS_METABOLISM))
possible_organ_slots -= ORGAN_SLOT_LIVER
if(HAS_TRAIT(human_holder, TRAIT_NOHUNGER))
possible_organ_slots -= ORGAN_SLOT_STOMACH
if(HAS_TRAIT(human_holder, TRAIT_BRAINLESS_CARBON))
possible_organ_slots -= ORGAN_SLOT_BRAIN
if(!length(organ_slots)) //what the hell
return
var/organ_slot = pick(possible_organ_slots)
if(preferred_organ in possible_organ_slots)
organ_slot = preferred_organ
var/obj/item/organ/prosthetic
switch(organ_slot)
if(ORGAN_SLOT_HEART)
prosthetic = new /obj/item/organ/heart/cybernetic/surplus
slot_string = "heart"
if(ORGAN_SLOT_LUNGS)
prosthetic = new /obj/item/organ/lungs/cybernetic/surplus
slot_string = "lungs"
if(ORGAN_SLOT_LIVER)
prosthetic = new /obj/item/organ/liver/cybernetic/surplus
slot_string = "liver"
if(ORGAN_SLOT_STOMACH)
prosthetic = new /obj/item/organ/stomach/cybernetic/surplus
slot_string = "stomach"
if(ORGAN_SLOT_BRAIN)
prosthetic = new /obj/item/organ/brain/cybernetic/surplus
slot_string = "brain"
medical_record_text = "During physical examination, patient was found to have a low-budget prosthetic [slot_string]. \
Removal of these organs is known to be dangerous to the patient as well as the practitioner."
old_organ = human_holder.get_organ_slot(organ_slot)
prosthetic.Insert(human_holder, special = TRUE)
old_organ.moveToNullspace()
STOP_PROCESSING(SSobj, old_organ)
/datum/quirk/prosthetic_organ/post_add()
to_chat(quirk_holder, span_bolddanger("Your [slot_string] has been replaced with a surplus organ. It is weak and highly unstable. \
Additionally, any EMP will make it cease full functions for a time."))
/datum/quirk/prosthetic_organ/remove()
var/mob/living/carbon/human/human_holder = quirk_holder
if(old_organ)
if(old_organ.slot == AUGMENT_SLOT_BRAIN) //there's no way this works again
var/obj/item/organ/brain/current_brain = human_holder.get_organ_slot(ORGAN_SLOT_BRAIN)
var/obj/item/organ/brain/insert_brain = old_organ //less confusing name
var/datum/mind/holder_mind = human_holder.mind
insert_brain.modular_persistence = current_brain.modular_persistence
insert_brain.modular_persistence?.owner = insert_brain
current_brain.modular_persistence = null
insert_brain.copy_traits_from(current_brain)
insert_brain.Insert(human_holder, special = TRUE)
current_brain.moveToNullspace() // Maybe this works also lmao
STOP_PROCESSING(SSobj, current_brain)
if(!holder_mind)
return
holder_mind.transfer_to(human_holder, TRUE)
else
old_organ.Insert(quirk_holder, special = TRUE)
old_organ = null
@@ -0,0 +1,15 @@
/atom/movable
// Text-to-blooper sounds
// yes. all atoms can have a say.
var/datum/blooper/blooper
var/blooper_speed = 50
var/blooper_pitch = 50
var/blooper_pitch_range = 50 //Actual pitch is (pitch - (blooper_pitch_range*0.5)) to (pitch + (blooper_pitch_range*0.5))
COOLDOWN_DECLARE(blooper_cooldown)
/atom/movable/send_speech(message, range = 7, obj/source = src, bubble_type, list/spans, datum/language/message_language, list/message_mods = list(), forced = FALSE, tts_message, list/tts_filter)
. = ..()
if(!blooper)
return
var/list/listeners = get_hearers_in_view(range, source)
blooper.play_bloop(source, listeners, message, range, BLOOPER_TRANSMIT_VOLUME, blooper_speed, blooper_pitch, blooper_pitch_range)
@@ -264,7 +264,6 @@
icon_state = "johnghoul"
attack_verb_continuous = list("ghouls")
attack_verb_simple = list("ghoul")
squeak_override = list('modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/ehh.ogg' = 1)
// plushie for BeoTheKobold
// sprite by Cepha, code by Mitryll
@@ -6,3 +6,8 @@
. = ..()
new /obj/item/stamp/granted(src)
new /obj/item/stamp/denied(src)
new /obj/item/autosurgeon/paperwork(src)
/obj/structure/closet/secure_closet/nanotrasen_consultant/PopulateContents()
. = ..()
new /obj/item/autosurgeon/paperwork(src)
@@ -0,0 +1,9 @@
/datum/changeling_profile
/// The blooper voice used by the profile source
var/datum/blooper/blooper
/// The blooper speed used by the profile source
var/blooper_speed
/// The blooper pitch used by the profile source
var/blooper_pitch
/// The blooper pitch range used by the profile source
var/blooper_pitch_range
@@ -1,109 +0,0 @@
/* Originally written by Bhijn & Myr on Citadel[1], with various other contributions since- see the Splurt & Citadel github for a full list of contributors.
It has also been further modified by Rashcat & other Fluffyfrontier contributors on Fluffy Frontier!
[1]https://github.com/Citadel-Station-13/Citadel-Station-13/pull/15677
*/
/atom/movable
// Text-to-blooper sounds
// yes. all atoms can have a say.
var/sound/blooper
var/blooper_id
var/blooper_pitch = 1
var/blooper_pitch_range = 0.2 //Actual pitch is (pitch - (blooper_pitch_range*0.5)) to (pitch + (blooper_pitch_range*0.5))
var/blooper_volume = 50
var/blooper_speed = 4 //Lower values are faster, higher values are slower
var/blooper_current_blooper //When bloopers are queued, this gets passed to the blooper proc. If blooper_current_blooper doesn't match the args passed to the blooper proc (if passed at all), then the blooper simply doesn't play. Basic curtailing of spam~
/atom/movable/proc/set_blooper(id)
if(!id)
return FALSE
var/datum/blooper/B = GLOB.blooper_list[id]
if(!B)
return FALSE
blooper = sound(initial(B.soundpath))
blooper_id = id
return blooper
/atom/movable/proc/blooper(list/listeners, distance, volume, pitch, queue_time)
if(!GLOB.blooper_allowed)
return
if(queue_time && blooper_current_blooper != queue_time)
return
if(!blooper)
if(!blooper_id || !set_blooper(blooper_id)) //just-in-time blooper generation
return
volume = min(volume, 100)
var/turf/T = get_turf(src)
for(var/mob/M in listeners)
M.playsound_local(T, vol = volume, vary = TRUE, frequency = pitch, max_distance = distance, falloff_distance = 0, falloff_exponent = BLOOPER_SOUND_FALLOFF_EXPONENT, sound_to_use = blooper, distance_multiplier = 1)
/atom/movable/send_speech(message, range = 7, obj/source = src, bubble_type, list/spans, datum/language/message_language, list/message_mods = list(), forced = FALSE, tts_message, list/tts_filter)
. = ..()
var/list/listeners = get_hearers_in_view(range, source)
if(blooper || blooper_id)
for(var/mob/M in listeners)
if(!M.client)
continue
if(!(M.client.prefs?.read_preference(/datum/preference/toggle/hear_sound_blooper)))
listeners -= M
var/bloopers = min(round((LAZYLEN(message) / blooper_speed)) + 1, BLOOPER_MAX_BLOOPERS)
var/total_delay
blooper_current_blooper = world.time //this is juuuuust random enough to reliably be unique every time send_speech() is called, in most scenarios
for(var/i in 1 to bloopers)
if(total_delay > BLOOPER_MAX_TIME)
break
addtimer(CALLBACK(src, PROC_REF(blooper), listeners, range, blooper_volume, BLOOPER_DO_VARY(blooper_pitch, blooper_pitch_range), blooper_current_blooper), total_delay)
total_delay += rand(DS2TICKS(blooper_speed / BLOOPER_SPEED_BASELINE), DS2TICKS(blooper_speed / BLOOPER_SPEED_BASELINE) + DS2TICKS(blooper_speed / BLOOPER_SPEED_BASELINE)) TICKS
/mob/living/carbon/human/Initialize(mapload)
. = ..()
// This gives a random vocal bark to a random created person
if(!client)
set_blooper(pick(GLOB.blooper_list))
blooper_pitch = BLOOPER_PITCH_RAND(gender)
blooper_pitch_range = BLOOPER_VARIANCE_RAND
blooper_speed = rand(BLOOPER_DEFAULT_MINSPEED, BLOOPER_DEFAULT_MAXSPEED)
/mob/living/send_speech(message_raw, message_range = 6, obj/source = src, bubble_type = bubble_icon, list/spans, datum/language/message_language = null, list/message_mods = list(), forced = null, tts_message, list/tts_filter)
. = ..()
if(!(client?.prefs.read_preference(/datum/preference/toggle/send_sound_blooper)))
return
blooper_volume = client?.prefs.read_preference(/datum/preference/numeric/sound_blooper_volume) //volume scales with your volume slider in game preferences.
if(HAS_TRAIT(src, TRAIT_SIGN_LANG) && !HAS_TRAIT(src, TRAIT_MUTE)) //if you can speak and you sign, your hands don't make a bark. Unless you are completely mute, you can have some hand bark.
return
if(message_mods[WHISPER_MODE])
blooper_volume = (client?.prefs.read_preference(/datum/preference/numeric/sound_blooper_volume)*0.5) //Whispered barked are half as loud.
message_range++
var/list/listening = get_hearers_in_view(message_range, source)
var/is_yell = (say_test(message_raw) == "2")
//Listening gets trimmed here if a blooper blooper's present. If anyone ever makes this proc return listening, make sure to instead initialize a copy of listening in here to avoid wonkiness
if(blooper || blooper_id)
for(var/mob/M in listening)
if(!M.client)
continue
var/tts_pref = M.client.prefs?.read_preference(/datum/preference/choiced/sound_tts)
var/hear_blooper = M.client.prefs?.read_preference(/datum/preference/toggle/hear_sound_blooper)
if(!hear_blooper) // Check pref for blooper
listening -= M
else if (CONFIG_GET(string/tts_http_url) && SStts.tts_enabled == TRUE) // TTS for vocal barks.
if (source.voice == "" || source.voice == "None") // "None" is for borgs
continue
if (tts_pref == TTS_SOUND_OFF)
continue
listening -= M
var/bloopers = min(round((LAZYLEN(message_raw) / blooper_speed)) + 1, BLOOPER_MAX_BLOOPERS)
var/total_delay
blooper_current_blooper = world.time
for(var/i in 1 to bloopers)
if(total_delay > BLOOPER_MAX_TIME)
break
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/movable, blooper), listening, message_range + 1, (blooper_volume * (is_yell ? 2 : 1)), BLOOPER_DO_VARY(blooper_pitch, blooper_pitch_range), blooper_current_blooper), total_delay) //The function is zero on the seventh tile. This makes it a maximum of 1 more.
total_delay += rand(DS2TICKS(blooper_speed / BLOOPER_SPEED_BASELINE), DS2TICKS(blooper_speed / BLOOPER_SPEED_BASELINE) + DS2TICKS((blooper_speed / BLOOPER_SPEED_BASELINE) * (is_yell ? 0.5 : 1))) TICKS
@@ -1,180 +0,0 @@
GLOBAL_VAR_INIT(blooper_allowed, TRUE) // For administrators
// we let borgs have some bark too
/mob/living/silicon/Login()
// This is the only found function that updates the client for borgs.
set_blooper(client.prefs.read_preference(/datum/preference/choiced/blooper))
blooper_pitch = client.prefs.read_preference(/datum/preference/numeric/blooper_speech_pitch)
blooper_speed = client.prefs.read_preference(/datum/preference/numeric/blooper_speech_speed)
blooper_pitch_range = client.prefs.read_preference(/datum/preference/numeric/blooper_pitch_range)
. = ..()
// Mechanics for Changelings
/datum/changeling_profile
var/blooper_id
var/blooper_pitch
var/blooper_pitch_range
var/blooper_speed
/datum/smite/normalblooper
name = "Normal blooper"
/datum/smite/normalblooper/effect(client/user, mob/living/carbon/human/target)
. = ..()
target.blooper = null
target.blooper_id = pick(GLOB.blooper_random_list)
target.blooper_speed = round((BLOOPER_DEFAULT_MINSPEED + BLOOPER_DEFAULT_MAXSPEED) / 2)
target.blooper_pitch = round((BLOOPER_DEFAULT_MINPITCH + BLOOPER_DEFAULT_MAXPITCH) / 2)
target.blooper_pitch_range = 0.2
/datum/admins/proc/toggleblooper()
set category = "Server"
set desc = "Toggle the annoying voices."
set name = "Toggle Vocal Barks"
toggle_blooper()
log_admin("[key_name(usr)] toggled Voice Barks.")
message_admins("[key_name_admin(usr)] toggled Voice Barks.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Voice Bark", "[GLOB.blooper_allowed ? "Enabled" : "Disabled"]")) // If you are copy-pasting this, ensure the 4th parameter is unique to the new proc!
/* /world/AVerbsAdmin()
. = ..()
return . + /datum/admins/proc/toggleblooper */
/proc/toggle_blooper(toggle = null)
if(toggle != null)
if(toggle != GLOB.blooper_allowed)
GLOB.blooper_allowed = toggle
else
return
else
GLOB.blooper_allowed = !GLOB.blooper_allowed
to_chat(world, "<span class='oocplain'><B>Vocal barks have been globally [GLOB.blooper_allowed ? "enabled" : "disabled"].</B></span>")
/datum/preference/choiced/blooper
category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "blooper_speech"
/datum/preference/choiced/blooper/init_possible_values()
return assoc_to_keys(GLOB.blooper_list)
/datum/preference/choiced/blooper/apply_to_human(mob/living/carbon/human/target, value)
target.set_blooper(value)
/datum/preference/choiced/blooper/compile_constant_data()
var/list/data = ..()
var/list/display_names = list()
for(var/id in GLOB.blooper_list)
var/datum/blooper/blooper_path = GLOB.blooper_list[id]
display_names[id] = blooper_path::name
data[CHOICED_PREFERENCE_DISPLAY_NAMES] = display_names
return data
/datum/preference_middleware/blooper
/// Cooldown on requesting a Blooper preview.
COOLDOWN_DECLARE(blooper_cooldown)
action_delegations = list(
"play_blooper" = PROC_REF(play_blooper),
)
/datum/preference_middleware/blooper/proc/play_blooper(list/params, mob/user)
if(!COOLDOWN_FINISHED(src, blooper_cooldown))
return TRUE
var/atom/movable/blooperbox = new(get_turf(user))
blooperbox.set_blooper(preferences.read_preference(/datum/preference/choiced/blooper))
blooperbox.blooper_pitch = preferences.read_preference(/datum/preference/numeric/blooper_speech_pitch)
blooperbox.blooper_speed = preferences.read_preference(/datum/preference/numeric/blooper_speech_speed)
blooperbox.blooper_pitch_range = preferences.read_preference(/datum/preference/numeric/blooper_pitch_range)
var/total_delay
for(var/i in 1 to (round((32 / blooperbox.blooper_speed)) + 1))
addtimer(CALLBACK(blooperbox, TYPE_PROC_REF(/atom/movable, blooper), list(user), 7, 70, BLOOPER_DO_VARY(blooperbox.blooper_pitch, blooperbox.blooper_pitch_range)), total_delay)
total_delay += rand(DS2TICKS(blooperbox.blooper_speed/4), DS2TICKS(blooperbox.blooper_speed/4) + DS2TICKS(blooperbox.blooper_speed/4)) TICKS
QDEL_IN(blooperbox, total_delay)
COOLDOWN_START(src, blooper_cooldown, 2 SECONDS)
return TRUE
/datum/preference/numeric/blooper_speech_speed
category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "blooper_speech_speed"
minimum = BLOOPER_DEFAULT_MINSPEED
maximum = BLOOPER_DEFAULT_MAXSPEED
step = 0.01
/datum/preference/numeric/blooper_speech_speed/apply_to_human(mob/living/carbon/human/target, value)
target.blooper_speed = value
/datum/preference/numeric/blooper_speech_speed/create_default_value()
return round((BLOOPER_DEFAULT_MINSPEED + BLOOPER_DEFAULT_MAXSPEED) / 2)
/datum/preference/numeric/blooper_speech_pitch
category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "blooper_speech_pitch"
minimum = BLOOPER_DEFAULT_MINPITCH
maximum = BLOOPER_DEFAULT_MAXPITCH
step = 0.01
/datum/preference/numeric/blooper_speech_pitch/apply_to_human(mob/living/carbon/human/target, value)
target.blooper_pitch = value
/datum/preference/numeric/blooper_speech_pitch/create_default_value()
return round((BLOOPER_DEFAULT_MINPITCH + BLOOPER_DEFAULT_MAXPITCH) / 2)
/datum/preference/numeric/blooper_pitch_range
category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "blooper_pitch_range"
minimum = BLOOPER_DEFAULT_MINVARY
maximum = BLOOPER_DEFAULT_MAXVARY
step = 0.01
/datum/preference/numeric/blooper_pitch_range/apply_to_human(mob/living/carbon/human/target, value)
target.blooper_pitch_range = value
/datum/preference/numeric/blooper_pitch_range/create_default_value()
return 0.2
/// Can I use the blooper?
/datum/preference/toggle/send_sound_blooper
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_key = "send_sound_blooper"
savefile_identifier = PREFERENCE_PLAYER
default_value = TRUE
/// Can I hear everyone else's bloops?
/datum/preference/toggle/hear_sound_blooper
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_key = "hear_sound_blooper"
savefile_identifier = PREFERENCE_PLAYER
default_value = TRUE
/// Can I have a slider to adjust the volume of the barks?
/datum/preference/numeric/sound_blooper_volume
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_key = "sound_blooper_volume"
savefile_identifier = PREFERENCE_PLAYER
minimum = 0
maximum = 60
step = 5
/// It's was stoolen from Splurt build >:3 and from fluffySTG!! nyeehehehheee!~
/datum/blooper
var/name = "None"
var/id = "No Voice"
var/soundpath
var/minpitch = BLOOPER_DEFAULT_MINPITCH
var/maxpitch = BLOOPER_DEFAULT_MAXPITCH
var/minvariance = BLOOPER_DEFAULT_MINVARY
var/maxvariance = BLOOPER_DEFAULT_MAXVARY
// Speed vars. Speed determines the number of characters required for each blooper, with lower speeds being faster with higher blooper density
var/minspeed = BLOOPER_DEFAULT_MINSPEED
var/maxspeed = BLOOPER_DEFAULT_MAXSPEED
// Visibility vars. Regardless of what's set below, these can still be obtained via adminbus and genetics. Rule of fun.
var/list/ckeys_allowed
var/ignore = FALSE // If TRUE - only for admins
var/allow_random = FALSE
@@ -1,438 +0,0 @@
/datum/blooper/mutedc2
name = "Muted String (Low)"
id = "mutedc2"
soundpath = 'sound/runtime/instruments/synthesis_samples/guitar/crisis_muted/C2.ogg'
allow_random = TRUE
/datum/blooper/mutedc3
name = "Muted String (Medium)"
id = "mutedc3"
soundpath = 'sound/runtime/instruments/synthesis_samples/guitar/crisis_muted/C3.ogg'
allow_random = TRUE
/datum/blooper/mutedc4
name = "Muted String (High)"
id = "mutedc4"
soundpath = 'sound/runtime/instruments/synthesis_samples/guitar/crisis_muted/C4.ogg'
allow_random = TRUE
/datum/blooper/banjoc3
name = "Banjo (Medium)"
id = "banjoc3"
soundpath = 'sound/runtime/instruments/banjo/Cn3.ogg'
allow_random = TRUE
/datum/blooper/banjoc4
name = "Banjo (High)"
id = "banjoc4"
soundpath = 'sound/runtime/instruments/banjo/Cn4.ogg'
allow_random = TRUE
/datum/blooper/squeaky
name = "Squeaky"
id = "squeak"
soundpath = 'sound/items/toy_squeak/toysqueak1.ogg'
maxspeed = 4
/datum/blooper/beep
name = "Beepy"
id = "beep"
soundpath = 'sound/machines/terminal/terminal_select.ogg'
maxpitch = 1 //Bringing the pitch higher just hurts your ears :<
maxspeed = 4 //This soundbyte's too short for larger speeds to not sound awkward
/datum/blooper/chitter
name = "Chittery"
id = "chitter"
minspeed = 4 //Even with the sound being replaced with a unique, shorter sound, this is still a little too long for higher speeds
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/chitter.ogg'
/datum/blooper/synthetic_grunt
name = "Synthetic (Grunt)"
id = "synthgrunt"
soundpath = 'sound/misc/bloop.ogg'
/datum/blooper/synthetic
name = "Synthetic (Normal)"
id = "synth"
soundpath = 'sound/machines/uplink/uplinkerror.ogg'
/datum/blooper/bullet
name = "Windy"
id = "bullet"
maxpitch = 1.6
soundpath = 'sound/items/weapons/bulletflyby.ogg'
/datum/blooper/coggers
name = "Brassy"
id = "coggers"
soundpath = 'sound/machines/clockcult/integration_cog_install.ogg'
/datum/blooper/moff/short
name = "Moff squeak"
id = "moffsqueak"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/mothsqueak.ogg'
allow_random = TRUE
ignore = FALSE
/datum/blooper/meow //Meow blooper?
name = "Meow"
id = "meow"
allow_random = TRUE
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/meow1.ogg'
minspeed = 5
maxspeed = 11
/datum/blooper/chirp
name = "Chirp"
id = "chirp"
allow_random = TRUE
soundpath = 'modular_zubbers/sound/voice/chirp.ogg'
/datum/blooper/caw
name = "Caw"
id = "caw"
allow_random = TRUE
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/caw.ogg'
//Undertale
/datum/blooper/alphys
name = "Alphys"
id = "alphys"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_alphys.ogg'
minvariance = 0
/datum/blooper/asgore
name = "Asgore"
id = "asgore"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_asgore.ogg'
minvariance = 0
/datum/blooper/flowey
name = "Flowey (normal)"
id = "flowey1"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_flowey_1.ogg'
minvariance = 0
/datum/blooper/flowey/evil
name = "Flowey (evil)"
id = "flowey2"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_flowey_2.ogg'
minvariance = 0
/datum/blooper/papyrus
name = "Papyrus"
id = "papyrus"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_papyrus.ogg'
minvariance = 0
/datum/blooper/ralsei
name = "Ralsei"
id = "ralsei"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_ralsei.ogg'
minvariance = 0
/datum/blooper/sans //real
name = "Sans"
id = "sans"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_sans.ogg'
minvariance = 0
/datum/blooper/toriel
name = "Toriel"
id = "toriel"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_toriel.ogg'
minvariance = 0
maxpitch = BLOOPER_DEFAULT_MAXPITCH*2
/datum/blooper/undyne
name = "Undyne"
id = "undyne"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_undyne.ogg'
minvariance = 0
/datum/blooper/temmie
name = "Temmie"
id = "temmie"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_temmie.ogg'
minvariance = 0
/datum/blooper/susie
name = "Susie"
id = "susie"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_susie.ogg'
minvariance = 0
/datum/blooper/gaster
name = "Gaster"
id = "gaster"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_gaster_1.ogg'
minvariance = 0
/datum/blooper/mettaton
name = "Mettaton"
id = "mettaton"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_metta_1.ogg'
minvariance = 0
/datum/blooper/gen_monster
name = "Generic Monster 1"
id = "gen_monster_1"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_monster1.ogg'
minvariance = 0
/datum/blooper/gen_monster/alt
name = "Generic Monster 2"
id = "gen_monster_2"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/undertale/voice_monster2.ogg'
minvariance = 0
/datum/blooper/wilson
name = "Wilson"
id = "wilson"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/dont_starve/wilson_blooper.ogg'
/datum/blooper/wolfgang
name = "Wolfgang"
id = "wolfgang"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/dont_starve/wolfgang_blooper.ogg'
minspeed = 4
maxspeed = 10
/datum/blooper/woodie
name = "Woodie"
id = "woodie"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/dont_starve/woodie_blooper.ogg'
minspeed = 4
maxspeed = 10
/datum/blooper/wurt
name = "Wurt"
id = "wurt"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/dont_starve/wurt_blooper.ogg'
/datum/blooper/wx78
name = "wx78"
id = "wx78"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/dont_starve/wx78_blooper.ogg'
minspeed = 3
maxspeed = 9
/datum/blooper/blub
name = "Blub"
id = "blub"
soundpath = 'goon/sounds/blub.ogg'
/datum/blooper/bottalk
name = "Bottalk 1"
id = "bottalk1"
soundpath = 'goon/sounds/bottalk_1.ogg'
minspeed = 3
maxspeed = 9
/datum/blooper/bottalk/alt1
name = "Bottalk 2"
id = "bottalk2"
soundpath = 'goon/sounds/bottalk_2.ogg'
/datum/blooper/bottalk/alt2
name = "Bottalk 3"
id = "bottalk3"
soundpath = 'goon/sounds/bottalk_3.ogg'
/datum/blooper/bottalk/alt3
name = "Bottalk 4"
id = "bottalk4"
soundpath = 'goon/sounds/bottalk_4.ogg'
/datum/blooper/buwoo
name = "Buwoo"
id = "buwoo"
soundpath = 'goon/sounds/buwoo.ogg'
/datum/blooper/cow
name = "Cow"
id = "cow"
soundpath = 'goon/sounds/cow.ogg'
/datum/blooper/lizard
name = "Lizard"
id = "lizard"
soundpath = 'goon/sounds/lizard.ogg'
/datum/blooper/pug
name = "Pug"
id = "pug"
soundpath = 'goon/sounds/pug.ogg'
/datum/blooper/pugg
name = "Pugg"
id = "pugg"
soundpath = 'goon/sounds/pugg.ogg'
/datum/blooper/radio
name = "Radio 1"
id = "radio1"
soundpath = 'goon/sounds/radio.ogg'
/datum/blooper/radio/short
name = "Radio 2"
id = "radio2"
soundpath = 'goon/sounds/radio2.ogg'
/datum/blooper/radio/ai
name = "Radio (AI)"
id = "radio_ai"
soundpath = 'goon/sounds/radio_ai.ogg'
/datum/blooper/roach //Turkish characters be like
name = "Roach"
id = "roach"
soundpath = 'goon/sounds/roach.ogg'
/datum/blooper/skelly
name = "Skelly"
id = "skelly"
soundpath = 'goon/sounds/skelly.ogg'
/datum/blooper/speak
name = "Speak 1"
id = "speak1"
soundpath = 'goon/sounds/speak_1.ogg'
/datum/blooper/speak/alt1
name = "Speak 2"
id = "speak2"
soundpath = 'goon/sounds/speak_2.ogg'
/datum/blooper/speak/alt2
name = "Speak 3"
id = "speak3"
soundpath = 'goon/sounds/speak_3.ogg'
/datum/blooper/speak/alt3
name = "Speak 4"
id = "speak4"
soundpath = 'goon/sounds/speak_4.ogg'
/datum/blooper/chitter/alt
name = "Chittery Alt"
id = "chitter2"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/moth/mothchitter2.ogg'
// The Mayhem Special
/datum/blooper/whistle
name = "Whistle 1"
id = "whistle1"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/birdwhistle.ogg'
/datum/blooper/whistle/alt1
name = "Whistle 2"
id = "whistle2"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/birdwhistle2.ogg'
/datum/blooper/caw/alt1
name = "Caw 2"
id = "caw2"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/caw.ogg'
minspeed = 4
maxspeed = 9
/datum/blooper/caw/alt2
name = "Caw 3"
id = "caw3"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/caw2.ogg'
minspeed = 3
maxspeed = 9
/datum/blooper/caw/alt3
name = "Caw 4"
id = "caw4"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/caw3.ogg'
minspeed = 3
maxspeed = 9
/datum/blooper/ehh
name = "Ehh 1"
id = "ehh1"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/ehh.ogg'
minspeed = 3
maxspeed = 9
/datum/blooper/ehh/alt1
name = "Ehh 2"
id = "ehh2"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/ehh2.ogg'
/datum/blooper/ehh/alt2
name = "Ehh 3"
id = "ehh3"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/ehh3.ogg'
/datum/blooper/ehh/alt3
name = "Ehh 4"
id = "ehh4"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/ehh4.ogg'
minspeed = 3
maxspeed = 9
/datum/blooper/ehh/alt5
name = "Ehh 5"
id = "ehh5"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/ehh5.ogg'
/datum/blooper/faucet
name = "Faucet 1"
id = "faucet1"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/faucet.ogg'
/datum/blooper/faucet/alt1
name = "Faucet 2"
id = "faucet2"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/faucet2.ogg'
/datum/blooper/ribbit
name = "Ribbit"
id = "ribbit"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/ribbit.ogg'
/datum/blooper/hoot
name = "Hoot"
id = "hoot"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/hoot.ogg'
minspeed = 4
maxspeed = 9
/datum/blooper/tweet
name = "Tweet"
id = "tweet"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/tweet.ogg'
/datum/blooper/dwoop
name = "Dwoop"
id = "dwoop"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/dwoop.ogg'
minspeed = 3
maxspeed = 9
/datum/blooper/uhm
name = "Uhm"
id = "uhm"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/uhm.ogg'
/datum/blooper/wurtesh
name = "Wurtesh"
id = "wurtesh"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/wurble1.ogg'
/datum/blooper/chitter2
name = "Chitter2"
id = "chitter2"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/kazooie/chitter1.ogg'
/datum/blooper/xenohiss
name = "Xenohiss"
id = "Xenohiss"
soundpath = 'modular_zubbers/code/modules/blooper/voice/bloopers/Xenohiss.ogg'
minspeed = 10
maxspeed = 16
@@ -4,7 +4,7 @@
icon_state = "moth_head_m"
limb_id = SPECIES_MOTH
is_dimorphic = TRUE
head_flags = HEAD_HAIR|HEAD_FACIAL_HAIR|HEAD_LIPS|HEAD_EYESPRITES|HEAD_EYEHOLES|HEAD_DEBRAIN //what the fuck, moths have lips?
head_flags = HEAD_HAIR|HEAD_FACIAL_HAIR|HEAD_LIPS|HEAD_EYESPRITES|HEAD_EYEHOLES|HEAD_DEBRAIN|HEAD_EYECOLOR //what the fuck, moths have lips?
teeth_count = 0
/obj/item/bodypart/chest/moth
@@ -0,0 +1,88 @@
/datum/preference/choiced/blooper
category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "blooper_choice"
/datum/preference/choiced/blooper/init_possible_values()
// no bloopers configured, return dummy list to avoid runtimes
if(!length(SSblooper.blooper_list))
return list("none")
return assoc_to_keys(SSblooper.blooper_list)
/datum/preference/choiced/blooper/apply_to_human(mob/living/carbon/human/target, value)
target.blooper = SSblooper.blooper_list[value]
/datum/preference/choiced/blooper/is_accessible(datum/preferences/preferences)
return ..() && length(SSblooper.blooper_list)
/datum/preference/choiced/blooper/compile_constant_data()
var/list/data = ..()
if(!length(SSblooper.blooper_list))
return data
var/list/display_names = list()
for(var/id in get_choices())
display_names[id] = astype(SSblooper.blooper_list[id], /datum/blooper).name
data[CHOICED_PREFERENCE_DISPLAY_NAMES] = display_names
return data
/datum/preference/numeric/blooper_speed
category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "blooper_speed"
minimum = 0
maximum = 100
/datum/preference/numeric/blooper_speed/apply_to_human(mob/living/carbon/human/target, value)
target.blooper_speed = value
/datum/preference/numeric/blooper_speed/create_default_value()
return 50
/datum/preference/numeric/blooper_speed/is_accessible(datum/preferences/preferences)
return ..() && length(SSblooper.blooper_list)
/datum/preference/numeric/blooper_pitch
category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "blooper_pitch"
minimum = 0
maximum = 100
/datum/preference/numeric/blooper_pitch/apply_to_human(mob/living/carbon/human/target, value)
target.blooper_pitch = value
/datum/preference/numeric/blooper_pitch/create_default_value()
return 50
/datum/preference/numeric/blooper_pitch/is_accessible(datum/preferences/preferences)
return ..() && length(SSblooper.blooper_list)
/datum/preference/numeric/blooper_pitch_range
category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "blooper_pitch_range"
minimum = 0
maximum = 100
/datum/preference/numeric/blooper_pitch_range/apply_to_human(mob/living/carbon/human/target, value)
target.blooper_pitch_range = value
/datum/preference/numeric/blooper_pitch_range/create_default_value()
return 30
/datum/preference/numeric/blooper_pitch_range/is_accessible(datum/preferences/preferences)
return ..() && length(SSblooper.blooper_list)
// Send vocal bloopers
/datum/preference/toggle/send_blooper
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_identifier = PREFERENCE_PLAYER
savefile_key = "blooper_send"
default_value = TRUE
// Hear vocal bloopers
/datum/preference/toggle/hear_blooper
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_identifier = PREFERENCE_PLAYER
savefile_key = "blooper_hear"
default_value = TRUE
@@ -0,0 +1,12 @@
/datum/preference_middleware/blooper
action_delegations = list(
"play_blooper" = PROC_REF(play_blooper),
)
/datum/preference_middleware/blooper/proc/play_blooper(list/params, mob/user)
var/datum/blooper/blooper_to_use = SSblooper.blooper_list[preferences.read_preference(/datum/preference/choiced/blooper)]
var/blooper_speed = preferences.read_preference(/datum/preference/numeric/blooper_speed)
var/blooper_pitch = preferences.read_preference(/datum/preference/numeric/blooper_pitch)
var/blooper_pitch_range = preferences.read_preference(/datum/preference/numeric/blooper_pitch_range)
blooper_to_use.play_bloop(user, list(user), "This is a test message to hear a blooper.", 7, 70, blooper_speed, blooper_pitch, blooper_pitch_range)
return TRUE
@@ -294,3 +294,12 @@
name = "Eastern Dragon 2"
icon_state = "easternd2"
affected_bodyparts = HEAD | CHEST | ARM_LEFT | ARM_RIGHT | HAND_LEFT | HAND_RIGHT | LEG_RIGHT | LEG_LEFT
/datum/body_marking_set/zombie
name = "Zombie"
body_marking_list = list("Zombie")
/datum/body_marking/bubber/zombie
name = "Zombie"
icon_state = "zombie"
affected_bodyparts = HEAD | CHEST | ARM_LEFT | ARM_RIGHT | HAND_LEFT | HAND_RIGHT | LEG_RIGHT | LEG_LEFT

Some files were not shown because too many files have changed in this diff Show More