diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm index 070837bee97..4b4417607b1 100644 --- a/code/__DEFINES/say.dm +++ b/code/__DEFINES/say.dm @@ -15,6 +15,7 @@ #define MODE_ALIEN "alientalk" #define MODE_HOLOPAD "holopad" #define MODE_CHANGELING "changeling" +#define MODE_VOCALCORDS "cords" //Spans. Robot speech, italics, etc. Applied in compose_message(). #define SPAN_ROBOT "robot" diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 1f2d86c97d8..37dfc5afb1a 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -206,6 +206,20 @@ if(start) return findtextEx(text, suffix, start, null) +//Checks if any of a given list of needles is in the haystack +/proc/text_in_list(haystack, list/needle_list) + for(var/needle in needle_list) + if(findtext(haystack, needle)) + return 1 + return 0 + +//Like above, but case sensitive +/proc/text_in_list_case(haystack, list/needle_list) + for(var/needle in needle_list) + if(findtextEx(haystack, needle)) + return 1 + return 0 + //Adds 'u' number of zeros ahead of the text 't' /proc/add_zero(t, u) while (length(t) < u) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index b108c7c47b9..6459bf34f12 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -17,6 +17,7 @@ var/list/department_radio_keys = list( ":o" = "AI Private", "#o" = "AI Private", ".o" = "AI Private", ":g" = "changeling", "#g" = "changeling", ".g" = "changeling", ":y" = "Centcom", "#y" = "Centcom", ".y" = "Centcom", + ":x" = "cords", "#x" = "cords", ".x" = "cords", ":R" = "right hand", "#R" = "right hand", ".R" = "right hand", ":L" = "left hand", "#L" = "left hand", ".L" = "left hand", @@ -36,6 +37,7 @@ var/list/department_radio_keys = list( ":O" = "AI Private", "#O" = "AI Private", ".O" = "AI Private", ":G" = "changeling", "#G" = "changeling", ".G" = "changeling", ":Y" = "Centcom", "#Y" = "Centcom", ".Y" = "Centcom", + ":X" = "cords", "#X" = "cords", ".X" = "cords", //kinda localization -- rastaf0 //same keys as above, but on russian keyboard layout. This file uses cp1251 as encoding. @@ -243,6 +245,13 @@ var/list/crit_allowed_modes = list(MODE_WHISPER,MODE_CHANGELING,MODE_ALIEN) if(hivecheck()) alien_talk(message) return 1 + if(message_mode == MODE_VOCALCORDS) + if(iscarbon(src)) + var/mob/living/carbon/C = src + var/obj/item/organ/vocal_cords/V = C.getorganslot("vocal_cords") + if(V.can_speak_with()) + C.say(V.speak_with(message), spans = V.spans) + return 1 return 0 /mob/living/proc/treat_message(message) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 0653bf47470..ef94ca5c846 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -46,7 +46,7 @@ Difficulty: Very Hard del_on_death = 1 medal_type = MEDAL_PREFIX score_type = COLOSSUS_SCORE - loot = list(/obj/machinery/anomalous_crystal/random, /obj/item/organ/colossus) + loot = list(/obj/machinery/anomalous_crystal/random, /obj/item/organ/vocal_cords/colossus) butcher_results = list(/obj/item/weapon/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30) deathmessage = "disintegrates, leaving a glowing core in its wake." death_sound = 'sound/magic/demon_dies.ogg' diff --git a/code/modules/surgery/organs/lavaland.dm b/code/modules/surgery/organs/lavaland.dm index 3be4be79079..a8f16a0853a 100644 --- a/code/modules/surgery/organs/lavaland.dm +++ b/code/modules/surgery/organs/lavaland.dm @@ -1,21 +1,27 @@ -/obj/item/organ/colossus +/obj/item/organ/vocal_cords/colossus name = "divine vocal cords" icon_state = "voice_of_god" zone = "mouth" slot = "vocal_cords" actions_types = list(/datum/action/item_action/organ_action/colossus) - -/datum/action/item_action/organ_action/colossus - name = "Voice of God" - var/next_command = null + var/next_command = 0 var/cooldown_stun = 900 var/cooldown_damage = 600 var/cooldown_meme = 300 var/cooldown_none = 150 var/base_multiplier = 1 + spans = list("colossus","yell") + +/datum/action/item_action/organ_action/colossus + name = "Voice of God" + var/obj/item/organ/vocal_cords/colossus/cords = null + +/datum/action/item_action/organ_action/colossus/New() + ..() + cords = target /datum/action/item_action/organ_action/colossus/IsAvailable() - if(world.time < next_command) + if(world.time < cords.next_command) return 0 if(!owner) return 0 @@ -28,20 +34,36 @@ /datum/action/item_action/organ_action/colossus/Trigger() . = ..() + if(!IsAvailable()) + if(world.time < cords.next_command) + owner << "You must wait [(cords.next_command - world.time)/10] seconds before Speaking again." + return var/command = stripped_input(owner, "Speak with the Voice of God", "Command", max_length = 140) if(!command) return + owner.say(".x[command]") + +/obj/item/organ/vocal_cords/colossus/can_speak_with() + if(world.time < next_command) + owner << "You must wait [(next_command - world.time)/10] seconds before Speaking again." + return 0 + if(!owner) + return 0 + if(!owner.can_speak()) + owner << "You are unable to speak!" + return 0 + if(owner.stat) + return 0 + return 1 + +/obj/item/organ/vocal_cords/colossus/speak_with(message) + var/spoken = uppertext(message) + playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 300, 1, 5) + var/mob/living/list/listeners = list() for(var/mob/living/L in get_hearers_in_view(8, owner)) if(!L.ear_deaf && L != owner && L.stat != DEAD) listeners += L - if(!IsAvailable()) - if(world.time < next_command) - owner << "You must wait [(next_command - world.time)/10] seconds before Speaking again." - return - var/spoken = uppertext(command) - owner.say(spoken, spans = list("colossus","yell")) - playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 300, 1, 5) if(!listeners.len) next_command = world.time + cooldown_none @@ -63,92 +85,92 @@ for(var/V in listeners) var/mob/living/L = V var/start - if(L.mind && L.mind.devilinfo && findtext(command, L.mind.devilinfo.truename)) - start = findtext(command, L.mind.devilinfo.truename) + if(L.mind && L.mind.devilinfo && findtext(message, L.mind.devilinfo.truename)) + start = findtext(message, L.mind.devilinfo.truename) listeners = list(L) power_multiplier *= 5 //if you're a devil and god himself addressed you, you fucked up //Cut out the name so it doesn't trigger commands - command = copytext(command, 0, start)+copytext(command, start + length(L.mind.devilinfo.truename), length(command) + 1) + message = copytext(message, 0, start)+copytext(message, start + length(L.mind.devilinfo.truename), length(message) + 1) break - else if(findtext(command, L.real_name)) - start = findtext(command, L.real_name) + else if(findtext(message, L.real_name)) + start = findtext(message, L.real_name) listeners = list(L) //focus on a particular person power_multiplier *= 2 //Cut out the name so it doesn't trigger commands - command = copytext(command, 0, start)+copytext(command, start + length(L.real_name), length(command) + 1) + message = copytext(message, 0, start)+copytext(message, start + length(L.real_name), length(message) + 1) break //STUN - if(findtext(command, "stop") || findtext(command, "wait") || findtext(command, "stand still") || findtext(command, "hold on") || findtext(command, "halt")) + if(text_in_list(message, list("stop","wait","stand still","hold on","halt"))) for(var/V in listeners) var/mob/living/L = V L.Stun(3 * power_multiplier) next_command = world.time + cooldown_stun //WEAKEN - else if(findtext(command, "drop") || findtext(command, "fall")) + else if(text_in_list(message, list("drop","fall"))) for(var/V in listeners) var/mob/living/L = V L.Weaken(3 * power_multiplier) next_command = world.time + cooldown_stun //SLEEP - else if(findtext(command, "sleep")) + else if(text_in_list(message, list("sleep"))) for(var/V in listeners) var/mob/living/L = V L.Sleeping(3 * power_multiplier) next_command = world.time + cooldown_stun //VOMIT - else if(findtext(command, "vomit") || findtext(command, "throw up")) + else if(text_in_list(message, list("vomit","throw up"))) for(var/mob/living/carbon/C in listeners) C.vomit(10 * power_multiplier) next_command = world.time + cooldown_stun //SILENCE - else if(findtext(command, "shut up") || findtext(command, "silence") || findtext(command, "ssh") || findtext(command, "quiet")) + else if(text_in_list(message, list("shut up","silence","ssh","quiet"))) for(var/mob/living/carbon/C in listeners) - if(owner.mind && (owner.mind.assigned_role == "Librarian" || owner.mind.assigned_role == "Mime") + if(owner.mind && (owner.mind.assigned_role == "Librarian" || owner.mind.assigned_role == "Mime")) power_multiplier *= 3 C.silent += (10 * power_multiplier) next_command = world.time + cooldown_stun //HALLUCINATE - else if(findtext(command, "see the truth") || findtext(command, "hallucinate")) + else if(text_in_list(message, list("see the truth","hallucinate"))) for(var/V in listeners) var/mob/living/L = V new /obj/effect/hallucination/delusion(get_turf(L),L,duration=150 * power_multiplier,skip_nearby=0) next_command = world.time + cooldown_damage //WAKE UP - else if(findtext(command, "wake up") || findtext(command, "awaken")) + else if(text_in_list(message, list("wake up","awaken"))) for(var/V in listeners) var/mob/living/L = V L.SetSleeping(0) next_command = world.time + cooldown_damage //HEAL - else if(findtext(command, "live") || findtext(command, "heal") || findtext(command, "survive") || findtext(command, "mend") || findtext(command, "heroes never die")) + else if(text_in_list(message, list("live","heal","survive","mend","heroes never die"))) for(var/V in listeners) var/mob/living/L = V L.heal_overall_damage(10 * power_multiplier, 10 * power_multiplier, 0, 0) next_command = world.time + cooldown_damage //BRUTE DAMAGE - else if(findtext(command, "die") || findtext(command, "suffer")) + else if(text_in_list(message, list("die","suffer"))) for(var/V in listeners) var/mob/living/L = V L.apply_damage(15 * power_multiplier, def_zone = "chest") next_command = world.time + cooldown_damage //BLEED - else if(findtext(command, "bleed")) + else if(text_in_list(message, list("bleed"))) for(var/mob/living/carbon/human/H in listeners) H.bleed_rate += (5 * power_multiplier) next_command = world.time + cooldown_damage //FIRE - else if(findtext(command, "burn") || findtext(command, "ignite") || findtext(command, "hell")) + else if(text_in_list(message, list("burn","ignite","hell"))) for(var/V in listeners) var/mob/living/L = V L.adjust_fire_stacks(1 * power_multiplier) @@ -156,7 +178,7 @@ next_command = world.time + cooldown_damage //REPULSE - else if(findtext(command, "shoo") || findtext(command, "go away") || findtext(command, "leave me alone") || findtext(command, "begone") || findtext(command, "flee") || findtext(command, "fus ro dah")) + else if(text_in_list(message, list("shoo","go away","leave me alone","begone","flee","fus ro dah"))) for(var/V in listeners) var/mob/living/L = V var/throwtarget = get_edge_target_turf(owner, get_dir(owner, get_step_away(L, owner))) @@ -164,7 +186,7 @@ next_command = world.time + cooldown_damage //WHO ARE YOU? - else if(findtext(command, "who are you") || findtext(command, "say your name") || findtext(command, "state your name") || findtext(command, "identify")) + else if(text_in_list(message, list("who are you","say your name","state your name","identify"))) for(var/V in listeners) var/mob/living/L = V if(L.mind && L.mind.devilinfo) @@ -174,85 +196,85 @@ next_command = world.time + cooldown_meme //SAY MY NAME - else if(findtext(command, "say my name")) + else if(text_in_list(message, list("say my name"))) for(var/V in listeners) var/mob/living/L = V L.say("[owner.name]!") //"Unknown!" next_command = world.time + cooldown_meme //KNOCK KNOCK - else if(findtext(command, "knock knock")) + else if(text_in_list(message, list("knock knock"))) for(var/V in listeners) var/mob/living/L = V L.say("Who's there?") next_command = world.time + cooldown_meme - //MOVE - else if(findtext(command, "state laws") || findtext(command, "state your laws")) + //STATE LAWS + else if(text_in_list(message, list("state laws","state your laws"))) for(var/mob/living/silicon/S in listeners) S.statelaws() next_command = world.time + cooldown_meme //MOVE - else if(findtext(command, "move")) + else if(text_in_list(message, list("move"))) for(var/V in listeners) var/mob/living/L = V step(L, pick(cardinal)) next_command = world.time + cooldown_meme //WALK - else if(findtext(command, "walk") || findtext(command, "slow down")) + else if(text_in_list(message, list("walk","slow down"))) for(var/V in listeners) var/mob/living/L = V L.m_intent = MOVE_INTENT_WALK next_command = world.time + cooldown_meme //RUN - else if(findtext(command, "run")) + else if(text_in_list(message, list("run"))) for(var/V in listeners) var/mob/living/L = V L.m_intent = MOVE_INTENT_RUN next_command = world.time + cooldown_meme //HELP INTENT - else if(findtext(command, "help")) + else if(text_in_list(message, list("help"))) for(var/mob/living/carbon/human/H in listeners) H.a_intent_change(INTENT_HELP) next_command = world.time + cooldown_meme //DISARM INTENT - else if(findtext(command, "disarm")) + else if(text_in_list(message, list("disarm"))) for(var/mob/living/carbon/human/H in listeners) H.a_intent_change(INTENT_DISARM) next_command = world.time + cooldown_meme //GRAB INTENT - else if(findtext(command, "grab")) + else if(text_in_list(message, list("grab"))) for(var/mob/living/carbon/human/H in listeners) H.a_intent_change(INTENT_GRAB) next_command = world.time + cooldown_meme //HARM INTENT - else if(findtext(command, "harm") || findtext(command, "fight")) + else if(text_in_list(message, list("harm","fight"))) for(var/mob/living/carbon/human/H in listeners) H.a_intent_change(INTENT_HARM) next_command = world.time + cooldown_meme //THROW/CATCH - else if(findtext(command, "throw") || findtext(command, "catch")) + else if(text_in_list(message, list("throw","catch"))) for(var/mob/living/carbon/C in listeners) C.throw_mode_on() next_command = world.time + cooldown_meme //FLIP - else if(findtext(command, "flip") || findtext(command, "rotate") || findtext(command, "revolve") || findtext(command, "roll") || findtext(command, "somersault")) + else if(text_in_list(message, list("flip","rotate","revolve","roll","somersault"))) for(var/V in listeners) var/mob/living/L = V L.emote("flip") next_command = world.time + cooldown_meme //REST - else if(findtext(command, "rest")) + else if(text_in_list(message, list("rest"))) for(var/V in listeners) var/mob/living/L = V if(!L.resting) @@ -260,7 +282,7 @@ next_command = world.time + cooldown_meme //GET UP - else if(findtext(command, "get up")) + else if(text_in_list(message, list("get up"))) for(var/V in listeners) var/mob/living/L = V if(L.resting) @@ -271,7 +293,7 @@ next_command = world.time + cooldown_damage //SIT - else if(findtext(command, "sit")) + else if(text_in_list(message, list("sit"))) for(var/V in listeners) var/mob/living/L = V for(var/obj/structure/chair/chair in get_turf(L)) @@ -280,7 +302,7 @@ next_command = world.time + cooldown_meme //STAND UP - else if(findtext(command, "stand")) + else if(text_in_list(message, list("stand"))) for(var/V in listeners) var/mob/living/L = V if(L.buckled && istype(L.buckled, /obj/structure/chair)) @@ -288,14 +310,14 @@ next_command = world.time + cooldown_meme //DANCE - else if(findtext(command, "dance")) + else if(text_in_list(message, list("dance"))) for(var/V in listeners) var/mob/living/L = V L.emote("dance") next_command = world.time + cooldown_meme //JUMP - else if(findtext(command, "jump")) + else if(text_in_list(message, list("jump"))) for(var/V in listeners) var/mob/living/L = V L.say("HOW HIGH?!!") @@ -303,28 +325,28 @@ next_command = world.time + cooldown_meme //SALUTE - else if(findtext(command, "salute")) + else if(text_in_list(message, list("salute"))) for(var/V in listeners) var/mob/living/L = V L.emote("salute") next_command = world.time + cooldown_meme //PLAY DEAD - else if(findtext(command, "play dead")) + else if(text_in_list(message, list("play dead"))) for(var/V in listeners) var/mob/living/L = V L.emote("deathgasp") next_command = world.time + cooldown_meme //PLEASE CLAP - else if(findtext(command, "clap") || findtext(command, "applaud")) + else if(text_in_list(message, list("clap","applaud"))) for(var/V in listeners) var/mob/living/L = V L.emote("clap") next_command = world.time + cooldown_meme //HONK - else if(findtext(command, "honk")) + else if(text_in_list(message, list("honk"))) playsound(get_turf(owner), "sound/items/bikehorn.ogg", 300, 1) if(owner.mind && owner.mind.assigned_role == "Clown") for(var/mob/living/carbon/C in listeners) @@ -332,7 +354,7 @@ next_command = world.time + cooldown_meme //RIGHT ROUND - else if(findtext(command, "like a record baby")) + else if(text_in_list(message, list("like a record baby"))) for(var/V in listeners) var/mob/living/L = V L.SpinAnimation(speed = 10, loops = 5) @@ -341,3 +363,5 @@ else next_command = world.time + cooldown_none + return spoken + diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 02bccedf315..16e6c8abe31 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -693,3 +693,16 @@ if(inflamed) S.reagents.add_reagent("????", 5) return S + +/obj/item/organ/vocal_cords //organs that are activated through speech with the :x channel + name = "vocal cords" + icon_state = "appendix" + zone = "mouth" + slot = "vocal_cords" + var/list/spans = null + +/obj/item/organ/vocal_cords/proc/can_speak_with() //if there is any limitation to speaking with these cords + return TRUE + +/obj/item/organ/vocal_cords/proc/speak_with(message) //do what the organ does and modify speech if needed + return message