Files
Bubberstation/code/datums/voice_of_god_command.dm
Penelope Haze 4c2a76ede3 Fix a large number of typos (#89254)
Fixes a very large number of typos. A few of these fixes also extend to
variable names, but only the really egregious ones like "concious".
2025-01-28 22:16:16 +01:00

485 lines
21 KiB
Plaintext

#define COOLDOWN_STUN (120 SECONDS)
#define COOLDOWN_DAMAGE (60 SECONDS)
#define COOLDOWN_MEME (30 SECONDS)
#define COOLDOWN_NONE (10 SECONDS)
/// Used to stop listeners with silly or clown-esque (first) names such as "Honk" or "Flip" from screwing up certain commands.
GLOBAL_DATUM(all_voice_of_god_triggers, /regex)
/// List of all voice of god commands
GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands())
/proc/init_voice_of_god_commands()
. = list()
var/all_triggers
var/separator
for(var/datum/voice_of_god_command/prototype as anything in subtypesof(/datum/voice_of_god_command))
var/init_trigger = initial(prototype.trigger)
if(!init_trigger)
continue
. += new prototype
all_triggers += "[separator][init_trigger]"
separator = "|" // Shouldn't be at the start or end of the regex, or it won't work.
GLOB.all_voice_of_god_triggers = regex(all_triggers, "i")
/*
* The main proc for the voice of god power. it makes the user shout a message in an ominous way,
* The first matching command (from a list of static datums) the listeners must obey,
* and the return value of this proc the cooldown variable of the command dictates. (only relevant for things with cooldowns i guess)
*/
/proc/voice_of_god(message, mob/living/user, list/span_list, base_multiplier = 1, include_speaker = FALSE, forced = null, ignore_spam = FALSE)
var/log_message = uppertext(message)
var/is_cultie = IS_CULTIST(user)
if(LAZYLEN(span_list) && is_cultie)
span_list = list("narsiesmall")
if(!user.say(message, spans = span_list, sanitize = FALSE, ignore_spam = ignore_spam, forced = forced))
return
message = LOWER_TEXT(message)
var/list/mob/living/listeners = list()
//used to check if the speaker specified a name or a job to focus on
var/list/specific_listeners = list()
// string to remove at the end of the following of the following loop, so saying "Burn Mr. Hopkins" doesn't also burn the HoP later when we check jobs.
var/to_remove_string
var/list/candidates = get_hearers_in_view(8, user) - (include_speaker ? null : user)
for(var/mob/living/candidate in candidates)
if(candidate.stat != DEAD && candidate.can_hear())
if(candidate.can_block_magic(MAGIC_RESISTANCE_HOLY|MAGIC_RESISTANCE_MIND, charge_cost = 0))
to_chat(user, span_userdanger("Something's wrong! [candidate] seems to be resisting your commands."))
continue
listeners += candidate
//Let's ensure the listener's name is not matched within another word or command (and vice-versa). e.g. "Saul" in "somersault"
var/their_first_name = first_name(candidate.name)
if(!GLOB.all_voice_of_god_triggers.Find(their_first_name) && findtext(message, regex("(\\L|^)[their_first_name](\\L|$)", "i")))
specific_listeners += candidate //focus on those with the specified name
to_remove_string += "[to_remove_string ? "|" : null][their_first_name]"
continue
var/their_last_name = last_name(candidate.name)
if(their_last_name != their_first_name && !GLOB.all_voice_of_god_triggers.Find(their_last_name) && findtext(message, regex("(\\L|^)[their_last_name](\\L|$)", "i")))
specific_listeners += candidate // Ditto
to_remove_string += "[to_remove_string ? "|" : null][their_last_name]"
if(!listeners.len)
return
if(to_remove_string)
to_remove_string = "(\\L|^)([to_remove_string])(\\L|$)"
message = replacetext(message, regex(to_remove_string, "i"), "")
var/power_multiplier = base_multiplier * (user.mind?.assigned_role.voice_of_god_power || 1)
//Cultists are closer to their gods and are more powerful, but they'll give themselves away
if(is_cultie)
power_multiplier *= 2
//Now get the proper job titles and check for matches.
var/job_message = get_full_job_name(message)
for(var/mob/living/candidate in candidates)
var/their_role = candidate.mind?.assigned_role.title
if(their_role && findtext(job_message, their_role))
specific_listeners |= candidate //focus on those with the specified job. "|=" instead "+=" so "Mrs. Capri the Captain" doesn't get affected twice.
if(specific_listeners.len)
listeners = specific_listeners
power_multiplier *= (1 + (1/specific_listeners.len)) //2x on a single guy, 1.5x on two and so on
for(var/datum/voice_of_god_command/command as anything in GLOB.voice_of_god_commands)
if(findtext(message, command.trigger))
. = command.execute(listeners, user, power_multiplier, message) || command.cooldown
break
if(!forced)
message_admins("[ADMIN_LOOKUPFLW(user)] said '[log_message]' with Voice of God, affecting [english_list(listeners)], with a power multiplier of [power_multiplier].")
user.log_message("said '[log_message]' with Voice of God[forced ? " forced by [forced]" : ""], affecting [english_list(listeners)], with a power multiplier of [power_multiplier].", LOG_GAME, color="red")
SSblackbox.record_feedback("tally", "voice_of_god", 1, log_message)
/// Voice of god command datums that are used in [/proc/voice_of_god()]
/datum/voice_of_god_command
///a text string or regex that triggers the command.
var/trigger
/// Is the trigger supposed to be a regex? If so, convert it to such on New()
var/is_regex = TRUE
/// cooldown variable which is normally returned to [proc/voice_of_god] and used as its return value.
var/cooldown = COOLDOWN_MEME
/datum/voice_of_god_command/New()
if(is_regex)
trigger = regex(trigger)
/*
* What happens when the command is triggered.
* If a return value is set, it'll be used in place of the 'cooldown' var.
* Args:
* * listeners: the list of living mobs who are affected by the command.
* * user: the one who casted Voice of God
* * power_multiplier: multiplies the power of the command, most times.
*/
/datum/voice_of_god_command/proc/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
return
/// This command knocks the listeners down.
/datum/voice_of_god_command/knockdown
trigger = "drop|fall|trip|knockdown"
cooldown = COOLDOWN_STUN
/datum/voice_of_god_command/knockdown/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.Knockdown(4 SECONDS * power_multiplier)
/// This command stops the listeners from moving.
/datum/voice_of_god_command/immobilize
trigger = "stop|wait|stand\\s*still|hold\\s*on|halt"
cooldown = COOLDOWN_STUN
/datum/voice_of_god_command/immobilize/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.Immobilize(4 SECONDS * power_multiplier)
/// This command makes carbon listeners throw up like Mr. Creosote.
/datum/voice_of_god_command/vomit
trigger = "vomit|throw\\s*up|sick"
cooldown = COOLDOWN_STUN
/datum/voice_of_god_command/vomit/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/carbon/target in listeners)
target.vomit(vomit_flags = (MOB_VOMIT_MESSAGE | MOB_VOMIT_HARM), lost_nutrition = (power_multiplier * 10), distance = power_multiplier)
/// This command silences the listeners. Thrice as effective is the user is a mime or curator.
/datum/voice_of_god_command/silence
trigger = "shut\\s*up|silence|be\\s*silent|ssh|quiet|hush"
cooldown = COOLDOWN_STUN
/datum/voice_of_god_command/silence/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
power_multiplier *= user.mind?.assigned_role?.voice_of_god_silence_power || 1
for(var/mob/living/carbon/target in listeners)
target.adjust_silence(20 SECONDS * power_multiplier)
/// This command makes the listeners see others as corgis, carps, skellies etcetera etcetera.
/datum/voice_of_god_command/hallucinate
trigger = "see\\s*the\\s*truth|hallucinate"
/datum/voice_of_god_command/hallucinate/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target in listeners)
target.cause_hallucination( \
get_random_valid_hallucination_subtype(/datum/hallucination/delusion/preset), \
"voice of god", \
duration = 15 SECONDS * power_multiplier, \
affects_us = FALSE, \
affects_others = TRUE, \
skip_nearby = FALSE, \
)
/// This command wakes up the listeners.
/datum/voice_of_god_command/wake_up
trigger = "wake\\s*up|awaken"
cooldown = COOLDOWN_DAMAGE
/datum/voice_of_god_command/wake_up/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.SetSleeping(0)
/// This command heals the listeners for 10 points of total damage.
/datum/voice_of_god_command/heal
trigger = "live|heal|survive|mend|life|heroes\\s*never\\s*die"
cooldown = COOLDOWN_DAMAGE
/datum/voice_of_god_command/heal/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.heal_overall_damage(10 * power_multiplier, 10 * power_multiplier)
/// This command applies 15 points of brute damage to the listeners. There's subtle theological irony in this being more powerful than healing.
/datum/voice_of_god_command/brute
trigger = "die|suffer|hurt|pain|death"
cooldown = COOLDOWN_DAMAGE
/datum/voice_of_god_command/brute/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.apply_damage(15 * power_multiplier, def_zone = BODY_ZONE_CHEST, wound_bonus = CANT_WOUND)
/// This command makes carbon listeners bleed from a random body part.
/datum/voice_of_god_command/bleed
trigger = "bleed|there\\s*will\\s*be\\s*blood"
cooldown = COOLDOWN_DAMAGE
/datum/voice_of_god_command/bleed/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/carbon/human/target in listeners)
var/obj/item/bodypart/chosen_part = pick(target.bodyparts)
chosen_part.adjustBleedStacks(5)
/// This command sets the listeners ablaze.
/datum/voice_of_god_command/burn
trigger = "burn|ignite"
cooldown = COOLDOWN_DAMAGE
/datum/voice_of_god_command/burn/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.adjust_fire_stacks(1 * power_multiplier)
target.ignite_mob()
/// This command heats the listeners up like boiling water.
/datum/voice_of_god_command/hot
trigger = "heat|hot|hell"
cooldown = COOLDOWN_DAMAGE
/datum/voice_of_god_command/hot/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.adjust_bodytemperature(50 * power_multiplier)
/// This command cools the listeners down like freezing water.
/datum/voice_of_god_command/cold
trigger = "cold|chill|freeze"
cooldown = COOLDOWN_DAMAGE
/datum/voice_of_god_command/cold/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.adjust_bodytemperature(-50 * power_multiplier)
/// This command throws the listeners away from the user.
/datum/voice_of_god_command/repulse
trigger = "shoo|go\\s*away|leave\\s*me\\s*alone|begone|flee|fus\\s*ro\\s*dah|get\\s*away|repulse"
cooldown = COOLDOWN_DAMAGE
/datum/voice_of_god_command/repulse/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
var/throwtarget = get_edge_target_turf(user, get_dir(user, get_step_away(target, user)))
target.throw_at(throwtarget, 3 * power_multiplier, 1 * power_multiplier)
/// This command throws the listeners at the user.
/datum/voice_of_god_command/attract
trigger = "come\\s*here|come\\s*to\\s*me|get\\s*over\\s*here|attract"
cooldown = COOLDOWN_DAMAGE
/datum/voice_of_god_command/attract/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.throw_at(get_step_towards(user, target), 3 * power_multiplier, 1 * power_multiplier)
/// This command forces the listeners to say their true name (so masks and hoods won't help).
/// Basic and simple mobs who are forced to state their name and don't have one already will... reveal their actual one!
/datum/voice_of_god_command/who_are_you
trigger = "who\\s*are\\s*you|say\\s*your\\s*name|state\\s*your\\s*name|identify"
/datum/voice_of_god_command/who_are_you/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
var/iteration = 1
for(var/mob/living/target as anything in listeners)
addtimer(CALLBACK(src, PROC_REF(state_name), target), 0.5 SECONDS * iteration)
iteration++
///just states the target's name, but also includes the renaming funny.
/datum/voice_of_god_command/who_are_you/proc/state_name(mob/living/target)
if(QDELETED(target))
return
var/gold_core_spawnable = NO_SPAWN
if(isbasicmob(target))
var/mob/living/basic/basic_bandy = target
gold_core_spawnable = basic_bandy.gold_core_spawnable
else if(isanimal(target))
var/mob/living/simple_animal/simple_sandy = target
gold_core_spawnable = simple_sandy.gold_core_spawnable
if(target.name == initial(target.name) && gold_core_spawnable == FRIENDLY_SPAWN)
var/canonical_deep_lore_name
switch(target.gender)
if(MALE)
canonical_deep_lore_name = pick(GLOB.first_names_male)
if(FEMALE)
canonical_deep_lore_name = pick(GLOB.first_names_female)
else
canonical_deep_lore_name = pick(GLOB.first_names)
target.fully_replace_character_name(target.real_name, canonical_deep_lore_name)
target.say(target.real_name)
/// This command forces the listeners to say the user's name
/datum/voice_of_god_command/say_my_name
trigger = "say\\s*my\\s*name|who\\s*am\\s*i"
/datum/voice_of_god_command/say_my_name/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
var/iteration = 1
var/regex/smartass_regex = regex(@"^say my name[.!]*$")
for(var/mob/living/target as anything in listeners)
var/to_say = user.name
// 0.1% chance to be a smartass
if(findtext(LOWER_TEXT(message), smartass_regex) && prob(0.1))
to_say = "My name"
addtimer(CALLBACK(target, TYPE_PROC_REF(/atom/movable, say), to_say), 0.5 SECONDS * iteration)
iteration++
/// This command forces the listeners to say "Who's there?".
/datum/voice_of_god_command/knock_knock
trigger = "knock\\s*knock"
/datum/voice_of_god_command/knock_knock/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
var/iteration = 1
for(var/mob/living/target as anything in listeners)
addtimer(CALLBACK(target, TYPE_PROC_REF(/atom/movable, say), "Who's there?"), 0.5 SECONDS * iteration)
iteration++
/// This command forces silicon listeners to state all their laws.
/datum/voice_of_god_command/state_laws
trigger = "state\\s*(your)?\\s*laws"
/datum/voice_of_god_command/state_laws/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
var/iteration = 0
for(var/mob/living/silicon/target in listeners)
addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living/silicon, statelaws), TRUE), (3 SECONDS * iteration) + 0.5 SECONDS)
iteration++
/// This command forces the listeners to take step in a direction chosen by the user, otherwise a random cardinal one.
/datum/voice_of_god_command/move
trigger = "move|walk"
var/static/up_words = regex("up|north|fore")
var/static/down_words = regex("down|south|aft")
var/static/left_words = regex("left|west|port")
var/static/right_words = regex("right|east|starboard")
/datum/voice_of_god_command/move/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
var/iteration = 1
var/direction
if(findtext(message, up_words))
direction = NORTH
else if(findtext(message, down_words))
direction = SOUTH
else if(findtext(message, left_words))
direction = WEST
else if(findtext(message, right_words))
direction = EAST
for(var/mob/living/target as anything in listeners)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_step), target, direction || pick(GLOB.cardinals)), 1 SECONDS * (iteration - 1))
iteration++
/// This command forces the listeners to switch to walk intent.
/datum/voice_of_god_command/walk
trigger = "slow\\s*down"
/datum/voice_of_god_command/walk/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
if(target.move_intent != MOVE_INTENT_WALK)
target.toggle_move_intent()
/// This command forces the listeners to switch to run intent.
/datum/voice_of_god_command/run
trigger = "run"
is_regex = FALSE
/datum/voice_of_god_command/walk/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
if(target.move_intent != MOVE_INTENT_RUN)
target.toggle_move_intent()
/// This command turns the listeners' throw mode on.
/datum/voice_of_god_command/throw_catch
trigger = "throw|catch"
/datum/voice_of_god_command/throw_catch/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/carbon/target in listeners)
target.throw_mode_on(THROW_MODE_TOGGLE)
/// This command forces the listeners to say a brain damage line.
/datum/voice_of_god_command/speak
trigger = "speak|say\\s*something"
/datum/voice_of_god_command/speak/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
var/iteration = 1
for(var/mob/living/target in listeners)
addtimer(CALLBACK(target, TYPE_PROC_REF(/atom/movable, say), pick_list_replacements(BRAIN_DAMAGE_FILE, "brain_damage")), 0.5 SECONDS * iteration)
iteration++
/// This command forces the listeners to get the fuck up, resetting all stuns.
/datum/voice_of_god_command/getup
trigger = "get\\s*up"
cooldown = COOLDOWN_DAMAGE
/datum/voice_of_god_command/getup/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.set_resting(FALSE)
target.SetAllImmobility(0)
/// This command forces each listener to buckle to a chair found on the same tile.
/datum/voice_of_god_command/sit
trigger = "sit"
is_regex = FALSE
/datum/voice_of_god_command/sit/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
var/obj/structure/chair/chair = locate(/obj/structure/chair) in get_turf(target)
chair?.buckle_mob(target)
/// This command forces each listener to unbuckle from whatever they are buckled to.
/datum/voice_of_god_command/stand
trigger = "stand"
is_regex = FALSE
/datum/voice_of_god_command/stand/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.buckled?.unbuckle_mob(target)
/// This command forces the listener to do the jump emote 3/4 of the times or reply "HOW HIGH?!!".
/datum/voice_of_god_command/jump
trigger = "jump"
is_regex = FALSE
/datum/voice_of_god_command/jump/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
var/iteration = 1
for(var/mob/living/target as anything in listeners)
if(prob(25))
addtimer(CALLBACK(target, TYPE_PROC_REF(/atom/movable, say), "HOW HIGH?!!"), 0.5 SECONDS * iteration)
else
addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living/, emote), "jump"), 0.5 SECONDS * iteration)
iteration++
///This command plays a bikehorn sound after 2 seconds and a half have passed, and also slips listeners if the user is a clown.
/datum/voice_of_god_command/honk
trigger = "ho+nk"
/datum/voice_of_god_command/honk/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), get_turf(user), 'sound/items/bikehorn.ogg', 300, 1), 2.5 SECONDS)
if(is_clown_job(user.mind?.assigned_role))
. = COOLDOWN_STUN //it slips.
for(var/mob/living/carbon/target in listeners)
target.slip(14 SECONDS * power_multiplier)
///This command spins the listeners 1800° degrees clockwise.
/datum/voice_of_god_command/multispin
trigger = "like\\s*a\\s*record\\s*baby|right\\s*round"
/datum/voice_of_god_command/multispin/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/target as anything in listeners)
target.SpinAnimation(speed = 10, loops = 5)
/// Supertype of all those commands that make people emote and nothing else. Fuck copypasta.
/datum/voice_of_god_command/emote
/// The emote to run.
var/emote_name = "dance"
/datum/voice_of_god_command/emote/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
var/iteration = 1
for(var/mob/living/target as anything in listeners)
addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living/, emote), emote_name), 0.5 SECONDS * iteration)
iteration++
/datum/voice_of_god_command/emote/flip
trigger = "flip|rotate|revolve|roll|somersault"
emote_name = "flip"
/datum/voice_of_god_command/emote/dance
trigger = "dance"
is_regex = FALSE
/datum/voice_of_god_command/emote/salute
trigger = "salute"
emote_name = "salute"
is_regex = FALSE
/datum/voice_of_god_command/emote/play_dead
trigger = "play\\s*dead"
emote_name = "deathgasp"
/datum/voice_of_god_command/emote/clap
trigger = "clap|applaud"
emote_name = "clap"
#undef COOLDOWN_STUN
#undef COOLDOWN_DAMAGE
#undef COOLDOWN_MEME
#undef COOLDOWN_NONE