diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index b86d5e613b5..5b189df3aed 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -1,20 +1,22 @@ /* These defines are specific to the atom/flags bitmask */ +#define ALL ~0 //For convenience. +#define NONE 0 + //FLAGS BITMASK #define STOPSPRESSUREDMAGE 1 //This flag is used on the flags variable for SUIT and HEAD items which stop pressure damage. Note that the flag 1 was previous used as ONBACK, so it is possible for some code to use (flags & 1) when checking if something can be put on your back. Replace this code with (inv_flags & SLOT_BACK) if you see it anywhere //To successfully stop you taking all pressure damage you must have both a suit and head item with this flag. -#define NODROP 2 // This flag makes it so that an item literally cannot be removed at all, or at least that's how it should be. Only deleted. -#define NOBLUDGEON 4 // when an item has this it produces no "X has been hit by Y with Z" message in the default attackby() -#define MASKINTERNALS 8 // mask allows internals -//#define SUITSPACE 8 // suit protects against space -//#define USEDELAY 16 // For adding extra delay to heavy items, not currently used -#define NOSHIELD 32 // weapon not affected by shield -#define CONDUCT 64 // conducts electricity (metal etc.) -#define ABSTRACT 128 // for all things that are technically items but used for various different stuff, made it 128 because it could conflict with other flags other way -#define FPRINT 256 // takes a fingerprint -#define ON_BORDER 512 // item has priority to check when entering or leaving +#define NODROP 2 // This flag makes it so that an item literally cannot be removed at all, or at least that's how it should be. Only deleted. +#define NOBLUDGEON 4 // when an item has this it produces no "X has been hit by Y with Z" message in the default attackby() +#define MASKINTERNALS 8 // mask allows internals +#define HEAR 16 // This flag is what recursive_hear_check() uses to determine wether to add an item to the hearer list or not. +#define NOSHIELD 32 // weapon not affected by shield +#define CONDUCT 64 // conducts electricity (metal etc.) +#define ABSTRACT 128 // for all things that are technically items but used for various different stuff, made it 128 because it could conflict with other flags other way +#define FPRINT 256 // takes a fingerprint +#define ON_BORDER 512 // item has priority to check when entering or leaving #define GLASSESCOVERSEYES 1024 @@ -61,4 +63,14 @@ #define NOBREATH 256 #define NOGUNS 512 #define NOBLOOD 1024 -#define NOFIRE 2048 \ No newline at end of file +#define NOFIRE 2048 + +/* + These defines are used specifically with the atom/movable/languages bitmask. + They are used in atom/movable/Hear() and atom/movable/say() to determine whether hearers can understand a message. +*/ +#define HUMAN 1 +#define MONKEY 2 +#define ALIEN 4 +#define ROBOT 8 +#define SLIME 16 diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 2210414cdad..440a907e527 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -32,7 +32,7 @@ // Like view but bypasses luminosity check -/proc/hear(var/range, var/atom/source) +/proc/get_hear(var/range, var/atom/source) var/lum = source.luminosity source.luminosity = 6 @@ -131,13 +131,29 @@ return turfs +//This is the new version of recursive_mob_check, used for say(). +//The other proc was left intact because morgue trays use it. +/proc/recursive_hear_check(var/atom/O) + var/list/processing_list = list(O) + var/list/processed_list = list() + var/list/found_mobs = list() -//var/debug_mob = 0 + while(processing_list.len) + var/atom/A = processing_list[1] + if(A.flags & HEAR) + found_mobs |= A + + for(var/atom/B in A) + if(!processed_list[B]) + processing_list |= B + + processing_list.Cut(1, 2) + processed_list[A] = A + + return found_mobs // Better recursive loop, technically sort of not actually recursive cause that shit is retarded, enjoy. -//No need for a recursive limit either - - +//No need for a recursive limit either /proc/recursive_mob_check(var/atom/O,var/client_check=1,var/sight_check=1,var/include_radio=1) var/list/processing_list = list(O) @@ -177,25 +193,22 @@ return found_mobs - -/proc/get_mobs_in_view(var/R, var/atom/source) - // Returns a list of mobs in range of R from source. Used in radio and say code. - + +/proc/get_hearers_in_view(var/R, var/atom/source) + // Returns a list of hearers in range of R from source. Used in saycode. var/turf/T = get_turf(source) var/list/hear = list() if(!T) return hear - var/list/range = hear(R, T) - + var/list/range = get_hear(R, T) for(var/atom/movable/A in range) - hear |= recursive_mob_check(A, 1, 0, 1) + hear |= recursive_hear_check(A) return hear - /proc/get_mobs_in_radio_ranges(var/list/obj/item/device/radio/radios) set background = BACKGROUND_ENABLED @@ -208,7 +221,7 @@ if(R) var/turf/speaker = get_turf(R) if(speaker) - for(var/turf/T in hear(R.canhear_range,speaker)) + for(var/turf/T in get_hear(R.canhear_range,speaker)) speaker_coverage[T] = T @@ -222,6 +235,7 @@ . |= M return . + #define SIGN(X) ((X<0)?-1:1) /proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) @@ -255,6 +269,7 @@ return 1 #undef SIGN + /proc/isInSight(var/atom/A, var/atom/B) var/turf/Aturf = get_turf(A) var/turf/Bturf = get_turf(B) @@ -268,6 +283,7 @@ else return 0 + /proc/get_cardinal_step_away(atom/start, atom/finish) //returns the position of a step from start away from finish, in one of the cardinal directions //returns only NORTH, SOUTH, EAST, or WEST var/dx = finish.x - start.x diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index e2ea79d57b1..24cb285cb27 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -283,6 +283,7 @@ Turf and target are seperate in case you want to teleport some distance from a t //Turns 1479 into 147.9 /proc/format_frequency(var/f) + f = text2num(f) return "[round(f / 10)].[f % 10]" diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm index bb806676109..3526359eadb 100644 --- a/code/datums/diseases/transformation.dm +++ b/code/datums/diseases/transformation.dm @@ -58,7 +58,6 @@ var/mob/living/new_mob = new new_form(affected_mob.loc) if(istype(new_mob)) new_mob.a_intent = "harm" - new_mob.universal_speak = 1 if(affected_mob.mind) affected_mob.mind.transfer_to(new_mob) else diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 22a06b41da6..48bc30e5b41 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -10,6 +10,7 @@ var/throw_range = 7 var/moved_recently = 0 var/mob/pulledby = null + var/languages = 0 //For say() and Hear() glide_size = 8 /atom/movable/Move() diff --git a/code/game/communications.dm b/code/game/communications.dm index 7f724237cf7..ac4576042c2 100644 --- a/code/game/communications.dm +++ b/code/game/communications.dm @@ -60,6 +60,31 @@ If receiving object don't know right key, it must ignore encrypted signal in its receive_signal. */ +/* the radio controller is a confusing piece of shit and didnt work + so i made radios not use the radio controller. +*/ +var/list/all_radios = list() +/proc/add_radio(var/obj/item/radio, freq) + if(!freq || !radio) + return + if(!all_radios["[freq]"]) + all_radios["[freq]"] = list(radio) + return freq + + all_radios["[freq]"] |= radio + return freq + +/proc/remove_radio(var/obj/item/radio, freq) + if(!freq || !radio) + return + if(!all_radios["[freq]"]) + return + + all_radios["[freq]"] -= radio + +/proc/remove_radio_all(var/obj/item/radio) + for(var/freq in all_radios) + all_radios["[freq]"] -= radio /* Frequency range: 1200 to 1600 @@ -109,8 +134,23 @@ var/list/radiochannels = list( "Syndicate" = 1213, "Supply" = 1347, "Service" = 1349, - "AI Private" = 1447, + "AI Private" = 1447 ) + +var/list/radiochannelsreverse = list( + "1459" = "Common", + "1351" = "Science", + "1353" = "Command", + "1355" = "Medical", + "1357" = "Engineering", + "1359" = "Security", + "1441" = "Deathsquad", + "1213" = "Syndicate", + "1347" = "Supply", + "1349" = "Service", + "1447" = "AI Private" +) + //depenging helpers var/const/SYND_FREQ = 1213 //nuke op frequency, coloured dark brown in chat window var/const/SUPP_FREQ = 1347 //supply, coloured light brown in chat window @@ -129,7 +169,7 @@ var/const/AIPRIV_FREQ = 1447 //AI private, colored magenta in chat window /* filters */ var/const/RADIO_TO_AIRALARM = "1" var/const/RADIO_FROM_AIRALARM = "2" -var/const/RADIO_CHAT = "3" +var/const/RADIO_CHAT = "3" //deprecated var/const/RADIO_ATMOSIA = "4" var/const/RADIO_NAVBEACONS = "5" var/const/RADIO_AIRLOCK = "6" @@ -282,7 +322,7 @@ var/list/pointers = list() src << S.debug_print() /obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param) - return null + return /datum/signal var/obj/source diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm index 2278380f5e9..188161d7232 100644 --- a/code/game/gamemodes/blob/blob_report.dm +++ b/code/game/gamemodes/blob/blob_report.dm @@ -2,7 +2,6 @@ /datum/game_mode/blob/send_intercept(var/report = 1) var/intercepttext = "" - var/interceptname = "Error" switch(report) if(0) ..() diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index d1f9b2885dc..9209b4f3df2 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -424,7 +424,6 @@ var/list/sacrificed = list() "A shape forms in the center of the rune. A shape of... a man.", \ "You hear liquid flowing.") D.real_name = "[pick(first_names_male)] [pick(last_names)]" - D.universal_speak = 1 D.status_flags = CANSTUN|CANWEAKEN|CANPARALYSE|CANPUSH D.key = ghost.key diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 4c5a70281e6..0bd36e3f74e 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -282,7 +282,7 @@ if(isXRay()) see = range(view_range, pos) else - see = hear(view_range, pos) + see = get_hear(view_range, pos) return see /atom/proc/auto_turn() diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm index 6a1518e311b..910feb64a41 100644 --- a/code/game/machinery/doppler_array.dm +++ b/code/game/machinery/doppler_array.dm @@ -8,7 +8,6 @@ var/list/doppler_arrays = list() density = 1 anchored = 1 - /obj/machinery/doppler_array/New() ..() doppler_arrays += src @@ -74,10 +73,11 @@ var/list/doppler_arrays = list() if(devastation_range < orig_dev_range || heavy_impact_range < orig_heavy_range || light_impact_range < orig_light_range) messages += "Theoretical: Epicenter radius: [orig_dev_range]. Outer radius: [orig_heavy_range]. Shockwave radius: [orig_light_range]." - for(var/mob/O in hearers(src, null)) - for(var/message in messages) - O.show_message("[src] states coldly, \"[message]\"",2) + for(var/message in messages) + say(message) +/obj/machinery/doppler_array/say_quote(text) + return "states coldly, \"[text]\"" /obj/machinery/doppler_array/power_change() if(stat & BROKEN) diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index f56c9eaf2d2..036c8ac5315 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -33,6 +33,8 @@ var/const/HOLOPAD_MODE = 0 name = "\improper AI holopad" desc = "It's a floor-mounted device for projecting holographic images. It is activated remotely." icon_state = "holopad0" + flags = HEAR + languages = ROBOT | HUMAN var/mob/living/silicon/ai/master//Which AI, if any, is controlling the object? Only one AI may control a hologram at any time. var/last_request = 0 //to prevent request spam. ~Carn var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. @@ -78,13 +80,12 @@ var/const/HOLOPAD_MODE = 0 /*This is the proc for special two-way communication between AI and holopad/people talking near holopad. For the other part of the code, check silicon say.dm. Particularly robot talk.*/ -/obj/machinery/hologram/holopad/hear_talk(mob/living/M, text) - if(M&&hologram&&master)//Master is mostly a safety in case lag hits or something. - if(!master.say_understands(M))//The AI will be able to understand most mobs talking through the holopad. - text = stars(text) - var/name_used = M.GetVoice() - //This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only. - var/rendered = "Holopad received, [name_used] [M.say_quote(text)]" +/obj/machinery/hologram/holopad/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + if(speaker && hologram && master && !radio_freq && speaker != master)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios. + if(!master.languages & speaker.languages)//The AI will be able to understand most mobs talking through the holopad. + raw_message = master.lang_treat(speaker, message_langs, raw_message) + var/name_used = speaker.GetVoice() + var/rendered = "Holopad received, [name_used] [speaker.say_quote(raw_message)]" master.show_message(rendered, 2) return @@ -207,4 +208,4 @@ Holographic project of everything else. name = "hologram projector" desc = "It makes a hologram appear...with magnets or something..." icon = 'icons/obj/stationobjs.dmi' - icon_state = "hologram0" \ No newline at end of file + icon_state = "hologram0" diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 29ee00bd7e0..9e1bd0616fa 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -1027,10 +1027,8 @@ obj/item/weapon/newspaper/attackby(obj/item/weapon/W as obj, mob/user as mob) // return //bode well with a newscaster network of 10+ machines. Let's just return it, as it's added in the machines list. /obj/machinery/newscaster/proc/newsAlert(channel) //This isn't Agouri's work, for it is ugly and vile. - var/turf/T = get_turf(src) //Who the fuck uses spawn(600) anyway, jesus christ - if(channel) - for(var/mob/O in hearers(world.view-1, T)) - O.show_message("[src.name] beeps, \"Breaking news from [channel]!\"",2) + if(channel) //Who the fuck uses spawn(600) anyway, jesus christ + say("Breaking news from [channel]!") src.alert = 1 src.update_icon() spawn(300) @@ -1038,7 +1036,9 @@ obj/item/weapon/newspaper/attackby(obj/item/weapon/W as obj, mob/user as mob) src.update_icon() playsound(src.loc, 'sound/machines/twobeep.ogg', 75, 1) else - for(var/mob/O in hearers(world.view-1, T)) - O.show_message("[src.name] beeps, \"Attention! Wanted issue distributed!\"",2) + say("Attention! Wanted issue distributed!") playsound(src.loc, 'sound/machines/warning-buzzer.ogg', 75, 1) - return \ No newline at end of file + return + +/obj/machinery/newscaster/say_quote(text) + return "beeps, \"[text]\"" diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index 0f0b7c069fe..07055117ca5 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -305,17 +305,15 @@ var/list/obj/machinery/requests_console/allConsoles = list() pass = 1 if(pass) - for (var/obj/machinery/requests_console/Console in allConsoles) if (ckey(Console.department) == ckey(href_list["department"])) switch(priority) - if(2) //High priority + if(2) //High priority Console.createmessage(src, "PRIORITY Alert in [department]", sending, 2, 1) if(3) // Extreme Priority Console.createmessage(src, "EXTREME PRIORITY Alert in [department]", sending, 3, 1) else // Normal priority Console.createmessage(src, "Message from [department]", sending, 1, 1) - screen = 6 Console.luminosity = 2 @@ -325,8 +323,7 @@ var/list/obj/machinery/requests_console/allConsoles = list() else messages += "To: [dpt]
[sending]" else - for (var/mob/O in hearers(4, src.loc)) - O.show_message("\icon[src] *The Requests Console beeps: 'NOTICE: No server detected!'") + say("NOTICE: No server detected!") //Handle screen switching @@ -369,7 +366,14 @@ var/list/obj/machinery/requests_console/allConsoles = list() updateUsrDialog() return + +/obj/machinery/say_quote(var/text) + var/ending = copytext(text, length(text) - 2) + if (ending == "!!!") + return "blares, \"[text]\"" + return "beeps, \"[text]\"" + /obj/machinery/requests_console/proc/createmessage(source, title, message, priority, paper) var/linkedsender var/unlinkedsender @@ -389,8 +393,7 @@ var/list/obj/machinery/requests_console/allConsoles = list() src.update_icon() if(!src.silent) playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) - for (var/mob/O in hearers(5, src.loc)) - O.show_message("\icon[src] *The Requests Console beeps: '[title]'") + say(title) src.messages += "High Priority
From: [linkedsender]
[message]" if(paper) var/obj/item/weapon/paper/slip = new /obj/item/weapon/paper(src.loc) @@ -403,8 +406,7 @@ var/list/obj/machinery/requests_console/allConsoles = list() src.update_icon() if(1) playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) - for (var/mob/O in hearers(7, src.loc)) - O.show_message("\icon[src] *The Requests Console yells: '[title]'") + say("title") src.messages += "!!!Extreme Priority!!!
From: [linkedsender]
[message]" var/obj/item/weapon/paper/slip = new /obj/item/weapon/paper(src.loc) if(paper) @@ -421,14 +423,13 @@ var/list/obj/machinery/requests_console/allConsoles = list() src.update_icon() if(!src.silent) playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) - for (var/mob/O in hearers(4, src.loc)) - O.show_message("\icon[src] *The Requests Console beeps: '[title]'") + say("title") src.messages += "From: [linkedsender]
[message]" if(paper) var/obj/item/weapon/paper/slip = new /obj/item/weapon/paper(src.loc) slip.info = "From: [unlinkedsender]
[message]" slip.name = "Message - [unlinkedsender]" - src.luminosity = 2 + src.luminosity = 2 /obj/machinery/requests_console/attackby(var/obj/item/weapon/O as obj, var/mob/user as mob) if (istype(O, /obj/item/weapon/crowbar)) diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index 410282a754f..4b97f504628 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -56,11 +56,10 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept if(signal.data["type"] == 0) /* ###### Broadcast a message using signal.data ###### */ - Broadcast_Message(signal.data["connection"], signal.data["mob"], - signal.data["vmask"], signal.data["vmessage"], - signal.data["radio"], signal.data["message"], - signal.data["name"], signal.data["job"], - signal.data["realname"], signal.data["vname"],, signal.data["compression"], signal.data["level"], signal.frequency) + Broadcast_Message(signal.data["mob"], + signal.data["vmask"], signal.data["radio"], + signal.data["message"], signal.data["name"], signal.data["job"], signal.data["realname"], + 0, signal.data["compression"], signal.data["level"], signal.frequency) /** #### - Simple Broadcast - #### **/ @@ -80,12 +79,11 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept /* ###### Broadcast a message using signal.data ###### */ // Parameter "data" as 4: AI can't track this person/mob - - Broadcast_Message(signal.data["connection"], signal.data["mob"], - signal.data["vmask"], signal.data["vmessage"], + Broadcast_Message(signal.data["mob"], + signal.data["vmask"], signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], - signal.data["realname"], signal.data["vname"], 4, signal.data["compression"], signal.data["level"], signal.frequency) + signal.data["realname"], 4, signal.data["compression"], signal.data["level"], signal.frequency) if(!message_delay) message_delay = 1 @@ -140,23 +138,12 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept sleep(signal.data["slow"]) // simulate the network lag if necessary /* ###### Broadcast a message using signal.data ###### */ - - var/datum/radio_frequency/connection = signal.data["connection"] - - if(connection.frequency == SYND_FREQ) // if syndicate broadcast, just - Broadcast_Message(signal.data["connection"], signal.data["mob"], - signal.data["vmask"], signal.data["vmessage"], + if(signal.frequency == SYND_FREQ) // if syndicate broadcast, just + Broadcast_Message(signal.data["mob"], + signal.data["vmask"], signal.data["radio"], signal.data["message"], signal.data["name"], signal.data["job"], - signal.data["realname"], signal.data["vname"],, signal.data["compression"], list(0), connection.frequency) - else - if(intercept) - Broadcast_Message(signal.data["connection"], signal.data["mob"], - signal.data["vmask"], signal.data["vmessage"], - signal.data["radio"], signal.data["message"], - signal.data["name"], signal.data["job"], - signal.data["realname"], signal.data["vname"], 3, signal.data["compression"], list(0), connection.frequency) - + signal.data["realname"],, signal.data["compression"], list(0, z), signal.frequency) /** @@ -164,9 +151,6 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept Here is the big, bad function that broadcasts a message given the appropriate parameters. - @param connection: - The datum generated in radio.dm, stored in signal.data["connection"]. - @param M: Reference to the mob/speaker, stored in signal.data["mob"] @@ -216,198 +200,77 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept **/ -/proc/Broadcast_Message(var/datum/radio_frequency/connection, var/mob/M, - var/vmask, var/vmessage, var/obj/item/device/radio/radio, - var/message, var/name, var/job, var/realname, var/vname, + +/proc/Broadcast_Message(var/atom/movable/AM, + var/vmask, var/obj/item/device/radio/radio, + var/message, var/name, var/job, var/realname, var/data, var/compression, var/list/level, var/freq) - /* ###### Prepare the radio connection ###### */ - - var/display_freq = freq - - var/list/obj/item/device/radio/radios = list() - - // Cut down on the message sizes. - message = copytext(message, 1, MAX_BROADCAST_LEN) - vmessage = copytext(vmessage, 1, MAX_BROADCAST_LEN) + if(!message) + return + + var/list/radios = list() + + var/atom/movable/virtualspeaker/virt = new(null) + virt.name = name + virt.job = job + virt.languages = AM.languages + virt.source = AM + virt.faketrack = data == 4 ? 1 : 0 + virt.radio = radio + + if(compression > 0) + message = Gibberish(message, compression + 40) // --- Broadcast only to intercom devices --- if(data == 1) - - for (var/obj/item/device/radio/intercom/R in connection.devices["[RADIO_CHAT]"]) - if(R.receive_range(display_freq, level) > -1) + for(var/obj/item/device/radio/intercom/R in all_radios["[freq]"]) + if(R.receive_range(freq, level) > -1) radios += R // --- Broadcast only to intercoms and station-bounced radios --- else if(data == 2) - for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"]) - + for(var/obj/item/device/radio/R in all_radios["[freq]"]) if(istype(R, /obj/item/device/radio/headset)) continue - if(R.receive_range(display_freq, level) > -1) - radios += R - - // --- Broadcast to syndicate radio! --- - - else if(data == 3) - - var/datum/radio_frequency/syndicateconnection = radio_controller.return_frequency(SYND_FREQ) - - for (var/obj/item/device/radio/R in syndicateconnection.devices["[RADIO_CHAT]"]) - - if(R.receive_range(SYND_FREQ, level) > -1) + if(R.receive_range(freq, level) > -1) radios += R // --- Broadcast to ALL radio devices --- else - - for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"]) - if(R.receive_range(display_freq, level) > -1) + for(var/obj/item/device/radio/R in all_radios["[freq]"]) + if(R.receive_range(freq, level) > -1) radios += R + var/freqtext = num2text(freq) + for(var/obj/item/device/radio/R in all_radios["[SYND_FREQ]"]) //syndicate radios use magic that allows them to hear everything. this was already the case, now it just doesn't need the allinone anymore. solves annoying bugs that aren't worth solving. + if(R.receive_range(SYND_FREQ, list(R.z)) > -1 && freqtext in radiochannelsreverse) + radios |= R + // Get a list of mobs who can hear from the radios we collected. - var/list/receive = get_mobs_in_radio_ranges(radios) - - /* ###### Organize the receivers into categories for displaying the message ###### */ - - // Understood the message: - var/list/heard_masked = list() // masked name or no real name - var/list/heard_normal = list() // normal message - - // Did not understand the message: - var/list/heard_voice = list() // voice message (ie "chimpers") - var/list/heard_garbled = list() // garbled message (ie "f*c* **u, **i*er!") - var/list/heard_gibberish= list() // completely screwed over message (ie "F%! (O*# *#!<>&**%!") - - for (var/mob/R in receive) - - /* --- Loop through the receivers and categorize them --- */ + var/list/receive = get_mobs_in_radio_ranges(radios) //this includes all hearers. + for(var/mob/R in receive) //Filter receiver list. if (R.client && !(R.client.prefs.toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios. - continue + receive -= R - if(istype(R, /mob/new_player)) // we don't want new players to hear messages. rare but generates runtimes. - continue - - - // --- Check for compression --- - if(compression > 0) - heard_gibberish += R - continue - - // --- Can understand the speech --- - - if (!M || R.say_understands(M)) - - // - Not human or wearing a voice mask - - if (!M || !ishuman(M) || vmask) - heard_masked += R - - // - Human and not wearing voice mask - - else - heard_normal += R - - // --- Can't understand the speech --- - - else - // - The speaker has a prespecified "voice message" to display if not understood - - if (vmessage) - heard_voice += R - - // - Just display a garbled message - - else - heard_garbled += R - - - /* ###### Begin formatting and sending the message ###### */ - if (length(heard_masked) || length(heard_normal) || length(heard_voice) || length(heard_garbled) || length(heard_gibberish)) - - /* --- Some miscellaneous variables to format the string output --- */ - var/part_a = "" // goes in the actual output - var/freq_text // the name of the channel - - // --- Set the name of the channel --- - switch(display_freq) - - if(SYND_FREQ) - freq_text = "#unkn" - if(COMM_FREQ) - freq_text = "Command" - if(SCI_FREQ) - freq_text = "Science" - if(MED_FREQ) - freq_text = "Medical" - if(ENG_FREQ) - freq_text = "Engineering" - if(SEC_FREQ) - freq_text = "Security" - if(SERV_FREQ) - freq_text = "Service" - if(SUPP_FREQ) - freq_text = "Supply" - if(AIPRIV_FREQ) - freq_text = "AI Private" - //There's probably a way to use the list var of channels in code\game\communications.dm to make the dept channels non-hardcoded, but I wasn't in an experimentive mood. --NEO - - - // --- If the frequency has not been assigned a name, just use the frequency as the name --- - - if(!freq_text) - freq_text = format_frequency(display_freq) - - // --- Some more pre-message formatting --- - - var/part_b_extra = "" - if(data == 3) // intercepted radio message - part_b_extra = " (Intercepted)" - var/part_b = " \[[freq_text]\][part_b_extra] " - var/part_c = "" - - if (display_freq==SYND_FREQ) - part_a = "" - else if (display_freq==COMM_FREQ) - part_a = "" - else if (display_freq==SCI_FREQ) - part_a = "" - else if (display_freq==MED_FREQ) - part_a = "" - else if (display_freq==ENG_FREQ) - part_a = "" - else if (display_freq==SEC_FREQ) - part_a = "" - else if (display_freq==SERV_FREQ) - part_a = "" - else if (display_freq==SUPP_FREQ) - part_a = "" - else if (display_freq==DSQUAD_FREQ) - part_a = "" - else if (display_freq==AIPRIV_FREQ) - part_a = "" - - // --- Filter the message; place it in quotes apply a verb --- - - var/quotedmsg = null - if(M) - quotedmsg = M.say_quote(message) - else - quotedmsg = "says, \"[message]\"" + var/rendered = virt.compose_message(virt, virt.languages, message, freq) //Always call this on the virtualspeaker to advoid issues. + for(var/atom/movable/hearer in receive) + hearer.Hear(rendered, virt, AM.languages, message, freq) + if(length(receive)) // --- This following recording is intended for research and feedback in the use of department radio channels --- - var/part_blackbox_b = " \[[freq_text]\] " - var/blackbox_msg = "[part_a][name][part_blackbox_b][quotedmsg][part_c]" - //var/blackbox_admin_msg = "[part_a][M.name] (Real name: [M.real_name])[part_blackbox_b][quotedmsg][part_c]" - - //BR.messages_admin += blackbox_admin_msg + var/blackbox_msg = "[AM] [AM.say_quote(message)]" if(istype(blackbox)) - switch(display_freq) + switch(freq) if(1459) blackbox.msg_common += blackbox_msg if(1351) @@ -431,106 +294,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept else blackbox.messages += blackbox_msg - //End of research and feedback code. - - var/aitrack = "" - - /* ###### Send the message ###### */ - - - /* --- Process all the mobs that heard a masked voice (understood) --- */ - - if (length(heard_masked)) - var/N = name - var/J = job - var/rendered = "[part_a][N][part_b][quotedmsg][part_c]" - for (var/mob/R in heard_masked) - aitrack = "" - if(data == 4) - aitrack = "" - - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][aitrack][N] ([J]) [part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) - - /* --- Process all the mobs that heard the voice normally (understood) --- */ - - if (length(heard_normal)) - var/rendered = "[part_a][realname][part_b][quotedmsg][part_c]" - - for (var/mob/R in heard_normal) - aitrack = "" - if(data == 4) - aitrack = "" - - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][aitrack][realname] ([job]) [part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) - - /* --- Process all the mobs that heard the voice normally (did not understand) --- */ - // Does not display message; displayes the mob's voice_message (ie "chimpers") - - if (length(heard_voice)) - var/rendered = "[part_a][vname][part_b][vmessage][part_c]" - - for (var/mob/R in heard_voice) - aitrack = "" - if(data == 4) - aitrack = "" - - - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][aitrack][vname] ([job]) [part_b][vmessage]][part_c]", 2) - else - R.show_message(rendered, 2) - - /* --- Process all the mobs that heard a garbled voice (did not understand) --- */ - // Displays garbled message (ie "f*c* **u, **i*er!") - - if (length(heard_garbled)) - if(M) - quotedmsg = M.say_quote(stars(message)) - else - quotedmsg = stars(quotedmsg) - - var/rendered = "[part_a][vname][part_b][quotedmsg][part_c]" - - for (var/mob/R in heard_garbled) - aitrack = "" - if(data == 4) - aitrack = "" - - - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][aitrack][vname][part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) - - - /* --- Complete gibberish. Usually happens when there's a compressed message --- */ - - if (length(heard_gibberish)) - if(M) - quotedmsg = M.say_quote(Gibberish(message, compression + 50)) - else - quotedmsg = Gibberish(quotedmsg, compression + 50) - - var/rendered = "[part_a][Gibberish(name, compression + 50)][part_b][quotedmsg][part_c]" - - for (var/mob/R in heard_gibberish) - aitrack = "" - if(data == 4) - aitrack = "" - - - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][aitrack][Gibberish(realname, compression + 50)] ([Gibberish(job, compression + 50)]) [part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) - - + spawn(50) + qdel(virt) /proc/Broadcast_SimpleMessage(var/source, var/frequency, var/text, var/data, var/mob/M, var/compression, var/level) @@ -613,7 +378,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept // --- Can understand the speech --- - if (R.say_understands(M)) + if (R.languages & M.languages) heard_normal += R @@ -791,7 +556,5 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept sleep(rand(10,25)) - //world.log << "Level: [signal.data["level"]] - Done: [signal.data["done"]]" - return signal diff --git a/code/game/machinery/telecomms/logbrowser.dm b/code/game/machinery/telecomms/logbrowser.dm index 5a5dbb3ff2f..a6de46e209e 100644 --- a/code/game/machinery/telecomms/logbrowser.dm +++ b/code/game/machinery/telecomms/logbrowser.dm @@ -80,27 +80,32 @@ if(mobtype in humans) race = "Human" - + language = race + + else if(mobtype in slimes) // NT knows a lot about slimes, but not aliens. Can identify slimes + race = "Slime" + language = race + else if(mobtype in monkeys) race = "Monkey" - language = race + language = race else if(mobtype in silicons || C.parameters["job"] == "AI") // sometimes M gets deleted prematurely for AIs... just check the job race = "Artificial Life" - - else if(mobtype in slimes) // NT knows a lot about slimes, but not aliens. Can identify slimes - race = "slime" language = race + + else if(istype(mobtype, /obj)) + race = "Machinery" + language = race else if(mobtype in animals) race = "Domestic Animal" language = race - + else race = "Unidentifiable" language = race - // -- If the orator is a human, or universal translate is active, OR mob has universal speech on -- if(language == "Human" || universal_translate || C.parameters["uspeech"]) diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index ff0ec5208cc..599c5a20429 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -43,7 +43,6 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() if(!on) return - //world << "[src] ([src.id]) - [signal.debug_print()]" var/send_count = 0 //signal.data["slow"] == 0 // apply some lag based on integrity @@ -79,12 +78,9 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() "name" = signal.data["name"], "job" = signal.data["job"], "key" = signal.data["key"], - "vmessage" = signal.data["vmessage"], - "vname" = signal.data["vname"], "vmask" = signal.data["vmask"], "compression" = signal.data["compression"], "message" = signal.data["message"], - "connection" = signal.data["connection"], "radio" = signal.data["radio"], "slow" = signal.data["slow"], "traffic" = signal.data["traffic"], @@ -281,7 +277,6 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() return if(!check_receive_level(signal)) return - if(signal.transmission_method == 2) if(is_freq_listening(signal)) // detect subspace signals @@ -370,7 +365,6 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() var/receiving = 1 /obj/machinery/telecomms/relay/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - // Add our level and send it back if(can_send(signal)) signal.data["level"] |= listening_level @@ -422,7 +416,6 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() /obj/machinery/telecomms/bus/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) if(is_freq_listening(signal)) - if(change_frequency) signal.frequency = change_frequency @@ -541,7 +534,6 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() ..() /obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) - if(signal.data["message"]) if(is_freq_listening(signal)) @@ -557,22 +549,16 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() update_logs() var/datum/comm_log_entry/log = new - var/mob/M = signal.data["mob"] // Copy the signal.data entries we want log.parameters["mobtype"] = signal.data["mobtype"] log.parameters["job"] = signal.data["job"] log.parameters["key"] = signal.data["key"] - log.parameters["vmessage"] = signal.data["message"] - log.parameters["vname"] = signal.data["vname"] log.parameters["message"] = signal.data["message"] log.parameters["name"] = signal.data["name"] log.parameters["realname"] = signal.data["realname"] - if(!istype(M, /mob/new_player) && M) - log.parameters["uspeech"] = M.universal_speak - else - log.parameters["uspeech"] = 0 + log.parameters["uspeech"] = signal.data["languages"] & HUMAN //good enough // If the signal is still compressed, make the log entry gibberish if(signal.data["compression"] > 0) @@ -580,7 +566,6 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() log.parameters["job"] = Gibberish(signal.data["job"], signal.data["compression"] + 50) log.parameters["name"] = Gibberish(signal.data["name"], signal.data["compression"] + 50) log.parameters["realname"] = Gibberish(signal.data["realname"], signal.data["compression"] + 50) - log.parameters["vname"] = Gibberish(signal.data["vname"], signal.data["compression"] + 50) log.input_type = "Corrupt File" // Log and store everything that needs to be logged diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 50da8e4965b..a76c3e2445f 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -406,8 +406,10 @@ if(!message) return - visible_message("[src] beeps, \"[message]\"") + say(message) +/obj/machinery/vending/say_quote(text) + return "beeps, \"[text]\"" /obj/machinery/vending/power_change() if(stat & BROKEN) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 59c6dffdfc4..116f2cd457d 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -240,9 +240,9 @@ /obj/mecha/proc/drop_item()//Derpfix, but may be useful in future for engineering exosuits. return -/obj/mecha/hear_talk(mob/M as mob, text) - if(M==occupant && radio.broadcasting) - radio.talk_into(M, text) +/obj/mecha/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + if(speaker == occupant && radio.broadcasting) + radio.talk_into(speaker, text) return //////////////////////////// diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm index 0281a7b0e9c..e576cc2e109 100644 --- a/code/game/objects/items/devices/PDA/radio.dm +++ b/code/game/objects/items/devices/PDA/radio.dm @@ -227,10 +227,10 @@ ..() if(radio_controller) initialize() - + /obj/item/radio/integrated/signal/initialize() if (src.frequency < 1441 || src.frequency > 1489) - src.frequency = sanitize_frequency(src.frequency) + src.frequency = sanitize_frequency(src.frequency) set_frequency(frequency) diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index d0374abb08c..27b7ead9398 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -18,13 +18,6 @@ transfer_ai("AICORE", "AICARD", M, user) return -/obj/item/device/aicard/attack(mob/living/silicon/decoy/M as mob, mob/user as mob) - if (!istype (M, /mob/living/silicon/decoy)) - return ..() - else - M.death() - user << "ERROR ERROR ERROR" - /obj/item/device/aicard/attack_self(mob/user) if (!in_range(src, user)) return @@ -63,7 +56,7 @@ dat += "AI nonfunctional" else if (!src.flush) - dat += {"Wipe AI"} + dat += {"Wipe AI"} else dat += "Wipe in progress" dat += "
" diff --git a/code/game/objects/items/devices/radio/beacon.dm b/code/game/objects/items/devices/radio/beacon.dm index 28f9936054a..a8e111e3097 100644 --- a/code/game/objects/items/devices/radio/beacon.dm +++ b/code/game/objects/items/devices/radio/beacon.dm @@ -6,7 +6,7 @@ var/code = "electronic" origin_tech = "bluespace=1" -/obj/item/device/radio/beacon/hear_talk() +/obj/item/device/radio/beacon/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) return diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 324d941cffc..c765eff2eb3 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -275,13 +275,16 @@ src.syndie = 1 - for (var/ch_name in channels) + for(var/ch_name in channels) + //this is the most hilarious piece of code i have seen this week, so im not going to remove it + /* if(!radio_controller) sleep(30) // Waiting for the radio_controller to be created. if(!radio_controller) src.name = "broken radio headset" return + */ - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + secure_radio_connections[ch_name] = add_radio(src, radiochannels[ch_name]) return diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 3e49179e76f..1397c2d1575 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -5,7 +5,6 @@ anchored = 1 w_class = 4.0 canhear_range = 2 - flags = CONDUCT var/number = 0 var/anyai = 1 var/mob/living/silicon/ai/ai = list() @@ -51,8 +50,8 @@ return canhear_range -/obj/item/device/radio/intercom/hear_talk(mob/M as mob, msg) - if(!src.anyai && !(M in src.ai)) +/obj/item/device/radio/intercom/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + if(!anyai && !(speaker in ai)) return ..() diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 398b2433620..9ab97b5cb93 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -16,6 +16,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use var/canhear_range = 3 // the range which mobs can hear this radio from var/obj/item/device/radio/patch_link = null var/datum/wires/radio/wires = null + var/list/secure_radio_connections var/prison_radio = 0 var/b_stat = 0 var/broadcasting = 0 @@ -27,8 +28,9 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use var/maxf = 1499 var/emped = 0 //Highjacked to track the number of consecutive EMPs on the radio, allowing consecutive EMP's to stack properly. // "Example" = FREQ_LISTENING|FREQ_BROADCASTING - flags = CONDUCT + flags = CONDUCT | HEAR slot_flags = SLOT_BELT + languages = HUMAN | ROBOT throw_speed = 3 throw_range = 7 w_class = 2 @@ -39,14 +41,9 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use var/const/FREQ_LISTENING = 1 //FREQ_BROADCASTING = 2 -/obj/item/device/radio - var/datum/radio_frequency/radio_connection - var/list/datum/radio_frequency/secure_radio_connections - - proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT) +/obj/item/device/radio/proc/set_frequency(new_frequency) + remove_radio(src, frequency) + frequency = add_radio(src, new_frequency) /obj/item/device/radio/New() wires = new(src) @@ -57,6 +54,8 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use if(radio_controller) initialize() +/obj/item/device/radio/Destroy() + remove_radio_all(src) //Just to be sure. /obj/item/device/radio/MouseDrop(obj/over_object as obj, src_location, over_location) var/mob/M = usr @@ -66,7 +65,6 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use /obj/item/device/radio/initialize() - if(freerange) if(frequency < 1200 || frequency > 1600) frequency = sanitize_frequency(frequency, maxf) @@ -78,7 +76,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use set_frequency(frequency) for (var/ch_name in channels) - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + secure_radio_connections[ch_name] = add_radio(src, radiochannels[ch_name]) /obj/item/device/radio/attack_self(mob/user as mob) @@ -202,8 +200,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use /obj/item/device/radio/proc/isWireCut(var/index) return wires.IsIndexCut(index) -/obj/item/device/radio/talk_into(mob/living/M as mob, message, channel) - +/obj/item/device/radio/talk_into(atom/movable/M, message, channel) if(!on) return // the device has to be on // Fix for permacell radios, but kinda eh about actually fixing them. if(!M || !message) return @@ -216,395 +213,190 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use if(!M.IsVocal()) return - if(GLOBAL_RADIO_TYPE == 1) // NEW RADIO SYSTEMS: By Doohl + /* Quick introduction: + This new radio system uses a very robust FTL signaling technology unoriginally + dubbed "subspace" which is somewhat similar to 'blue-space' but can't + actually transmit large mass. Headsets are the only radio devices capable + of sending subspace transmissions to the Communications Satellite. - /* Quick introduction: - This new radio system uses a very robust FTL signaling technology unoriginally - dubbed "subspace" which is somewhat similar to 'blue-space' but can't - actually transmit large mass. Headsets are the only radio devices capable - of sending subspace transmissions to the Communications Satellite. + A headset sends a signal to a subspace listener/reciever elsewhere in space, + the signal gets processed and logged, and an audible transmission gets sent + to each individual headset. + */ + + /* + be prepared to disregard any comments in all of tcomms code. i tried my best to keep them somewhat up-to-date, but eh + */ - A headset sends a signal to a subspace listener/reciever elsewhere in space, - the signal gets processed and logged, and an audible transmission gets sent - to each individual headset. - */ - - //#### Grab the connection datum ####// - var/datum/radio_frequency/connection = null - if(channel && channels && channels.len > 0) - if (channel == "department") - //world << "DEBUG: channel=\"[channel]\" switching to \"[channels[1]]\"" - channel = channels[1] - connection = secure_radio_connections[channel] - if (!channels[channel]) // if the channel is turned off, don't broadcast - return - else - connection = radio_connection - channel = null - if (!istype(connection)) - return - if (!connection) + //get the frequency you buttface. radios no longer use the radio_controller. confusing for future generations, convenient for me. + var/freq + if(channel && channels && channels.len > 0) + if (channel == "department") + channel = channels[1] + freq = secure_radio_connections[channel] + if (!channels[channel]) // if the channel is turned off, don't broadcast return + else + freq = frequency + channel = null - var/turf/position = get_turf(src) + var/turf/position = get_turf(src) - //#### Tagging the signal with all appropriate identity values ####// + //#### Tagging the signal with all appropriate identity values ####// - // ||-- The mob's name identity --|| - var/displayname = M.name // grab the display name (name you get when you hover over someone's icon) - var/real_name = M.real_name // mob's real name - var/mobkey = "none" // player key associated with mob - var/voicemask = 0 // the speaker is wearing a voice mask - if(M.client) - mobkey = M.key // assign the mob's key + // ||-- The mob's name identity --|| + var/displayname = M.name // grab the display name (name you get when you hover over someone's icon) + var/real_name = M.name // mob's real name + var/mobkey = "none" // player key associated with mob + var/voicemask = 0 // the speaker is wearing a voice mask + var/voice = M.GetVoice() // Why reinvent the wheel when there is a proc that does nice things already + if(ismob(M)) + var/mob/speaker = M + real_name = speaker.real_name + if(speaker.client) + mobkey = speaker.key // assign the mob's key - var/jobname // the mob's "job" + var/jobname // the mob's "job" - // --- Human: use their job as seen on the crew manifest - makes it unneeded to carry an ID for an AI to see their job - if (ishuman(M)) - var/voice = M.GetVoice() // Why reinvent the wheel when there is a proc that does nice things already - var/datum/data/record/findjob = find_record("name", voice, data_core.general) + // --- Human: use their job as seen on the crew manifest - makes it unneeded to carry an ID for an AI to see their job + if (ishuman(M)) + var/datum/data/record/findjob = find_record("name", voice, data_core.general) - if(voice != real_name) - displayname = voice - voicemask = 1 - if(findjob) - jobname = findjob.fields["rank"] - else - jobname = "Unknown" - - // --- Carbon Nonhuman --- - else if (iscarbon(M)) // Nonhuman carbon mob - jobname = "No id" - - // --- AI --- - else if (isAI(M)) - jobname = "AI" - - // --- Cyborg --- - else if (isrobot(M)) - var/mob/living/silicon/robot/B = M - jobname = "[B.designation] Cyborg" - - // --- Personal AI (pAI) --- - else if (istype(M, /mob/living/silicon/pai)) - jobname = "Personal AI" - - // --- Unidentifiable mob --- + if(voice != real_name) + displayname = voice + voicemask = 1 + if(findjob) + jobname = findjob.fields["rank"] else jobname = "Unknown" + // --- Carbon Nonhuman --- + else if (iscarbon(M)) // Nonhuman carbon mob + jobname = "No id" + // --- AI --- + else if (isAI(M)) + jobname = "AI" - /* ###### Radio headsets can only broadcast through subspace ###### */ + // --- Cyborg --- + else if (isrobot(M)) + var/mob/living/silicon/robot/B = M + jobname = "[B.designation] Cyborg" - if(subspace_transmission) - // First, we want to generate a new radio signal - var/datum/signal/signal = new - signal.transmission_method = 2 // 2 would be a subspace transmission. - // transmission_method could probably be enumerated through #define. Would be neater. + // --- Personal AI (pAI) --- + else if (istype(M, /mob/living/silicon/pai)) + jobname = "Personal AI" - // --- Finally, tag the actual signal with the appropriate values --- - signal.data = list( - // Identity-associated tags: - "mob" = M, // store a reference to the mob - "mobtype" = M.type, // the mob's type - "realname" = real_name, // the mob's real name - "name" = displayname, // the mob's display name - "job" = jobname, // the mob's job - "key" = mobkey, // the mob's key - "vmessage" = M.voice_message, // the message to display if the voice wasn't understood - "vname" = M.voice_name, // the name to display if the voice wasn't understood - "vmask" = voicemask, // 1 if the mob is using a voice gas mask + // --- Cold, emotionless machines. --- + else if(isobj(M)) + jobname = "Machine" - // We store things that would otherwise be kept in the actual mob - // so that they can be logged even AFTER the mob is deleted or something - - // Other tags: - "compression" = rand(45,50), // compressed radio signal - "message" = message, // the actual sent message - "connection" = connection, // the radio connection to use - "radio" = src, // stores the radio used for transmission - "slow" = 0, // how much to sleep() before broadcasting - simulates net lag - "traffic" = 0, // dictates the total traffic sum that the signal went through - "type" = 0, // determines what type of radio input it is: normal broadcast - "server" = null, // the last server to log this signal - "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery - "level" = position.z // The source's z level - ) - signal.frequency = connection.frequency // Quick frequency set - - //#### Sending the signal to all subspace receivers ####// - - for(var/obj/machinery/telecomms/receiver/R in telecomms_list) - R.receive_signal(signal) - - // Allinone can act as receivers. - for(var/obj/machinery/telecomms/allinone/R in telecomms_list) - R.receive_signal(signal) - - // Receiving code can be located in Telecommunications.dm - return - - - /* ###### Intercoms and station-bounced radios ###### */ - - var/filter_type = 2 - - /* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */ - if(istype(src, /obj/item/device/radio/intercom)) - filter_type = 1 + // --- Unidentifiable mob --- + else + jobname = "Unknown" + /* ###### Radio headsets can only broadcast through subspace ###### */ + if(subspace_transmission) + // First, we want to generate a new radio signal var/datum/signal/signal = new - signal.transmission_method = 2 - - - /* --- Try to send a normal subspace broadcast first */ - + signal.transmission_method = 2 // 2 would be a subspace transmission. + // transmission_method could probably be enumerated through #define. Would be neater. + // --- Finally, tag the actual signal with the appropriate values --- signal.data = list( - + // Identity-associated tags: "mob" = M, // store a reference to the mob "mobtype" = M.type, // the mob's type "realname" = real_name, // the mob's real name - "name" = displayname, // the mob's display name + "name" = voice, // the mob's display name "job" = jobname, // the mob's job "key" = mobkey, // the mob's key - "vmessage" = M.voice_message, // the message to display if the voice wasn't understood - "vname" = M.voice_name, // the name to display if the voice wasn't understood - "vmask" = voicemask, // 1 if the mob is using a voice gas mas + "vmask" = voicemask, // 1 if the mob is using a voice gas mask - "compression" = 0, // uncompressed radio signal + // We store things that would otherwise be kept in the actual mob + // so that they can be logged even AFTER the mob is deleted or something + + // Other tags: + "compression" = rand(35,65), // compressed radio signal "message" = message, // the actual sent message - "connection" = connection, // the radio connection to use "radio" = src, // stores the radio used for transmission - "slow" = 0, - "traffic" = 0, - "type" = 0, - "server" = null, - "reject" = 0, - "level" = position.z - ) - signal.frequency = connection.frequency // Quick frequency set + "slow" = 0, // how much to sleep() before broadcasting - simulates net lag + "traffic" = 0, // dictates the total traffic sum that the signal went through + "type" = 0, // determines what type of radio input it is: normal broadcast + "server" = null, // the last server to log this signal + "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery + "level" = position.z, // The source's z level + "languages" = M.languages //The languages M is talking in. + ) + signal.frequency = freq + + //#### Sending the signal to all subspace receivers ####// for(var/obj/machinery/telecomms/receiver/R in telecomms_list) R.receive_signal(signal) + // Allinone can act as receivers. + for(var/obj/machinery/telecomms/allinone/R in telecomms_list) + R.receive_signal(signal) - sleep(rand(10,25)) // wait a little... + // Receiving code can be located in Telecommunications.dm + return + + + /* ###### Intercoms and station-bounced radios ###### */ + + var/filter_type = 2 + + var/datum/signal/signal = new + signal.transmission_method = 2 + + + /* --- Try to send a normal subspace broadcast first */ + + signal.data = list( + "mob" = M, // store a reference to the mob + "mobtype" = M.type, // the mob's type + "realname" = real_name, // the mob's real name + "name" = displayname, // the mob's display name + "job" = jobname, // the mob's job + "key" = mobkey, // the mob's key + "vmask" = voicemask, // 1 if the mob is using a voice gas mas + + "compression" = 0, // uncompressed radio signal + "message" = message, // the actual sent message + "radio" = src, // stores the radio used for transmission + "slow" = 0, + "traffic" = 0, + "type" = 0, + "server" = null, + "reject" = 0, + "level" = position.z + ) + signal.frequency = text2num(freq) // Quick frequency set + for(var/obj/machinery/telecomms/receiver/R in telecomms_list) + R.receive_signal(signal) + + + spawn(20) // wait a little... if(signal.data["done"] && position.z in signal.data["level"]) // we're done here. return - // Oh my god; the comms are down or something because the signal hasn't been broadcasted yet in our level. - // Send a mundane broadcast with limited targets: + // Oh my god; the comms are down or something because the signal hasn't been broadcasted yet in our level. + // Send a mundane broadcast with limited targets: + Broadcast_Message(M, voicemask, + src, message, displayname, jobname, real_name, + filter_type, signal.data["compression"], list(position.z), freq) - //THIS IS TEMPORARY. - if(!connection) return //~Carn - - Broadcast_Message(connection, M, voicemask, M.voice_message, - src, message, displayname, jobname, real_name, M.voice_name, - filter_type, signal.data["compression"], list(position.z), connection.frequency) - - - - else // OLD RADIO SYSTEMS: By Goons? - - var/datum/radio_frequency/connection = null - if(channel && channels && channels.len > 0) - if (channel == "department") - //world << "DEBUG: channel=\"[channel]\" switching to \"[channels[1]]\"" - channel = channels[1] - connection = secure_radio_connections[channel] - else - connection = radio_connection - channel = null - if (!istype(connection)) - return - var/display_freq = connection.frequency - - //world << "DEBUG: used channel=\"[channel]\" frequency= \"[display_freq]\" connection.devices.len = [connection.devices.len]" - - var/eqjobname - - if (ishuman(M)) - eqjobname = M:get_assignment() - else if (iscarbon(M)) - eqjobname = "No id" //only humans can wear ID - else if (isAI(M)) - eqjobname = "AI" - else if (isrobot(M)) - eqjobname = "Cyborg"//Androids don't really describe these too well, in my opinion. - else if (istype(M, /mob/living/silicon/pai)) - eqjobname = "Personal AI" - else - eqjobname = "Unknown" - - if (isWireCut(WIRE_TRANSMIT)) - return - - var/list/receive = list() - - //for (var/obj/item/device/radio/R in radio_connection.devices) - for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"]) - //if(R.accept_rad(src, message)) - receive |= R.send_hear(display_freq, 0) - - //world << "DEBUG: receive.len=[receive.len]" - var/list/heard_masked = list() // masked name or no real name - var/list/heard_normal = list() // normal message - var/list/heard_voice = list() // voice message - var/list/heard_garbled = list() // garbled message - - for (var/mob/R in receive) - if (R.client && !(R.client.prefs.toggles & CHAT_RADIO)) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios. - continue - if (R.say_understands(M)) - if (ishuman(M) && M.GetVoice() != M.real_name) - heard_masked += R - else - heard_normal += R - else - if (M.voice_message) - heard_voice += R - else - heard_garbled += R - - if (length(heard_masked) || length(heard_normal) || length(heard_voice) || length(heard_garbled)) - var/part_a = "" - //var/part_b = " \icon[src]\[[format_frequency(frequency)]\] " - var/freq_text - switch(display_freq) - if(SYND_FREQ) - freq_text = "#unkn" - if(COMM_FREQ) - freq_text = "Command" - if(SCI_FREQ) - freq_text = "Science" - if(MED_FREQ) - freq_text = "Medical" - if(ENG_FREQ) - freq_text = "Engineering" - if(SEC_FREQ) - freq_text = "Security" - if(SERV_FREQ) - freq_text = "Service" - if(SUPP_FREQ) - freq_text = "Supply" - if(AIPRIV_FREQ) - freq_text = "AI Private" - //There's probably a way to use the list var of channels in code\game\communications.dm to make the dept channels non-hardcoded, but I wasn't in an experimentive mood. --NEO - - if(!freq_text) - freq_text = format_frequency(display_freq) - - var/part_b = " \[[freq_text]\] " - var/part_c = "" - - if (display_freq==SYND_FREQ) - part_a = "" - else if (display_freq==COMM_FREQ) - part_a = "" - else if (display_freq==SCI_FREQ) - part_a = "" - else if (display_freq==MED_FREQ) - part_a = "" - else if (display_freq==ENG_FREQ) - part_a = "" - else if (display_freq==SEC_FREQ) - part_a = "" - else if (display_freq==SERV_FREQ) - part_a = "" - else if (display_freq==SUPP_FREQ) - part_a = "" - else if (display_freq==DSQUAD_FREQ) - part_a = "" - else if (display_freq==AIPRIV_FREQ) - part_a = "" - var/quotedmsg = M.say_quote(message) - - //This following recording is intended for research and feedback in the use of department radio channels. - - var/part_blackbox_b = " \[[freq_text]\] " - var/blackbox_msg = "[part_a][M.name][part_blackbox_b][quotedmsg][part_c]" - //var/blackbox_admin_msg = "[part_a][M.name] (Real name: [M.real_name])[part_blackbox_b][quotedmsg][part_c]" - if(istype(blackbox)) - //BR.messages_admin += blackbox_admin_msg - switch(display_freq) - if(1459) - blackbox.msg_common += blackbox_msg - if(1351) - blackbox.msg_science += blackbox_msg - if(1353) - blackbox.msg_command += blackbox_msg - if(1355) - blackbox.msg_medical += blackbox_msg - if(1357) - blackbox.msg_engineering += blackbox_msg - if(1359) - blackbox.msg_security += blackbox_msg - if(1441) - blackbox.msg_deathsquad += blackbox_msg - if(1213) - blackbox.msg_syndicate += blackbox_msg - if(1349) - blackbox.msg_service += blackbox_msg - if(1347) - blackbox.msg_cargo += blackbox_msg - else - blackbox.messages += blackbox_msg - - //End of research and feedback code. - - if (length(heard_masked)) - var/N = M.name - var/J = eqjobname - if(ishuman(M) && M.GetVoice() != M.real_name) - N = M.GetVoice() - J = "Unknown" - var/rendered = "[part_a][N][part_b][quotedmsg][part_c]" - for (var/mob/R in heard_masked) - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][N] ([J]) [part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) - - if (length(heard_normal)) - var/rendered = "[part_a][M.real_name][part_b][quotedmsg][part_c]" - - for (var/mob/R in heard_normal) - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][M.real_name] ([eqjobname]) [part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) - - if (length(heard_voice)) - var/rendered = "[part_a][M.voice_name][part_b][M.voice_message][part_c]" - - for (var/mob/R in heard_voice) - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][M.voice_name] ([eqjobname]) [part_b][M.voice_message][part_c]", 2) - else - R.show_message(rendered, 2) - - if (length(heard_garbled)) - quotedmsg = M.say_quote(stars(message)) - var/rendered = "[part_a][M.voice_name][part_b][quotedmsg][part_c]" - - for (var/mob/R in heard_voice) - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][M.voice_name][part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) - -/obj/item/device/radio/hear_talk(mob/M as mob, msg) - - if (broadcasting) - if(get_dist(src, M) <= canhear_range) - talk_into(M, msg) +/obj/item/device/radio/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + if(radio_freq) + return + if(broadcasting) + if(get_dist(src, speaker) <= canhear_range) + talk_into(speaker, raw_message) /* /obj/item/device/radio/proc/accept_rad(obj/item/device/radio/R as obj, message) @@ -632,21 +424,21 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use if(!position || !(position.z in level)) return -1 if(freq == SYND_FREQ) - if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys + if(!(src.syndie)) //Checks to see if it's allowed on that frequency, based on the encryption keys return -1 if (!on) return -1 - if (!freq) //recieved on main frequency + if (!freq) //received on main frequency if (!listening) return -1 else var/accept = (freq==frequency && listening) if (!accept) - for (var/ch_name in channels) - var/datum/radio_frequency/RF = secure_radio_connections[ch_name] - if (RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING)) - accept = 1 - break + for(var/ch_name in channels) + if(channels[ch_name] & FREQ_LISTENING) + if(radiochannels[ch_name] == text2num(freq) || syndie) //the radiochannels list is located in communications.dm + accept = 1 + break if (!accept) return -1 return canhear_range @@ -655,7 +447,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use var/range = receive_range(freq, level) if(range > -1) - return get_mobs_in_view(canhear_range, src) + return get_hearers_in_view(canhear_range, src) /obj/item/device/radio/examine() @@ -781,7 +573,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use src.name = "broken radio" return - secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT) + secure_radio_connections[ch_name] = add_radio(src, radiochannels[ch_name]) return diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 200c0a0a855..3945db0dd83 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -4,7 +4,9 @@ icon_state = "taperecorder_empty" item_state = "analyzer" w_class = 2 + flags = HEAR slot_flags = SLOT_BELT + languages = ALL //this is a translator, after all. m_amt = 60 g_amt = 30 force = 2 @@ -89,23 +91,10 @@ icon_state = "taperecorder_idle" -/obj/item/device/taperecorder/hear_talk(mob/living/M as mob, msg) +/obj/item/device/taperecorder/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) if(mytape && recording) - var/ending = copytext(msg, length(msg)) mytape.timestamp += mytape.used_capacity - if(M.stuttering) - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] stammers, \"[msg]\"" - return - if(M.getBrainLoss() >= 60) - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] gibbers, \"[msg]\"" - return - if(ending == "?") - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] asks, \"[msg]\"" - return - else if(ending == "!") - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] exclaims, \"[msg]\"" - return - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] says, \"[msg]\"" + mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [message]" /obj/item/device/taperecorder/verb/record() @@ -194,19 +183,16 @@ break if(mytape.storedinfo.len < i) break - var/turf/T = get_turf(src) - T.visible_message("Tape Recorder: [mytape.storedinfo[i]]") + say(mytape.storedinfo[i]) if(mytape.storedinfo.len < i + 1) playsleepseconds = 1 sleep(10) - T = get_turf(src) - T.visible_message("Tape Recorder: End of recording.") + say("End of recording.") else playsleepseconds = mytape.timestamp[i + 1] - mytape.timestamp[i] if(playsleepseconds > 14) sleep(10) - T = get_turf(src) - T.visible_message("Tape Recorder: Skipping [playsleepseconds] seconds of silence") + say("Skipping [playsleepseconds] seconds of silence") playsleepseconds = 1 i++ @@ -299,4 +285,4 @@ //Random colour tapes /obj/item/device/tape/random/New() - icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]" \ No newline at end of file + icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]" diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 4126181ea89..64951705275 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -64,11 +64,6 @@ if(!attached_device) return attached_device.HasProximity(AM) return -/obj/item/device/transfer_valve/hear_talk(mob/living/M, msg) - if(!attached_device) return - attached_device.hear_talk(M, msg) - return - /obj/item/device/transfer_valve/attack_self(mob/user as mob) user.set_machine(src) var/dat = {" Valve properties: diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 39cdf599d33..7d8a694085b 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -154,12 +154,6 @@ if(nadeassembly) nadeassembly.on_found(finder) -/obj/item/weapon/grenade/chem_grenade/hear_talk(mob/living/M, msg) - if(nadeassembly) - nadeassembly.hear_talk(M, msg) - - - /obj/item/weapon/grenade/chem_grenade/prime() if(stage != READY) return diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index dac4c8ba93b..f45a8b5766f 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -1,4 +1,5 @@ /obj + languages = HUMAN //var/datum/module/mod //not used var/m_amt = 0 // metal var/g_amt = 0 // glass @@ -126,18 +127,6 @@ /obj/proc/hide(h) return - -/obj/proc/hear_talk(mob/M as mob, text) -/* - var/mob/mo = locate(/mob) in src - if(mo) - var/rendered = "[M.name]: [text]" - mo.show_message(rendered, 2) - */ - return - - - //If a mob logouts/logins in side of an object you can use this proc /obj/proc/on_log() ..() diff --git a/code/game/say.dm b/code/game/say.dm new file mode 100644 index 00000000000..4688b10a46f --- /dev/null +++ b/code/game/say.dm @@ -0,0 +1,154 @@ +/* + Miauw's big Say() rewrite. + This file has the basic atom/movable level speech procs. + And the base of the send_speech() proc, which is the core of saycode. +*/ +var/list/freqtospan = list( + SCI_FREQ = "sciradio", + MED_FREQ = "medradio", + ENG_FREQ = "engradio", + SUPP_FREQ = "suppradio", + SERV_FREQ = "servradio", + SEC_FREQ = "secradio", + COMM_FREQ = "comradio", + AIPRIV_FREQ = "aiprivradio", + SYND_FREQ = "syndradio", + DSQUAD_FREQ = "dsquadradio" + ) + +var/freqtoname = list( + SCI_FREQ = "Science", + MED_FREQ = "Medical", + ENG_FREQ = "Engineering", + SUPP_FREQ = "Supply", + SERV_FREQ = "Service", + SEC_FREQ = "Security", + COMM_FREQ = "Command", + AIPRIV_FREQ = "AI Private", + SYND_FREQ = "#unkn" + ) + +/atom/movable/proc/say(message) + if(!can_speak()) + return + if(message == "" || !message) + return + send_speech(message) + +/atom/movable/proc/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + return + +/atom/movable/proc/can_speak() + return 1 + +/atom/movable/proc/send_speech(message, range) + var/rendered = compose_message(src, languages, message) + for(var/atom/movable/AM in get_hearers_in_view(range, src)) + AM.Hear(rendered, src, languages, message) + +/atom/movable/proc/compose_message(atom/movable/speaker, message_langs, raw_message, radio_freq) + //This proc uses text() because it is faster than appending strings. Thanks BYOND. + //Basic span + var/spanpart1 = "" + //Start name span. + var/spanpart2 = "" + //Radio freq/name display + var/freqpart = radio_freq ? "\[[get_radio_name(radio_freq)]\] " : "" + //Speaker name + var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]" + //End name span. + var/endspanpart = "" + //Message + var/messagepart = " [lang_treat(speaker, message_langs, raw_message)]" + + return "[spanpart1][spanpart2][freqpart][compose_track_href(speaker, message_langs, raw_message, radio_freq)][namepart][compose_job(speaker, message_langs, raw_message, radio_freq)][endspanpart][messagepart]" + +/atom/movable/proc/compose_track_href(atom/movable/speaker, message_langs, raw_message, radio_freq) + return "" + +/atom/movable/proc/compose_job(atom/movable/speaker, message_langs, raw_message, radio_freq) + return "" + +/atom/movable/proc/say_quote(var/text) + if(!text) + return "says, \"...\"" //not the best solution, but it will stop a large number of runtimes. The cause is somewhere in the Tcomms code + var/ending = copytext(text, length(text)) + if (ending == "?") + return "asks, \"[text]\"" + if (ending == "!") + return "exclaims, \"[text]\"" + + return "says, \"[text]\"" + +/atom/movable/proc/lang_treat(atom/movable/speaker, message_langs, raw_message) + if(languages & message_langs) + var/atom/movable/AM = speaker.GetSource() + if(AM) + return AM.say_quote(raw_message) + else + return speaker.say_quote(raw_message) + else if(message_langs & HUMAN) + var/atom/movable/AM = speaker.GetSource() + if(AM) + return AM.say_quote(stars(raw_message)) + else + return speaker.say_quote(stars(raw_message)) + else if(message_langs & MONKEY) + return "chimpers." + else if(message_langs & ALIEN) + return "hisses." + else if(message_langs & ROBOT) + return "beeps rapidly." + else + return "makes a strange sound." + +/proc/get_radio_span(freq) + var/returntext = freqtospan[freq] + if(returntext) + return returntext + return "radio" + +/proc/get_radio_name(freq) + var/returntext = freqtoname[freq] + if(returntext) + return returntext + return "[copytext("[freq]", 1, 4)].[copytext("[freq]", 4, 5)]" + +/atom/movable/proc/GetVoice() + return name + +/atom/movable/proc/IsVocal() + return 1 + +/atom/movable/proc/get_alt_name() + return + +//these exist mostly to deal with the AIs hrefs and job stuff. +/atom/movable/proc/GetJob() + return + +/atom/movable/proc/GetTrack() + return + +/atom/movable/proc/GetSource() + return + +/atom/movable/proc/GetRadio() + +/atom/movable/virtualspeaker + var/job + var/faketrack + var/atom/movable/source + var/obj/item/device/radio/radio + +/atom/movable/virtualspeaker/GetJob() + return job + +/atom/movable/virtualspeaker/GetTrack() + return faketrack + +/atom/movable/virtualspeaker/GetSource() + return source + +/atom/movable/virtualspeaker/GetRadio() + return radio diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 96db681687e..3d4d7b0c1cf 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -453,19 +453,20 @@ var/list/admin_verbs_hideable = list( /client/proc/make_sound(var/obj/O in world) set category = "Special Verbs" - set name = "Make Sound" - set desc = "Display a message to everyone who can hear the target" - if(O) - var/message = input("What do you want the message to be?", "Make Sound") as text|null + set name = "Osay" + set desc = "Makes an object say something." + if(istype(O)) + var/message = input("What do you want the message to be?", "Make Sound") as text | null if(!message) - return - for (var/mob/V in hearers(O)) - V.show_message(message, 2) - log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound") - message_admins("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound", 1) + return + var/templanguages = O.languages + O.languages |= ALL + O.say(message) + O.languages = templanguages + log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z] say [message]") + message_admins("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z]. say [message]", 1) feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - /client/proc/togglebuildmodeself() set name = "Toggle Build Mode Self" set category = "Special Verbs" diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm index f17048048f6..af2f1a73b59 100644 --- a/code/modules/assembly/bomb.dm +++ b/code/modules/assembly/bomb.dm @@ -81,10 +81,6 @@ if(bombassembly) bombassembly.on_found(finder) -/obj/item/device/onetankbomb/hear_talk(mob/living/M as mob, msg) - if(bombassembly) - bombassembly.hear_talk(M, msg) - // ---------- Procs below are for tanks that are used exclusively in 1-tank bombs ---------- diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index d764c35ead0..b2bde69df19 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -19,8 +19,6 @@ /obj/item/device/assembly_holder/proc/process_activation(var/obj/item/device/D) return - - /obj/item/device/assembly_holder/IsAssemblyHolder() return 1 @@ -87,20 +85,12 @@ if(a_right) a_right.on_found(finder) - -/obj/item/device/assembly_holder/hear_talk(mob/living/M as mob, msg) - if(a_left) - a_left.hear_talk(M, msg) - if(a_right) - a_right.hear_talk(M, msg) - /obj/item/device/assembly_holder/Move() ..() if(a_left && a_right) a_left.holder_movement() a_right.holder_movement() - return - + return /obj/item/device/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess if(a_left && a_right) @@ -166,13 +156,3 @@ if(master) master.receive_signal() return 1 - - - - - - - - - - diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm index d8ee49932ce..ad873803a34 100644 --- a/code/modules/assembly/voice.dm +++ b/code/modules/assembly/voice.dm @@ -5,26 +5,27 @@ m_amt = 500 g_amt = 50 origin_tech = "magnets=1" + flags = HEAR var/listening = 0 var/recorded //the activation message - -/obj/item/device/assembly/voice/hear_talk(mob/living/M as mob, msg) + +/obj/item/device/assembly/voice/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) if(listening) - recorded = msg + recorded = raw_message listening = 0 - var/turf/T = get_turf(src) //otherwise it won't work in hand - T.visible_message("\icon[src] beeps, \"Activation message is '[recorded]'.\"") - else - if(findtext(msg, recorded)) + say("Activation message is '[recorded]'.") + else + if(findtext(raw_message, recorded)) pulse(0) - + /obj/item/device/assembly/voice/activate() if(secured) if(!holder) listening = !listening - var/turf/T = get_turf(src) - T.visible_message("\icon[src] beeps, \"[listening ? "Now" : "No longer"] recording input.\"") + say("[listening ? "Now" : "No longer"] recording input.") +/obj/machinery/vending/say_quote(text) + return "beeps, \"[text]\"" /obj/item/device/assembly/voice/attack_self(mob/user) if(!user) return 0 @@ -33,5 +34,5 @@ /obj/item/device/assembly/voice/toggle_secure() - . = ..() - listening = 0 \ No newline at end of file + . = ..() + listening = 0 diff --git a/code/modules/mob/dead/observer/login.dm.bak b/code/modules/mob/dead/observer/login.dm.bak deleted file mode 100644 index b71c51271eb..00000000000 --- a/code/modules/mob/dead/observer/login.dm.bak +++ /dev/null @@ -1,14 +0,0 @@ -/mob/observer/Login() - ..() - if (!src.start) - src.start = 1 - var/A = locate(/area/start) - var/list/L = list( ) - for(var/turf/T in A) - L += T - src.loc = pick(L) - src.client.screen = null - if (!isturf(src.loc)) - src.client.eye = src.loc - src.client.perspective = EYE_PERSPECTIVE - return \ No newline at end of file diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 982545d4381..9082eeb01f7 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -10,6 +10,7 @@ blinded = 0 anchored = 1 // don't get pushed around invisibility = INVISIBILITY_OBSERVER + languages = ALL var/can_reenter_corpse var/datum/hud/living/carbon/hud = null // hud var/bootime = 0 diff --git a/code/modules/mob/dead/observer/observer.dm.bak b/code/modules/mob/dead/observer/observer.dm.bak deleted file mode 100644 index 766b14c1fa7..00000000000 --- a/code/modules/mob/dead/observer/observer.dm.bak +++ /dev/null @@ -1,170 +0,0 @@ -/mob/observer/New(mob/corpse) - set invisibility = 10 - - ..() - - if(corpse) - src.corpse = corpse - src.loc = get_turf(corpse.loc) - src.real_name = corpse.real_name - src.name = corpse.real_name - - src.sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS - src.see_invisible = 10 - src.see_in_dark = 100 - src.verbs += /mob/observer/proc/dead_tele - src.verbs += /mob/observer/proc/reenter_corpse - -/mob/proc/ghostize() - set name = "Ghost" - set desc = "You cannot be revived as a ghost" - if(src.client) - src.client.mob = new/mob/observer(src) - return - -/mob/observer/Move(NewLoc, direct) - if(NewLoc) - src.loc = NewLoc - return - if((direct & NORTH) && src.y < world.maxy) - src.y++ - if((direct & SOUTH) && src.y > 1) - src.y-- - if((direct & EAST) && src.x < world.maxx) - src.x++ - if((direct & WEST) && src.x > 1) - src.x-- - -/mob/observer/examine() - if(usr) usr << src.desc - -/mob/observer/can_use_hands() return 0 -/mob/observer/is_active() return 0 - -/mob/observer/Stat() - ..() - statpanel("Status") - if (src.client.statpanel == "Status") - if(ticker && ticker.mode) - if (ticker.timeleft) - stat(null, "ETA-[ticker.timeleft / 600 % 60]:[ticker.timeleft / 100 % 6][ticker.timeleft / 100 % 10]") - - if(ticker.mode.name == "Corporate Restructuring" && ticker.target) - var/icon = ticker.target.name - var/icon2 = ticker.target.real_name - var/area = get_area(ticker.target) - stat(null, text("Target: [icon2] (as [icon]) is in [area]")) - - if(ticker.mode.name == "AI malfunction" && ticker.processing) - stat(null, text("Time until all [station_name()]'s systems are taken over: [(ticker.AIwin - ticker.AItime) / 600 % 60]:[(ticker.AIwin - ticker.AItime) / 100 % 6][(ticker.AIwin - ticker.AItime) / 10 % 10]")) - - if (ticker.mode.name == "ctf") - stat(null, text("Red Team - [ticker.red_score]")) - stat(null, text("Green Team - [ticker.green_score]")) - -/mob/observer/proc/reenter_corpse() - set category = "Special Verbs" - set name = "Re-enter Corpse" - if(!corpse) - alert("You don't have a corpse!") - return - if(corpse.stat == 2) - alert("Your body is dead!") - return - if(src.client && src.client.holder && src.client.holder.state == 2) - var/rank = src.client.holder.rank - src.client.clear_admin_verbs() - src.client.holder.state = 1 - src.client.update_admins(rank) - src.client.mob = corpse - del(src) - -/mob/observer/proc/dead_tele() - set category = "Special Verbs" - set name = "Teleport" - set desc= "Teleport" - if((usr.stat != 2) || !istype(usr, /mob/observer)) - usr << "Not when you're not dead!" - return - var/A - usr.verbs -= /mob/observer/proc/dead_tele - spawn(50) - usr.verbs += /mob/observer/proc/dead_tele - A = input("Area to jump to", "BOOYEA", A) in list("Engine","Hallways","Toxins","Storage","Maintenance","Crew Quarters","Medical","Security","Chapel","Bridge") - - switch (A) - if ("Engine") - var/list/L = list() - for(var/area/B in world) - if(istype(B, /area/engine) && !istype(B, /area/engine/combustion) && !istype(B, /area/engine/engine_walls)) - L += B - A = pick(L) - if ("Hallways") - var/list/L = list() - for(var/area/B in world) - if(istype(B, /area/hallway)) - L += B - A = pick(L) - if ("Toxins") - var/list/L = list() - for(var/area/B in world) - if(istype(B, /area/toxins) && !istype(B, /area/toxins/test_chamber)) - L += B - A = pick(L) - if ("Storage") - var/list/L = list() - for(var/area/B in world) - if(istype(B, /area/storage)) - L += B - A = pick(L) - if ("Maintenance") - var/list/L = list() - for(var/area/B in world) - if(istype(B, /area/maintenance)) - L += B - A = pick(L) - if ("Crew Quarters") - var/list/L = list() - for(var/area/B in world) - if(istype(B, /area/crew_quarters)) - L += B - A = pick(L) - if ("Medical") - var/list/L = list() - for(var/area/B in world) - if(istype(B, /area/medical)) - L += B - A = pick(L) - if ("Security") - var/list/L = list() - for(var/area/B in world) - if(istype(B, /area/security)) - L += B - A = pick(L) - if ("Chapel") - var/list/L = list() - for(var/area/B in world) - if(istype(B, /area/chapel)) - L += B - A = pick(L) - if ("Bridge") - var/list/L = list() - for(var/area/B in world) - if(istype(B, /area/bridge)) - L += B - A = pick(L) - - var/list/L = list() - for(var/turf/T in A) - if(!T.density) - var/clear = 1 - for(var/obj/O in T) - if(O.density) - clear = 0 - break - if(clear) - L+=T - - usr.loc = pick(L) - - diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index 51f5fb8a1ea..37831db6f0a 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -1,6 +1,3 @@ -/mob/dead/observer/say_understands(var/other) - return 1 - /mob/dead/observer/say(var/message) message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) @@ -18,6 +15,10 @@ return . = src.say_dead(message) + +/mob/dead/observer/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + src << message + /* for (var/mob/M in hearers(null, null)) if (!M.stat) diff --git a/code/modules/mob/dead/observer/say.dm.bak b/code/modules/mob/dead/observer/say.dm.bak deleted file mode 100644 index 5f330b2ab93..00000000000 --- a/code/modules/mob/dead/observer/say.dm.bak +++ /dev/null @@ -1,30 +0,0 @@ -/mob/observer/say_understands(var/other) - return 1 - -/mob/observer/say(var/message) - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - - if (!message) - return - - world.log_say("Ghost/[src.key] : [message]") - - if (src.muted) - return - - . = src.say_dead(message) - - for (var/mob/M in hearers(null, null)) - if (!M.stat) - if(M.job == "Chaplain") - if (prob (49)) - M.show_message("You hear muffled speech... but nothing is there...", 2) - else - M.show_message("[stutter(message)]", 2) - else - if (prob(50)) - return - else if (prob (95)) - M.show_message("You hear muffled speech... but nothing is there...", 2) - else - M.show_message("[stutter(message)]", 2) diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 0263237a03d..402f6e9597d 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -5,19 +5,17 @@ /mob/living/carbon/alien name = "alien" voice_name = "alien" - voice_message = "hisses" say_message = "hisses" icon = 'icons/mob/alien.dmi' gender = NEUTER dna = null faction = list("alien") ventcrawler = 2 + languages = ALIEN var/storedPlasma = 250 var/max_plasma = 500 - alien_talk_understand = 1 - var/obj/item/weapon/card/id/wear_id = null // Fix for station bounced radios -- Skie var/has_fine_manipulation = 0 diff --git a/code/modules/mob/living/carbon/alien/say.dm b/code/modules/mob/living/carbon/alien/say.dm index a8e2a3e0034..953213ba49e 100644 --- a/code/modules/mob/living/carbon/alien/say.dm +++ b/code/modules/mob/living/carbon/alien/say.dm @@ -1,35 +1,7 @@ -/mob/living/carbon/alien/say_understands(var/other) - if (istype(other, /mob/living/carbon/alien)) - return 1 - return ..() - /mob/living/carbon/alien/say(var/message) - - if (silent) - return - - if (length(message) >= 2) - if (copytext(message, 1, 3) == ":a" || copytext(message, 1, 3) == "#a" || copytext(message, 1, 3) == ".a" ) - message = copytext(message, 3) - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - if (stat == 2) - return say_dead(message) - else - alien_talk(message) - else - if (copytext(message, 1, 2) != "*" && !stat) - playsound(loc, "hiss", 25, 1, 1)//So aliens can hiss while they hiss yo/N - return ..(message, "A") - else - -// ~lol~ -/mob/living/carbon/alien/say_quote(var/text) -// var/ending = copytext(text, length(text)) - - return "[say_message], \"[text]\""; + return ..(message, "A") /mob/living/proc/alien_talk(var/message) - log_say("[key_name(src)] : [message]") message = trim(message) @@ -38,42 +10,17 @@ var/message_a = say_quote(message) var/rendered = "Hivemind, [name] [message_a]" - for (var/mob/living/S in player_list) - if(!S.stat) - if(S.alien_talk_understand) - if(S.alien_talk_understand == alien_talk_understand) - S.show_message(rendered, 2) - else if (S.hivecheck()) - S.show_message(rendered, 2) + for (var/mob/S in player_list) + if((!S.stat && (S.hivecheck())) || S.stat == DEAD) + S << rendered - var/list/listening = hearers(1, src) - listening -= src - listening += src +/mob/living/carbon/alien/handle_inherent_channels(message, message_mode) + if(!..()) + if(message_mode == MODE_ALIEN) + if(hivecheck()) + alien_talk(message) + return 1 + return 0 - var/list/heard = list() - for (var/mob/M in listening) - if(!istype(M, /mob/living/carbon/alien) && !M.alien_talk_understand) - heard += M - - - if (length(heard)) - var/message_b - - message_b = "hsssss" - message_b = say_quote(message_b) - message_b = "[message_b]" - - rendered = "[voice_name] [message_b]" - - for (var/mob/M in heard) - M.show_message(rendered, 2) - - message = say_quote(message) - - rendered = "Hivemind, [name] [message_a]" - - for (var/mob/M in player_list) - if (istype(M, /mob/new_player)) - continue - if (M.stat > 1) - M.show_message(rendered, 2) \ No newline at end of file +/mob/living/carbon/alien/hivecheck() + return 1 diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index 6b5196a25c9..2f0d259284c 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -1,6 +1,7 @@ //This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 /mob/living/carbon/brain + languages = HUMAN var/obj/item/container = null var/timeofhostdeath = 0 var/emp_damage = 0//Handles a type of MMI damage @@ -16,35 +17,7 @@ death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA ghostize() //Ghostize checks for key so nothing else is necessary. ..() - -/mob/living/carbon/brain/say_understands(var/other)//Goddamn is this hackish, but this say code is so odd - if (istype(other, /mob/living/silicon/ai)) - if(!(container && istype(container, /obj/item/device/mmi))) - return 0 - else - return 1 - if (istype(other, /mob/living/silicon/decoy)) - if(!(container && istype(container, /obj/item/device/mmi))) - return 0 - else - return 1 - if (istype(other, /mob/living/silicon/pai)) - if(!(container && istype(container, /obj/item/device/mmi))) - return 0 - else - return 1 - if (istype(other, /mob/living/silicon/robot)) - if(!(container && istype(container, /obj/item/device/mmi))) - return 0 - else - return 1 - if (istype(other, /mob/living/carbon/human)) - return 1 - if (istype(other, /mob/living/carbon/slime)) - return 1 - return ..() - - + /mob/living/carbon/brain/update_canmove() if(in_contents_of(/obj/mecha)) canmove = 1 else canmove = 0 diff --git a/code/modules/mob/living/carbon/brain/say.dm b/code/modules/mob/living/carbon/brain/say.dm index 4b8174cc21b..5c9d10c9ae5 100644 --- a/code/modules/mob/living/carbon/brain/say.dm +++ b/code/modules/mob/living/carbon/brain/say.dm @@ -1,7 +1,4 @@ /mob/living/carbon/brain/say(var/message) - if (silent) - return - if(!(container && istype(container, /obj/item/device/mmi))) return //No MMI, can't speak, bucko./N else @@ -10,8 +7,14 @@ return else message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher - if(istype(container, /obj/item/device/mmi/radio_enabled)) - var/obj/item/device/mmi/radio_enabled/R = container - if(R.radio) - spawn(0) R.radio.hear_talk(src, sanitize(message)) - ..() \ No newline at end of file + ..() + +/mob/living/carbon/brain/radio(message, message_mode) + if(message_mode && istype(container, /obj/item/device/mmi/radio_enabled)) + var/obj/item/device/mmi/radio_enabled/R = container + if(R.radio) + R.radio.talk_into(src, sanitize(message)) + return ITALICS | REDUCE_RANGE + +/mob/living/carbon/brain/lingcheck() + return 0 diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 3fffa8a5958..cd5c6e591e5 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -1,4 +1,5 @@ /mob/living/carbon/human + languages = HUMAN //Hair colour and style var/hair_color = "000" var/hair_style = "Bald" diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index be98cf67ed9..205decd4442 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -1,50 +1,4 @@ /mob/living/carbon/human/say(var/message) - - if(silent) - return - - // Needed so when they die they can talk in dead chat normally without needing to ghost. - if(stat != DEAD) - - //Mimes dont speak! Changeling hivemind and emotes are allowed. - if(!IsVocal()) - if(length(message) >= 2) - if(mind && mind.changeling) - if(copytext(message, 1, 2) != "*" && copytext(message, 1, 3) != ":g" && copytext(message, 1, 3) != ":G" && copytext(message, 1, 3) != ":ï") - return - else - return ..(message) - if(stat == DEAD) - return ..(message) - - if(length(message) >= 1) //In case people forget the '*help' command, this will slow them the message and prevent people from saying one letter at a time - if (copytext(message, 1, 2) != "*") - return - - if(dna) - message = dna.species.handle_speech(message,src) - - if(viruses.len) - for(var/datum/disease/pierrot_throat/D in viruses) - var/list/temp_message = text2list(message, " ") //List each word in the message - var/list/pick_list = list() - for(var/i = 1, i <= temp_message.len, i++) //Create a second list for excluding words down the line - pick_list += i - for(var/i=1, ((i <= D.stage) && (i <= temp_message.len)), i++) //Loop for each stage of the disease or until we run out of words - if(prob(3 * D.stage)) //Stage 1: 3% Stage 2: 6% Stage 3: 9% Stage 4: 12% - var/H = pick(pick_list) - if(findtext(temp_message[H], "*") || findtext(temp_message[H], ";") || findtext(temp_message[H], ":")) continue - temp_message[H] = "HONK" - pick_list -= H //Make sure that you dont HONK the same word twice - message = list2text(temp_message, " ") - - if(wear_mask) - message = wear_mask.speechModification(message) - - if ((HULK in mutations) && health >= 25 && length(message)) - if(copytext(message, 1, 2) != "*") - message = "[uppertext(replacetext(message, ".", "!"))]!!" //because I don't know how to code properly in getting vars from other files -Bro - ..(message) /mob/living/carbon/human/say_quote(text) @@ -67,53 +21,30 @@ return "says, \"[text]\""; -/mob/living/carbon/human/proc/forcesay(list/append) - if(stat == CONSCIOUS) - if(client) - var/virgin = 1 //has the text been modified yet? - var/temp = winget(client, "input", "text") - if(findtextEx(temp, "Say \"", 1, 7) && length(temp) > 5) //case sensitive means +/mob/living/carbon/human/treat_message(message) + if(dna) + message = dna.species.handle_speech(message,src) - temp = replacetext(temp, ";", "") //general radio + if ((HULK in mutations) && health >= 25 && length(message)) + message = "[uppertext(replacetext(message, ".", "!"))]!!" //because I don't know how to code properly in getting vars from other files -Bro - if(findtext(trim_left(temp), ":", 6, 7)) //dept radio - temp = copytext(trim_left(temp), 8) - virgin = 0 + if(viruses.len) + for(var/datum/disease/pierrot_throat/D in viruses) + var/list/temp_message = text2list(message, " ") //List each word in the message + var/list/pick_list = list() + for(var/i = 1, i <= temp_message.len, i++) //Create a second list for excluding words down the line + pick_list += i + for(var/i=1, ((i <= D.stage) && (i <= temp_message.len)), i++) //Loop for each stage of the disease or until we run out of words + if(prob(3 * D.stage)) //Stage 1: 3% Stage 2: 6% Stage 3: 9% Stage 4: 12% + var/H = pick(pick_list) + if(findtext(temp_message[H], "*") || findtext(temp_message[H], ";") || findtext(temp_message[H], ":")) continue + temp_message[H] = "HONK" + pick_list -= H //Make sure that you dont HONK the same word twice + message = list2text(temp_message, " ") - if(virgin) - temp = copytext(trim_left(temp), 6) //normal speech - virgin = 0 - - while(findtext(trim_left(temp), ":", 1, 2)) //dept radio again (necessary) - temp = copytext(trim_left(temp), 3) - - if(findtext(temp, "*", 1, 2)) //emotes - return - - var/trimmed = trim_left(temp) - if(length(trimmed)) - if(append) - temp += pick(append) - - say(temp) - winset(client, "input", "text=[null]") - - -/mob/living/carbon/human/say_understands(var/other) - if (istype(other, /mob/living/silicon/ai)) - return 1 - if (istype(other, /mob/living/silicon/decoy)) - return 1 - if (istype(other, /mob/living/silicon/pai)) - return 1 - if (istype(other, /mob/living/silicon/robot)) - return 1 - if (istype(other, /mob/living/carbon/brain)) - return 1 - if (istype(other, /mob/living/carbon/slime)) - return 1 - return ..() + message = ..(message) + return message /mob/living/carbon/human/GetVoice() if(istype(src.wear_mask, /obj/item/clothing/mask/gas/voice)) @@ -138,11 +69,78 @@ special_voice = new_voice return - /mob/living/carbon/human/proc/UnsetSpecialVoice() special_voice = "" return - /mob/living/carbon/human/proc/GetSpecialVoice() - return special_voice \ No newline at end of file + return special_voice + +/mob/living/carbon/human/binarycheck() + if(ears) + var/obj/item/device/radio/headset/dongle = ears + if(!istype(dongle)) return 0 + if(dongle.translate_binary) return 1 + +/mob/living/carbon/human/radio(message, message_mode) + . = ..() + if(. != 0) + return . + + switch(message_mode) + if(MODE_HEADSET) + if (ears) + ears.talk_into(src, message) + return ITALICS | REDUCE_RANGE + + if(MODE_SECURE_HEADSET) + if (ears) + ears.talk_into(src, message, 1) + return ITALICS | REDUCE_RANGE + + if(MODE_DEPARTMENT) + if (ears) + ears.talk_into(src, message, message_mode) + return ITALICS | REDUCE_RANGE + + if(message_mode in radiochannels) + if(ears) + ears.talk_into(src, message, message_mode) + return ITALICS | REDUCE_RANGE + + return 0 + +/mob/living/carbon/human/get_alt_name() + if(name != GetVoice()) + return " (as [get_id_name("Unknown")])" + +/mob/living/carbon/human/proc/forcesay(list/append) //this proc is at the bottom of the file because quote fuckery makes notepad++ cri + if(stat == CONSCIOUS) + if(client) + var/virgin = 1 //has the text been modified yet? + var/temp = winget(client, "input", "text") + if(findtextEx(temp, "Say \"", 1, 7) && length(temp) > 5) //"case sensitive means + + temp = replacetext(temp, ";", "") //general radio + + if(findtext(trim_left(temp), ":", 6, 7)) //dept radio + temp = copytext(trim_left(temp), 8) + virgin = 0 + + if(virgin) + temp = copytext(trim_left(temp), 6) //normal speech + virgin = 0 + + while(findtext(trim_left(temp), ":", 1, 2)) //dept radio again (necessary) + temp = copytext(trim_left(temp), 3) + + if(findtext(temp, "*", 1, 2)) //emotes + return + + var/trimmed = trim_left(temp) + if(length(trimmed)) + if(append) + temp += pick(append) + + say(temp) + winset(client, "input", "text=[null]") diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm index cd48a67b575..7423fdf456e 100644 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ b/code/modules/mob/living/carbon/human/whisper.dm @@ -1,44 +1,28 @@ /mob/living/carbon/human/whisper(message as text) - if(!IsVocal()) return if(say_disabled) //This is here to try to identify lag problems usr << "Speech is currently admin-disabled." return - + message = trim(copytext(strip_html_simple(message), 1, MAX_MESSAGE_LEN)) - - if (!message || silent) + if(!can_speak(message)) return - + + message = "[message]" log_whisper("[src.name]/[src.key] : [message]") if (src.client) if (src.client.prefs.muted & MUTE_IC) src << "You cannot whisper (muted)." - return + return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return + log_whisper("[src.name]/[src.key] : [message]") - - if (src.stat == DEAD) - return src.say_dead(message) - - var/alt_name = "" - if (src.name != GetVoice()) - alt_name = " (as [get_id_name("Unknown")])" - // Mute disability - if (src.sdisabilities & MUTE) - return - - if (is_muzzled()) - return + var/alt_name = get_alt_name() var/whispers = "whispers" - - var/critical = InCritical() // We are unconscious but not in critical, so don't allow them to whisper. @@ -54,99 +38,35 @@ message = Ellipsis(message, 10, 1) whispers = "whispers in their final breath" - var/italics = 1 - var/message_range = 1 + message = treat_message(message) - if(src.wear_mask) - message = wear_mask.speechModification(message) + var/list/listening_dead = list() + for(var/mob/M in player_list) + if(M.stat == DEAD && (M.client.prefs.toggles & CHAT_GHOSTEARS) && client) + listening_dead |= M - if (src.stuttering) - message = stutter(message) - - for (var/obj/O in view(message_range, src)) - spawn (0) - if (O) - O.hear_talk(src, message) - - var/list/listening = hearers(message_range, src) - listening -= src - if(!critical) - listening += src + var/list/listening = get_hear(1, src) | listening_dead var/list/eavesdropping = hearers(2, src) - eavesdropping -= src eavesdropping -= listening var/list/watching = hearers(5, src) - watching -= src watching -= listening watching -= eavesdropping - var/list/heard_a = list() // understood us - var/list/heard_b = list() // didn't understand us + var/rendered - for (var/mob/M in listening) - if (M.say_understands(src)) - heard_a += M - else - heard_b += M - - var/rendered = null - - for (var/mob/M in watching) - if (M.say_understands(src)) - rendered = "[src.name] [whispers] something." - else - rendered = "[src.voice_name] [whispers] something." + rendered = "[src.name] [whispers] something." + for(var/mob/M in watching) M.show_message(rendered, 2) - if (length(heard_a)) - var/message_a = message - - if (italics) - message_a = "[message_a]" - //This appears copied from carbon/living say.dm so the istype check for mob is probably not needed. Appending for src is also not needed as the game will check that automatically. - rendered = "[GetVoice()][alt_name] [whispers], \"[message_a]\"" - - for (var/mob/M in heard_a) - M.show_message(rendered, 2) - - if (length(heard_b)) - var/message_b - - if (src.voice_message) - message_b = src.voice_message - else - message_b = stars(message) - - if (italics) - message_b = "[message_b]" - - rendered = "[src.voice_name] [whispers], \"[message_b]\"" - - for (var/mob/M in heard_b) - M.show_message(rendered, 2) - - for (var/mob/M in eavesdropping) - if (M.say_understands(src)) - var/message_c - message_c = stars(message) - rendered = "[GetVoice()][alt_name] [whispers], \"[message_c]\"" - M.show_message(rendered, 2) - else - rendered = "[src.voice_name] [whispers] something." - M.show_message(rendered, 2) - - if (italics) - message = "[message]" rendered = "[GetVoice()][alt_name] [whispers], \"[message]\"" - for (var/mob/M in dead_mob_list) - if (!(M.client)) - continue - if (M.stat > 1 && !(M in heard_a)) - M.show_message(rendered, 2) + for(var/mob/M in listening) + M.Hear(rendered, src, languages, message) - // We whispered our final breath, now we die and show the message you have sent - // since it might have been cut off and it would be annoying not being able to know. - if(critical) - src << rendered + message = stars(message) + rendered = "[GetVoice()][alt_name] [whispers], \"[message]\"" + for(var/mob/M in eavesdropping) + M.Hear(rendered, src, languages, message) + + if(critical) //Dying words. succumb(1) diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index 51d5fbd1bc7..7d0f4cf4e4e 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -1,12 +1,12 @@ /mob/living/carbon/monkey name = "monkey" voice_name = "monkey" - voice_message = "chimpers" say_message = "chimpers" icon = 'icons/mob/monkey.dmi' icon_state = "monkey1" gender = NEUTER pass_flags = PASSTABLE + languages = MONKEY update_icon = 0 ///no need to call regenerate_icon ventcrawler = 1 diff --git a/code/modules/mob/living/carbon/monkey/say.dm b/code/modules/mob/living/carbon/monkey/say.dm index b2136593683..58cfe0e35f8 100644 --- a/code/modules/mob/living/carbon/monkey/say.dm +++ b/code/modules/mob/living/carbon/monkey/say.dm @@ -1,8 +1,2 @@ -/mob/living/carbon/monkey/say(var/message) - if (silent) - return - else - return ..() - /mob/living/carbon/monkey/say_quote(var/text) - return "[src.say_message], \"[text]\""; + return "[say_message], \"[text]\""; diff --git a/code/modules/mob/living/carbon/say.dm b/code/modules/mob/living/carbon/say.dm new file mode 100644 index 00000000000..61f1fef423e --- /dev/null +++ b/code/modules/mob/living/carbon/say.dm @@ -0,0 +1,11 @@ +/mob/living/carbon/treat_message(message) + message = ..(message) + if(wear_mask) + message = wear_mask.speechModification(message) + + return message + +/mob/living/carbon/can_speak_vocal(message) + if(!..() || silent) + return 0 + return 1 diff --git a/code/modules/mob/living/carbon/slime/powers.dm b/code/modules/mob/living/carbon/slime/powers.dm index e474e32523c..ef91582c3ec 100644 --- a/code/modules/mob/living/carbon/slime/powers.dm +++ b/code/modules/mob/living/carbon/slime/powers.dm @@ -217,7 +217,7 @@ var/mob/living/carbon/slime/new_slime = pick(babies) new_slime.a_intent = "harm" - new_slime.universal_speak = universal_speak + new_slime.languages = languages if(src.mind) src.mind.transfer_to(new_slime) else diff --git a/code/modules/mob/living/carbon/slime/say.dm b/code/modules/mob/living/carbon/slime/say.dm index 9c2a59dbe94..0ede0e1d1bf 100644 --- a/code/modules/mob/living/carbon/slime/say.dm +++ b/code/modules/mob/living/carbon/slime/say.dm @@ -1,8 +1,5 @@ /mob/living/carbon/slime/say(var/message) - if (silent) - return - else - return ..() + ..() /mob/living/carbon/slime/say_quote(var/text) var/ending = copytext(text, length(text)) @@ -14,8 +11,10 @@ return "telepathically chirps, \"[text]\""; -/mob/living/carbon/slime/say_understands(var/other) - if (istype(other, /mob/living/carbon/slime)) - return 1 - return ..() - +/mob/living/carbon/slime/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + if(speaker != src && !radio_freq) + if (speaker in Friends) + speech_buffer = list() + speech_buffer += speaker.name + speech_buffer += lowertext(html_decode(message)) + ..() diff --git a/code/modules/mob/living/carbon/slime/slime.dm b/code/modules/mob/living/carbon/slime/slime.dm index e038ddc8bfe..4bec30c1aa5 100644 --- a/code/modules/mob/living/carbon/slime/slime.dm +++ b/code/modules/mob/living/carbon/slime/slime.dm @@ -3,10 +3,10 @@ icon = 'icons/mob/slimes.dmi' icon_state = "grey baby slime" pass_flags = PASSTABLE - voice_message = "bleb!" say_message = "hums" ventcrawler = 2 var/is_adult = 0 + languages = SLIME | HUMAN layer = 5 diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index a1de3adb051..c93828d01f2 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -1,5 +1,6 @@ /mob/living see_invisible = SEE_INVISIBLE_LIVING + languages = HUMAN //Health and life related vars var/maxHealth = 100 //Maximum health that should be possible. diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index adc313a9c40..4b5ecff807d 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -1,3 +1,21 @@ +//bitflag #defines for radio return. +#define ITALICS 1 +#define REDUCE_RANGE 2 +#define NOPASS 4 + +//message modes. you're not supposed to mess with these. +#define MODE_HEADSET "headset" +#define MODE_ROBOT "robot" +#define MODE_R_HAND "right hand" +#define MODE_L_HAND "left hand" +#define MODE_INTERCOM "intercom" +#define MODE_BINARY "binary" +#define MODE_WHISPER "whisper" +#define MODE_SECURE_HEADSET "secure headset" +#define MODE_DEPARTMENT "department" +#define MODE_ALIEN "alientalk" +#define MODE_HOLOPAD "holopad" +#define MODE_CHANGELING "changeling" var/list/department_radio_keys = list( ":r" = "right hand", "#r" = "right hand", ".r" = "right hand", @@ -54,339 +72,97 @@ var/list/department_radio_keys = list( ) /mob/living/proc/binarycheck() - if (istype(src, /mob/living/silicon/pai)) - return - if (issilicon(src)) - return 1 - if (!ishuman(src)) - return - var/mob/living/carbon/human/H = src - if (H.ears) - var/obj/item/device/radio/headset/dongle = H.ears - if(!istype(dongle)) return - if(dongle.translate_binary) return 1 + return 0 -/mob/living/proc/IsVocal() - return 1 - -/mob/living/proc/hivecheck() - if (isalien(src)) return 1 - if (!ishuman(src)) return - var/mob/living/carbon/human/H = src - if (H.ears) - var/obj/item/device/radio/headset/dongle = H.ears - if(!istype(dongle)) return - if(dongle.translate_hive) return 1 - -/mob/living/say(var/message, var/bubble_type) +/mob/living/say(message, bubble_type) message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - if (!message) + if(stat == DEAD) + say_dead(message) return - if (stat == 2) - return say_dead(message) - - if (src.client) - if(client.prefs.muted & MUTE_IC) - src << "You cannot speak in IC (muted)." - return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return - - // stat == 2 is handled above, so this stops transmission of uncontious messages - if (stat) + if(stat) return - // Mute disability - if (sdisabilities & MUTE) + if(check_emote(message)) return - // emotes - if (copytext(message, 1, 2) == "*" && !stat) - return emote(copytext(message, 2)) + if(!can_speak_basic(message)) //Stat is seperate so I can handle whispers properly. + return - var/alt_name = "" - if (istype(src, /mob/living/carbon/human) && name != GetVoice()) - var/mob/living/carbon/human/H = src - alt_name = " (as [H.get_id_name("Unknown")])" - var/italics = 0 - var/message_range = null - var/message_mode = null + var/message_mode = get_message_mode(message) - if (getBrainLoss() >= 60 && prob(50)) - if (ishuman(src)) - message_mode = "headset" - // Special message handling - else if (copytext(message, 1, 2) == ";") - if (ishuman(src)) - message_mode = "headset" - else if(ispAI(src) || isrobot(src)) - message_mode = "pAI" + if(message_mode == MODE_HEADSET || message_mode == MODE_ROBOT) + message = copytext(message, 2) + else if(message_mode) + message = copytext(message, 3) + if(findtext(message, " ", 1, 2)) message = copytext(message, 2) - else if (length(message) >= 2) - var/channel_prefix = copytext(message, 1, 3) - - message_mode = department_radio_keys[channel_prefix] - //world << "channel_prefix=[channel_prefix]; message_mode=[message_mode]" - if (message_mode) - message = trim(copytext(message, 3)) - if (!(ishuman(src) || istype(src, /mob/living/simple_animal/parrot) || isrobot(src)) && (message_mode=="department" || (message_mode in radiochannels))) // If they're not a human, parrot, or robot, and they're trying to use a radio channel - message_mode = null //only humans can use headsets - // Check changed so that parrots can use headsets. Other simple animals do not have ears and will cause runtimes. - // And borgs -Sieve - - if (!message) + if(handle_inherent_channels(message, message_mode)) //Hiveminds, binary chat & holopad. return - // :downs: - if (getBrainLoss() >= 60) - message = derpspeech(message, stuttering) - - if (stuttering) - message = stutter(message) - -/* //qw do not have beesease atm. - if(virus) - if(virus.name=="beesease" && virus.stage>=2) - if(prob(virus.stage*10)) - var/bzz = length(message) - message = "B" - for(var/i=0,i[mind.changeling.changelingID]: [message]" - return - - if (message_mode == "alientalk") - if(alien_talk_understand || hivecheck()) - //message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) //seems redundant - alien_talk(message) + if(!can_speak_vocal(message)) return - if(is_muzzled()) // Intentionally after changeling hivemind check + message = treat_message(message) + + if(!message || message == "") return - var/list/obj/item/used_radios = new - - switch (message_mode) - if ("headset") - if (src:ears) - src:ears.talk_into(src, message) - used_radios += src:ears - - message_range = 1 - italics = 1 - - - if ("secure headset") - if (src:ears) - src:ears.talk_into(src, message, 1) - used_radios += src:ears - - message_range = 1 - italics = 1 - - if ("right hand") - if (r_hand) - r_hand.talk_into(src, message) - used_radios += src:r_hand - - message_range = 1 - italics = 1 - - if ("left hand") - if (l_hand) - l_hand.talk_into(src, message) - used_radios += src:l_hand - - message_range = 1 - italics = 1 - - if ("intercom") - for (var/obj/item/device/radio/intercom/I in view(1, null)) - I.talk_into(src, message) - used_radios += I - - message_range = 1 - italics = 1 - - //I see no reason to restrict such way of whispering - if ("whisper") - whisper(message) - return - - if ("binary") - if(robot_talk_understand || binarycheck()) - //message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) //seems redundant - robot_talk(message) - return - - if ("department") - if (src:ears) - src:ears.talk_into(src, message, message_mode) - used_radios += src:ears - message_range = 1 - italics = 1 - - if ("pAI") - if (src:radio) - src:radio.talk_into(src, message) - used_radios += src:radio - message_range = 1 - italics = 1 - -////SPECIAL HEADSETS START - else - //world << "SPECIAL HEADSETS" - if (message_mode in radiochannels) - if(isrobot(src))//Seperates robots to prevent runtimes from the ear stuff - var/mob/living/silicon/robot/R = src - if(R.radio)//Sanityyyy - R.radio.talk_into(src, message, message_mode) - used_radios += R.radio - else - if (src:ears) - src:ears.talk_into(src, message, message_mode) - used_radios += src:ears - message_range = 1 - italics = 1 -/////SPECIAL HEADSETS END - - if(!IsVocal()) + var/message_range = 7 + var/radio_return = radio(message, message_mode) + if(radio_return & NOPASS) //There's a whisper() message_mode, no need to continue the proc if that is called return + if(radio_return & ITALICS) + message = "[message]" + if(radio_return & REDUCE_RANGE) + message_range = 1 - var/list/listening + send_speech(message, message_range, src, bubble_type) - listening = get_mobs_in_view(message_range, src) + log_say("[name]/[key] : [message]") + +/mob/living/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + if(!client) + return + var/deaf_message + var/deaf_type + if(speaker != src) + deaf_message = "[name] talks but you cannot hear them." + deaf_type = 1 + else + deaf_message = "You can't hear yourself!" + deaf_type = 2 // Since you should be able to hear yourself without looking + if(!(message_langs & languages) || force_compose) //force_compose is so AIs don't end up without their hrefs. + message = compose_message(speaker, message_langs, raw_message, radio_freq) + show_message(message, 2, deaf_message, deaf_type) + return message + +/mob/living/send_speech(message, message_range = 7, obj/source = src, bubble_type) + var/list/listening = get_hearers_in_view(message_range, source) + var/list/listening_dead = list() for(var/mob/M in player_list) - if (!M.client) - continue //skip monkeys and leavers - if (istype(M, /mob/new_player)) - continue - if(M.stat == DEAD && (M.client.prefs.toggles & CHAT_GHOSTEARS) && src.client) // src.client is so that ghosts don't have to listen to mice - listening|=M + if(M.stat == DEAD && (M.client.prefs.toggles & CHAT_GHOSTEARS) && client) // client is so that ghosts don't have to listen to mice + listening_dead |= M - var/turf/T = get_turf(src) - var/list/W = hear(message_range, T) + listening -= listening_dead //so ghosts dont hear stuff twice - for (var/obj/O in ((W | contents)-used_radios)) - W |= O + var/rendered = compose_message(src, languages, message) + for(var/atom/movable/AM in listening) + AM.Hear(rendered, src, languages, message) - for (var/mob/M in W) - W |= M.contents - - for (var/atom/A in W) - if(istype(A, /mob/living/simple_animal/parrot)) //Parrot speech mimickry - if(A == src) - continue //Dont imitate ourselves - - var/mob/living/simple_animal/parrot/P = A - if(P.speech_buffer.len >= 10) - P.speech_buffer.Remove(pick(P.speech_buffer)) - P.speech_buffer.Add(html_decode(message)) - - if(isslime(A)) //Slimes answering to people - if (A == src) - continue - - var/mob/living/carbon/slime/S = A - if (src in S.Friends) - S.speech_buffer = list() - S.speech_buffer.Add(src) - S.speech_buffer.Add(lowertext(html_decode(message))) - - if(istype(A, /obj/)) //radio in pocket could work, radio in backpack wouldn't --rastaf0 - var/obj/O = A - spawn (0) - if(O && !istype(O.loc, /obj/item/weapon/storage)) - O.hear_talk(src, message) - - -/* Commented out as replaced by code above from BS12 - for (var/obj/O in ((V | contents)-used_radios)) //radio in pocket could work, radio in backpack wouldn't --rastaf0 - spawn (0) - if (O) - O.hear_talk(src, message) -*/ - -/* if(isbrain(src))//For brains to properly talk if they are in an MMI..or in a brain. Could be extended to other mobs I guess. - for(var/obj/O in loc)//Kinda ugly but whatever. - if(O) - spawn(0) - O.hear_talk(src, message) -*/ - - - var/list/heard_a = list() // understood us - var/list/heard_b = list() // didn't understand us - - for (var/M in listening) - if(hascall(M,"say_understands")) - if (M:say_understands(src)) - heard_a += M - else - heard_b += M - - var/rendered = null - if (length(heard_a)) - var/message_a = say_quote(message) - - if (italics) - message_a = "[message_a]" - - rendered = "[GetVoice()][alt_name] [message_a]" - - for (var/M in heard_a) - if(hascall(M,"show_message")) - var/deaf_message = "" - var/deaf_type = 1 - if(M != src) - deaf_message = "[name][alt_name] talks but you cannot hear them." - else - deaf_message = "You cannot hear yourself!" - deaf_type = 2 // Since you should be able to hear yourself without looking - M:show_message(rendered, 2, deaf_message, deaf_type) - - if (length(heard_b)) - var/message_b - - if (voice_message) - message_b = voice_message - else - message_b = stars(message) - message_b = say_quote(message_b) - - if (italics) - message_b = "[message_b]" - - rendered = "[voice_name] [message_b]" - - - for (var/M in heard_b) - if(hascall(M,"show_message")) - M:show_message(rendered, 2) + for(var/mob/M in listening_dead) + M.Hear(rendered, src, languages, message) //speech bubble var/list/speech_bubble_recipients = list() - for(var/mob/M in heard_a + heard_b) + for(var/mob/M in (listening + listening_dead)) if(M.client) speech_bubble_recipients.Add(M.client) spawn(0) flick_overlay(image('icons/mob/talk.dmi', src, "h[bubble_type][say_test(message)]",MOB_LAYER+1), speech_bubble_recipients, 30) - log_say("[name]/[key] : [message]") - -/mob/living/proc/GetVoice() - return name - /mob/living/proc/say_test(var/text) var/ending = copytext(text, length(text)) if (ending == "?") @@ -394,3 +170,103 @@ var/list/department_radio_keys = list( else if (ending == "!") return "2" return "0" + +/mob/living/can_speak(message) //For use outside of Say() + if(can_speak_basic(message) && can_speak_vocal(message)) + return 1 + +/mob/living/proc/can_speak_basic(message) //Check BEFORE handling of xeno and ling channels + if(!message || message == "") + return + + if(client) + if(client.prefs.muted & MUTE_IC) + src << "You cannot speak in IC (muted)." + return + if(client.handle_spam_prevention(message,MUTE_IC)) + return + + return 1 + +/mob/living/proc/can_speak_vocal(message) //Check AFTER handling of xeno and ling channels + if(!message) + return + + if(sdisabilities & MUTE) + return + + if(is_muzzled()) + return + + if(!IsVocal()) + return + + return 1 + +/mob/living/proc/check_emote(message) + if(copytext(message, 1, 2) == "*") + emote(copytext(message, 2)) + return 1 + +/mob/living/proc/get_message_mode(message) + if(copytext(message, 1, 2) == ";") + return MODE_HEADSET + else if(length(message) > 2) + return department_radio_keys[copytext(message, 1, 3)] + +/mob/living/proc/handle_inherent_channels(message, message_mode) + if(message_mode == MODE_CHANGELING) + if(lingcheck()) + log_say("[mind.changeling.changelingID]/[src.key] : [message]") + for(var/mob/M in mob_list) + if(M.lingcheck() || M.stat == DEAD) + M << "[mind.changeling.changelingID]: [message]" + return 1 + return 0 + +/mob/living/proc/treat_message(message) + if(getBrainLoss() >= 60) + message = derpspeech(message, stuttering) + + if(stuttering) + message = stutter(message) + + return message + +/mob/living/proc/radio(message, message_mode, steps) + switch(message_mode) + if(MODE_R_HAND) + if (r_hand) + r_hand.talk_into(src, message) + return ITALICS | REDUCE_RANGE + + if(MODE_L_HAND) + if (l_hand) + l_hand.talk_into(src, message) + return ITALICS | REDUCE_RANGE + + if(MODE_INTERCOM) + for (var/obj/item/device/radio/intercom/I in view(1, null)) + I.talk_into(src, message) + return ITALICS | REDUCE_RANGE + + if(MODE_BINARY) + if(binarycheck()) + robot_talk(message) + return ITALICS | REDUCE_RANGE //Does not return 0 since this is only reached by humans, not borgs or AIs. + + if(MODE_WHISPER) + whisper(message) + return NOPASS + return 0 + +/mob/living/lingcheck() + if(mind && mind.changeling) + return 1 + +/mob/living/say_quote() + if (stuttering) + return "stammers, \"[text]\"" + if (getBrainLoss() >= 60) + return "gibbers, \"[text]\"" + return ..() diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index b634a85b137..ceea1384d12 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -19,6 +19,7 @@ var/list/ai_list = list() anchored = 1 density = 1 status_flags = CANSTUN|CANPARALYSE|CANPUSH + force_compose = 1 //This ensures that the AI always composes it's own hear message. Needed for hrefs and job display. var/list/network = list("SS13") var/obj/machinery/camera/current = null var/list/connected_robots = list() diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index d6906b3e2d2..cb1c31f8c41 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -1,22 +1,23 @@ /mob/living/silicon/ai/say(var/message) - if(parent && istype(parent) && parent.stat != 2) + if(parent && istype(parent) && parent.stat != 2) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. parent.say(message) return - //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. ..(message) -/mob/living/silicon/ai/say_understands(var/other) - if (istype(other, /mob/living/carbon/human)) - return 1 - if (istype(other, /mob/living/silicon/robot)) - return 1 - if (istype(other, /mob/living/silicon/decoy)) - return 1 - if (istype(other, /mob/living/carbon/brain)) - return 1 - if (istype(other, /mob/living/silicon/pai)) - return 1 - return ..() +/mob/living/silicon/ai/compose_track_href(atom/movable/speaker, message_langs, raw_message, radio_freq) + //this proc assumes that the message originated from a radio. if the speaker is not a virtual speaker this will probably fuck up hard. + var/mob/M = speaker.GetSource() + var/obj/item/device/radio = speaker.GetRadio() + if(M) + var/faketrack = "byond://?src=\ref[radio];track2=\ref[src];track=\ref[M]" + if(speaker.GetTrack()) + faketrack = "byond://?src=\ref[radio];track2=\ref[src];faketrack=\ref[M]" + return "" + return "" + +/mob/living/silicon/ai/compose_job(atom/movable/speaker, message_langs, raw_message, radio_freq) + //Also includes the for AI hrefs, for convenience. + return " [radio_freq ? "(" + speaker.GetJob() + ")" : ""]" + "[speaker.GetSource() ? "" : ""]" /mob/living/silicon/ai/say_quote(var/text) var/ending = copytext(text, length(text)) @@ -31,6 +32,39 @@ /mob/living/silicon/ai/IsVocal() return !config.silent_ai +/mob/living/silicon/ai/get_message_mode(message) + if(copytext(message, 1, 3) in list(":h", ":H", ".h", ".H", "#h", "#H")) + return MODE_HOLOPAD + else + return ..() + +/mob/living/silicon/ai/handle_inherent_channels(message, message_mode) + . = ..() + if(.) + return . + + if(message_mode == MODE_HOLOPAD) + holopad_talk(message) + return 1 + +//For holopads only. Usable by AI. +/mob/living/silicon/ai/proc/holopad_talk(var/message) + log_say("[key_name(src)] : [message]") + + message = trim(message) + + if (!message) + return + + var/obj/machinery/hologram/holopad/T = current + if(istype(T) && T.hologram && T.master == src)//If there is a hologram and its master is the user. + send_speech(message, 7, T, "R") + src << "Holopad transmitted, [real_name] \"[message]\""//The AI can "hear" its own message. + else + src << "No holopad connected." + return + + // Make sure that the code compiles with AI_VOX undefined #ifdef AI_VOX diff --git a/code/modules/mob/living/silicon/decoy/death.dm b/code/modules/mob/living/silicon/decoy/death.dm deleted file mode 100644 index d3996e2fa70..00000000000 --- a/code/modules/mob/living/silicon/decoy/death.dm +++ /dev/null @@ -1,10 +0,0 @@ -/mob/living/silicon/decoy/death(gibbed) - if(stat == DEAD) return - stat = DEAD - icon_state = "ai-crash" - spawn(10) - explosion(loc, 3, 6, 12, 15) - - for(var/obj/machinery/ai_status_display/O in world) //change status - O.mode = 2 - return ..(gibbed) \ No newline at end of file diff --git a/code/modules/mob/living/silicon/decoy/decoy.dm b/code/modules/mob/living/silicon/decoy/decoy.dm deleted file mode 100644 index 5881e860eeb..00000000000 --- a/code/modules/mob/living/silicon/decoy/decoy.dm +++ /dev/null @@ -1,32 +0,0 @@ -/mob/living/silicon/decoy - name = "AI" - icon = 'icons/mob/AI.dmi'// - icon_state = "ai" - anchored = 1 - canmove = 0 - -/mob/living/silicon/decoy/New() - ..() - src.icon = 'icons/mob/AI.dmi' - src.icon_state = "ai" - src.anchored = 1 - src.canmove = 0 - -/mob/living/silicon/decoy/say_understands(var/other) - if (istype(other, /mob/living/carbon/human)) - return 1 - if (istype(other, /mob/living/silicon/robot)) - return 1 - if (istype(other, /mob/living/silicon/ai)) - return 1 - return ..() - -/mob/living/silicon/decoy/say_quote(var/text) - var/ending = copytext(text, length(text)) - - if (ending == "?") - return "queries, \"[text]\""; - else if (ending == "!") - return "declares, \"[copytext(text, 1, length(text))]\""; - - return "states, \"[text]\""; \ No newline at end of file diff --git a/code/modules/mob/living/silicon/decoy/life.dm b/code/modules/mob/living/silicon/decoy/life.dm deleted file mode 100644 index 8a336b8353b..00000000000 --- a/code/modules/mob/living/silicon/decoy/life.dm +++ /dev/null @@ -1,15 +0,0 @@ -/mob/living/silicon/decoy/Life() - if (src.stat == 2) - return - else - if (src.health <= config.health_threshold_dead && src.stat != 2) - death() - return - - -/mob/living/silicon/decoy/updatehealth() - if(status_flags & GODMODE) - health = maxHealth - stat = CONSCIOUS - return - health = maxHealth - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 820f646421f..042f0430361 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -4,8 +4,6 @@ mouse_opacity density = 0 - robot_talk_understand = 0 - var/network = "SS13" var/obj/machinery/camera/current = null @@ -13,7 +11,6 @@ var/list/software = list() var/userDNA // The DNA string of our assigned user var/obj/item/device/paicard/card // The card we inhabit - var/obj/item/device/radio/radio // Our primary radio var/speakStatement = "states" var/speakExclamation = "declares" diff --git a/code/modules/mob/living/silicon/pai/say.dm b/code/modules/mob/living/silicon/pai/say.dm index d314224682b..4b1cd2c1626 100644 --- a/code/modules/mob/living/silicon/pai/say.dm +++ b/code/modules/mob/living/silicon/pai/say.dm @@ -1,18 +1,3 @@ -/mob/living/silicon/pai/say_understands(var/other) - if (istype(other, /mob/living/carbon/human)) - return 1 - if (istype(other, /mob/living/silicon/robot)) - return 1 - if (istype(other, /mob/living/silicon/pai)) - return 1 - if (istype(other, /mob/living/silicon/ai)) - return 1 - if (istype(other, /mob/living/silicon/decoy)) - return 1 - if (istype(other, /mob/living/carbon/brain)) - return 1 - return ..() - /mob/living/silicon/pai/say_quote(var/text) var/ending = copytext(text, length(text)) @@ -27,4 +12,7 @@ if(silence_time) src << "Communication circuits remain unitialized." else - ..(msg) \ No newline at end of file + ..(msg) + +/mob/living/silicon/pai/binarycheck() + return 0 diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index b3f4a4e35de..eb72fb2b9f8 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -242,7 +242,7 @@ src.medHUD = !src.medHUD if("translator") if(href_list["toggle"]) - src.universal_speak = !src.universal_speak + languages = languages == ALL ? HUMAN & ROBOT : ALL if("doorjack") if(href_list["jack"]) if(src.cable && src.cable.machine) @@ -301,7 +301,7 @@ if(s == "medical HUD") dat += "Medical Analysis Suite[(src.medHUD) ? " On" : " Off"]
" if(s == "universal translator") - dat += "Universal Translator[(src.universal_speak) ? " On" : " Off"]
" + dat += "Universal Translator[(languages == ALL) ? " On" : " Off"]
" if(s == "projection array") dat += "Projection Array
" if(s == "camera jack") @@ -454,7 +454,7 @@ /mob/living/silicon/pai/proc/softwareTranslator() . = {"

Universal Translator


When enabled, this device will automatically convert all spoken and written language into a format that any known recipient can understand.

- The device is currently [ (src.universal_speak) ? "en" : "dis" ]abled.
+ The device is currently [ (languages == ALL) ? "en" : "dis" ]abled.
Toggle Device
"} return . diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index 2ea5dffaa79..e5634f9708c 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -207,4 +207,4 @@ else for(var/mob/O in hearers(src, null)) O.show_message(message, m_type) - return \ No newline at end of file + return diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 02ac07af28c..59106208f25 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -26,7 +26,6 @@ var/module_state_2 = null var/module_state_3 = null - var/obj/item/device/radio/borg/radio = null var/mob/living/silicon/ai/connected_ai = null var/obj/item/weapon/stock_parts/cell/cell = null var/obj/machinery/camera/camera = null diff --git a/code/modules/mob/living/silicon/robot/say.dm b/code/modules/mob/living/silicon/robot/say.dm index 065a23bd8bf..7c252092b3e 100644 --- a/code/modules/mob/living/silicon/robot/say.dm +++ b/code/modules/mob/living/silicon/robot/say.dm @@ -1,18 +1,3 @@ -/mob/living/silicon/robot/say_understands(var/other) - if (istype(other, /mob/living/silicon/ai)) - return 1 - if (istype(other, /mob/living/silicon/decoy)) - return 1 - if (istype(other, /mob/living/carbon/human)) - return 1 - if (istype(other, /mob/living/carbon/brain)) - return 1 - if (istype(other, /mob/living/silicon/pai)) - return 1 -// if (istype(other, /mob/living/silicon/hivebot)) -// return 1 - return ..() - /mob/living/silicon/robot/say_quote(var/text) var/ending = copytext(text, length(text)) @@ -24,4 +9,4 @@ return "states, \"[text]\""; /mob/living/silicon/robot/IsVocal() - return !config.silent_borg \ No newline at end of file + return !config.silent_borg diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 3e9b78638ab..574a739793d 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -1,97 +1,8 @@ /mob/living/silicon/say(var/message) - if (!message) - return - - if (src.client) - if(client.prefs.muted & MUTE_IC) - src << "You cannot send IC messages (muted)." - return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return - - if (stat == 2) - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - return say_dead(message) - - //Must be concious to speak - if (stat) - return - - if (length(message) >= 2) - if ((copytext(message, 1, 3) == ":b") || (copytext(message, 1, 3) == ":B") || \ - (copytext(message, 1, 3) == "#b") || (copytext(message, 1, 3) == "#B") || \ - (copytext(message, 1, 3) == ".b") || (copytext(message, 1, 3) == ".B")) - if(istype(src, /mob/living/silicon/pai)) - return ..(message, "R") - message = copytext(message, 3) - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - robot_talk(message) - else if ((copytext(message, 1, 3) == ":h") || (copytext(message, 1, 3) == ":H") || \ - (copytext(message, 1, 3) == "#h") || (copytext(message, 1, 3) == "#H") || \ - (copytext(message, 1, 3) == ".h") || (copytext(message, 1, 3) == ".H")) - if(isAI(src)&&client)//For patching directly into AI holopads. - var/mob/living/silicon/ai/U = src - message = copytext(message, 3) - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - U.holopad_talk(message) - else//Will not allow anyone by an active AI to use this function. - src << "This function is not available to you." - return - else - return ..(message, "R") - else - return ..(message, "R") - -//For holopads only. Usable by AI. -/mob/living/silicon/ai/proc/holopad_talk(var/message) - - log_say("[key_name(src)] : [message]") - - message = trim(message) - - if (!message) - return - - var/obj/machinery/hologram/holopad/T = src.current - if(istype(T) && T.hologram && T.master == src)//If there is a hologram and its master is the user. - var/message_a = say_quote(message) - - //Human-like, sorta, heard by those who understand humans. - var/rendered_a = "[name] [message_a]" - - //Speach distorted, heard by those who do not understand AIs. - message = stars(message) - var/message_b = say_quote(message) - var/rendered_b = "[voice_name] [message_b]" - - src << "Holopad transmitted, [real_name] [message_a]"//The AI can "hear" its own message. - var/list/speech_bubble_recipients = list() - for(var/mob/M in hearers(T.loc))//The location is the object, default distance. - if(M.say_understands(src))//If they understand AI speak. Humans and the like will be able to. - M.show_message(rendered_a, 2) - else//If they do not. - M.show_message(rendered_b, 2) - if(M.client) - speech_bubble_recipients.Add(M.client) - - //speech bubble - spawn(0) - flick_overlay(image('icons/mob/talk.dmi', src, "hR[say_test(message)]",MOB_LAYER+1), speech_bubble_recipients, 30) - - /*Radios "filter out" this conversation channel so we don't need to account for them. - This is another way of saying that we won't bother dealing with them.*/ - else - src << "No holopad connected." - return + return ..(message, "R") /mob/living/proc/robot_talk(var/message) - log_say("[key_name(src)] : [message]") - - message = trim(message) - - if (!message) - return var/desig = "Default Cyborg" //ezmode for taters if(istype(src, /mob/living/silicon)) var/mob/living/silicon/S = src @@ -100,46 +11,50 @@ var/rendered = "Robotic Talk, [name] [message_a]" for (var/mob/living/S in living_mob_list) - if(S.robot_talk_understand && (S.robot_talk_understand == robot_talk_understand)) // This SHOULD catch everything caught by the one below, but I'm not going to change it. + if(S.binarycheck() || S.stat == DEAD) if(istype(S , /mob/living/silicon/ai)) var/renderedAI = "Robotic Talk, [name] ([desig]) [message_a]" - S.show_message(renderedAI, 2) + S << renderedAI else - S.show_message(rendered, 2) + S << rendered +/mob/living/silicon/binarycheck() + return 1 - else if (S.binarycheck()) - if(istype(S , /mob/living/silicon/ai)) - var/renderedAI = "Robotic Talk, [name] ([desig]) [message_a]" - S.show_message(renderedAI, 2) - else - S.show_message(rendered, 2) +/mob/living/silicon/lingcheck() + return 0 //Borged or AI'd lings can't speak on the ling channel. - var/list/listening = hearers(1, src) - listening -= src - listening += src +/mob/living/silicon/radio(message, message_mode) + . = ..() + if(. != 0) + return . - var/list/heard = list() - for (var/mob/M in listening) - if(!istype(M, /mob/living/silicon) && !M.robot_talk_understand) - heard += M + if(message_mode == "robot") + if (radio) + radio.talk_into(src, message) + return REDUCE_RANGE - if (length(heard)) - var/message_b + else if(message_mode in radiochannels) + if(radio) + radio.talk_into(src, message, message_mode) + return ITALICS | REDUCE_RANGE - message_b = "beep beep beep" - message_b = say_quote(message_b) - message_b = "[message_b]" + return 0 - rendered = "[voice_name] [message_b]" +/mob/living/silicon/get_message_mode(message) + . = ..() + if(..() == MODE_HEADSET) + return MODE_ROBOT + else + return . - for (var/mob/M in heard) - M.show_message(rendered, 2) - - message = say_quote(message) - - rendered = "Robotic Talk, [name] [message_a]" - - for (var/mob/M in dead_mob_list) - if(!istype(M,/mob/new_player) && !(istype(M,/mob/living/carbon/brain)))//No meta-evesdropping - M.show_message(rendered, 2) \ No newline at end of file +/mob/living/silicon/handle_inherent_channels(message, message_mode) + . = ..() + if(.) + return . + + if(message_mode == MODE_BINARY) + if(binarycheck()) + robot_talk(message) + return 1 + return 0 diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index d9997f76364..d23561abee5 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -1,13 +1,13 @@ /mob/living/silicon gender = NEUTER - robot_talk_understand = 1 voice_name = "synthesized voice" + languages = ROBOT | HUMAN var/syndicate = 0 var/datum/ai_laws/laws = null//Now... THEY ALL CAN ALL HAVE LAWS var/list/alarms_to_show = list() var/list/alarms_to_clear = list() var/designation = "" - + var/obj/item/device/radio/borg/radio = null //AIs dont use this but this is at the silicon level to advoid copypasta in say() var/list/alarm_types_show = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0) var/list/alarm_types_clear = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 88b0ae3b0cf..f5e164ca75d 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -6,14 +6,9 @@ * AI * Procs / Verbs (usable by players) * Sub-types + * Hear & say (the things we do for gimmicks) */ -/*So you want to delete parrots eh? -heres the locations of their snowflake code: -lines 294-301 in living/say.dm (speech buffer) -135 in living/say.dm (parrots talking into headsets) -*/ - /* * Defines */ @@ -33,7 +28,7 @@ lines 294-301 in living/say.dm (speech buffer) /mob/living/simple_animal/parrot name = "parrot" - desc = "The parrot squaks, \"It's a Parrot! BAWWK!\"" + desc = "The parrot squaks, \"It's a Parrot! BAWWK!\"" //' icon = 'icons/mob/animal.dmi' icon_state = "parrot_fly" icon_living = "parrot_fly" @@ -128,6 +123,41 @@ lines 294-301 in living/say.dm (speech buffer) stat("Held Item", held_item) stat("Mode",a_intent) +/mob/living/simple_animal/parrot/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + if(speaker != src && prob(20)) //Dont imitate ourselves + if(speech_buffer.len >= 20) + speech_buffer -= pick(speech_buffer) + speech_buffer |= html_decode(raw_message) + ..() + +/mob/living/simple_animal/parrot/radio(message, message_mode) //literally copied from human/radio(), but there's no other way to do this. at least it's better than it used to be. + . = ..() + if(. != 0) + return . + + switch(message_mode) + if(MODE_HEADSET) + if (ears) + ears.talk_into(src, message) + return ITALICS | REDUCE_RANGE + + if(MODE_SECURE_HEADSET) + if (ears) + ears.talk_into(src, message, 1) + return ITALICS | REDUCE_RANGE + + if(MODE_DEPARTMENT) + if (ears) + ears.talk_into(src, message, message_mode) + return ITALICS | REDUCE_RANGE + + if(message_mode in radiochannels) + if(ears) + ears.talk_into(src, message, message_mode) + return ITALICS | REDUCE_RANGE + + return 0 + /* * Inventory */ @@ -167,7 +197,7 @@ lines 294-301 in living/say.dm (speech buffer) ears = null for(var/possible_phrase in speak) if(copytext(possible_phrase,1,3) in department_radio_keys) - possible_phrase = copytext(possible_phrase,3,length(possible_phrase)) + possible_phrase = copytext(possible_phrase,3) else usr << "There is nothing to remove from its [remove_from]." return @@ -329,7 +359,7 @@ lines 294-301 in living/say.dm (speech buffer) //-----SPEECH /* Parrot speech mimickry! - Phrases that the parrot hears in mob/living/say() get added to speach_buffer. + Phrases that the parrot Hear()s get added to speach_buffer. Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list. Then it clears the buffer to make sure they dont magically remember something from hours ago. */ if(speech_buffer.len && prob(10)) @@ -372,9 +402,9 @@ lines 294-301 in living/say.dm (speech buffer) useradio = 1 if(copytext(possible_phrase,1,3) in department_radio_keys) - possible_phrase = "[useradio?pick(available_channels):""] [copytext(possible_phrase,3,length(possible_phrase)+1)]" //crop out the channel prefix + possible_phrase = "[useradio?pick(available_channels):""][copytext(possible_phrase,3)]" //crop out the channel prefix else - possible_phrase = "[useradio?pick(available_channels):""] [possible_phrase]" + possible_phrase = "[useradio?pick(available_channels):""][possible_phrase]" newspeak.Add(possible_phrase) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index ee89070f77a..7721a60bda4 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -18,7 +18,6 @@ var/turns_per_move = 1 var/turns_since_move = 0 - universal_speak = 1 var/meat_amount = 0 var/meat_type var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 89817980775..603d3228b8b 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -2,7 +2,7 @@ density = 1 layer = 4 animate_movement = 2 - flags = NOREACT + flags = NOREACT | HEAR var/datum/mind/mind var/stat = 0 //Whether a mob is alive or dead. TODO: Move this to living - Nodrak @@ -121,7 +121,6 @@ //see: setup.dm for list of mutations var/voice_name = "unidentifiable voice" - var/voice_message = null // When you are not understood by others (replaced with just screeches, hisses, chimpers etc.) var/say_message = null // When you are understood by others. Currently only used by aliens and monkeys in their say_quote procs var/list/faction = list("neutral") //A list of factions that this mob is currently in, for hostile mob targetting, amongst other things @@ -157,12 +156,8 @@ var/list/radar_blips = list() // list of screen objects, radar blips var/radar_open = 0 // nonzero is radar is open + var/force_compose = 0 //If this is nonzero, the mob will always compose it's own hear message instead of using the one given in the arguments. var/obj/control_object //Used by admins to possess objects. All mobs should have this var - //Whether or not mobs can understand other mobtypes. These stay in /mob so that ghosts can hear everything. - var/universal_speak = 0 // Set to 1 to enable the mob to speak to everyone - var/robot_talk_understand = 0 - var/alien_talk_understand = 0 - var/turf/listed_turf = null //the current turf being examined in the stat panel diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 49a78224254..0c9d0eced7e 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -5,6 +5,8 @@ var/spawning = 0//Referenced when you want to delete the new_player later on in the code. var/totalPlayers = 0 //Player counts for the Lobby tab var/totalPlayersReady = 0 + + flags = NONE invisibility = 101 diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 82b229243ee..5bf229f61d5 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -1,11 +1,4 @@ -/mob/proc/say() - return - -/mob/verb/whisper() - set name = "Whisper" - set category = "IC" - return - +//Speech verbs. /mob/verb/say_verb(message as text) set name = "Say" set category = "IC" @@ -14,6 +7,11 @@ return usr.say(message) +/mob/verb/whisper(message as text) + set name = "Whisper" + set category = "IC" + return + /mob/verb/me_verb(message as text) set name = "Me" set category = "IC" @@ -53,42 +51,14 @@ if(M.client && M.client.holder && (M.client.prefs.toggles & CHAT_DEAD)) //admins can toggle deadchat on and off. This is a proc in admin.dm and is only give to Administrators and above M << rendered //Admins can hear deadchat, if they choose to, no matter if they're blind/deaf or not. else if(M.stat == DEAD) - M.show_message(rendered, 2) //Takes into account blindness and such. - return - -/mob/proc/say_understands(var/mob/other) - if (src.stat == 2) - return 1 - else if (istype(other, src.type)) - return 1 - else if(other.universal_speak || src.universal_speak) - return 1 - return 0 - -/mob/proc/say_quote(var/text) - if(!text) - return "says, \"...\""; //not the best solution, but it will stop a large number of runtimes. The cause is somewhere in the Tcomms code - var/ending = copytext(text, length(text)) - if (src.stuttering) - return "stammers, \"[text]\""; - if(isliving(src)) - var/mob/living/L = src - if (L.getBrainLoss() >= 60) - return "gibbers, \"[text]\""; - if (ending == "?") - return "asks, \"[text]\""; - if (ending == "!") - return "exclaims, \"[text]\""; - - return "says, \"[text]\""; + //M.show_message(rendered, 2) //Takes into account blindness and such. //preserved so you can look at it and cry at the stupidity of oldcoders. whoever coded this should be punched into the sun + M << rendered /mob/proc/emote(var/act) return -/mob/proc/get_ear() - // returns an atom representing a location on the map from which this - // mob can hear things +/mob/proc/hivecheck() + return 0 - // should be overloaded for all mobs whose "ear" is separate from their "mob" - - return get_turf(src) \ No newline at end of file +/mob/proc/lingcheck() + return 0 diff --git a/code/modules/mob/say_readme.dm b/code/modules/mob/say_readme.dm new file mode 100644 index 00000000000..f57b4249549 --- /dev/null +++ b/code/modules/mob/say_readme.dm @@ -0,0 +1,163 @@ +/*============================================================= +======================MIAUW'S SAY REWRITE====================== +=============================================================== + +This is a basic explanation of how say() works. Read this if you don't understand something. + +The basic "flow" of say() is that a speaker says a message, which is heard by hearers. What appears on screen +is constructed by each hearer seperately, and not by the speaker. + +This rewrite was needed, but is far from perfect. Report any bugs you come across and feel free to fix things up. +Radio code, while very much related to saycode, is not something I wanted to touch, so the code related to that may be messy. + +If you came here to see how to use saycode, all you will ever really need to call is say(message). +To have things react when other things speak around them, add the HEAR flag to their flags variable and +override their Hear() proc. + +=======================PROCS & VARIABLES======================= + Here follows a list of say()-related procs and variables. +global procs + get_radio_span(freq) + Returns the span class associated with that frequency. + + get_radio_name(freq) + Returns the name of that frequency. + + get_hearers_in_view(R, atom/source) + Self-explanatory. Calls get_hear() and then calls recursive_hear_check on everything that get_hear() returns. + + recursive_hear_check(atom/O) + Checks for hearers by looping through the contents of O and the contents of the contents of O and etc and checking + each object for the HEAR flag. Returns a list of objects with the HEAR flag. + + get_hear(range, atom/source) + Like view(), but ignores luminosity. + +/atom/movable + flags + The HEAR flag determines whether something is a hearer or not. + Hear() is only called on procs with this flag. + + languages + Bitmask variable. + What languages this object speaks/understands. If the languages of the speaker don't match the languages + of the hearer, the message will be modified in the hearer's lang_treat(). + + say(message) + Say() is the "mother-proc". It calls all the other procs required for speaking, but does little itself. + At the atom/movable level, say() just calls send_speech. + + Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + This proc handles hearing. What it does varies. For mobs, it treats the message with hearer-specific things + like language and deafness, then outputs it to the hearer. + + IMPORTANT NOTE: If radio_freq is not null, the code will assume that the speaker is virtual! (more info on this in the Radios section below) + + send_speech(message, range) + This proc composes a list of hearers (things with the HEAR flag + dead people) and calls Hear() on them. + Message treatment or composition of output are not done by this proc, these are handled by the rest of + say() and the hearer respectively. + + lang_treat(message, atom/movable/speaker, message_langs, raw_message) + Modifies the message by comparing the languages of the speaker with the languages of the hearer. + Called on the hearer. + + say_quote(text) + Adds a verb and quotes to a message, according to the type of mob. Called on the speaker. + +/mob + say_dead(message) + Sends a message to all dead people. Does not use Hear(). + + compose_message(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + Composes the message mobs see on their screen when they hear something. + + compose_track_href(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + Composes the href tags used by the AI for tracking. Returns "" for all mobs except AIs. + + compose_job(message, atom/movable/speaker, message_langs, raw_message, radio_freq) + Composes the job and the end tag for tracking hrefs. Returns "" for all mobs except AIs. + + hivecheck() + Returns 1 if the mob can hear and talk in the alien hivemind. + + lingcheck() + Returns 1 if the mob can hear and talk in the changeling hivemind. + +/mob/living + say(message) + The say() of mob_living is significantly more complex than that of objects. + Most of the extra code has to do with radios and message treatment. + + check_emote(message) + Checks if the message begins with an * and is thus an emote. + + can_speak(message) + Calls can_speak_basic() and can_speak_vocal() + + can_speak_basic(message) + Sees if the mob can "think" the message. Does not include vocalization or stat checks. + Vocalization checks are in can_speak_vocal, stat checks have to be done manually. + Called right before handle_inherent_channels() + + can_speak_vocal(message) + Checks if the mob can vocalize their message. This is seperate so, for example, muzzles don't block + hivemind chat. + Called right after handle_inherent_channels() + + get_message_mode(message) + Checks the start of the message for a message mode, then returns said message mode. + DOES NOT TRIM THE MESSAGE. This is done manually. + + handle_inherent_channels(message, message_mode) + If message_mode is MODE_BINARY, MODE_ALIEN or MODE_CHANGELING (or, for AIs, MODE_HOLOPAD), this will + handle speaking in those modes. Return 1 to exit say(). + + treat_message(message) + What it says on the tin. Treats the message according to masks, mutantraces, mutations, etc. + Please try to keep things in a logical order (e.g. don't have masks handled before mutations), + even if that means you have to call ..() in the middle of the proc. + + radio(message, message_mode) + Handles talking into radios. Uses a switch to determine what radio to speak into and in which manner to do so. + + Return is a bitflag. + NOPASS = terminate say() (used for whispers) + ITALICS = add italics to the message + REDUCE_RANGE = reduce the message range to one tile. + + Return 0 if no radio was spoken into. + IMPORTANT: remember to call ..() and check for ..()'s return value properly! + +============================RADIOS============================= + +I did not want to interfere with radios too much, but I sort of had to. +For future generations, here is how radio code works: +First, talk_into() is called on a radio. This sends a signal datum into the magic machine that is tcomms, which +eventually results in broadcast_message() being called. + +Broadcast_message() does NOT call say() on radios, but rather calls Hear() on everyone in range of a radio. +This is because the system does not like repeating says. + +Furthermore, I changed radios to not be in the radio_controller. Instead, they are in a global list called all_radios. +This is an associative list, and the numbers as strings are the keys. The values are lists of radios that can hear said frequency. + +To add a radio, simply use add_radio(radio, frequency). To remove a radio, use remove_radio(radio, frequency). +To remove a radio from ALL frequencies, use remove_radio_all(radio). + +VIRTUAL SPEAKERS: +Virtual speakers are simply atom/movables with a few extra variables. +If radio_freq is not null, the code will rely on the fact that the speaker is virtual. This means that several procs will return something: + (all of these procs are defined at the atom/movable level and return "" at that level.) + GetJob() + Returns the job string variable of the virtual speaker. + GetTrack() + Returns wether the tracking href should be fake or not. + GetSource() + Returns the source of the virtual speaker. + GetRadio() + Returns the radio that was spoken through by the source. Needed for AI tracking. + +This is fairly hacky, but it means that I can advoid using istypes. It's mainly relevant for AI tracking and AI job display. + +That's all, folks!*/ \ No newline at end of file diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 609bee39892..88fa0d37683 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -223,11 +223,11 @@ var/list/seen if(!isAi) //crappy check, but without it AI photos would be subject to line of sight from the AI Eye object. Made the best of it by moving the sec camera check inside if(user.client) //To make shooting through security cameras possible - seen = hear(world.view, user.client.eye) //To make shooting through security cameras possible + seen = get_hear(world.view, user.client.eye) //To make shooting through security cameras possible else - seen = hear(world.view, user) + seen = get_hear(world.view, user) else - seen = hear(world.view, target) + seen = get_hear(world.view, target) var/list/turfs = list() for(var/turf/T in range(1, target)) @@ -428,4 +428,4 @@ obj/item/device/camera/siliconcam/robot_camera/proc/borgprint() p.pixel_y = rand(-10, 10) C.toner -= 20 //Cyborgs are very ineffeicient at printing an image visible_message("[C.name] spits out a photograph from a narrow slot on it's chassis.") - usr << "You print a photograph." \ No newline at end of file + usr << "You print a photograph." diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index c6d74483dfe..c11469e8e12 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -143,7 +143,7 @@ proc/wabbajack(mob/living/M) switch(randomize) if("monkey") new_mob = new /mob/living/carbon/monkey(M.loc) - new_mob.universal_speak = 1 + new_mob.languages |= HUMAN if("robot") if(prob(30)) new_mob = new /mob/living/silicon/robot/syndicate(M.loc) @@ -160,13 +160,13 @@ proc/wabbajack(mob/living/M) if(prob(50)) var/mob/living/carbon/slime/Slime = new_mob Slime.is_adult = 1 - new_mob.universal_speak = 1 + new_mob.languages |= HUMAN if("xeno") if(prob(50)) new_mob = new /mob/living/carbon/alien/humanoid/hunter(M.loc) else new_mob = new /mob/living/carbon/alien/humanoid/sentinel(M.loc) - new_mob.universal_speak = 1 + new_mob.languages |= HUMAN /*var/alien_caste = pick("Hunter","Sentinel","Drone","Larva") switch(alien_caste) @@ -174,7 +174,7 @@ proc/wabbajack(mob/living/M) if("Sentinel") new_mob = new /mob/living/carbon/alien/humanoid/sentinel(M.loc) if("Drone") new_mob = new /mob/living/carbon/alien/humanoid/drone(M.loc) else new_mob = new /mob/living/carbon/alien/larva(M.loc) - new_mob.universal_speak = 1*/ + new_mob.languages |= HUMAN*/ if("animal") if(prob(50)) var/beast = pick("carp","bear","mushroom","statue", "bat", "goat","killertomato") @@ -199,7 +199,7 @@ proc/wabbajack(mob/living/M) if("cow") new_mob = new /mob/living/simple_animal/cow(M.loc) if("lizard") new_mob = new /mob/living/simple_animal/lizard(M.loc) else new_mob = new /mob/living/simple_animal/chick(M.loc) - new_mob.universal_speak = 1 + new_mob.languages |= HUMAN if("human") new_mob = new /mob/living/carbon/human(M.loc) diff --git a/code/modules/scripting/Implementations/Telecomms.dm b/code/modules/scripting/Implementations/Telecomms.dm index 7af28b4a418..ab3b73cdbfa 100644 --- a/code/modules/scripting/Implementations/Telecomms.dm +++ b/code/modules/scripting/Implementations/Telecomms.dm @@ -300,9 +300,18 @@ datum/signal freq *= 10 // shift the decimal one place if(!job) - job = "?" + job = "Unknown" + + //SAY REWRITE RELATED CODE. + //This code is a little hacky, but it *should* work. Even though it'll result in a virtual speaker referencing another virtual speaker. vOv + var/atom/movable/virtualspeaker/virt = new(null) + virt.name = source + virt.job = job + virt.faketrack = 1 + virt.languages = HUMAN + //END SAY REWRITE RELATED CODE. - newsign.data["mob"] = null + newsign.data["mob"] = virt newsign.data["mobtype"] = /mob/living/carbon/human newsign.data["name"] = source newsign.data["realname"] = newsign.data["name"] @@ -314,15 +323,12 @@ datum/signal freq = text2num(freq) newsign.frequency = freq - var/datum/radio_frequency/connection = radio_controller.return_frequency(freq) - newsign.data["connection"] = connection - newsign.data["radio"] = hradio newsign.data["vmessage"] = message newsign.data["vname"] = source newsign.data["vmask"] = 0 - newsign.data["level"] = list() + newsign.data["level"] = data["level"] newsign.sanitize_data() diff --git a/code/unused/hivebot/hivebotdefine.dm b/code/unused/hivebot/hivebotdefine.dm index 03222ca9998..50702de79b7 100644 --- a/code/unused/hivebot/hivebotdefine.dm +++ b/code/unused/hivebot/hivebotdefine.dm @@ -4,8 +4,6 @@ icon_state = "basic" health = 80 var/health_max = 80 - robot_talk_understand = 2 - //HUD var/obj/screen/cells = null var/obj/screen/inv1 = null @@ -37,7 +35,6 @@ icon_state = "hive_main" health = 200 var/health_max = 200 - robot_talk_understand = 2 anchored = 1 var/online = 1 diff --git a/tgstation.dme b/tgstation.dme index da6e81f5e41..d95ef20d0cf 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -221,6 +221,7 @@ #include "code\game\atoms_movable.dm" #include "code\game\communications.dm" #include "code\game\dna.dm" +#include "code\game\say.dm" #include "code\game\shuttle_engines.dm" #include "code\game\skincmd.dm" #include "code\game\smoothwall.dm" @@ -1032,9 +1033,6 @@ #include "code\modules\mob\living\silicon\ai\freelook\eye.dm" #include "code\modules\mob\living\silicon\ai\freelook\read_me.dm" #include "code\modules\mob\living\silicon\ai\freelook\update_triggers.dm" -#include "code\modules\mob\living\silicon\decoy\death.dm" -#include "code\modules\mob\living\silicon\decoy\decoy.dm" -#include "code\modules\mob\living\silicon\decoy\life.dm" #include "code\modules\mob\living\silicon\pai\death.dm" #include "code\modules\mob\living\silicon\pai\examine.dm" #include "code\modules\mob\living\silicon\pai\hud.dm"