From eb0e842320f37e26db26c52bd7ec16cd25acdc74 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Thu, 20 Mar 2025 16:36:56 -0500 Subject: [PATCH] Hallucination revamps and additions (#89865) ## About The Pull Request 1. Hallucination effects are now tiered Hallucinations now all have tiers, ranging from common to special. If you are just hallucinating a teeny bit, you will not experience the more extreme hallucinations, like bubblegum or your mom. But if you're hallucinating off your butt, you will be a bit more likely to experience them. 2. Hallucination rate has been tweaked Default hallucination cooldown is now 20-80 seconds, up from 10-60 seconds. However the cooldown will *also* vary depending on just how much you're hallucinating, going down to 10-40 seconds. 3. RDS is now capped a bit lower (meaning you don't see the higher tiers like bubblegum). But I added a preference to uncap it. For the people who actually like bubblegum visits. 4. If a hallucination fails to trigger, the cooldown will partially reset. (by 75%) 5. "Fake chat" hallucination will pick more viable subjects. Fake chat will try to find someone who can actually speak your language, rather than make a monkey speak mothic or something. (I may revisit this so if you're super-hallucinating it reverts to old behavior though.) 6. Adds a hallucination: Fake blood You hallucinate that you start bleeding, very simple. 7. Adds a hallucination: Fake telepathy You hallucinate a random telepathic message, similar to fake chat. 8. Adds a hallucination: Eyes in the Dark A nearby dark turf will have a set of glowing red eyes shine through the dark. A classic. 9. Adds some new sub-hallucination: PDA ringtone (fake sound), summon guns/magic (fake item) Funny prank. 10. Makes mindbreaker a bit more effective at combating RDS. Pretty much does nothing right now unless you gulp like 50u. ## Why It's Good For The Game Hallucinations are pretty one note if you experience them for longer than 10 minutes. This is due to two fold: - Many hallucinations are goofy, and not subtle - Hallucinations trigger very rapidly You will never fall for a hallucination because in between "You see John Greytide put the blueprints away", you get your mom yelling at you, everyone looking like syndies (again), and bubblegum This pr addresses it by - Limiting the wacky hallucinations for when you're really off your gourd - Reducing the period between triggers - Adding a few hallucinations If the wackier hallucinations are reserved for when you're really off your rocker, this lets the more subtle ones sink in over time, leaves more room for second guessing ## Changelog :cl: Melbert add: Adds 4-5 new hallucinations. Collect them all. balance: If you are only hallucinating a little bit, the game will prefer to pick more subtle hallucinations. If you are hallucinating a ton, it will prefer the more wacky hallucinations. balance: If you are only hallucinating a little bit, the cooldown between hallucinations is longer. If you are hallucinating a ton, it will be shorter. balance: If a hallucination fails to trigger (such as a deaf person getting a sound hallucination) the next one will be a lot sooner. balance: RDS hallucination amount is capped at mid tier hallucinations. This means bubblegum and co. will be a lot rarer, or will even never show. HOWEVER, there is now a preference allowing you to uncap your RDS hallucinations. balance: Mindbreaker toxin is more effective at suppressing RDS. balance: Some hallucinations effects have been tweaked up or down according to the new thresholds. Madness mask as an example. fix: "Fake Fire" Hallucination works again, and now has a unique message for if you stop-drop-roll that other people see. /:cl: --- code/__HELPERS/hallucinations.dm | 83 +++++++++++------ code/_onclick/hud/alert.dm | 5 +- code/datums/brain_damage/mild.dm | 10 +- .../datums/quirks/negative_quirks/insanity.dm | 7 +- .../status_effects/buffs/stop_drop_roll.dm | 78 +++++++++++++--- .../status_effects/debuffs/hallucination.dm | 67 +++++++++++--- code/game/objects/items/grenades/hypno.dm | 4 +- .../abductor/equipment/glands/mindshock.dm | 2 +- .../antagonists/heretic/items/madness_mask.dm | 2 +- code/modules/client/preferences/rds_limit.dm | 11 +++ code/modules/clothing/head/perceptomatrix.dm | 5 +- .../modules/clothing/suits/reactive_armour.dm | 2 +- code/modules/hallucination/_hallucination.dm | 2 + code/modules/hallucination/battle.dm | 1 + code/modules/hallucination/blood_flow.dm | 69 ++++++++++++++ code/modules/hallucination/body.dm | 5 + code/modules/hallucination/bolted_airlocks.dm | 1 + .../modules/hallucination/bubblegum_attack.dm | 1 + code/modules/hallucination/delusions.dm | 2 + code/modules/hallucination/eyes_in_dark.dm | 91 +++++++++++++++++++ code/modules/hallucination/fake_alert.dm | 1 + code/modules/hallucination/fake_chat.dm | 62 +++++++++---- code/modules/hallucination/fake_death.dm | 1 + code/modules/hallucination/fake_message.dm | 1 + .../modules/hallucination/fake_plasmaflood.dm | 1 + code/modules/hallucination/fake_sound.dm | 38 +++++++- code/modules/hallucination/hazard.dm | 1 + code/modules/hallucination/hud_screw.dm | 1 + code/modules/hallucination/ice_cube.dm | 1 + .../modules/hallucination/inhand_fake_item.dm | 19 ++++ code/modules/hallucination/mother.dm | 2 + .../modules/hallucination/nearby_fake_item.dm | 1 + code/modules/hallucination/on_fire.dm | 46 ++++++++-- .../hallucination/screwy_health_doll.dm | 1 + code/modules/hallucination/shock.dm | 3 +- code/modules/hallucination/station_message.dm | 1 + code/modules/hallucination/stray_bullet.dm | 1 + code/modules/hallucination/telepathy.dm | 36 ++++++++ code/modules/hallucination/xeno_attack.dm | 1 + .../chemistry/reagents/drug_reagents.dm | 2 +- .../chemistry/reagents/other_reagents.dm | 7 +- .../surgery/organs/internal/lungs/_lungs.dm | 3 +- strings/hallucination.json | 5 +- tgstation.dme | 4 + .../character_preferences/rds_limit.tsx | 9 ++ 45 files changed, 591 insertions(+), 105 deletions(-) create mode 100644 code/modules/client/preferences/rds_limit.dm create mode 100644 code/modules/hallucination/blood_flow.dm create mode 100644 code/modules/hallucination/eyes_in_dark.dm create mode 100644 code/modules/hallucination/telepathy.dm create mode 100644 tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/rds_limit.tsx diff --git a/code/__HELPERS/hallucinations.dm b/code/__HELPERS/hallucinations.dm index d3d4a2630ed..110a94e862d 100644 --- a/code/__HELPERS/hallucinations.dm +++ b/code/__HELPERS/hallucinations.dm @@ -1,6 +1,18 @@ /// A global list of all ongoing hallucinations, primarily for easy access to be able to stop (delete) hallucinations. GLOBAL_LIST_EMPTY(all_ongoing_hallucinations) +// Hallucination tiers +/// Very common hallucinations, minor stuff that'll make you double-take but is otherwise very subtle. +#define HALLUCINATION_TIER_COMMON 1 +/// Uncommon hallucinations, more noticeable and potentially more impactful (causing temporary stuns or stamina damage). +#define HALLUCINATION_TIER_UNCOMMON 2 +/// Rarer hallucinations which are usually pretty obvious, but also pretty impactful. +#define HALLUCINATION_TIER_RARE 3 +/// Hallucinations which are generally just for laughs and are obviously fake +#define HALLUCINATION_TIER_VERYSPECIAL 4 +/// Hallucinations which are never picked, only forced +#define HALLUCINATION_TIER_NEVER 5 + /// What typepath of the hallucination #define HALLUCINATION_ARG_TYPE 1 /// Where the hallucination came from, for logging @@ -111,7 +123,7 @@ GLOBAL_LIST_EMPTY(all_ongoing_hallucinations) to_chat(nearby_living, pick(optional_messages)) /// Global weighted list of all hallucinations that can show up randomly. -GLOBAL_LIST_INIT(random_hallucination_weighted_list, generate_hallucination_weighted_list()) +GLOBAL_LIST_INIT_TYPED(random_hallucination_weighted_list, /list, generate_hallucination_weighted_list()) /// Generates the global weighted list of random hallucinations. /proc/generate_hallucination_weighted_list() @@ -124,49 +136,68 @@ GLOBAL_LIST_INIT(random_hallucination_weighted_list, generate_hallucination_weig if(weight <= 0) continue - weighted_list[hallucination_type] = weight + LAZYSET(weighted_list["[initial(hallucination_type.hallucination_tier)]"], hallucination_type, weight) return weighted_list +/// Select a random hallucination from the hallucination pool +/// +/// * tier - the tier of hallucination to select from +/// * strict - if true, only select from the passed tier. If false, select from the passed tier and all tiers below it. +/proc/get_random_hallucination(tier = HALLUCINATION_TIER_COMMON, strict = FALSE) + if(!GLOB.random_hallucination_weighted_list[tier]) + CRASH("get_random_hallucination - No hallucinations in tier \[[tier]\].") + + var/list/pool = GLOB.random_hallucination_weighted_list["[tier]"].Copy() + if(!strict) + tier -= 1 + while(tier >= HALLUCINATION_TIER_COMMON) + pool += GLOB.random_hallucination_weighted_list["[tier]"] + tier -= 1 + + return pick_weight(pool) + /// Debug proc for getting the total weight of the random_hallucination_weighted_list /proc/debug_hallucination_weighted_list() var/total_weight = 0 - for(var/datum/hallucination/hallucination_type as anything in GLOB.random_hallucination_weighted_list) - total_weight += GLOB.random_hallucination_weighted_list[hallucination_type] + for(var/tier in GLOB.random_hallucination_weighted_list) + for(var/datum/hallucination/hallucination_type as anything in GLOB.random_hallucination_weighted_list[tier]) + total_weight += GLOB.random_hallucination_weighted_list[tier][hallucination_type] to_chat(usr, span_boldnotice("The total weight of the hallucination weighted list is [total_weight].")) return total_weight ADMIN_VERB(debug_hallucination_weighted_list_per_type, R_DEBUG, "Show Hallucination Weights", "View the weight of each hallucination subtype in the random weighted list.", ADMIN_CATEGORY_DEBUG) - var/header = "Type Weight Percent" + var/header = "Type Weight Tier Percent" var/total_weight = debug_hallucination_weighted_list() var/list/all_weights = list() var/datum/hallucination/last_type var/last_type_weight = 0 - for(var/datum/hallucination/hallucination_type as anything in GLOB.random_hallucination_weighted_list) - var/this_weight = GLOB.random_hallucination_weighted_list[hallucination_type] - // Last_type is the abstract parent of the last hallucination type we iterated over - if(last_type) - // If this hallucination is the same path as the last type (subtype), add it to the total of the last type weight - if(ispath(hallucination_type, last_type)) - last_type_weight += this_weight - continue + for(var/tier in GLOB.random_hallucination_weighted_list) + for(var/datum/hallucination/hallucination_type as anything in GLOB.random_hallucination_weighted_list[tier]) + var/this_weight = GLOB.random_hallucination_weighted_list[tier][hallucination_type] + // Last_type is the abstract parent of the last hallucination type we iterated over + if(last_type) + // If this hallucination is the same path as the last type (subtype), add it to the total of the last type weight + if(ispath(hallucination_type, last_type)) + last_type_weight += this_weight + continue - // Otherwise we moved onto the next hallucination subtype so we can stop + // Otherwise we moved onto the next hallucination subtype so we can stop + else + all_weights["[last_type] [last_type_weight] / [total_weight] [initial(hallucination_type.hallucination_tier)] [round(100 * (last_type_weight / total_weight), 0.01)]% chance"] = last_type_weight + + // Set last_type to the abstract parent of this hallucination + last_type = initial(hallucination_type.abstract_hallucination_parent) + // If last_type is the base hallucination it has no distinct subtypes so we can total it up immediately + if(last_type == /datum/hallucination) + all_weights["[hallucination_type] [this_weight] / [total_weight] [initial(hallucination_type.hallucination_tier)] [round(100 * (this_weight / total_weight), 0.01)]% chance"] = this_weight + last_type = null + + // Otherwise we start the weight sum for the next entry here else - all_weights["[last_type] [last_type_weight] / [total_weight] [round(100 * (last_type_weight / total_weight), 0.01)]% chance"] = last_type_weight - - // Set last_type to the abstract parent of this hallucination - last_type = initial(hallucination_type.abstract_hallucination_parent) - // If last_type is the base hallucination it has no distinct subtypes so we can total it up immediately - if(last_type == /datum/hallucination) - all_weights["[hallucination_type] [this_weight] / [total_weight] [round(100 * (this_weight / total_weight), 0.01)]% chance"] = this_weight - last_type = null - - // Otherwise we start the weight sum for the next entry here - else - last_type_weight = this_weight + last_type_weight = this_weight // Sort by weight descending, where weight is the values (not the keys). We assoc_to_keys later to get JUST the text sortTim(all_weights, GLOBAL_PROC_REF(cmp_numeric_dsc), associative = TRUE) diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index e0f1472b6a8..2178554bf00 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -310,7 +310,10 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." if(!(living_owner.mobility_flags & MOBILITY_MOVE)) return FALSE - return living_owner.resist_fire() + return handle_stop_drop_roll(owner) + +/atom/movable/screen/alert/fire/proc/handle_stop_drop_roll(mob/living/roller) + return roller.resist_fire() /atom/movable/screen/alert/give // information set when the give alert is made icon_state = "default" diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm index 846869901d6..d50de8ed943 100644 --- a/code/datums/brain_damage/mild.dm +++ b/code/datums/brain_damage/mild.dm @@ -11,17 +11,19 @@ scan_desc = "schizophrenia" gain_text = span_warning("You feel your grip on reality slipping...") lose_text = span_notice("You feel more grounded.") + /// Whether the hallucinations we give are uncapped, ie all the wacky ones + var/uncapped = FALSE /datum/brain_trauma/mild/hallucinations/on_life(seconds_per_tick, times_fired) - if(owner.stat != CONSCIOUS || owner.IsSleeping() || owner.IsUnconscious()) + if(owner.stat >= UNCONSCIOUS) return if(HAS_TRAIT(owner, TRAIT_RDS_SUPPRESSED)) owner.remove_language(/datum/language/aphasia, source = LANGUAGE_APHASIA) + owner.adjust_hallucinations(-10 SECONDS * seconds_per_tick) return - if(!HAS_TRAIT(owner, TRAIT_RDS_SUPPRESSED)) - owner.grant_language(/datum/language/aphasia, source = LANGUAGE_APHASIA) - owner.adjust_hallucinations_up_to(10 SECONDS * seconds_per_tick, 100 SECONDS) + owner.grant_language(/datum/language/aphasia, source = LANGUAGE_APHASIA) + owner.adjust_hallucinations_up_to(((uncapped ? 12 SECONDS : 5 SECONDS) * seconds_per_tick), (uncapped ? 240 SECONDS : 60 SECONDS)) /datum/brain_trauma/mild/hallucinations/on_lose() owner.remove_status_effect(/datum/status_effect/hallucination) diff --git a/code/datums/quirks/negative_quirks/insanity.dm b/code/datums/quirks/negative_quirks/insanity.dm index c5fc1afdf0d..9401d006b67 100644 --- a/code/datums/quirks/negative_quirks/insanity.dm +++ b/code/datums/quirks/negative_quirks/insanity.dm @@ -17,7 +17,7 @@ if(!iscarbon(quirk_holder)) return var/mob/living/carbon/carbon_quirk_holder = quirk_holder - + // Setup our special RDS mild hallucination. // Not a unique subtype so not to plague subtypesof, // also as we inherit the names and values from our quirk. @@ -28,6 +28,7 @@ added_trauma.scan_desc = LOWER_TEXT(name) added_trauma.gain_text = null added_trauma.lose_text = null + added_trauma.uncapped = client_source?.prefs?.read_preference(/datum/preference/toggle/rds_limit) carbon_quirk_holder.gain_trauma(added_trauma) added_trama_ref = WEAKREF(added_trauma) @@ -39,3 +40,7 @@ /datum/quirk/insanity/remove() QDEL_NULL(added_trama_ref) + +/datum/quirk_constant_data/rds_limit + associated_typepath = /datum/quirk/insanity + customization_options = list(/datum/preference/toggle/rds_limit) diff --git a/code/datums/status_effects/buffs/stop_drop_roll.dm b/code/datums/status_effects/buffs/stop_drop_roll.dm index 96db96372dd..e7ff393a1dd 100644 --- a/code/datums/status_effects/buffs/stop_drop_roll.dm +++ b/code/datums/status_effects/buffs/stop_drop_roll.dm @@ -17,13 +17,7 @@ RegisterSignal(owner, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(body_position_changed)) ADD_TRAIT(owner, TRAIT_HANDS_BLOCKED, TRAIT_STATUS_EFFECT(id)) // they're kinda busy! - owner.visible_message( - span_danger("[owner] rolls on the floor, trying to put [owner.p_them()]self out!"), - span_notice("You stop, drop, and roll!"), - ) - // Start with one weaker roll - owner.spin(spintime = actual_interval, speed = actual_interval / 4) - owner.adjust_fire_stacks(-0.25) + start_rolling() for (var/obj/item/dropped in owner.loc) dropped.extinguish() // Effectively extinguish your items by rolling on them @@ -33,6 +27,14 @@ UnregisterSignal(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_LIVING_SET_BODY_POSITION)) REMOVE_TRAIT(owner, TRAIT_HANDS_BLOCKED, TRAIT_STATUS_EFFECT(id)) +/datum/status_effect/stop_drop_roll/proc/start_rolling() + owner.visible_message( + span_danger("[owner] rolls on the floor, trying to put [owner.p_them()]self out!"), + span_notice("You stop, drop, and roll!"), + ) + // Start with one weaker roll + reduce_firestacks(0.25) + /datum/status_effect/stop_drop_roll/tick(seconds_between_ticks) if(HAS_TRAIT(owner, TRAIT_IMMOBILIZED) || HAS_TRAIT(owner, TRAIT_INCAPACITATED)) qdel(src) @@ -44,17 +46,17 @@ return owner.spin(spintime = actual_interval, speed = actual_interval / 4) - owner.adjust_fire_stacks(-1) - - if(owner.fire_stacks > 0) + if(!reduce_firestacks(1)) return - owner.visible_message( - span_danger("[owner] successfully extinguishes [owner.p_them()]self!"), - span_notice("You extinguish yourself."), - ) - qdel(src) + stop_rolling_successful() +/// Return TRUE to stop the us from rolling. +/datum/status_effect/stop_drop_roll/proc/reduce_firestacks(amt = 1) + owner.adjust_fire_stacks(-1 * amt) + return owner.fire_stacks <= 0 + +/// Called when we just, stop rolling, due to movement or other reasons. Maybe still on fire, maybe not. /datum/status_effect/stop_drop_roll/proc/stop_rolling(datum/source, ...) SIGNAL_HANDLER @@ -62,8 +64,54 @@ to_chat(owner, span_notice("You stop rolling around.")) qdel(src) +/// Called when we've successfully extinguished ourselves. +/datum/status_effect/stop_drop_roll/proc/stop_rolling_successful() + owner.visible_message( + span_danger("[owner] successfully extinguishes [owner.p_them()]self!"), + span_notice("You extinguish yourself."), + ) + qdel(src) + /datum/status_effect/stop_drop_roll/proc/body_position_changed(datum/source, new_value, old_value) SIGNAL_HANDLER if(new_value != LYING_DOWN) stop_rolling() + +/// Subtype of rolling triggered when someone hallucinating fire tries to stop, drop, and roll. +/datum/status_effect/stop_drop_roll/hallucinating + /// Weakref to the fire hallucination + var/datum/weakref/hallucination_weakref + +/datum/status_effect/stop_drop_roll/hallucinating/on_creation(mob/living/new_owner, datum/weakref/hallucination_weakref) + src.hallucination_weakref = hallucination_weakref + return ..() + +/datum/status_effect/stop_drop_roll/hallucinating/start_rolling() + owner.visible_message( + span_danger("[owner] starts rolling around on the floor, flailing about!"), + span_notice("You stop, drop, and roll!"), + ) + reduce_firestacks(1) // more effective cause it's not real + +/datum/status_effect/stop_drop_roll/hallucinating/reduce_firestacks(amt = 1) + var/datum/hallucination/fire/hallucination = hallucination_weakref?.resolve() + if(!istype(hallucination)) + return TRUE + + hallucination.fake_firestacks += (-1 * amt) + if(hallucination.fake_firestacks <= 0) + hallucination.clear_fire() + return TRUE + return FALSE + +/datum/status_effect/stop_drop_roll/hallucinating/stop_rolling_successful() + var/datum/hallucination/fire/hallucination = hallucination_weakref?.resolve() + if(istype(hallucination)) + hallucination.clear_fire() + + owner.visible_message( + span_danger("[owner] stops flailing around on the ground."), + span_notice("You extinguish yourself."), + ) + qdel(src) diff --git a/code/datums/status_effects/debuffs/hallucination.dm b/code/datums/status_effects/debuffs/hallucination.dm index 66e85f1900a..c45dd1451b1 100644 --- a/code/datums/status_effects/debuffs/hallucination.dm +++ b/code/datums/status_effects/debuffs/hallucination.dm @@ -5,22 +5,26 @@ alert_type = null tick_interval = 2 SECONDS remove_on_fullheal = TRUE + processing_speed = STATUS_EFFECT_NORMAL_PROCESS /// Biotypes which cannot hallucinate. var/barred_biotypes = NO_HALLUCINATION_BIOTYPES /// The lower range of when the next hallucination will trigger after one occurs. - var/lower_tick_interval = 10 SECONDS + var/lower_tick_interval = 20 SECONDS /// The upper range of when the next hallucination will trigger after one occurs. - var/upper_tick_interval = 60 SECONDS + var/upper_tick_interval = 80 SECONDS + /// The maximum hallucination tier that can be picked. + var/max_hallucination_tier = HALLUCINATION_TIER_COMMON + /// If TRUE, we only select hallucinations from the hallucination_tier. + /// If FALSE, it will also include anything below the hallucination_tier. + var/strict_tier = FALSE + /// Tier can be variable, based on the duration of the hallucination. + var/variable_tier = TRUE /// The cooldown for when the next hallucination can occur COOLDOWN_DECLARE(hallucination_cooldown) -/datum/status_effect/hallucination/on_creation(mob/living/new_owner, duration, lower_tick_interval, upper_tick_interval) - if(isnum(duration)) - src.duration = duration - if(isnum(lower_tick_interval)) - src.lower_tick_interval = lower_tick_interval - if(isnum(upper_tick_interval)) - src.upper_tick_interval = upper_tick_interval +/datum/status_effect/hallucination/on_creation(mob/living/new_owner, new_duration) + if(isnum(new_duration)) + src.duration = new_duration return ..() /datum/status_effect/hallucination/on_apply() @@ -86,15 +90,39 @@ if(!COOLDOWN_FINISHED(src, hallucination_cooldown)) return - var/datum/hallucination/picked_hallucination = pick_weight(GLOB.random_hallucination_weighted_list) - owner.cause_hallucination(picked_hallucination, "[id] status effect") - COOLDOWN_START(src, hallucination_cooldown, rand(lower_tick_interval, upper_tick_interval)) + var/lower_cd = lower_tick_interval + var/upper_cd = upper_tick_interval + if(!variable_tier) + var/seconds_left = (duration - world.time) / 10 + switch(seconds_left) + if(0 to 20) + max_hallucination_tier = HALLUCINATION_TIER_COMMON + lower_tick_interval *= 1.2 + upper_tick_interval *= 1.2 + if(20 to 60) + max_hallucination_tier = prob(10) ? HALLUCINATION_TIER_RARE : HALLUCINATION_TIER_UNCOMMON + if(60 to 120) + max_hallucination_tier = HALLUCINATION_TIER_RARE + lower_cd *= 0.75 + upper_cd *= 0.75 + if(120 to INFINITY) + max_hallucination_tier = HALLUCINATION_TIER_VERYSPECIAL + lower_cd *= 0.5 + upper_cd *= 0.5 + + var/datum/hallucination/picked_hallucination = get_random_hallucination(max_hallucination_tier, strict_tier) + if(!owner.cause_hallucination(picked_hallucination, "[id] status effect")) + lower_cd *= 0.25 + upper_cd *= 0.25 + COOLDOWN_START(src, hallucination_cooldown, rand(lower_cd, upper_cd)) // Sanity related hallucinations /datum/status_effect/hallucination/sanity id = "low sanity" status_type = STATUS_EFFECT_REFRESH duration = STATUS_EFFECT_PERMANENT // This lasts "forever", only goes away with sanity gain + max_hallucination_tier = HALLUCINATION_TIER_UNCOMMON + variable_tier = FALSE /datum/status_effect/hallucination/sanity/on_health_scan(datum/source, list/render_list, advanced, mob/user, mode, tochat) return @@ -130,3 +158,18 @@ else stack_trace("[type] was assigned a mob which was not crazy or insane. (was: [owner.mob_mood.sanity_level])") qdel(src) + +/datum/status_effect/hallucination/perceptomatrix + id = "perceptomatrix_hallucination" + status_type = STATUS_EFFECT_REFRESH + strict_tier = TRUE + variable_tier = FALSE + +/datum/status_effect/hallucination/perceptomatrix/refresh(mob/living/refresh_owner, new_duration) + src.duration += new_duration + +/datum/status_effect/hallucination/perceptomatrix/on_creation(mob/living/new_owner, new_duration) + if(isnum(new_duration)) + src.lower_tick_interval = new_duration * 0.2 + src.upper_tick_interval = new_duration + return ..() diff --git a/code/game/objects/items/grenades/hypno.dm b/code/game/objects/items/grenades/hypno.dm index 0d7601df204..d77049419ab 100644 --- a/code/game/objects/items/grenades/hypno.dm +++ b/code/game/objects/items/grenades/hypno.dm @@ -52,7 +52,7 @@ living_mob.Paralyze(10) living_mob.Knockdown(100) to_chat(living_mob, span_hypnophrase("The sound echoes in your brain...")) - living_mob.adjust_hallucinations(100 SECONDS) + living_mob.adjust_hallucinations(150 SECONDS) else if(distance <= 1) @@ -60,7 +60,7 @@ living_mob.Knockdown(30) if(hypno_sound) to_chat(living_mob, span_hypnophrase("The sound echoes in your brain...")) - living_mob.adjust_hallucinations(100 SECONDS) + living_mob.adjust_hallucinations(150 SECONDS) //Flash if(living_mob.flash_act(affect_silicon = 1)) diff --git a/code/modules/antagonists/abductor/equipment/glands/mindshock.dm b/code/modules/antagonists/abductor/equipment/glands/mindshock.dm index a4aa88b8da9..3480d20de4e 100644 --- a/code/modules/antagonists/abductor/equipment/glands/mindshock.dm +++ b/code/modules/antagonists/abductor/equipment/glands/mindshock.dm @@ -28,7 +28,7 @@ target.adjust_confusion(15 SECONDS) target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10, 160) if(3) - target.adjust_hallucinations(120 SECONDS) + target.adjust_hallucinations(150 SECONDS) /obj/item/organ/heart/gland/mindshock/mind_control(command, mob/living/user) if(!ownerCheck() || !mind_control_uses || active_mind_control) diff --git a/code/modules/antagonists/heretic/items/madness_mask.dm b/code/modules/antagonists/heretic/items/madness_mask.dm index 564715d7dd8..1be99d41012 100644 --- a/code/modules/antagonists/heretic/items/madness_mask.dm +++ b/code/modules/antagonists/heretic/items/madness_mask.dm @@ -62,7 +62,7 @@ human_in_range.mob_mood.direct_sanity_drain(rand(-2, -20) * seconds_per_tick) if(SPT_PROB(60, seconds_per_tick)) - human_in_range.adjust_hallucinations_up_to(10 SECONDS, 240 SECONDS) + human_in_range.adjust_hallucinations_up_to(10 SECONDS, 120 SECONDS) if(SPT_PROB(40, seconds_per_tick)) human_in_range.set_jitter_if_lower(10 SECONDS) diff --git a/code/modules/client/preferences/rds_limit.dm b/code/modules/client/preferences/rds_limit.dm new file mode 100644 index 00000000000..d035791bbb8 --- /dev/null +++ b/code/modules/client/preferences/rds_limit.dm @@ -0,0 +1,11 @@ +/datum/preference/toggle/rds_limit + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED + savefile_key = "rds_limit" + savefile_identifier = PREFERENCE_CHARACTER + default_value = FALSE + +/datum/preference/toggle/rds_limit/apply_to_human(mob/living/carbon/human/target, value) + return + +/datum/preference/toggle/rds_limit/is_accessible(datum/preferences/preferences) + return ..() && (/datum/quirk/insanity::name in preferences.all_quirks) diff --git a/code/modules/clothing/head/perceptomatrix.dm b/code/modules/clothing/head/perceptomatrix.dm index ed45ce0f9e3..9e31361e0c9 100644 --- a/code/modules/clothing/head/perceptomatrix.dm +++ b/code/modules/clothing/head/perceptomatrix.dm @@ -240,10 +240,7 @@ cast_on.emote("scream") cast_on.set_eye_blur_if_lower(eye_blur_duration) cast_on.adjust_staggered(stagger_duration) - cast_on.apply_status_effect(/datum/status_effect/hallucination, hallucination_duration, \ - hallucination_duration * 0.2, hallucination_duration) // lower/upper hallucination freq. bound - - return + cast_on.apply_status_effect(/datum/status_effect/hallucination/perceptomatrix, hallucination_duration, HALLUCINATION_TIER_RARE) #undef PERCEPTOMATRIX_INACTIVE_FLAGS #undef PERCEPTOMATRIX_ACTIVE_FLAGS diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm index eaa997607f0..813f296506f 100644 --- a/code/modules/clothing/suits/reactive_armour.dm +++ b/code/modules/clothing/suits/reactive_armour.dm @@ -349,7 +349,7 @@ /obj/item/clothing/suit/armor/reactive/hallucinating/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) owner.visible_message(span_danger("[src] blocks [attack_text], but pulls a massive charge of mental energy into [owner] from the surrounding environment!")) - owner.adjust_hallucinations_up_to(50 SECONDS, 300 SECONDS) + owner.adjust_hallucinations_up_to(50 SECONDS, 240 SECONDS) reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration return TRUE diff --git a/code/modules/hallucination/_hallucination.dm b/code/modules/hallucination/_hallucination.dm index df3bbe74757..12510d0230d 100644 --- a/code/modules/hallucination/_hallucination.dm +++ b/code/modules/hallucination/_hallucination.dm @@ -9,6 +9,8 @@ /datum/hallucination /// What is this hallucination's weight in the random hallucination pool? var/random_hallucination_weight = 0 + /// What tier of hallucination is this? Rarer ones should be higher + var/hallucination_tier = HALLUCINATION_TIER_NEVER /// Who's our next highest abstract parent type? var/abstract_hallucination_parent = /datum/hallucination /// Extra info about the hallucination displayed in the log. diff --git a/code/modules/hallucination/battle.dm b/code/modules/hallucination/battle.dm index 2a50093e3a0..6bcbafea52d 100644 --- a/code/modules/hallucination/battle.dm +++ b/code/modules/hallucination/battle.dm @@ -2,6 +2,7 @@ /datum/hallucination/battle abstract_hallucination_parent = /datum/hallucination/battle random_hallucination_weight = 3 + hallucination_tier = HALLUCINATION_TIER_COMMON /// Subtype of battle hallucination for gun based battles, where it sounds like someone is being shot. /datum/hallucination/battle/gun diff --git a/code/modules/hallucination/blood_flow.dm b/code/modules/hallucination/blood_flow.dm new file mode 100644 index 00000000000..aaaba4ba2c9 --- /dev/null +++ b/code/modules/hallucination/blood_flow.dm @@ -0,0 +1,69 @@ +/datum/hallucination/blood_flow + random_hallucination_weight = 3 + hallucination_tier = HALLUCINATION_TIER_COMMON + /// The bleeding hallucination's image + var/image/bleeding + +/datum/hallucination/blood_flow/start() + if(!hallucinator.client || !iscarbon(hallucinator)) + return FALSE + + var/mob/living/carbon/carb_hallucinator = hallucinator + if(!length(carb_hallucinator.bodyparts) || HAS_TRAIT(carb_hallucinator, TRAIT_NOBLOOD)) + return FALSE + var/obj/item/bodypart/picked + var/list/bodyparts = carb_hallucinator.bodyparts.Copy() + while(isnull(picked) && length(bodyparts)) + picked = pick_n_take(bodyparts) + if(!picked.can_bleed()) + picked = null + + if(isnull(picked)) + return FALSE + + feedback_details += "Bleeding: [picked]" + + RegisterSignals(picked, list(COMSIG_QDELETING, COMSIG_BODYPART_REMOVED), PROC_REF(stop_bleeding)) + RegisterSignal(hallucinator, SIGNAL_ADDTRAIT(TRAIT_NOBLOOD), PROC_REF(stop_bleeding)) + + to_chat(hallucinator, span_warning("Your [picked.plaintext_zone] looses a spray of blood!")) + var/bleed_duration = rand(16 SECONDS, 40 SECONDS) + addtimer(CALLBACK(src, PROC_REF(stop_bleeding), picked), bleed_duration) + if(prob(25)) + addtimer(CALLBACK(src, PROC_REF(by_god), picked), bleed_duration * pick(0.5, 0.66)) + stamina_loop() + + hallucinator.playsound_local(get_turf(hallucinator), pick('sound/effects/wounds/blood1.ogg', 'sound/effects/wounds/blood2.ogg', 'sound/effects/wounds/blood3.ogg'), 50, TRUE) + bleeding = image( + icon = 'icons/mob/effects/bleed_overlays.dmi', + icon_state = "[picked.body_zone]_[pick(2, 3)]", + loc = hallucinator, + ) + bleeding.layer = -WOUND_LAYER + hallucinator.client?.images += bleeding + return TRUE + +/datum/hallucination/blood_flow/Destroy() + hallucinator.client?.images -= bleeding + return ..() + +/datum/hallucination/blood_flow/proc/by_god(obj/item/bodypart/picked) + if(QDELETED(src) || QDELETED(hallucinator) || QDELETED(picked)) + return + + to_chat(hallucinator, span_warning("The blood doesn't stop flowing, yet [picked.plaintext_zone] doesn't seem to hurt...")) + +/datum/hallucination/blood_flow/proc/stop_bleeding(obj/item/bodypart/source) + SIGNAL_HANDLER + UnregisterSignal(source, list(COMSIG_QDELETING, COMSIG_BODYPART_REMOVED)) + UnregisterSignal(hallucinator, SIGNAL_ADDTRAIT(TRAIT_NOBLOOD)) + if(!QDELETED(source)) + to_chat(hallucinator, span_warning("Your [source.plaintext_zone] stops bleeding.")) + if(!QDELETED(src)) + qdel(src) + +/datum/hallucination/blood_flow/proc/stamina_loop() + set waitfor = FALSE + while(!QDELETED(src) && !QDELETED(hallucinator)) + hallucinator.adjustStaminaLoss(5) + sleep(4 SECONDS) diff --git a/code/modules/hallucination/body.dm b/code/modules/hallucination/body.dm index cba2ce0c31d..96cf60c66b7 100644 --- a/code/modules/hallucination/body.dm +++ b/code/modules/hallucination/body.dm @@ -1,6 +1,7 @@ /// Makes a random body appear and disappear quickly in view of the hallucinator. /datum/hallucination/body abstract_hallucination_parent = /datum/hallucination/body + hallucination_tier = HALLUCINATION_TIER_COMMON /// The file to make the body image from. var/body_image_file /// The icon state to make the body image form. @@ -126,6 +127,7 @@ /datum/hallucination/body/weird random_hallucination_weight = 0.1 // These are very uncommon abstract_hallucination_parent = /datum/hallucination/body/weird + hallucination_tier = HALLUCINATION_TIER_RARE /datum/hallucination/body/weird/alien body_image_file = 'icons/mob/nonhuman-player/alien.dmi' @@ -140,6 +142,7 @@ body_image_file = 'icons/mob/simple/mob.dmi' body_image_state = "chronostuck" body_floats = TRUE + hallucination_tier = HALLUCINATION_TIER_VERYSPECIAL /datum/hallucination/body/weird/god body_image_file = 'icons/mob/simple/mob.dmi' @@ -158,6 +161,7 @@ /datum/hallucination/body/weird/bones body_image_file = 'icons/obj/trader_signs.dmi' body_image_state = "mrbones" + hallucination_tier = HALLUCINATION_TIER_VERYSPECIAL /datum/hallucination/body/weird/freezer random_hallucination_weight = 0.3 // Slightly more common since it's cool (heh) @@ -165,6 +169,7 @@ body_image_state = "the_freezer" body_layer = ABOVE_ALL_MOB_LAYER spawn_under_hallucinator = TRUE + hallucination_tier = HALLUCINATION_TIER_VERYSPECIAL /datum/hallucination/body/weird/freezer/make_body_image(turf/location) var/image/body = ..() diff --git a/code/modules/hallucination/bolted_airlocks.dm b/code/modules/hallucination/bolted_airlocks.dm index f50c8876100..fd8e13279f4 100644 --- a/code/modules/hallucination/bolted_airlocks.dm +++ b/code/modules/hallucination/bolted_airlocks.dm @@ -1,5 +1,6 @@ /datum/hallucination/bolts random_hallucination_weight = 7 + hallucination_tier = HALLUCINATION_TIER_COMMON /// A list of weakrefs to airlocks we bolt down around us var/list/datum/weakref/airlocks_to_hit /// A list of weakrefs to fake lock hallucinations we've created diff --git a/code/modules/hallucination/bubblegum_attack.dm b/code/modules/hallucination/bubblegum_attack.dm index 529d67dcd35..66dfa606dc3 100644 --- a/code/modules/hallucination/bubblegum_attack.dm +++ b/code/modules/hallucination/bubblegum_attack.dm @@ -1,6 +1,7 @@ /// Sends a fake bubblegum charging through a nearby wall to our target. /datum/hallucination/oh_yeah random_hallucination_weight = 1 + hallucination_tier = HALLUCINATION_TIER_RARE /// An image overlayed to the wall bubblegum comes out of, to look destroyed. var/image/fake_broken_wall /// An image put where bubblegum is expected to land, to mimic his charge "rune" icon. diff --git a/code/modules/hallucination/delusions.dm b/code/modules/hallucination/delusions.dm index c6795794bc1..c90b60d2f41 100644 --- a/code/modules/hallucination/delusions.dm +++ b/code/modules/hallucination/delusions.dm @@ -1,6 +1,7 @@ /// A hallucination that makes us and (possibly) other people look like something else. /datum/hallucination/delusion abstract_hallucination_parent = /datum/hallucination/delusion + hallucination_tier = HALLUCINATION_TIER_UNCOMMON /// The duration of the delusions var/duration = 30 SECONDS @@ -249,6 +250,7 @@ delusion_name = "Syndicate" affects_others = TRUE affects_us = FALSE + hallucination_tier = HALLUCINATION_TIER_RARE /datum/hallucination/delusion/preset/syndies/make_delusion_image(mob/over_who) delusion_appearance = get_dynamic_human_appearance( diff --git a/code/modules/hallucination/eyes_in_dark.dm b/code/modules/hallucination/eyes_in_dark.dm new file mode 100644 index 00000000000..60f54bb80ab --- /dev/null +++ b/code/modules/hallucination/eyes_in_dark.dm @@ -0,0 +1,91 @@ +/datum/hallucination/eyes_in_dark + random_hallucination_weight = 2 + hallucination_tier = HALLUCINATION_TIER_COMMON + /// The floating eye effect, somewhere in the world + var/obj/effect/abstract/floating_eyes/eyes + +/datum/hallucination/eyes_in_dark/Destroy() + if(QDELETED(eyes)) + eyes = null + else + QDEL_NULL(eyes) + return ..() + +/datum/hallucination/eyes_in_dark/start() + if(!hallucinator.client) + return FALSE + + if(hallucinator.lighting_cutoff >= 2.5) + return FALSE + + var/list/valid = list() + for(var/turf/open/nearby in view(hallucinator)) + if(nearby.get_lumcount() > LIGHTING_TILE_IS_DARK) + continue + valid += nearby + + if(!length(valid)) + return FALSE + + if(prob(5)) + to_chat(hallucinator, span_warning("You feel like you're being watched...")) + + var/turf/selected = pick(valid) + feedback_details += "Eye coords: [selected.x], [selected.y], [selected.z]" + eyes = new(selected, hallucinator) + RegisterSignal(eyes, COMSIG_QDELETING, PROC_REF(end_hallucination)) + addtimer(CALLBACK(src, PROC_REF(end_hallucination_gracefully)), rand(60 SECONDS, 180 SECONDS)) + return TRUE + +/datum/hallucination/eyes_in_dark/proc/end_hallucination_gracefully() + animate(eyes, alpha = 0, time = 1 SECONDS) + QDEL_IN(src, 1.2 SECONDS) + +/datum/hallucination/eyes_in_dark/proc/end_hallucination() + SIGNAL_HANDLER + if(!QDELETED(src)) + qdel(src) + +/obj/effect/abstract/floating_eyes + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + // Who sees the eyes? + var/datum/weakref/seer_ref + +/obj/effect/abstract/floating_eyes/Initialize(mapload, mob/seer) + . = ..() + if(isnull(seer)) + return INITIALIZE_HINT_QDEL + + seer_ref = WEAKREF(seer) + var/image/make_invis = image(icon = null, icon_state = null, loc = src) + make_invis.override = TRUE + add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/one_person/reversed, "hallucination", make_invis, null, seer) + START_PROCESSING(SSfastprocess, src) + update_appearance() + +/obj/effect/abstract/floating_eyes/Destroy() + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/obj/effect/abstract/floating_eyes/update_overlays() + . = ..() + var/mutable_appearance/r_eye = mutable_appearance(icon = 'icons/mob/human/human_face.dmi', icon_state = "eyes_glow_r") + r_eye.color = COLOR_DARK_RED + . += r_eye + var/mutable_appearance/l_eye = mutable_appearance(icon = 'icons/mob/human/human_face.dmi', icon_state = "eyes_glow_l") + l_eye.color = COLOR_DARK_RED + . += l_eye + + . += emissive_appearance('icons/mob/human/human_face.dmi', "eyes_glow_l", src) + . += emissive_appearance('icons/mob/human/human_face.dmi', "eyes_glow_r", src) + +/obj/effect/abstract/floating_eyes/process(seconds_per_tick) + var/turf/below_us = get_turf(src) + var/mob/seer = seer_ref?.resolve() + if(below_us.get_lumcount() < LIGHTING_TILE_IS_DARK || seer?.lighting_cutoff >= 2.5 || get_dist(seer, src) <= 1) + graceful_delete() + +/obj/effect/abstract/floating_eyes/proc/graceful_delete() + STOP_PROCESSING(SSfastprocess, src) + animate(src, alpha = 0, time = 0.5 SECONDS) + QDEL_IN(src, 0.75 SECONDS) diff --git a/code/modules/hallucination/fake_alert.dm b/code/modules/hallucination/fake_alert.dm index 6e10daf73aa..50ddf2e194f 100644 --- a/code/modules/hallucination/fake_alert.dm +++ b/code/modules/hallucination/fake_alert.dm @@ -2,6 +2,7 @@ /datum/hallucination/fake_alert abstract_hallucination_parent = /datum/hallucination/fake_alert random_hallucination_weight = 1 + hallucination_tier = HALLUCINATION_TIER_COMMON var/del_timer_id /// The duration of the alert being thrown. diff --git a/code/modules/hallucination/fake_chat.dm b/code/modules/hallucination/fake_chat.dm index 10431724496..3f2d39e4661 100644 --- a/code/modules/hallucination/fake_chat.dm +++ b/code/modules/hallucination/fake_chat.dm @@ -1,6 +1,7 @@ /// Sends a fake chat message to the hallucinator. /datum/hallucination/chat random_hallucination_weight = 100 + hallucination_tier = HALLUCINATION_TIER_COMMON /// If TRUE, we force the message to be hallucinated from common radio. Only set in New() var/force_radio @@ -12,30 +13,59 @@ src.specific_message = specific_message return ..() +/// When passed a mob, returns a list of languages that mob could theoretically speak IF a blank slate. +/datum/hallucination/chat/proc/get_hallucinating_spoken_languages(atom/movable/who) + var/override_typepath + if(iscarbon(who)) + var/mob/living/carbon/human_who = who + override_typepath = human_who.dna?.species?.species_language_holder + + var/datum/language_holder/what_they_speak = GLOB.prototype_language_holders[override_typepath || who.initial_language_holder] + return what_they_speak?.spoken_languages?.Copy() || list() + /datum/hallucination/chat/start() var/mob/living/carbon/human/speaker - var/datum/language/understood_language = hallucinator.get_random_understood_language() - for(var/mob/living/carbon/nearby_human in view(hallucinator)) - if(nearby_human == hallucinator) - continue + var/list/datum/language/understood_languages = hallucinator.get_language_holder().understood_languages + var/understood_language - if(!speaker) - speaker = nearby_human - else if(get_dist(hallucinator, nearby_human) < get_dist(hallucinator, speaker)) + if(!force_radio) + var/list/valid_humans = list() + var/list/valid_corpses = list() + for(var/mob/living/carbon/nearby_human in view(hallucinator)) + if(nearby_human == hallucinator) + continue + if(nearby_human.stat == DEAD) + valid_corpses += nearby_human + continue + valid_humans += nearby_human + + // pick a nearby human which can speak a language the hallucinator understands + for(var/mob/living/carbon/nearby_human in shuffle(valid_humans)) + var/list/shared_languages = get_hallucinating_spoken_languages(nearby_human) & understood_languages + if(!length(shared_languages)) // future idea : have people hallucinating off their minds ignore this check + continue speaker = nearby_human + understood_language = pick(shared_languages) + break + + // corpses disrespect language because they're... dead + if(isnull(speaker) && length(valid_corpses)) + speaker = pick(valid_corpses) // Get person to affect if radio hallucination - var/is_radio = !speaker || force_radio + var/is_radio = force_radio || isnull(speaker) if(is_radio) - var/list/humans = list() + for(var/datum/mind/crew_mind in shuffle(get_crewmember_minds())) + if(crew_mind == hallucinator.mind) + continue + var/list/shared_languages = get_hallucinating_spoken_languages(crew_mind.current) & understood_languages + if(!length(shared_languages)) + continue + speaker = crew_mind.current + understood_language = pick(shared_languages) + break - for(var/datum/mind/crew_mind in get_crewmember_minds()) - if(crew_mind.current) - humans += crew_mind.current - if(humans.len) - speaker = pick(humans) - - if(!speaker) + if(isnull(speaker)) return // Time to generate a message. diff --git a/code/modules/hallucination/fake_death.dm b/code/modules/hallucination/fake_death.dm index 9583418f232..7d0b603c9a4 100644 --- a/code/modules/hallucination/fake_death.dm +++ b/code/modules/hallucination/fake_death.dm @@ -1,6 +1,7 @@ // This hallucinations makes us suddenly think we died, stopping us / changing our hud / sending a fake deadchat message. /datum/hallucination/death random_hallucination_weight = 1 + hallucination_tier = HALLUCINATION_TIER_UNCOMMON /// Determines whether we floor them or just immobilize them var/floor_them = TRUE diff --git a/code/modules/hallucination/fake_message.dm b/code/modules/hallucination/fake_message.dm index de5616d83c6..b6fc640b0b2 100644 --- a/code/modules/hallucination/fake_message.dm +++ b/code/modules/hallucination/fake_message.dm @@ -1,5 +1,6 @@ /datum/hallucination/message random_hallucination_weight = 60 + hallucination_tier = HALLUCINATION_TIER_COMMON /datum/hallucination/message/start() var/list/nearby_humans = list() diff --git a/code/modules/hallucination/fake_plasmaflood.dm b/code/modules/hallucination/fake_plasmaflood.dm index 5b3a67f5312..5019bec8912 100644 --- a/code/modules/hallucination/fake_plasmaflood.dm +++ b/code/modules/hallucination/fake_plasmaflood.dm @@ -5,6 +5,7 @@ /// Plasma starts flooding from the nearby vent /datum/hallucination/fake_flood random_hallucination_weight = 7 + hallucination_tier = HALLUCINATION_TIER_UNCOMMON var/list/image/flood_images = list() var/list/obj/effect/plasma_image_holder/flood_image_holders = list() diff --git a/code/modules/hallucination/fake_sound.dm b/code/modules/hallucination/fake_sound.dm index 1d93be83f94..7b2e2ee8825 100644 --- a/code/modules/hallucination/fake_sound.dm +++ b/code/modules/hallucination/fake_sound.dm @@ -1,6 +1,7 @@ /// Hallucination that plays a fake sound somewhere nearby. /datum/hallucination/fake_sound abstract_hallucination_parent = /datum/hallucination/fake_sound + hallucination_tier = HALLUCINATION_TIER_COMMON /// Volume of the fake sound var/volume = 50 @@ -10,6 +11,9 @@ var/sound_type /datum/hallucination/fake_sound/start() + if(!hallucinator.can_hear()) + return FALSE + var/sound_to_play = islist(sound_type) ? pick(sound_type) : sound_type play_fake_sound(random_far_turf(), sound_to_play) feedback_details += "Sound: [sound_to_play]" @@ -148,9 +152,31 @@ volume = 90 sound_type = 'sound/items/weapons/flash.ogg' +/datum/hallucination/fake_sound/normal/ringtone + volume = 50 + +/datum/hallucination/fake_sound/normal/ringtone/New(mob/living/hallucinator) + . = ..() + if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) + sound_type = pick( + 'sound/machines/beep/twobeep_voice1.ogg', + 'sound/machines/beep/twobeep_voice2.ogg', + ) + else + sound_type = 'sound/machines/beep/twobeep_high.ogg' + +/datum/hallucination/fake_sound/normal/ringtone/play_fake_sound(turf/source, sound_to_play = sound_type) + if(prob(33)) + source = get_turf(hallucinator) + var/obj/item/modular_computer/pda/pda = locate() in hallucinator.get_all_contents() + var/datum/computer_file/program/messenger/messenger_app = locate() in pda?.stored_files + hallucinator.balloon_alert(hallucinator, "*[messenger_app?.ringtone || MESSENGER_RINGTONE_DEFAULT]*") + return ..() + /datum/hallucination/fake_sound/weird abstract_hallucination_parent = /datum/hallucination/fake_sound/weird random_hallucination_weight = 1 + hallucination_tier = HALLUCINATION_TIER_VERYSPECIAL /// if FALSE, we will pass "null" in as the turf source, meaning the sound will just play without direction / etc. var/no_source = FALSE @@ -187,6 +213,7 @@ sound_type = 'sound/effects/magic/clockwork/invoke_general.ogg' /datum/hallucination/fake_sound/weird/creepy + hallucination_tier = HALLUCINATION_TIER_COMMON /datum/hallucination/fake_sound/weird/creepy/New(mob/living/hallucinator) . = ..() @@ -202,6 +229,7 @@ /datum/hallucination/fake_sound/weird/game_over sound_vary = FALSE sound_type = 'sound/machines/compiler/compiler-failure.ogg' + hallucination_tier = HALLUCINATION_TIER_RARE /datum/hallucination/fake_sound/weird/hallelujah sound_vary = FALSE @@ -216,8 +244,10 @@ sound_vary = FALSE no_source = TRUE sound_type = 'sound/runtime/hyperspace/hyperspace_begin.ogg' + hallucination_tier = HALLUCINATION_TIER_COMMON /datum/hallucination/fake_sound/weird/laugher + hallucination_tier = HALLUCINATION_TIER_COMMON sound_type = list( 'sound/mobs/humanoids/human/laugh/womanlaugh.ogg', 'sound/mobs/humanoids/human/laugh/manlaugh1.ogg', @@ -228,6 +258,7 @@ volume = 15 sound_vary = FALSE sound_type = 'sound/items/weapons/ring.ogg' + hallucination_tier = HALLUCINATION_TIER_RARE /datum/hallucination/fake_sound/weird/phone/play_fake_sound(turf/source, sound_to_play) for(var/next_ring in 1 to 3) @@ -236,6 +267,7 @@ return ..() /datum/hallucination/fake_sound/weird/spell + hallucination_tier = HALLUCINATION_TIER_RARE sound_type = list( 'sound/effects/magic/disintegrate.ogg', 'sound/effects/magic/ethereal_enter.ogg', @@ -247,15 +279,18 @@ ) /datum/hallucination/fake_sound/weird/spell/just_jaunt // A few antags use jaunts, so this sound specifically is fun to isolate + hallucination_tier = HALLUCINATION_TIER_RARE sound_type = 'sound/effects/magic/ethereal_enter.ogg' /datum/hallucination/fake_sound/weird/summon_sound // Heretic circle sound, notably volume = 75 + hallucination_tier = HALLUCINATION_TIER_RARE sound_type = 'sound/effects/magic/castsummon.ogg' /datum/hallucination/fake_sound/weird/tesloose volume = 35 sound_type = 'sound/effects/magic/lightningbolt.ogg' + hallucination_tier = HALLUCINATION_TIER_RARE /datum/hallucination/fake_sound/weird/tesloose/play_fake_sound(turf/source, sound_to_play) . = ..() @@ -265,6 +300,7 @@ /datum/hallucination/fake_sound/weird/xeno random_hallucination_weight = 2 // Some of these are ambience sounds too volume = 25 + hallucination_tier = HALLUCINATION_TIER_RARE sound_type = list( 'sound/mobs/non-humanoids/hiss/lowHiss1.ogg', 'sound/mobs/non-humanoids/hiss/lowHiss2.ogg', @@ -283,7 +319,7 @@ sound_type = 'sound/effects/hallucinations/radio_static.ogg' /datum/hallucination/fake_sound/weird/ice_crack - random_hallucination_weight = 2 + random_hallucination_weight = 0 volume = 100 no_source = TRUE sound_type = 'sound/effects/ice_shovel.ogg' diff --git a/code/modules/hallucination/hazard.dm b/code/modules/hallucination/hazard.dm index 34bcee62f6a..81596ee1ea7 100644 --- a/code/modules/hallucination/hazard.dm +++ b/code/modules/hallucination/hazard.dm @@ -2,6 +2,7 @@ /datum/hallucination/hazard abstract_hallucination_parent = /datum/hallucination/hazard random_hallucination_weight = 5 + hallucination_tier = HALLUCINATION_TIER_UNCOMMON /// The type of effect we create var/hazard_type = /obj/effect/client_image_holder/hallucination/danger diff --git a/code/modules/hallucination/hud_screw.dm b/code/modules/hallucination/hud_screw.dm index 88a9fccc078..346c25f7e99 100644 --- a/code/modules/hallucination/hud_screw.dm +++ b/code/modules/hallucination/hud_screw.dm @@ -2,6 +2,7 @@ /datum/hallucination/screwy_hud abstract_hallucination_parent = /datum/hallucination/screwy_hud random_hallucination_weight = 4 + hallucination_tier = HALLUCINATION_TIER_COMMON /// The type of hud we give to the hallucinator var/screwy_hud_type = SCREWYHUD_NONE diff --git a/code/modules/hallucination/ice_cube.dm b/code/modules/hallucination/ice_cube.dm index d5ec89649de..40c86ffcc4f 100644 --- a/code/modules/hallucination/ice_cube.dm +++ b/code/modules/hallucination/ice_cube.dm @@ -1,6 +1,7 @@ /// Causes the hallucinator to believe themselves frozen in ice. Man am I glad he's frozen in there etc etc /datum/hallucination/ice random_hallucination_weight = 3 + hallucination_tier = HALLUCINATION_TIER_COMMON /// What icon file to use for our hallucinator var/ice_icon = 'icons/effects/freeze.dmi' diff --git a/code/modules/hallucination/inhand_fake_item.dm b/code/modules/hallucination/inhand_fake_item.dm index 665c8811339..06e83375a3e 100644 --- a/code/modules/hallucination/inhand_fake_item.dm +++ b/code/modules/hallucination/inhand_fake_item.dm @@ -2,6 +2,7 @@ /datum/hallucination/fake_item abstract_hallucination_parent = /datum/hallucination/fake_item random_hallucination_weight = 1 + hallucination_tier = HALLUCINATION_TIER_COMMON /// A flag of slots this fake item can appear in. var/valid_slots = ITEM_SLOT_HANDS|ITEM_SLOT_BELT|ITEM_SLOT_LPOCKET|ITEM_SLOT_RPOCKET @@ -114,6 +115,24 @@ return hallucinated_item +/datum/hallucination/fake_item/summon_guns + hallucination_tier = HALLUCINATION_TIER_RARE + valid_slots = ITEM_SLOT_HANDS + +/datum/hallucination/fake_item/summon_guns/make_fake_item(where_to_put_it, equip_flags) + template_item_type = pick(GLOB.summoned_guns) + . = ..() + hallucinator.playsound_local(get_turf(hallucinator), 'sound/effects/magic/summon_guns.ogg', 50, TRUE) + +/datum/hallucination/fake_item/summon_magic + hallucination_tier = HALLUCINATION_TIER_RARE + valid_slots = ITEM_SLOT_HANDS + +/datum/hallucination/fake_item/summon_magic/make_fake_item(where_to_put_it, equip_flags) + template_item_type = pick(GLOB.summoned_magic + GLOB.summoned_special_magic) + . = ..() + hallucinator.playsound_local(get_turf(hallucinator), 'sound/effects/magic/summon_magic.ogg', 50, TRUE) + /obj/item/hallucinated name = "mirage" plane = ABOVE_HUD_PLANE diff --git a/code/modules/hallucination/mother.dm b/code/modules/hallucination/mother.dm index 12b31b04f05..4e65aed6a54 100644 --- a/code/modules/hallucination/mother.dm +++ b/code/modules/hallucination/mother.dm @@ -1,6 +1,8 @@ /// Your mother appears to scold you. /datum/hallucination/your_mother random_hallucination_weight = 2 + hallucination_tier = HALLUCINATION_TIER_VERYSPECIAL + var/obj/effect/client_image_holder/hallucination/your_mother/mother /datum/hallucination/your_mother/start() diff --git a/code/modules/hallucination/nearby_fake_item.dm b/code/modules/hallucination/nearby_fake_item.dm index 33ef6b14b25..a93c9ece396 100644 --- a/code/modules/hallucination/nearby_fake_item.dm +++ b/code/modules/hallucination/nearby_fake_item.dm @@ -2,6 +2,7 @@ /datum/hallucination/nearby_fake_item abstract_hallucination_parent = /datum/hallucination/nearby_fake_item random_hallucination_weight = 1 + hallucination_tier = HALLUCINATION_TIER_COMMON /// The icon file to draw from for left hand icons var/left_hand_file diff --git a/code/modules/hallucination/on_fire.dm b/code/modules/hallucination/on_fire.dm index 3e64618e719..048d8dcc96c 100644 --- a/code/modules/hallucination/on_fire.dm +++ b/code/modules/hallucination/on_fire.dm @@ -3,6 +3,7 @@ /datum/hallucination/fire random_hallucination_weight = 3 + hallucination_tier = HALLUCINATION_TIER_UNCOMMON /// Are we currently burning our mob? var/active = TRUE @@ -27,21 +28,29 @@ /// How long have we spent on fire? var/time_spent = 0 -/datum/hallucination/fire/New(mob/living/hallucinator) - if(ismonkey(hallucinator)) - fire_icon_state = "monkey_big_fire" + var/fake_firestacks = 0 - else if(!ishuman(hallucinator)) - fire_icon_state = "generic_fire" +/datum/hallucination/fire/proc/make_overlay() + var/mutable_appearance/real_overlay = hallucinator.get_fire_overlay(fake_firestacks) + if(!real_overlay) + return null + if(real_overlay.icon_state == fire_overlay?.icon_state) + return fire_overlay - return ..() + var/image/new_overlay = image(real_overlay.icon, hallucinator, real_overlay.icon_state, real_overlay.layer) + new_overlay.appearance_flags = real_overlay.appearance_flags + return new_overlay /datum/hallucination/fire/start() - fire_overlay = image(fire_icon, hallucinator, fire_icon_state, ABOVE_MOB_LAYER) - SET_PLANE_EXPLICIT(fire_overlay, ABOVE_GAME_PLANE, hallucinator) + fake_firestacks = rand(5, 15) + fire_overlay = make_overlay() + if(!fire_overlay) + return FALSE + hallucinator.client?.images |= fire_overlay to_chat(hallucinator, span_userdanger("You're set on fire!")) - hallucinator.throw_alert(ALERT_FIRE, /atom/movable/screen/alert/fire, override = TRUE) + var/atom/movable/screen/alert/fire/fake/alert = hallucinator.throw_alert(ALERT_FIRE, /atom/movable/screen/alert/fire/fake, override = TRUE) + alert.hallucination_weakref = WEAKREF(src) times_to_lower_stamina = rand(5, 10) addtimer(CALLBACK(src, PROC_REF(start_expanding)), 2 SECONDS) return TRUE @@ -66,8 +75,15 @@ if(QDELETED(src)) return - if(hallucinator.fire_stacks <= 0) + fake_firestacks -= (0.25 * seconds_per_tick) + if(fake_firestacks <= 0) clear_fire() + else + var/new_overlay = make_overlay() + if(new_overlay && new_overlay != fire_overlay) + hallucinator.client?.images -= fire_overlay + fire_overlay = new_overlay + hallucinator.client?.images |= fire_overlay time_spent += seconds_per_tick @@ -100,6 +116,8 @@ /datum/hallucination/fire/proc/update_temp() if(stage <= 0) hallucinator.clear_alert(ALERT_TEMPERATURE, clear_override = TRUE) + if(!active) + qdel(src) else hallucinator.clear_alert(ALERT_TEMPERATURE, clear_override = TRUE) hallucinator.throw_alert(ALERT_TEMPERATURE, /atom/movable/screen/alert/hot, stage, override = TRUE) @@ -117,3 +135,11 @@ #undef RAISE_FIRE_COUNT #undef RAISE_FIRE_TIME + +/// This alert is thrown when hallucinating fire +/atom/movable/screen/alert/fire/fake + /// We need to track the original hallucination so we can pass it to the status effect + var/datum/weakref/hallucination_weakref + +/atom/movable/screen/alert/fire/fake/handle_stop_drop_roll(mob/living/roller) + return !!roller.apply_status_effect(/datum/status_effect/stop_drop_roll/hallucinating, hallucination_weakref) diff --git a/code/modules/hallucination/screwy_health_doll.dm b/code/modules/hallucination/screwy_health_doll.dm index 2a8eeba16e2..35f7356b4d4 100644 --- a/code/modules/hallucination/screwy_health_doll.dm +++ b/code/modules/hallucination/screwy_health_doll.dm @@ -1,6 +1,7 @@ ///Causes the target to see incorrect health damages on the healthdoll /datum/hallucination/fake_health_doll random_hallucination_weight = 12 + hallucination_tier = HALLUCINATION_TIER_COMMON /// The duration of the hallucination var/duration diff --git a/code/modules/hallucination/shock.dm b/code/modules/hallucination/shock.dm index 5c99328dafb..85f144b050e 100644 --- a/code/modules/hallucination/shock.dm +++ b/code/modules/hallucination/shock.dm @@ -1,6 +1,7 @@ /// Causes a fake "zap" to the hallucinator. /datum/hallucination/shock - random_hallucination_weight = 1 + random_hallucination_weight = 1 // really low weight, as it also has a snowflake check to trigger when bumping airlocks + hallucination_tier = HALLUCINATION_TIER_COMMON var/electrocution_icon = 'icons/mob/human/human.dmi' var/electrocution_icon_state = "electrocuted_base" diff --git a/code/modules/hallucination/station_message.dm b/code/modules/hallucination/station_message.dm index 55b44d18463..7f3ebdc6a54 100644 --- a/code/modules/hallucination/station_message.dm +++ b/code/modules/hallucination/station_message.dm @@ -1,6 +1,7 @@ /datum/hallucination/station_message abstract_hallucination_parent = /datum/hallucination/station_message random_hallucination_weight = 1 + hallucination_tier = HALLUCINATION_TIER_RARE /datum/hallucination/station_message/start() qdel(src) // To be implemented by subtypes, call parent for easy cleanup diff --git a/code/modules/hallucination/stray_bullet.dm b/code/modules/hallucination/stray_bullet.dm index 33462d2ab43..f3775671f2f 100644 --- a/code/modules/hallucination/stray_bullet.dm +++ b/code/modules/hallucination/stray_bullet.dm @@ -1,6 +1,7 @@ /// Shoots a random, fake projectile to the hallucinator /datum/hallucination/stray_bullet random_hallucination_weight = 7 + hallucination_tier = HALLUCINATION_TIER_UNCOMMON /datum/hallucination/stray_bullet/start() var/list/turf/starting_locations = list() diff --git a/code/modules/hallucination/telepathy.dm b/code/modules/hallucination/telepathy.dm new file mode 100644 index 00000000000..df161579341 --- /dev/null +++ b/code/modules/hallucination/telepathy.dm @@ -0,0 +1,36 @@ +/datum/hallucination/telepathy + random_hallucination_weight = 4 + hallucination_tier = HALLUCINATION_TIER_COMMON + +/datum/hallucination/telepathy/start() + var/datum/action/cooldown/spell/list_target/telepathy/mimiced_type = pick(typesof(/datum/action/cooldown/spell/list_target/telepathy)) + hallucinator.balloon_alert(hallucinator, "you hear a voice") + to_chat(hallucinator, "\ + You hear a voice in your head...\ + [get_telepath_message()]\ + ") + return TRUE + +/datum/hallucination/telepathy/proc/get_telepath_message() + if(prob(0.001)) + return "horse" + + var/memo = pick( + pick_list_replacements(HALLUCINATION_FILE, "advice"), + pick_list_replacements(HALLUCINATION_FILE, "aggressive"), + pick_list_replacements(HALLUCINATION_FILE, "conversation"), + pick_list_replacements(HALLUCINATION_FILE, "didyouhearthat"), + pick_list_replacements(HALLUCINATION_FILE, "doubt"), + pick_list_replacements(HALLUCINATION_FILE, "escape"), + pick_list_replacements(HALLUCINATION_FILE, "getout"), + pick_list_replacements(HALLUCINATION_FILE, "greetings"), + pick_list_replacements(HALLUCINATION_FILE, "suspicion"), + ) + var/names = pick( + first_name(hallucinator.name), + last_name(hallucinator.name), + first_name(hallucinator.real_name), + last_name(hallucinator.real_name), + ) + + return replacetext(memo, "%TARGETNAME%", names) diff --git a/code/modules/hallucination/xeno_attack.dm b/code/modules/hallucination/xeno_attack.dm index 0d8b15490d9..33ebc6e593c 100644 --- a/code/modules/hallucination/xeno_attack.dm +++ b/code/modules/hallucination/xeno_attack.dm @@ -1,6 +1,7 @@ /// Xeno crawls from nearby vent, jumps at you, and goes back in. /datum/hallucination/xeno_attack random_hallucination_weight = 2 + hallucination_tier = HALLUCINATION_TIER_RARE /datum/hallucination/xeno_attack/start() var/turf/xeno_attack_source diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index 175405211e7..d4003b8034e 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -884,7 +884,7 @@ active_hallucination_weakref = WEAKREF(affected_mob.cause_hallucination(greatest_fear, name, duration = 5 MINUTES, skip_nearby = !overdosed)) else // if they're just some random schmuck, give them random hallucinations - affected_mob.adjust_hallucinations_up_to(4 SECONDS * REM * seconds_per_tick, 20 SECONDS) + affected_mob.adjust_hallucinations_up_to(4 SECONDS * REM * seconds_per_tick, 30 SECONDS) /datum/reagent/drug/syndol/on_mob_end_metabolize(mob/living/affected_mob) . = ..() diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 562ad5d8190..54cdc5bc2cf 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -2640,10 +2640,9 @@ /datum/reagent/bz_metabolites/on_mob_life(mob/living/carbon/target, seconds_per_tick, times_fired) . = ..() - if(target.mind) - var/datum/antagonist/changeling/changeling = IS_CHANGELING(target) - if(changeling) - changeling.adjust_chemicals(-2 * REM * seconds_per_tick) + target.adjust_hallucinations(5 SECONDS * REM * seconds_per_tick) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(target) + changeling?.adjust_chemicals(-2 * REM * seconds_per_tick) /datum/reagent/pax/peaceborg name = "Synthpax" diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm index 00a80ac21b6..938b1ceaf8c 100644 --- a/code/modules/surgery/organs/internal/lungs/_lungs.dm +++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm @@ -378,8 +378,7 @@ /// Too much funny gas, time to get brain damage /obj/item/organ/lungs/proc/too_much_bz(mob/living/carbon/breather, datum/gas_mixture/breath, bz_pp, old_bz_pp) if(bz_pp > BZ_trip_balls_min) - breather.adjust_hallucinations(20 SECONDS) - breather.reagents.add_reagent(/datum/reagent/bz_metabolites, 5) + breather.reagents.add_reagent(/datum/reagent/bz_metabolites, clamp(bz_pp, 1, 5)) if(bz_pp > BZ_brain_damage_min && prob(33)) breather.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3, 150, ORGAN_ORGANIC) diff --git a/strings/hallucination.json b/strings/hallucination.json index d6e13d570d9..e52ba2611f0 100644 --- a/strings/hallucination.json +++ b/strings/hallucination.json @@ -1,9 +1,10 @@ { "suspicion": [ - "@pick(add_name)i'm watching you...", "@pick(add_name)i know what you're doing", + "@pick(add_name)i'm watching you...", "@pick(add_name)what are you hiding?", - "I saw that" + "I saw that", + "I'm watching you" ], "conversation": [ diff --git a/tgstation.dme b/tgstation.dme index 8770518455f..8aacf2a5efa 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3857,6 +3857,7 @@ #include "code\modules\client\preferences\prosthetic_limb.dm" #include "code\modules\client\preferences\prosthetic_organ.dm" #include "code\modules\client\preferences\random.dm" +#include "code\modules\client\preferences\rds_limit.dm" #include "code\modules\client\preferences\runechat.dm" #include "code\modules\client\preferences\scaling_method.dm" #include "code\modules\client\preferences\scarred_eye.dm" @@ -4306,10 +4307,12 @@ #include "code\modules\forensics\forensics_helpers.dm" #include "code\modules\hallucination\_hallucination.dm" #include "code\modules\hallucination\battle.dm" +#include "code\modules\hallucination\blood_flow.dm" #include "code\modules\hallucination\body.dm" #include "code\modules\hallucination\bolted_airlocks.dm" #include "code\modules\hallucination\bubblegum_attack.dm" #include "code\modules\hallucination\delusions.dm" +#include "code\modules\hallucination\eyes_in_dark.dm" #include "code\modules\hallucination\fake_alert.dm" #include "code\modules\hallucination\fake_chat.dm" #include "code\modules\hallucination\fake_death.dm" @@ -4327,6 +4330,7 @@ #include "code\modules\hallucination\shock.dm" #include "code\modules\hallucination\station_message.dm" #include "code\modules\hallucination\stray_bullet.dm" +#include "code\modules\hallucination\telepathy.dm" #include "code\modules\hallucination\xeno_attack.dm" #include "code\modules\holiday\foreign_calendar.dm" #include "code\modules\holiday\holidays.dm" diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/rds_limit.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/rds_limit.tsx new file mode 100644 index 00000000000..fa32abe2d2e --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/rds_limit.tsx @@ -0,0 +1,9 @@ +import { CheckboxInput, FeatureToggle } from '../base'; + +export const rds_limit: FeatureToggle = { + name: 'Unlimit Hallucinations', + description: + 'Checking this box will remove limitations on hallucinations, \ + causing them to be more frequent, intrusive, and (generally) wacky.', + component: CheckboxInput, +};