diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index a692639d802..a47f261f9c1 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -167,8 +167,6 @@ // The old system would loop through lists for a total of 5000 per function call, in an empty server. // This new system will loop at around 1000 in an empty server. -// SCREW THAT SHIT, we're not recursing. - /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. @@ -180,48 +178,40 @@ var/list/range = hear(R, T) - for(var/mob/M in range) - hear += M + for(var/atom/A in range) + if(ismob(A)) + var/mob/M = A + if(M.client) + hear += M + //world.log << "Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])" + else if(istype(A, /obj/item/device/radio)) + hear += A - var/list/objects = list() + if(isobj(A) || ismob(A)) + hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) - for(var/obj/O in range) //Get a list of objects in hearing range. We'll check to see if any clients have their "eye" set to the object - objects += O - - for(var/client/C in clients) - if(!istype(C) || !C.eye || istype(C.eye, /mob/aiEye)) - continue //I have no idea when this client check would be needed, but if this runtimes people won't hear anything - //So kinda paranoid about runtime avoidance. - if(istype(C.eye, /obj/machinery/camera)) - continue //No microphones in cameras. - - if(C.mob in hear) - continue - - var/list/hear_and_objects = (hear|objects) //Combined these lists here instead of doing the combine 3 more times. - - if(C.eye in hear_and_objects) - hear += C.mob - - else if(C.mob && C.mob.loc in hear_and_objects) - hear += C.mob - else if(C.mob && C.mob.loc.loc in hear_and_objects) - hear += C.mob - else if(C.mob && C.mob.loc.loc.loc in hear_and_objects) - hear += C.mob return hear /proc/get_mobs_in_radio_ranges(var/list/obj/item/device/radio/radios) - //set background = 1 + set background = 1 . = list() // Returns a list of mobs who can hear any of the radios given in @radios var/list/speaker_coverage = list() - for(var/i = 1; i <= radios.len; i++) - var/obj/item/device/radio/R = radios[i] + for(var/obj/item/device/radio/R in radios) if(R) + //Cyborg checks. Receiving message uses a bit of cyborg's charge. + var/obj/item/device/radio/borg/BR = R + if(istype(BR) && BR.myborg) + var/mob/living/silicon/robot/borg = BR.myborg + var/datum/robot_component/CO = borg.get_component("radio") + if(!CO) + continue //No radio component (Shouldn't happen) + if(!borg.is_component_functioning("radio") || !borg.use_power(CO.energy_consumption)) + continue //No power. + var/turf/speaker = get_turf(R) if(speaker) for(var/turf/T in hear(R.canhear_range,speaker)) @@ -239,6 +229,7 @@ . |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down. return . + #define SIGN(X) ((X<0)?-1:1) proc diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 515f1788f66..441e48139c8 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -28,6 +28,7 @@ var/global/list/airlocks = list() //list of all airlocks //Languages/species/whitelist. var/global/list/all_species[0] var/global/list/all_languages[0] +var/global/list/language_keys[0] // Table of say codes for all languages var/global/list/all_nations[0] var/global/list/whitelisted_species = list() @@ -114,6 +115,13 @@ var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Al var/datum/language/L = new T all_languages[L.name] = L + for (var/language_name in all_languages) + var/datum/language/L = all_languages[language_name] + if(!(L.flags & NONGLOBAL)) + language_keys[":[lowertext(L.key)]"] = L + language_keys[".[lowertext(L.key)]"] = L + language_keys["#[lowertext(L.key)]"] = L + var/rkey = 0 paths = typesof(/datum/species)-/datum/species for(var/T in paths) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index a790124c152..320e2ecd8d7 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -49,15 +49,19 @@ proc/random_facial_hair_style(gender, species = "Human") return f_style -proc/random_name(gender, speciesName = "Human") - var/datum/species/S = all_species[speciesName] - if(S) - return S.makeName(gender) - else - if(gender==FEMALE) +proc/random_name(gender, species = "Human") + + var/datum/species/current_species + if(species) + current_species = all_species[species] + + if(!current_species || current_species.name == "Human") + if(gender==FEMALE) return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names)) - else - return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names)) + else + return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names)) + else + return current_species.get_random_name(gender) proc/random_skin_tone() switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino")) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index cb2fad75ddb..108246c5ad5 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -341,4 +341,5 @@ proc/checkhtml(var/t) return sanitize(input) /proc/trim_strip_html_properly(var/input, var/max_length = MAX_MESSAGE_LEN) - return trim(strip_html_properly(input, max_length)) + return trim(strip_html_properly(input, max_length)) + \ No newline at end of file diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index d8b5617d2a1..210140bdf9f 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -64,10 +64,11 @@ // Now you can click through portals, wormholes, gateways, and teleporters while observing. -Sayu /obj/machinery/teleport/hub/attack_ghost(mob/user as mob) - var/atom/l = loc - var/obj/machinery/computer/teleporter/com = locate(/obj/machinery/computer/teleporter, locate(l.x - 2, l.y, l.z)) - if(com && com.locked) - user.loc = get_turf(com.locked) + var/obj/machinery/teleport/station/S = power_station + if(S) + var/obj/machinery/computer/teleporter/com = S.teleporter_console + if(com && com.target) + user.loc = get_turf(com.target) /obj/effect/portal/attack_ghost(mob/user as mob) if(target) diff --git a/code/controllers/communications.dm b/code/controllers/communications.dm index b3a5a9f58d1..523cc7055b9 100644 --- a/code/controllers/communications.dm +++ b/code/controllers/communications.dm @@ -124,30 +124,14 @@ On the map: 1455 for AI access */ -var/list/radiochannels = list( - "Common" = 1459, - "Science" = 1351, - "Command" = 1353, - "Medical" = 1355, - "Engineering" = 1357, - "Security" = 1359, - "Response Team" = 1443, - "Deathsquad" = 1441, - "Syndicate" = 1213, - "Supply" = 1347, - "Service" = 1349, - "AI Private" = 1447 -) -//depenging helpers -var/list/DEPT_FREQS = list(1351, 1355, 1357, 1359, 1213, 1443, 1441, 1347, 1349) - -// central command channels, i.e deathsquid & response teams -var/list/CENT_FREQS = list(1441, 1443) - -var/const/COMM_FREQ = 1353 //command, colored gold in chat window +var/const/COMM_FREQ = 1353 var/const/SYND_FREQ = 1213 +var/const/ERT_FREQ = 1345 +var/const/DTH_FREQ = 1341 +var/const/AI_FREQ = 1447 // department channels +var/const/PUB_FREQ = 1459 var/const/SEC_FREQ = 1359 var/const/ENG_FREQ = 1357 var/const/SCI_FREQ = 1351 @@ -155,12 +139,65 @@ var/const/MED_FREQ = 1355 var/const/SUP_FREQ = 1347 var/const/SRV_FREQ = 1349 -// other channels -var/const/AIPRIV_FREQ = 1447 +var/list/radiochannels = list( + "Common" = PUB_FREQ, + "Science" = SCI_FREQ, + "Command" = COMM_FREQ, + "Medical" = MED_FREQ, + "Engineering" = ENG_FREQ, + "Security" = SEC_FREQ, + "Response Team" = ERT_FREQ, + "Special Ops" = DTH_FREQ, + "Syndicate" = SYND_FREQ, + "Supply" = SUP_FREQ, + "Service" = SRV_FREQ, + "AI Private" = AI_FREQ +) + +// central command channels, i.e deathsquid & response teams +var/list/CENT_FREQS = list(ERT_FREQ, DTH_FREQ) + +// Antag channels, i.e. Syndicate +var/list/ANTAG_FREQS = list(SYND_FREQ) + +//depending helpers +var/list/DEPT_FREQS = list(SCI_FREQ, MED_FREQ, ENG_FREQ, SEC_FREQ, SUP_FREQ, SRV_FREQ, ERT_FREQ, SYND_FREQ, DTH_FREQ) #define TRANSMISSION_WIRE 0 #define TRANSMISSION_RADIO 1 +/proc/frequency_span_class(var/frequency) + // Antags! + if (frequency in ANTAG_FREQS) + return "syndradio" + // centcomm channels (deathsquid and ert) + else if(frequency in CENT_FREQS) + return "centradio" + // command channel + else if(frequency == COMM_FREQ) + return "comradio" + // AI private channel + else if(frequency == AI_FREQ) + return "airadio" + // department radio formatting (poorly optimized, ugh) + else if(frequency == SEC_FREQ) + return "secradio" + else if (frequency == ENG_FREQ) + return "engradio" + else if(frequency == SCI_FREQ) + return "sciradio" + else if(frequency == MED_FREQ) + return "medradio" + else if(frequency == SUP_FREQ) // cargo + return "supradio" + else if(frequency == SRV_FREQ) // service + return "srvradio" + // If all else fails and it's a dept_freq, color me purple! + else if(frequency in DEPT_FREQS) + return "deptradio" + + return "radio" + /* filters */ var/const/RADIO_TO_AIRALARM = "1" var/const/RADIO_FROM_AIRALARM = "2" diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 160137a2e5b..062a650c9ba 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -138,7 +138,7 @@ var/list/station_levels = list(1) // Defines which Z-levels the station exists on. var/list/admin_levels= list(2) // Defines which Z-levels which are for admin functionality, for example including such areas as Central Command and the Syndicate Shuttle var/list/contact_levels = list(1, 5) // Defines which Z-levels which, for example, a Code Red announcement may affect - var/list/player_levels = list(1, 3, 4, 5, 6) // Defines all Z-levels a character can typically reach + var/list/player_levels = list(1, 3, 4, 5, 6, 7) // Defines all Z-levels a character can typically reach var/const/minutes_to_ticks = 60 * 10 // Event settings diff --git a/code/datums/wires/radio.dm b/code/datums/wires/radio.dm index a8171270008..9da8890ac32 100644 --- a/code/datums/wires/radio.dm +++ b/code/datums/wires/radio.dm @@ -21,11 +21,24 @@ var/const/WIRE_TRANSMIT = 4 var/obj/item/device/radio/R = holder switch(index) if(WIRE_SIGNAL) - R.listening = !R.listening - R.broadcasting = R.listening + R.listening = !R.listening && !IsIndexCut(WIRE_RECEIVE) + R.broadcasting = R.listening && !IsIndexCut(WIRE_TRANSMIT) if(WIRE_RECEIVE) - R.listening = !R.listening + R.listening = !R.listening && !IsIndexCut(WIRE_SIGNAL) if(WIRE_TRANSMIT) - R.broadcasting = !R.broadcasting \ No newline at end of file + R.broadcasting = !R.broadcasting && !IsIndexCut(WIRE_SIGNAL) + +/datum/wires/radio/UpdateCut(var/index, var/mended) + var/obj/item/device/radio/R = holder + switch(index) + if(WIRE_SIGNAL) + R.listening = mended && !IsIndexCut(WIRE_RECEIVE) + R.broadcasting = mended && !IsIndexCut(WIRE_TRANSMIT) + + if(WIRE_RECEIVE) + R.listening = mended && !IsIndexCut(WIRE_SIGNAL) + + if(WIRE_TRANSMIT) + R.broadcasting = mended && !IsIndexCut(WIRE_SIGNAL) diff --git a/code/defines/procs/radio.dm b/code/defines/procs/radio.dm new file mode 100644 index 00000000000..1e0e045928b --- /dev/null +++ b/code/defines/procs/radio.dm @@ -0,0 +1,81 @@ +#define TELECOMMS_RECEPTION_NONE 0 +#define TELECOMMS_RECEPTION_SENDER 1 +#define TELECOMMS_RECEPTION_RECEIVER 2 +#define TELECOMMS_RECEPTION_BOTH 3 + +/proc/get_frequency_name(var/display_freq) + var/freq_text + + // the name of the channel + if(display_freq in ANTAG_FREQS) + freq_text = "#unkn" + else + for(var/channel in radiochannels) + if(radiochannels[channel] == display_freq) + freq_text = channel + break + + // --- 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) + + return freq_text + +/datum/reception + var/obj/machinery/message_server/message_server = null + var/telecomms_reception = TELECOMMS_RECEPTION_NONE + var/message = "" + +/datum/receptions + var/obj/machinery/message_server/message_server = null + var/sender_reception = TELECOMMS_RECEPTION_NONE + var/list/receiver_reception = new + +/proc/get_message_server() + if(message_servers) + for (var/obj/machinery/message_server/MS in message_servers) + if(MS.active) + return MS + return null + +/proc/check_signal(var/datum/signal/signal) + return signal && signal.data["done"] + +/proc/get_sender_reception(var/atom/sender, var/datum/signal/signal) + return check_signal(signal) ? TELECOMMS_RECEPTION_SENDER : TELECOMMS_RECEPTION_NONE + +/proc/get_receiver_reception(var/receiver, var/datum/signal/signal) + if(receiver && check_signal(signal)) + var/turf/pos = get_turf(receiver) + if(pos && (pos.z in signal.data["level"])) + return TELECOMMS_RECEPTION_RECEIVER + return TELECOMMS_RECEPTION_NONE + +/proc/get_reception(var/atom/sender, var/receiver, var/message = "", var/do_sleep = 1) + var/datum/reception/reception = new + + // check if telecomms I/O route 1459 is stable + reception.message_server = get_message_server() + + var/datum/signal/signal = sender.telecomms_process(do_sleep) // Be aware that this proc calls sleep, to simulate transmition delays + reception.telecomms_reception |= get_sender_reception(sender, signal) + reception.telecomms_reception |= get_receiver_reception(receiver, signal) + reception.message = signal && signal.data["compression"] > 0 ? Gibberish(message, signal.data["compression"] + 50) : message + + return reception + +/proc/get_receptions(var/atom/sender, var/list/atom/receivers, var/do_sleep = 1) + var/datum/receptions/receptions = new + receptions.message_server = get_message_server() + + var/datum/signal/signal + if(sender) + signal = sender.telecomms_process(do_sleep) + receptions.sender_reception = get_sender_reception(sender, signal) + + for(var/atom/receiver in receivers) + if(!signal) + signal = receiver.telecomms_process() + receptions.receiver_reception[receiver] = get_receiver_reception(receiver, signal) + + return receptions diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 43c52ec018c..7d5547f3c45 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -2268,6 +2268,7 @@ area/security/podbay /area/awaycontent name = "space" + report_alerts = 0 /area/awaycontent/a1 icon_state = "awaycontent1" diff --git a/code/game/dna/dna2_domutcheck.dm b/code/game/dna/dna2_domutcheck.dm index 6f0e3412f19..545e65b691d 100644 --- a/code/game/dna/dna2_domutcheck.dm +++ b/code/game/dna/dna2_domutcheck.dm @@ -5,6 +5,7 @@ // flags: See below, bitfield. #define MUTCHK_FORCED 1 /proc/domutcheck(var/mob/living/M, var/connected=null, var/flags=0) + for(var/datum/dna/gene/gene in dna_genes) if(!M || !M.dna) return @@ -75,6 +76,14 @@ var/gene_active = (gene.flags & GENE_ALWAYS_ACTIVATE) if(!gene_active) gene_active = M.dna.GetSEState(gene.block) + + var/defaultgenes // Do not mutate inherent species abilities + if(ishuman(M)) + var/mob/living/carbon/human/H = M + defaultgenes = H.species.default_genes + + if((gene in defaultgenes) && gene_active) + return // Prior state var/gene_prior_status = (gene.type in M.active_genes) diff --git a/code/game/dna/genes/goon_disabilities.dm b/code/game/dna/genes/goon_disabilities.dm index 5de6a0c35c3..1214919a441 100644 --- a/code/game/dna/genes/goon_disabilities.dm +++ b/code/game/dna/genes/goon_disabilities.dm @@ -335,7 +335,7 @@ if(L) usr.attack_log += text("\[[time_stamp()]\] [usr.real_name] ([usr.ckey]) cast the spell [name] on [L.real_name] ([L.ckey]).") - msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast the spell [name] on [L.real_name] ([L.ckey]) (JMP)") + msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast the spell [name] on [L.real_name] ([L.ckey]) (JMP)") L.adjust_fire_stacks(0.5) L.visible_message("\red [L.name] suddenly bursts into flames!") diff --git a/code/game/dna/genes/goon_powers.dm b/code/game/dna/genes/goon_powers.dm index 5ded91894bf..00a7879906f 100644 --- a/code/game/dna/genes/goon_powers.dm +++ b/code/game/dna/genes/goon_powers.dm @@ -162,12 +162,12 @@ H.visible_message("\red [usr] sprays a cloud of fine ice crystals, engulfing [H]!", "[usr] sprays a cloud of fine ice crystals over your [H.head]'s visor.") log_admin("[ckey(usr.key)] has used cryokinesis on [ckey(C.key)], internals yes, suit yes") - msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), (JMP)") + msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), (JMP)") else H.visible_message("\red [usr] sprays a cloud of fine ice crystals engulfing, [H]!", "[usr] sprays a cloud of fine ice crystals cover your [H.head]'s visor and make it into your air vents!.") log_admin("[usr.real_name] ([ckey(usr.key)]) has used cryokinesis on [C.real_name] ([ckey(C.key)]), ()") - msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), (JMP)") + msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), (JMP)") H.bodytemperature = max(0, H.bodytemperature - 50) H.adjustFireLoss(5) if(!handle_suit) @@ -177,7 +177,7 @@ C.visible_message("\red [usr] sprays a cloud of fine ice crystals, engulfing [C]!") log_admin("[ckey(usr.key)] has used cryokinesis on [ckey(C.key)], internals no, suit no") - msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), (JMP)") + msg_admin_attack("[usr.real_name] ([usr.ckey]) has cast cryokinesis on [C.real_name] ([C.ckey]), (JMP)") //playsound(usr.loc, 'bamf.ogg', 50, 0) diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 76d198d796f..d981da20efe 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -310,13 +310,4 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon" user << "We already have this DNA in storage!" return 1 - -//More snowflake stuff, but for languages. -/datum/changeling/proc/changeling_update_languages(var/updated_languages, var/mob/living/carbon/user) - if(!istype(user) || !user) - return - user.languages = list() - for(var/language in updated_languages) - user.languages += language - - return \ No newline at end of file + \ No newline at end of file diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm index 370a65d42a0..32ab8473d66 100644 --- a/code/game/gamemodes/changeling/evolution_menu.dm +++ b/code/game/gamemodes/changeling/evolution_menu.dm @@ -360,10 +360,11 @@ var/list/sting_paths sting_paths = init_subtypes(/obj/effect/proc_holder/changeling) if(mind.changeling.purchasedpowers) remove_changeling_powers(1) + + add_language("Changeling") for(var/language in languages) - if(!(language in mind.changeling.absorbed_languages)) - mind.changeling.absorbed_languages += language + mind.changeling.absorbed_languages |= language // purchase free powers. for(var/obj/effect/proc_holder/changeling/path in sting_paths) @@ -376,6 +377,17 @@ var/list/sting_paths var/mob/living/carbon/C = src //only carbons have dna now, so we have to typecaste mind.changeling.absorbed_dna |= C.dna return 1 + +//Used to dump the languages from the changeling datum into the actual mob. +/mob/proc/changeling_update_languages(var/updated_languages) + + for(var/datum/language/L in updated_languages) + add_language("L.name") + + //This isn't strictly necessary but just to be safe... + add_language("Changeling") + + return /datum/changeling/proc/reset() chosen_sting = null diff --git a/code/game/gamemodes/changeling/powers/absorb.dm b/code/game/gamemodes/changeling/powers/absorb.dm index 6acb6e5a66d..bb9785286dc 100644 --- a/code/game/gamemodes/changeling/powers/absorb.dm +++ b/code/game/gamemodes/changeling/powers/absorb.dm @@ -94,7 +94,7 @@ for(var/language in T.languages) if(!(language in absorbed_languages)) absorbed_languages += language - changeling_update_languages(absorbed_languages, user) + T.changeling_update_languages(absorbed_languages) absorbedcount++ store_dna(new_dna, user) diff --git a/code/game/gamemodes/changeling/powers/humanform.dm b/code/game/gamemodes/changeling/powers/humanform.dm index 81828120875..42e0bb3dad1 100644 --- a/code/game/gamemodes/changeling/powers/humanform.dm +++ b/code/game/gamemodes/changeling/powers/humanform.dm @@ -65,7 +65,7 @@ I.implanted = O user.mind.transfer_to(O) - O.mind.changeling.changeling_update_languages(changeling.absorbed_languages) + O.changeling_update_languages(changeling.absorbed_languages) O.mind.changeling.purchasedpowers -= src //O.mind.changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/lesserform(null) feedback_add_details("changeling_powers","LFT") diff --git a/code/game/gamemodes/changeling/powers/lesserform.dm b/code/game/gamemodes/changeling/powers/lesserform.dm index f8ebed5798e..a769ee47bd0 100644 --- a/code/game/gamemodes/changeling/powers/lesserform.dm +++ b/code/game/gamemodes/changeling/powers/lesserform.dm @@ -60,7 +60,7 @@ C.mind.transfer_to(O) O.mind.changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null) - O.mind.changeling.changeling_update_languages(changeling.absorbed_languages) + O.changeling_update_languages(changeling.absorbed_languages) feedback_add_details("changeling_powers","LF") qdel(C) diff --git a/code/game/gamemodes/changeling/powers/transform.dm b/code/game/gamemodes/changeling/powers/transform.dm index b6a9cf0af30..f1ff51a42a2 100644 --- a/code/game/gamemodes/changeling/powers/transform.dm +++ b/code/game/gamemodes/changeling/powers/transform.dm @@ -21,6 +21,8 @@ user.set_species() user.UpdateAppearance() domutcheck(user, null) + + user.changeling_update_languages(changeling.absorbed_languages) feedback_add_details("changeling_powers","TR") return 1 diff --git a/code/game/gamemodes/events/ninja_equipment.dm b/code/game/gamemodes/events/ninja_equipment.dm index 9ed459ef8ce..95b3243ae84 100644 --- a/code/game/gamemodes/events/ninja_equipment.dm +++ b/code/game/gamemodes/events/ninja_equipment.dm @@ -1351,21 +1351,21 @@ ________________________________________________________________________________ var/chance = rand(1,100) switch(chance) if(1 to 70)//High chance of a regular name. - voice = "[rand(0,1)==1?pick(first_names_female):pick(first_names_male)] [pick(last_names)]" + changer.voice = "[rand(0,1)==1?pick(first_names_female):pick(first_names_male)] [pick(last_names)]" if(71 to 80)//Smaller chance of a clown name. - voice = "[pick(clown_names)]" + changer.voice = "[pick(clown_names)]" if(81 to 90)//Small chance of a wizard name. - voice = "[pick(wizard_first)] [pick(wizard_second)]" + changer.voice = "[pick(wizard_first)] [pick(wizard_second)]" if(91 to 100)//Small chance of an existing crew name. var/names[] = new() for(var/mob/living/carbon/human/M in player_list) if(M==U||!M.client||!M.real_name) continue names.Add(M.real_name) - voice = !names.len ? "Cuban Pete" : pick(names) - U << "You are now mimicking [voice]." + changer.voice = !names.len ? "Cuban Pete" : pick(names) + U << "You are now mimicking [changer.voice]." else - U << "The voice synthesizer is [voice!="Unknown"?"now":"already"] deactivated." - voice = "Unknown" + U << "The voice synthesizer is [changer.voice!="Unknown"?"now":"already"] deactivated." + changer.voice = "Unknown" return /obj/item/clothing/mask/gas/voice/space_ninja/proc/switchm() @@ -1408,7 +1408,7 @@ ________________________________________________________________________________ if(3) mode = "Meson Scanner" usr << "[mode] is active."//Leaving usr here since it may be on the floor or on a person. - usr << "Voice mimicking algorithm is set [!vchange?"inactive":"active"]." + usr << "Voice mimicking algorithm is set [!changer.active?"inactive":"active"]." /* diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index de7bc051054..6deb483f0fa 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -132,6 +132,7 @@ proc/issyndicate(mob/living/M as mob) synd_mind.current.loc = synd_spawn[spawnpos] forge_syndicate_objectives(synd_mind) + create_syndicate(synd_mind) greet_syndicate(synd_mind) equip_syndicate(synd_mind.current) @@ -162,7 +163,38 @@ proc/issyndicate(mob/living/M as mob) spawn (rand(waittime_l, waittime_h)) send_intercept() return ..() + +/datum/game_mode/proc/create_syndicate(var/datum/mind/synd_mind) // So we don't have inferior species as ops - randomize a human + var/mob/living/carbon/human/M = synd_mind.current + M.set_species("Human",1) + M.dna.ready_dna(M) // Quadriplegic Nuke Ops won't be participating in the paralympics + var/hair_c = pick("#8B4513","#000000","#FF4500","#FFD700") // Brown, black, red, blonde + var/eye_c = pick("#000000","#8B4513","1E90FF") // Black, brown, blue + var/skin_tone = pick(35, 60, 75, 90, 105, 120, 135, 150) // Caucasian - black + var/hair_style = "Bald" + var/facial_hair_style = "Shaved" + if(M.gender == MALE) + hair_style = pick(hair_styles_male_list) + facial_hair_style = pick(facial_hair_styles_list) + else + hair_style = pick(hair_styles_female_list) + if(prob(5)) + facial_hair_style = pick(facial_hair_styles_list) + + M.r_facial = hex2num(copytext(hair_c, 2, 4)) + M.g_facial = hex2num(copytext(hair_c, 4, 6)) + M.b_facial = hex2num(copytext(hair_c, 6, 8)) + M.r_hair = hex2num(copytext(hair_c, 2, 4)) + M.g_hair = hex2num(copytext(hair_c, 4, 6)) + M.b_hair = hex2num(copytext(hair_c, 6, 8)) + M.r_eyes = hex2num(copytext(eye_c, 2, 4)) + M.g_eyes = hex2num(copytext(eye_c, 4, 6)) + M.b_eyes = hex2num(copytext(eye_c, 6, 8)) + M.s_tone = skin_tone + 35 + M.s_tone = -M.s_tone + 35 + M.h_style = hair_style + M.f_style = facial_hair_style /datum/game_mode/proc/prepare_syndicate_leader(var/datum/mind/synd_mind, var/nuke_code) var/leader_title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord") diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm index 330949c3caf..fe3062a194c 100644 --- a/code/game/gamemodes/nuclear/nuclearbomb.dm +++ b/code/game/gamemodes/nuclear/nuclearbomb.dm @@ -23,10 +23,7 @@ var/bomb_set var/timing_wire var/removal_stage = 0 // 0 is no removal, 1 is covers removed, 2 is covers open, 3 is sealant open, 4 is unwrenched, 5 is removed from bolts. var/lastentered - var/data[0] - var/uiwidth - var/uiheight - var/uititle + flags = FPRINT use_power = 0 unacidable = 1 @@ -84,7 +81,7 @@ var/bomb_set if (istype(O, /obj/item/device/multitool) || istype(O, /obj/item/weapon/wirecutters)) ui_interact(user) - + if (src.extended) if (istype(O, /obj/item/weapon/disk/nuclear)) usr.drop_item() @@ -186,8 +183,12 @@ var/bomb_set obj/machinery/nuclearbomb/proc/nukehack_win(mob/user as mob) ui_interact(user) - + /obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + var/uiwidth + var/uiheight + var/uititle if(!src.opened) data["hacking"] = 0 data["auth"] = src.auth @@ -197,12 +198,12 @@ obj/machinery/nuclearbomb/proc/nukehack_win(mob/user as mob) else data["authstatus"] = "Auth. S2" else - if (src.timing) + if (src.timing) data["authstatus"] = "Set" else data["authstatus"] = "Auth. S1" - data["safe"] = src.safety ? "Safe" : "Engaged" - data["time"] = src.timeleft + data["safe"] = src.safety ? "Safe" : "Engaged" + data["time"] = src.timeleft data["timer"] = src.timing data["safety"] = src.safety data["anchored"] = src.anchored @@ -217,7 +218,7 @@ obj/machinery/nuclearbomb/proc/nukehack_win(mob/user as mob) uititle = "Nuke Control Panel" else data["hacking"] = 1 - var/list/tempwires[0] + var/list/tempwires[0] for(var/wire in src.wires) tempwires.Add(list(list("name" = wire, "cut" = src.wires[wire]))) data["wires"] = tempwires @@ -227,10 +228,10 @@ obj/machinery/nuclearbomb/proc/nukehack_win(mob/user as mob) uiwidth = 420 uiheight = 440 uititle = "Nuclear Bomb Defusion" - ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) ui = new(user, src, ui_key, "nuclear_bomb.tmpl", uititle, uiwidth, uiheight) - ui.set_initial_data(data) + ui.set_initial_data(data) ui.open() /obj/machinery/nuclearbomb/verb/make_deployable() @@ -337,8 +338,8 @@ obj/machinery/nuclearbomb/proc/nukehack_win(mob/user as mob) if (src.timing == -1.0) return if (src.safety) - usr << "\red The safety is still on." - nanomanager.update_uis(src) + usr << "\red The safety is still on." + nanomanager.update_uis(src) return src.timing = !( src.timing ) if (src.timing) @@ -426,7 +427,7 @@ obj/machinery/nuclearbomb/proc/nukehack_win(mob/user as mob) if(ticker.mode.name == "nuclear emergency") ticker.mode:nukes_left -- else if(off_station == 1) - world << "A nuclear device was set off, but the explosion was out of reach of the station!" + world << "A nuclear device was set off, but the explosion was out of reach of the station!" else if(off_station == 2) world << "A nuclear device was set off, but the device was not on the station!" else diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index be2ac536afb..af9cfac6fbe 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -727,10 +727,14 @@ datum/objective/absorb var/n_p = 1 //autowin if (ticker.current_state == GAME_STATE_SETTING_UP) for(var/mob/new_player/P in player_list) - if(P.client && P.ready && P.mind!=owner) + if(P.client && P.ready && P.mind != owner) + if(P.client.prefs && (P.client.prefs.species == "Vox" || P.client.prefs.species == "Slime People" || P.client.prefs.species == "Machine")) // Special check for species that can't be absorbed. No better solution. + continue n_p ++ else if (ticker.current_state == GAME_STATE_PLAYING) for(var/mob/living/carbon/human/P in player_list) + if((P.species.flags & IS_SYNTHETIC) || (P.species.flags & NO_SCAN)) + continue if(P.client && !(P.mind in ticker.mode.changelings) && P.mind!=owner) n_p ++ target_amount = min(target_amount, n_p) diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm index 96b1a3d7920..f32258c13db 100644 --- a/code/game/gamemodes/setupgame.dm +++ b/code/game/gamemodes/setupgame.dm @@ -134,22 +134,6 @@ //testing("DNA2: [numsToAssign.len] blocks are unused: [english_list(numsToAssign)]") -// Run AFTER genetics setup and AFTER species setup. -/proc/setup_species() - // SPECIES GENETICS FUN - for(var/name in all_species) - // I hate BYOND. Can't just call while it's in the list. - var/datum/species/species = all_species[name] - if(species.default_block_names.len>0) - testing("Setting up genetics for [species.name] (needs [english_list(species.default_block_names)])") - species.default_blocks.Cut() - for(var/block=1;block= 5) //Cannot reach target. Give up and announce the issue. - speak("Summon command failed, destination unreachable.", radio_frequency, radio_name) + speak("Summon command failed, destination unreachable.", radio_name) bot_reset() /obj/machinery/bot/proc/summon_step() diff --git a/code/game/machinery/bots/cleanbot.dm b/code/game/machinery/bots/cleanbot.dm index e30bfa45283..a77235dad26 100644 --- a/code/game/machinery/bots/cleanbot.dm +++ b/code/game/machinery/bots/cleanbot.dm @@ -38,7 +38,6 @@ var/failed_steps var/next_dest var/next_dest_loc - radio_frequency = SRV_FREQ //Service radio_name = "Service" bot_type = CLEAN_BOT bot_type_name = "Cleanbot" diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm index 703b0b7dd82..bffd8a07032 100644 --- a/code/game/machinery/bots/ed209bot.dm +++ b/code/game/machinery/bots/ed209bot.dm @@ -33,7 +33,6 @@ var/arrest_type = 0 //If true, don't handcuff var/projectile = /obj/item/projectile/energy/electrode //Holder for projectile type var/shoot_sound = 'sound/weapons/Taser.ogg' - radio_frequency = SEC_FREQ radio_name = "Security" bot_type = SEC_BOT bot_type_name = "ED-209" @@ -269,7 +268,7 @@ Auto Patrol[]"}, if(declare_arrests) var/area/location = get_area(src) - speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].",radio_frequency, radio_name) + speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].", radio_name) target.visible_message("[target] has been stunned by [src]!",\ "[target] has been stunned by [src]!") diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm index 3752796f90f..e9a8c67093f 100644 --- a/code/game/machinery/bots/floorbot.dm +++ b/code/game/machinery/bots/floorbot.dm @@ -49,7 +49,6 @@ var/oldloc = null req_one_access = list(access_construction, access_robotics) var/targetdirection - radio_frequency = ENG_FREQ //Engineering channel radio_name = "Engineering" bot_type = FLOOR_BOT bot_type_name = "Floorbot" @@ -318,7 +317,7 @@ /obj/machinery/bot/floorbot/proc/nag() //Annoy everyone on the channel to refill us! if(!nagged) var/area/location = get_area(src) - speak("Requesting refill at [location]!", radio_frequency, radio_name) + speak("Requesting refill at [location]!", radio_name) nagged = 1 /obj/machinery/bot/floorbot/proc/is_hull_breach(var/turf/t) //Ignore space tiles not considered part of a structure, also ignores shuttle docking areas. diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm index 084514ecd85..aa0dd6c94ab 100644 --- a/code/game/machinery/bots/medbot.dm +++ b/code/game/machinery/bots/medbot.dm @@ -30,7 +30,6 @@ var/declare_crit = 1 //If active, the bot will transmit a critical patient alert to MedHUD users. var/declare_cooldown = 0 //Prevents spam of critical patient alerts. var/stationary_mode = 0 //If enabled, the Medibot will not move automatically. - radio_frequency = MED_FREQ //Medical frequency radio_name = "Medical" //Setting which reagents to use to treat what by default. By id. var/treatment_brute = "tricordrazine" @@ -533,7 +532,7 @@ if((skin == "bezerk")) return var/area/location = get_area(src) - speak("Medical emergency! [crit_patient ? "[crit_patient]" : "A patient"] is in critical condition at [location]!",radio_frequency, radio_name) + speak("Medical emergency! [crit_patient ? "[crit_patient]" : "A patient"] is in critical condition at [location]!", radio_name) declare_cooldown = 1 spawn(200) //Twenty seconds declare_cooldown = 0 diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm index 19ef2058f43..ac28b903947 100644 --- a/code/game/machinery/bots/mulebot.dm +++ b/code/game/machinery/bots/mulebot.dm @@ -71,7 +71,7 @@ var/global/mulebot_count = 0 cell.charge = 2000 cell.maxcharge = 2000 - spawn(5) // must wait for map loading to finish + spawn(50) // must wait for map loading to finish add_to_beacons(bot_filter) mulebot_count += 1 @@ -238,7 +238,7 @@ var/global/mulebot_count = 0 //user << browse("M.U.L.E. Mk. III [suffix ? "([suffix])" : ""][dat]", "window=mulebot;size=350x500") //onclose(user, "mulebot") - var/datum/browser/popup = new(user, "mulebot", "M.U.L.E. Mk. V [suffix ? "([suffix])" : ""]", 350, 535) + var/datum/browser/popup = new(user, "mulebot", "M.U.L.E. Mk. V [suffix ? "([suffix])" : ""]", 350, 620) popup.set_content(dat) popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() @@ -313,6 +313,8 @@ var/global/mulebot_count = 0 updateDialog() if("destination") + if(!delivery_beacons.len) + post_signal(beacon_freq, "findbeacon", "delivery") refresh=0 var/new_dest = input("Select M.U.L.E. Destination", "Mulebot [suffix ? "([suffix])" : ""]", destination) as null|anything in delivery_beacons refresh=1 @@ -683,7 +685,6 @@ var/global/mulebot_count = 0 // called when bot reaches current target /obj/machinery/bot/mulebot/proc/at_target() if(!reached_target) - radio_frequency = SUP_FREQ //Supply channel radio_name = "Supply" Radio.config(list("[radio_name]" = 0)) visible_message("[src] makes a chiming sound!", "You hear a chime.") @@ -696,12 +697,11 @@ var/global/mulebot_count = 0 calling_ai << "\icon[src] [src] wirelessly plays a chiming sound!" playsound(calling_ai, 'sound/machines/chime.ogg',40, 0) calling_ai = null - radio_frequency = AIPRIV_FREQ //Report on AI Private instead if the AI is controlling us. radio_name = "AI Private" Radio.config(list("[radio_name]" = 0)) if(load) // if loaded, unload at target - speak("Destination [destination] reached. Unloading [load].", radio_frequency, radio_name) + speak("Destination [destination] reached. Unloading [load].", radio_name) unload(loaddir) else // not loaded @@ -717,7 +717,7 @@ var/global/mulebot_count = 0 if(AM) load(AM) if(report_delivery) - speak("Now loading [load] at [get_area(src)].", radio_frequency, radio_name) + speak("Now loading [load] at [get_area(src)].", radio_name) // whatever happened, check to see if we return home if(auto_return && destination != home_destination) diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm index d7d9d24a774..2b70de39797 100644 --- a/code/game/machinery/bots/secbot.dm +++ b/code/game/machinery/bots/secbot.dm @@ -25,7 +25,6 @@ var/arrest_type = 0 //If true, don't handcuff var/harmbaton = 0 var/base_icon = "secbot" - radio_frequency = SEC_FREQ //Security channel radio_name = "Security" bot_type = SEC_BOT bot_type_name = "Secbot" @@ -50,7 +49,6 @@ /obj/machinery/bot/secbot/pingsky name = "Officer Pingsky" desc = "It's Officer Pingsky! Delegated to satellite guard duty for harbouring anti-human sentiment." - radio_frequency = AIPRIV_FREQ radio_name = "AI Private" /obj/machinery/bot/secbot/ofitser @@ -256,7 +254,7 @@ Auto Patrol: []"}, if(declare_arrests) var/area/location = get_area(src) - speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].",radio_frequency, radio_name) + speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].", radio_name) target.visible_message("[target] has been [harmbaton ? "beaten" : "stunned"] by [src]!",\ "[target] has been [harmbaton ? "beaten" : "stunned"] by [src]!") diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index 1d60db31a3c..dd7c175ce4d 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -4,7 +4,7 @@ /mob/living/silicon/ai/proc/InvalidTurf(turf/T as turf) if(!T) return 1 - if((T.z in config.station_levels)) + if((T.z in config.admin_levels)) return 1 if(T.z > 6) return 1 diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 265145b4a04..a04ed3f2053 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -81,13 +81,20 @@ 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, verb) +/obj/machinery/hologram/holopad/hear_talk(mob/living/M, text, verb, datum/language/speaking) 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) + if(!master.say_understands(M, speaking))//The AI will be able to understand most mobs talking through the holopad. + if(speaking) + text = speaking.scramble(text) + else + 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] [verb], \"[text]\"" + var/rendered + if(speaking) + rendered = "Holopad received, [name_used] [speaking.format_message(text, verb)]" + else + rendered = "Holopad received, [name_used] [verb], \"[text]\"" master.show_message(rendered, 2) return diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index d2dbe48ce03..16b3e3860c8 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -20,7 +20,6 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept use_power = 1 idle_power_usage = 25 machinetype = 5 - heatgen = 0 delay = 7 circuitboard = "/obj/item/weapon/circuitboard/telecomms/broadcaster" @@ -59,7 +58,9 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept 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) + signal.data["realname"], signal.data["vname"],, + signal.data["compression"], signal.data["level"], signal.frequency, + signal.data["verb"], signal.data["language"] ) /** #### - Simple Broadcast - #### **/ @@ -84,7 +85,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept signal.data["vmask"], signal.data["vmessage"], 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"], signal.data["vname"], 4, signal.data["compression"], signal.data["level"], signal.frequency, + signal.data["verb"], signal.data["language"]) if(!message_delay) message_delay = 1 @@ -95,7 +97,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept /* --- Do a snazzy animation! --- */ flick("broadcaster_send", src) -/obj/machinery/telecomms/broadcaster/Destroy() +/obj/machinery/telecomms/broadcaster/Del() // In case message_delay is left on 1, otherwise it won't reset the list and people can't say the same thing twice anymore. if(message_delay) message_delay = 0 @@ -117,7 +119,6 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept use_power = 0 idle_power_usage = 0 machinetype = 6 - heatgen = 0 var/intercept = 0 // if nonzero, broadcasts all messages to syndicate channel /obj/machinery/telecomms/allinone/receive_signal(datum/signal/signal) @@ -142,19 +143,21 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept var/datum/radio_frequency/connection = signal.data["connection"] - if(connection.frequency == SYND_FREQ) // if syndicate broadcast, just + if(connection.frequency in ANTAG_FREQS) // if antag broadcast, just 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"], list(0), connection.frequency) + signal.data["realname"], signal.data["vname"],, signal.data["compression"], list(0), connection.frequency, + signal.data["verb"], signal.data["language"]) 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["vname"], 3, signal.data["compression"], list(0), connection.frequency, + signal.data["verb"], signal.data["language"]) @@ -218,7 +221,8 @@ 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, - var/data, var/compression, var/list/level, var/freq) + var/data, var/compression, var/list/level, var/freq, var/verbage = "says", var/datum/language/speaking = null) + /* ###### Prepare the radio connection ###### */ @@ -246,16 +250,14 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept if(R.receive_range(display_freq, level) > -1) radios += R - // --- Broadcast to syndicate radio! --- + // --- Broadcast to antag radios! --- 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) - radios += R + for(var/antag_freq in ANTAG_FREQS) + var/datum/radio_frequency/antag_connection = radio_controller.return_frequency(antag_freq) + for (var/obj/item/device/radio/R in antag_connection.devices["[RADIO_CHAT]"]) + if(R.receive_range(antag_freq, level) > -1) + radios += R // --- Broadcast to ALL radio devices --- @@ -283,6 +285,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept /* --- Loop through the receivers and categorize them --- */ + 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(istype(R, /mob/new_player)) // we don't want new players to hear messages. rare but generates runtimes. continue @@ -324,88 +328,17 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept 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(1351) - freq_text = "Science" - if(1355) - freq_text = "Medical" - if(1357) - freq_text = "Engineering" - if(1359) - freq_text = "Security" - if(1347) - freq_text = "Supply" - if(1349) - freq_text = "Service" - if(1441) - freq_text = "Special Ops" - if(1443) - freq_text = "Response Team" - if(1447) - 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/freq_text = get_frequency_name(display_freq) var/part_b_extra = "" if(data == 3) // intercepted radio message part_b_extra = " (Intercepted)" - var/part_b = " \icon[radio]\[[freq_text]\][part_b_extra] " // Tweaked for security headsets -- TLE + var/part_a = "\icon[radio]\[[freq_text]\][part_b_extra] " // goes in the actual output + + // --- Some more pre-message formatting --- + var/part_b = " " // Tweaked for security headsets -- TLE var/part_c = "" - // syndies! - if (display_freq == SYND_FREQ) - part_a = "" - - // centcomm channels (deathsquid and ert) - else if (display_freq in CENT_FREQS) - part_a = "" - - // command channel - else if (display_freq == COMM_FREQ) - part_a = "" - - // AI private channel - else if (display_freq == 1447) - part_a = "" - - // department radio formatting (poorly optimized, ugh) - else if (display_freq == SEC_FREQ) - part_a = "" - - else if (display_freq == ENG_FREQ) - part_a = "" - - else if (display_freq == SCI_FREQ) - part_a = "" - - else if (display_freq == MED_FREQ) - part_a = "" - - else if (display_freq == SUP_FREQ) // cargo - part_a = "" - - else if (display_freq == SRV_FREQ) // cargo - part_a = "" - // If all else fails and it's a dept_freq, color me purple! - else if (display_freq in DEPT_FREQS) - part_a = "" - // --- Filter the message; place it in quotes apply a verb --- @@ -424,133 +357,67 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept //BR.messages_admin += blackbox_admin_msg if(istype(blackbox)) switch(display_freq) - if(1459) + if(PUB_FREQ) blackbox.msg_common += blackbox_msg - if(1351) + if(SCI_FREQ) blackbox.msg_science += blackbox_msg - if(1353) + if(COMM_FREQ) blackbox.msg_command += blackbox_msg - if(1355) + if(MED_FREQ) blackbox.msg_medical += blackbox_msg - if(1357) + if(ENG_FREQ) blackbox.msg_engineering += blackbox_msg - if(1359) + if(SEC_FREQ) blackbox.msg_security += blackbox_msg - if(1441) + if(DTH_FREQ) blackbox.msg_deathsquad += blackbox_msg - if(1213) + if(SYND_FREQ) blackbox.msg_syndicate += blackbox_msg - if(1347) + if(SUP_FREQ) blackbox.msg_cargo += blackbox_msg - if(1349) + if(SRV_FREQ) blackbox.msg_service += blackbox_msg else blackbox.messages += blackbox_msg //End of research and feedback code. - var/aitrack = "" - var/aiopen = "" /* ###### 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 = "" - aiopen = "\[OPEN\] " - if(data == 4) - aitrack = "" - - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][aitrack][N] ([J]) [aiopen][part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) + R.hear_radio(message,verbage, speaking, part_a, part_b, M, 0, name) /* --- 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 = "" - aiopen = "\[OPEN\] " - if(data == 4) - aitrack = "" - - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][aitrack][realname] ([job]) [aiopen][part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) + R.hear_radio(message, verbage, speaking, part_a, part_b, M, 0, realname) /* --- Process all the mobs that heard the voice normally (did not understand) --- */ if (length(heard_voice)) - var/rendered = "[part_a][vname][part_b][vmessage][part_c]" - for (var/mob/R in heard_voice) - aitrack = "" - aiopen = "\[OPEN\] " - if(data == 4) - aitrack = "" - - - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][aitrack][vname] ([job]) [aiopen][part_b][vmessage]][part_c]", 2) - else - R.show_message(rendered, 2) + R.hear_radio(message,verbage, speaking, part_a, part_b, M,0, vname) /* --- 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 = "" - aiopen = "\[OPEN\] " - if(data == 4) - aitrack = "" - - - if(istype(R, /mob/living/silicon/ai)) - R.show_message("[part_a][aitrack][vname][aiopen][part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) + R.hear_radio(message, verbage, speaking, part_a, part_b, M, 1, vname) /* --- 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 = "" - aiopen = "\[OPEN\] " - 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)]) [aiopen][part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) - + R.hear_radio(message, verbage, speaking, part_a, part_b, M, 1) + return 1 /proc/Broadcast_SimpleMessage(var/source, var/frequency, var/text, var/data, var/mob/M, var/compression, var/level) @@ -588,15 +455,15 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept receive |= R.send_hear(display_freq) - // --- Broadcast to syndicate radio! --- + // --- Broadcast to antag radios! --- 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]"]) - var/turf/position = get_turf(R) - if(position && position.z == level) - receive |= R.send_hear(SYND_FREQ) + for(var/freq in ANTAG_FREQS) + var/datum/radio_frequency/antag_connection = radio_controller.return_frequency(freq) + for (var/obj/item/device/radio/R in antag_connection.devices["[RADIO_CHAT]"]) + var/turf/position = get_turf(R) + if(position && position.z == level) + receive |= R.send_hear(freq) // --- Broadcast to ALL radio devices --- @@ -621,6 +488,8 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept /* --- Loop through the receivers and categorize them --- */ + 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 // --- Check for compression --- @@ -648,34 +517,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept /* --- 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(1351) - freq_text = "Science" - if(1355) - freq_text = "Medical" - if(1357) - freq_text = "Engineering" - if(1359) - freq_text = "Security" - if(1347) - freq_text = "Supply" - if(1349) - freq_text = "Service" - //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) + var/freq_text = get_frequency_name(display_freq) // --- Some more pre-message formatting --- @@ -689,7 +531,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept var/part_b = " \icon[radio]\[[freq_text]\][part_b_extra] " // Tweaked for security headsets -- TLE var/part_c = "" - if (display_freq==SYND_FREQ) + if (display_freq in ANTAG_FREQS) part_a = "" else if (display_freq==COMM_FREQ) part_a = "" @@ -705,25 +547,25 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept //BR.messages_admin += blackbox_admin_msg if(istype(blackbox)) switch(display_freq) - if(1459) + if(PUB_FREQ) blackbox.msg_common += blackbox_msg - if(1351) + if(SCI_FREQ) blackbox.msg_science += blackbox_msg - if(1353) + if(COMM_FREQ) blackbox.msg_command += blackbox_msg - if(1355) + if(MED_FREQ) blackbox.msg_medical += blackbox_msg - if(1357) + if(ENG_FREQ) blackbox.msg_engineering += blackbox_msg - if(1359) + if(SEC_FREQ) blackbox.msg_security += blackbox_msg - if(1441) + if(DTH_FREQ) blackbox.msg_deathsquad += blackbox_msg - if(1213) + if(SYND_FREQ) blackbox.msg_syndicate += blackbox_msg - if(1347) + if(SUP_FREQ) blackbox.msg_cargo += blackbox_msg - if(1349) + if(SRV_FREQ) blackbox.msg_service += blackbox_msg else blackbox.messages += blackbox_msg @@ -767,7 +609,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept var/turf/position = get_turf(src) return (position.z in signal.data["level"] && signal.data["done"]) -/atom/proc/telecomms_process() +/atom/proc/telecomms_process(var/do_sleep = 1) // First, we want to generate a new radio signal var/datum/signal/signal = new @@ -785,13 +627,14 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept "done" = 0, "level" = pos.z // The level it is being broadcasted at. ) - signal.frequency = 1459// Common channel + signal.frequency = PUB_FREQ// Common channel //#### Sending the signal to all subspace receivers ####// for(var/obj/machinery/telecomms/receiver/R in telecomms_list) R.receive_signal(signal) - sleep(rand(10,25)) + if(do_sleep) + sleep(rand(10,25)) //world.log << "Level: [signal.data["level"]] - Done: [signal.data["done"]]" diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index cdd866e41c6..e184b0fb066 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -161,19 +161,13 @@ - - - - - - @@ -265,8 +259,11 @@ removePersonality() if(href_list["wires"]) var/t1 = text2num(href_list["wires"]) - if(radio) - radio.wires.CutWireIndex(t1) + switch(t1) + if(4) + radio.ToggleBroadcast() + if(2) + radio.ToggleReception() if(href_list["setlaws"]) var/newlaws = copytext(sanitize(input("Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.pai_laws) as message),1,MAX_MESSAGE_LEN) if(newlaws) diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 32bf7da3c7a..29854facc67 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -127,7 +127,7 @@ /obj/item/device/radio/headset/heads/captain/alt name = "\proper the captain's bowman headset" - desc = "The headset of the boss. Protects ears from flashbangs. \nChannels are as follows: :c - command, :s - security, :e - engineering, :u - supply, :v - service, :m - medical, :n - science." + desc = "The headset of the boss. Protects ears from flashbangs. \nChannels are as follows: :c - command, :s - security, :e - engineering, :u - supply, :zz - service, :m - medical, :n - science." flags = EARBANGPROTECT icon_state = "com_headset_alt" item_state = "com_headset_alt" diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 28fdc835308..503fc2b5041 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -1,8 +1,3 @@ -var/GLOBAL_RADIO_TYPE = 1 // radio type to use - // 0 = old radios - // 1 = new radios (subspace technology) - - /obj/item/device/radio icon = 'icons/obj/radio.dmi' name = "station bounced radio" @@ -11,19 +6,18 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use item_state = "walkietalkie" var/on = 1 // 0 for off var/last_transmission - var/frequency = 1459 //common chat + var/frequency = PUB_FREQ //common chat var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies 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/prison_radio = 0 var/b_stat = 0 var/broadcasting = 0 var/listening = 1 var/freerange = 0 // 0 - Sanitize frequencies, 1 - Full range - var/list/channels = list() //see communications.dm for full list. First channels is a "default" for :h + var/list/channels = list() //see communications.dm for full list. First channes is a "default" for :h var/subspace_transmission = 0 - var/syndie = 0//Holder to see if it's a syndicate encrypted radio + var/syndie = 0//Holder to see if it's a syndicate encrpyed radio var/maxf = 1499 // "Example" = FREQ_LISTENING|FREQ_BROADCASTING flags = CONDUCT @@ -31,32 +25,31 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use throw_speed = 2 throw_range = 9 w_class = 2 - g_amt = 25 + m_amt = 75 - - var/const/TRANSMISSION_DELAY = 5 // only 2/second/radio + g_amt = 25 var/const/FREQ_LISTENING = 1 - //FREQ_BROADCASTING = 2 + var/prison_radio = 0 - var/always_talk=0 // ALWAYS catch signals. Useful for covert listening devices. /obj/item/device/radio var/datum/radio_frequency/radio_connection - var/list/datum/radio_frequency/secure_radio_connections + var/list/datum/radio_frequency/secure_radio_connections = new 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/New() - wires = new(src) - if(prison_radio) - wires.CutWireIndex(WIRE_TRANSMIT) - secure_radio_connections = new ..() + wires = new(src) if(radio_controller) initialize() + + if(prison_radio) + wires.CutWireIndex(WIRE_TRANSMIT) /obj/item/device/radio/initialize() @@ -117,9 +110,16 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use /obj/item/device/radio/proc/text_sec_channel(var/chan_name, var/chan_stat) var/list = !!(chan_stat&FREQ_LISTENING)!=0 return {" - [chan_name]: [list ? "Engaged" : "Disengaged"]
+ [chan_name]
+ Speaker: [list ? "Engaged" : "Disengaged"]
"} +/obj/item/device/radio/proc/ToggleBroadcast() + broadcasting = !broadcasting && !(wires.IsIndexCut(WIRE_TRANSMIT) || wires.IsIndexCut(WIRE_SIGNAL)) + +/obj/item/device/radio/proc/ToggleReception() + listening = !listening && !(wires.IsIndexCut(WIRE_RECEIVE) || wires.IsIndexCut(WIRE_SIGNAL)) + /obj/item/device/radio/Topic(href, href_list) //..() if (usr.stat || !on) @@ -129,13 +129,6 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use usr << browse(null, "window=radio") return usr.set_machine(src) - if (href_list["open"]) - var/mob/target = locate(href_list["open"]) - var/mob/living/silicon/ai/A = locate(href_list["open2"]) - if(A && target) - A.open_nearest_door(target) - return - if (href_list["track"]) var/mob/target = locate(href_list["track"]) var/mob/living/silicon/ai/A = locate(href_list["track2"]) @@ -143,23 +136,6 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use A.ai_actual_track(target) return - else if (href_list["faketrack"]) - var/mob/target = locate(href_list["track"]) - var/mob/living/silicon/ai/A = locate(href_list["track2"]) - if(A && target) - - A:cameraFollow = target - A << text("Now tracking [] on camera.", target.name) - if (usr.machine == null) - usr.machine = usr - - while (usr:cameraFollow == target) - usr << "Target is not on or near any active cameras on the station. We'll check again in 5 seconds (unless you use the cancel-camera verb)." - sleep(40) - continue - - return - else if (href_list["freq"]) var/new_frequency = (frequency + text2num(href_list["freq"])) if (!freerange || (frequency < 1200 || frequency > 1600)) @@ -171,16 +147,17 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use return else if (href_list["talk"]) - broadcasting = text2num(href_list["talk"]) + ToggleBroadcast() else if (href_list["listen"]) var/chan_name = href_list["ch_name"] if (!chan_name) - listening = text2num(href_list["listen"]) + ToggleReception() else if (channels[chan_name] & FREQ_LISTENING) channels[chan_name] &= ~FREQ_LISTENING else channels[chan_name] |= FREQ_LISTENING + if (!( master )) if (istype(loc, /mob)) interact(loc) @@ -193,10 +170,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use updateDialog() add_fingerprint(usr) -/obj/item/device/radio/proc/isWireCut(var/index) - return wires.IsIndexCut(index) - -/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel, var/zlevel = list(1)) //BS12 EDIT +/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel, var/zlevel = config.contact_levels) //BS12 EDIT var/datum/radio_frequency/connection = null if(channel && channels && channels.len > 0) if (channel == "department") @@ -219,173 +193,115 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use del(A) return -/obj/item/device/radio/talk_into(mob/living/M as mob, message, channel) - if(!on) return // the device has to be on +// Interprets the message mode when talking into a radio, possibly returning a connection datum +/obj/item/device/radio/proc/handle_message_mode(mob/living/M as mob, message, message_mode) + // If a channel isn't specified, send to common. + if(!message_mode || message_mode == "headset") + return radio_connection + + // Otherwise, if a channel is specified, look for it. + if(channels && channels.len > 0) + if (message_mode == "department") // Department radio shortcut + message_mode = channels[1] + + if (channels[message_mode]) // only broadcast if the channel is set on + return secure_radio_connections[message_mode] + + // If we were to send to a channel we don't have, drop it. + return null + +/obj/item/device/radio/talk_into(mob/living/M as mob, message, channel, var/verb = "says", var/datum/language/speaking = null) + if(!on) return 0 // the device has to be on // Fix for permacell radios, but kinda eh about actually fixing them. - if(!M || !message) return + if(!M || !message) return 0 // Uncommenting this. To the above comment: // The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom - if(isWireCut(WIRE_TRANSMIT)) // The device has to have all its wires and shit intact - return + if(wires.IsIndexCut(WIRE_TRANSMIT)) // The device has to have all its wires and shit intact + return 0 + + M.last_target_click = world.time + + /* 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. + */ + + //#### Grab the connection datum ####// + var/datum/radio_frequency/connection = handle_message_mode(M, message, channel) + if (!istype(connection)) + return 0 + if (!connection) + return 0 + + var/turf/position = get_turf(src) + + //#### 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 - if(GLOBAL_RADIO_TYPE == 1) // NEW RADIO SYSTEMS: By Doohl + var/jobname // the mob's "job" - /* 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. + // --- Human: use their actual job --- + if (ishuman(M)) + var/mob/living/carbon/human/H = M + jobname = H.get_assignment() - 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. - */ + // --- Carbon Nonhuman --- + else if (iscarbon(M)) // Nonhuman carbon mob + jobname = "No id" - //#### Grab the connection datum ####// - var/datum/radio_frequency/connection = null - //testing("[src]: talk_into([M], [message], [channel])") - if(channel == "headset") - channel = null - if(channel) // If a channel is specified, look for it. - if(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 - // If we were to send to a channel we don't have, drop it. - return - else // If a channel isn't specified, send to common. - connection = radio_connection - channel = null - if (!istype(connection)) - return - if (!connection) - return + // --- AI --- + else if (isAI(M)) + jobname = "AI" - var/turf/position = get_turf(src) + // --- Cyborg --- + else if (isrobot(M)) + jobname = "Cyborg" - //#### Tagging the signal with all appropriate identity values ####// + // --- Personal AI (pAI) --- + else if (istype(M, /mob/living/silicon/pai)) + jobname = "Personal AI" - // ||-- 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 + // --- Unidentifiable mob --- + else + jobname = "Unknown" - var/jobname // the mob's "job" + // --- Modifications to the mob's identity --- - // --- Human: use their actual job --- - if (ishuman(M)) - jobname = M:get_assignment() - - // --- 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 --- - else - jobname = "Unknown" - - - // --- Modifications to the mob's identity --- - - // The mob is disguising their identity: - if (ishuman(M) && M.GetVoice() != real_name) - displayname = M.GetVoice() - jobname = "Unknown" - voicemask = 1 + // The mob is disguising their identity: + if (ishuman(M) && M.GetVoice() != real_name) + displayname = M.GetVoice() + jobname = "Unknown" + voicemask = 1 - /* ###### 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 // 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 - "job" = jobname, // the mob's job - "key" = mobkey, // the mob's key - "vmessage" = pick(M.speak_emote), // 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 - - // 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 - + /* ###### 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 @@ -394,215 +310,110 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use "key" = mobkey, // the mob's key "vmessage" = pick(M.speak_emote), // 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(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, - "traffic" = 0, - "type" = 0, - "server" = null, - "reject" = 0, - "level" = position.z + "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 + "language" = speaking, + "verb" = verb ) 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) - sleep(rand(10,25)) // 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: - - //THIS IS TEMPORARY. - if(!connection) return //~Carn - - Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), - src, message, displayname, jobname, real_name, M.voice_name, - filter_type, signal.data["compression"], list(position.z), connection.frequency) + // Receiving code can be located in Telecommunications.dm + return signal.data["done"] && position.z in signal.data["level"] + /* ###### Intercoms and station-bounced radios ###### */ - else // OLD RADIO SYSTEMS: By Goons? + var/filter_type = 2 - 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 + /* --- 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 - //world << "DEBUG: used channel=\"[channel]\" frequency= \"[display_freq]\" connection.devices.len = [connection.devices.len]" - var/eqjobname + var/datum/signal/signal = new + signal.transmission_method = 2 - 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 + /* --- Try to send a normal subspace broadcast first */ - var/list/receive = list() + signal.data = list( - //for (var/obj/item/device/radio/R in radio_connection.devices) - for (var/obj/item/device/radio/R in connection.devices["[RADIO_CHAT]"]) // Modified for security headset code -- TLE - //if(R.accept_rad(src, message)) - receive |= R.send_hear(display_freq, 0) + "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" = pick(M.speak_emote), // 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 - //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 + "compression" = 0, // uncompressed 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, + "language" = speaking, + "verb" = verb + ) + signal.frequency = connection.frequency // Quick frequency set - for (var/mob/R in receive) - if (R.say_understands(M)) - if (ishuman(M) && M.GetVoice() != M.real_name) - heard_masked += R - else - heard_normal += R - else - heard_voice += R + for(var/obj/machinery/telecomms/receiver/R in telecomms_list) + R.receive_signal(signal) - 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(1351) - freq_text = "Science" - if(1355) - freq_text = "Medical" - if(1357) - freq_text = "Engineering" - if(1359) - freq_text = "Security" - if(1347) - freq_text = "Supply" - //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) + sleep(rand(10,25)) // wait a little... - var/part_b = " \icon[src]\[[freq_text]\] " // Tweaked for security headsets -- TLE - var/part_c = "" + if(signal.data["done"] && position.z in signal.data["level"]) + // we're done here. + return 1 - if (display_freq==SYND_FREQ) - part_a = "" - else if (display_freq==COMM_FREQ) - part_a = "" - else if (display_freq in DEPT_FREQS) - part_a = "" + // 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: - var/quotedmsg = M.say_quote(message) + //THIS IS TEMPORARY. YEAH RIGHT + if(!connection) return 0 //~Carn - //This following recording is intended for research and feedback in the use of department radio channels. + return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), + src, message, displayname, jobname, real_name, M.voice_name, + filter_type, signal.data["compression"], list(position.z), connection.frequency,verb,speaking) - var/part_blackbox_b = " \[[freq_text]\] " // Tweaked for security headsets -- TLE - 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(1347) - blackbox.msg_cargo += blackbox_msg - if(1349) - blackbox.msg_service += 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]) \[OPEN\] [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]) \[OPEN\] [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][pick(M.speak_emote)][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]) \[OPEN\] [part_b][pick(M.speak_emote)][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]\[OPEN\] [part_b][quotedmsg][part_c]", 2) - else - R.show_message(rendered, 2) - -/obj/item/device/radio/hear_talk(mob/M as mob, msg) +/obj/item/device/radio/hear_talk(mob/M as mob, msg, var/verb = "says", var/datum/language/speaking = null) if (broadcasting) if(get_dist(src, M) <= canhear_range) - talk_into(M, msg) + talk_into(M, msg,null,verb,speaking) + + /* /obj/item/device/radio/proc/accept_rad(obj/item/device/radio/R as obj, message) @@ -621,7 +432,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use // what the range is in which mobs will hear the radio // returns: -1 if can't receive, range otherwise - if (isWireCut(WIRE_RECEIVE)) + if (wires.IsIndexCut(WIRE_RECEIVE)) return -1 if(!listening) return -1 @@ -629,7 +440,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use var/turf/position = get_turf(src) if(!position || !(position.z in level)) return -1 - if(freq == SYND_FREQ) + if(freq in ANTAG_FREQS) if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys return -1 if (!on) @@ -656,15 +467,13 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use return get_mobs_in_view(canhear_range, src) -/obj/item/device/radio/examine() - set src in view() - - ..() - if ((in_range(src, usr) || loc == usr)) +/obj/item/device/radio/examine(mob/user) + . = ..() + if ((in_range(src, user) || loc == user)) if (b_stat) - usr.show_message("\blue \the [src] can be attached and modified!") + user.show_message("\blue \the [src] can be attached and modified!") else - usr.show_message("\blue \the [src] can not be modified or attached!") + user.show_message("\blue \the [src] can not be modified or attached!") return /obj/item/device/radio/attackby(obj/item/weapon/W as obj, mob/user as mob) @@ -697,8 +506,14 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use //Giving borgs their own radio to have some more room to work with -Sieve /obj/item/device/radio/borg + var/mob/living/silicon/robot/myborg = null // Cyborg which owns this radio. Used for power checks var/obj/item/device/encryptionkey/keyslot = null//Borg radios can handle a single encryption key - + var/shut_up = 0 + icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. + icon_state = "radio" + canhear_range = 3 + subspace_transmission = 1 + /obj/item/device/radio/borg/syndicate syndie = 1 keyslot = new /obj/item/device/encryptionkey/syndicate @@ -713,6 +528,13 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use ..() set_frequency(1441) +/obj/item/device/radio/borg/talk_into() + . = ..() + if (isrobot(src.loc)) + var/mob/living/silicon/robot/R = src.loc + var/datum/robot_component/C = R.components["radio"] + R.use_power(C.energy_consumption) + /obj/item/device/radio/borg/attackby(obj/item/weapon/W as obj, mob/user as mob) // ..() user.set_machine(src) @@ -758,18 +580,25 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use src.channels = list() src.syndie = 0 + var/mob/living/silicon/robot/D = src.loc + if(D.module) + for(var/ch_name in D.module.channels) + if(ch_name in src.channels) + continue + src.channels += ch_name + src.channels[ch_name] += D.module.channels[ch_name] if(keyslot) for(var/ch_name in keyslot.channels) if(ch_name in src.channels) continue src.channels += ch_name - src.channels[ch_name] = keyslot.channels[ch_name] + src.channels[ch_name] += keyslot.channels[ch_name] if(keyslot.syndie) src.syndie = 1 - for (var/ch_name in channels) + for (var/ch_name in src.channels) if(!radio_controller) sleep(30) // Waiting for the radio_controller to be created. if(!radio_controller) @@ -784,12 +613,23 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use if(usr.stat || !on) return if (href_list["mode"]) - subspace_transmission = !subspace_transmission - if(!subspace_transmission)//Simple as fuck, clears the channel list to prevent talking/listening over them if subspace transmission is disabled + if(subspace_transmission != 1) + subspace_transmission = 1 + usr << "Subspace Transmission is enabled" + else + subspace_transmission = 0 + usr << "Subspace Transmission is disabled" + if(subspace_transmission == 0)//Simple as fuck, clears the channel list to prevent talking/listening over them if subspace transmission is disabled channels = list() else recalculateChannels() - usr << "Subspace Transmission is [(subspace_transmission) ? "enabled" : "disabled"]" + if (href_list["shutup"]) // Toggle loudspeaker mode, AKA everyone around you hearing your radio. + shut_up = !shut_up + if(shut_up) + canhear_range = 0 + else + canhear_range = 3 + ..() /obj/item/device/radio/borg/interact(mob/user as mob) @@ -806,6 +646,7 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use + +
Toggle Broadcast Mode
+ Loudspeaker: [shut_up ? "Disengaged" : "Engaged"]
"} if(subspace_transmission)//Don't even bother if subspace isn't turned on @@ -830,11 +671,3 @@ var/GLOBAL_RADIO_TYPE = 1 // radio type to use /obj/item/device/radio/off listening = 0 - -/obj/item/device/radio/Destroy() - if(radio_connection) - radio_connection.remove_listener(src) - if(isrobot(src.loc)) - var/mob/living/silicon/robot/R = src.loc - R.radio = null - ..() diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index a3e0d2f7b78..1dc2142d31a 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -63,6 +63,10 @@ attached_device.HasProximity(AM) return +/obj/item/device/transfer_valve/hear_talk(mob/living/M as mob, msg) + ..() + for(var/obj/O in contents) + O.hear_talk(M, msg) /obj/item/device/transfer_valve/attack_self(mob/user as mob) ui_interact(user) diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 23bed41262c..41520fabb93 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -32,9 +32,11 @@ R.icon_state = "robot" del(R.module) R.module = null - R.camera.network.Remove(list("Engineering","Medical","MINE")) + R.camera.network.Remove(list("Engineering","Medical","Mining Outpost")) R.updatename("Default") R.status_flags |= CANPUSH + R.languages = list() + R.speech_synthesizer_langs = list() R.notify_ai(2) R.updateicon() diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index d8e22940639..7bf35f6c6e0 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -4,151 +4,83 @@ density = 1 layer = 2 var/state = 0 + var/health = 200 - attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/wrench) && state == 0) - if(anchored && !istype(src,/obj/structure/girder/displaced)) - playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) - user << "\blue Now disassembling the girder" - if(do_after(user,40)) - if(!src) return - user << "\blue You dissasembled the girder!" - new /obj/item/stack/sheet/metal(get_turf(src)) - del(src) - else if(!anchored) - playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) - user << "\blue Now securing the girder" - if(get_turf(user, 40)) - user << "\blue You secured the girder!" - new/obj/structure/girder( src.loc ) - del(src) - - else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - user << "\blue Now slicing apart the girder" - if(do_after(user,30)) +/obj/structure/girder/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/wrench) && state == 0) + if(anchored && !istype(src,/obj/structure/girder/displaced)) + playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) + user << "\blue Now disassembling the girder" + if(do_after(user,40)) if(!src) return - user << "\blue You slice apart the girder!" + user << "\blue You dissasembled the girder!" new /obj/item/stack/sheet/metal(get_turf(src)) del(src) - - else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) - user << "\blue You drill through the girder!" - new /obj/item/stack/sheet/metal(get_turf(src)) - del(src) - - else if(istype(W, /obj/item/weapon/screwdriver) && state == 2 && istype(src,/obj/structure/girder/reinforced)) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) - user << "\blue Now unsecuring support struts" - if(do_after(user,40)) - if(!src) return - user << "\blue You unsecured the support struts!" - state = 1 - - else if(istype(W, /obj/item/weapon/wirecutters) && istype(src,/obj/structure/girder/reinforced) && state == 1) - playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) - user << "\blue Now removing support struts" - if(do_after(user,40)) - if(!src) return - user << "\blue You removed the support struts!" + else if(!anchored) + playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) + user << "\blue Now securing the girder" + if(get_turf(user, 40)) + user << "\blue You secured the girder!" new/obj/structure/girder( src.loc ) del(src) - else if(istype(W, /obj/item/weapon/crowbar) && state == 0 && anchored ) - playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) - user << "\blue Now dislodging the girder" - if(do_after(user, 40)) - if(!src) return - user << "\blue You dislodged the girder!" - new/obj/structure/girder/displaced( src.loc ) - del(src) + else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + user << "\blue Now slicing apart the girder" + if(do_after(user,30)) + if(!src) return + user << "\blue You slice apart the girder!" + new /obj/item/stack/sheet/metal(get_turf(src)) + del(src) - else if(istype(W, /obj/item/stack/sheet)) + else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) + user << "\blue You drill through the girder!" + new /obj/item/stack/sheet/metal(get_turf(src)) + del(src) - var/obj/item/stack/sheet/S = W - switch(S.type) + else if(istype(W, /obj/item/weapon/screwdriver) && state == 2 && istype(src,/obj/structure/girder/reinforced)) + playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) + user << "\blue Now unsecuring support struts" + if(do_after(user,40)) + if(!src) return + user << "\blue You unsecured the support struts!" + state = 1 - if(/obj/item/stack/sheet/metal, /obj/item/stack/sheet/metal/cyborg) - if(!anchored) - if(S.amount < 2) return - var/pdiff=performWallPressureCheck(src.loc) - if(!pdiff) - S.use(2) - user << "\blue You create a false wall! Push on it to open or close the passage." - new /obj/structure/falsewall (src.loc) - del(src) - else - user << "\red There is too much air moving through the gap! The door wouldn't stay closed if you built it." - message_admins("Attempted false wall made by [user.real_name] ([formatPlayerPanel(user,user.ckey)]) at [formatJumpTo(loc)] had a pressure difference of [pdiff]!") - log_admin("Attempted false wall made by [user.real_name] (user.ckey) at [loc] had a pressure difference of [pdiff]!") - return - else - if(S.amount < 2) return ..() - user << "\blue Now adding plating..." - if (do_after(user,40)) - if(!src || !S || S.amount < 2) return - S.use(2) - user << "\blue You added the plating!" - var/turf/Tsrc = get_turf(src) - Tsrc.ChangeTurf(/turf/simulated/wall) - for(var/turf/simulated/wall/X in Tsrc.loc) - if(X) X.add_hiddenprint(usr) - del(src) - return + else if(istype(W, /obj/item/weapon/wirecutters) && istype(src,/obj/structure/girder/reinforced) && state == 1) + playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1) + user << "\blue Now removing support struts" + if(do_after(user,40)) + if(!src) return + user << "\blue You removed the support struts!" + new/obj/structure/girder( src.loc ) + del(src) - if(/obj/item/stack/sheet/plasteel) - if(!anchored) - if(S.amount < 2) return - var/pdiff=performWallPressureCheck(src.loc) - if(!pdiff) - S.use(2) - user << "\blue You create a false wall! Push on it to open or close the passage." - new /obj/structure/falsewall/reinforced (src.loc) - del(src) - else - user << "\red There is too much air moving through the gap! The door wouldn't stay closed if you built it." - message_admins("Attempted false rwall made by [user.real_name] ([formatPlayerPanel(user,user.ckey)]) at [formatJumpTo(loc)] had a pressure difference of [pdiff]!") - log_admin("Attempted false rwall made by [user.real_name] ([user.ckey]) at [loc] had a pressure difference of [pdiff]!") - return - else - if (src.icon_state == "reinforced") //I cant believe someone would actually write this line of code... - if(S.amount < 1) return ..() - user << "\blue Now finalising reinforced wall." - if(do_after(user, 50)) - if(!src || !S || S.amount < 1) return - S.use(1) - user << "\blue Wall fully reinforced!" - var/turf/Tsrc = get_turf(src) - Tsrc.ChangeTurf(/turf/simulated/wall/r_wall) - for(var/turf/simulated/wall/r_wall/X in Tsrc.loc) - if(X) X.add_hiddenprint(usr) - del(src) - return - else - if(S.amount < 1) return ..() - user << "\blue Now reinforcing girders" - if (do_after(user,60)) - if(!src || !S || S.amount < 1) return - S.use(1) - user << "\blue Girders reinforced!" - new/obj/structure/girder/reinforced( src.loc ) - del(src) - return + else if(istype(W, /obj/item/weapon/crowbar) && state == 0 && anchored ) + playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) + user << "\blue Now dislodging the girder" + if(do_after(user, 40)) + if(!src) return + user << "\blue You dislodged the girder!" + new/obj/structure/girder/displaced( src.loc ) + del(src) - if(S.sheettype) - var/M = S.sheettype + else if(istype(W, /obj/item/stack/sheet)) + + var/obj/item/stack/sheet/S = W + switch(S.type) + + if(/obj/item/stack/sheet/metal, /obj/item/stack/sheet/metal/cyborg) if(!anchored) if(S.amount < 2) return var/pdiff=performWallPressureCheck(src.loc) if(!pdiff) S.use(2) user << "\blue You create a false wall! Push on it to open or close the passage." - var/F = text2path("/obj/structure/falsewall/[M]") - new F (src.loc) + new /obj/structure/falsewall (src.loc) del(src) else user << "\red There is too much air moving through the gap! The door wouldn't stay closed if you built it." - message_admins("Attempted false [M] wall made by [user.real_name] ([formatPlayerPanel(user,user.ckey)]) at [formatJumpTo(loc)] had a pressure difference of [pdiff]!") - log_admin("Attempted false [M] wall made by [user.real_name] ([user.ckey]) at [loc] had a pressure difference of [pdiff]!") + message_admins("Attempted false wall made by [user.real_name] ([formatPlayerPanel(user,user.ckey)]) at [formatJumpTo(loc)] had a pressure difference of [pdiff]!") + log_admin("Attempted false wall made by [user.real_name] (user.ckey) at [loc] had a pressure difference of [pdiff]!") return else if(S.amount < 2) return ..() @@ -158,67 +90,138 @@ S.use(2) user << "\blue You added the plating!" var/turf/Tsrc = get_turf(src) - Tsrc.ChangeTurf(text2path("/turf/simulated/wall/mineral/[M]")) - for(var/turf/simulated/wall/mineral/X in Tsrc.loc) + Tsrc.ChangeTurf(/turf/simulated/wall) + for(var/turf/simulated/wall/X in Tsrc.loc) if(X) X.add_hiddenprint(usr) del(src) return - add_hiddenprint(usr) + if(/obj/item/stack/sheet/plasteel) + if(!anchored) + if(S.amount < 2) return + var/pdiff=performWallPressureCheck(src.loc) + if(!pdiff) + S.use(2) + user << "\blue You create a false wall! Push on it to open or close the passage." + new /obj/structure/falsewall/reinforced (src.loc) + del(src) + else + user << "\red There is too much air moving through the gap! The door wouldn't stay closed if you built it." + message_admins("Attempted false rwall made by [user.real_name] ([formatPlayerPanel(user,user.ckey)]) at [formatJumpTo(loc)] had a pressure difference of [pdiff]!") + log_admin("Attempted false rwall made by [user.real_name] ([user.ckey]) at [loc] had a pressure difference of [pdiff]!") + return + else + if (src.icon_state == "reinforced") //I cant believe someone would actually write this line of code... + if(S.amount < 1) return ..() + user << "\blue Now finalising reinforced wall." + if(do_after(user, 50)) + if(!src || !S || S.amount < 1) return + S.use(1) + user << "\blue Wall fully reinforced!" + var/turf/Tsrc = get_turf(src) + Tsrc.ChangeTurf(/turf/simulated/wall/r_wall) + for(var/turf/simulated/wall/r_wall/X in Tsrc.loc) + if(X) X.add_hiddenprint(usr) + del(src) + return + else + if(S.amount < 1) return ..() + user << "\blue Now reinforcing girders" + if (do_after(user,60)) + if(!src || !S || S.amount < 1) return + S.use(1) + user << "\blue Girders reinforced!" + new/obj/structure/girder/reinforced( src.loc ) + del(src) + return - else if(istype(W, /obj/item/pipe)) - var/obj/item/pipe/P = W - if (P.pipe_type in list(0, 1, 5)) //simple pipes, simple bends, and simple manifolds. - user.drop_item() - P.loc = src.loc - user << "\blue You fit the pipe into the [src]!" - else - ..() - - - bullet_act(var/obj/item/projectile/Proj) - if(istype(Proj ,/obj/item/projectile/beam/pulse)) - src.ex_act(2) - ..() - return 0 - - blob_act() - if(prob(40)) - del(src) - - bullet_act(var/obj/item/projectile/Proj) - if(istype(Proj ,/obj/item/projectile/beam/pulse)) - src.ex_act(2) - ..() - return 0 - - ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - if (prob(75)) - var/remains = pick(/obj/item/stack/rods,/obj/item/stack/sheet/metal) - new remains(loc) - qdel(src) - return - if(3.0) - if (prob(30)) - var/remains = pick(/obj/item/stack/rods,/obj/item/stack/sheet/metal) - new remains(loc) - qdel(src) - return + if(S.sheettype) + var/M = S.sheettype + if(!anchored) + if(S.amount < 2) return + var/pdiff=performWallPressureCheck(src.loc) + if(!pdiff) + S.use(2) + user << "\blue You create a false wall! Push on it to open or close the passage." + var/F = text2path("/obj/structure/falsewall/[M]") + new F (src.loc) + del(src) + else + user << "\red There is too much air moving through the gap! The door wouldn't stay closed if you built it." + message_admins("Attempted false [M] wall made by [user.real_name] ([formatPlayerPanel(user,user.ckey)]) at [formatJumpTo(loc)] had a pressure difference of [pdiff]!") + log_admin("Attempted false [M] wall made by [user.real_name] ([user.ckey]) at [loc] had a pressure difference of [pdiff]!") + return else - return + if(S.amount < 2) return ..() + user << "\blue Now adding plating..." + if (do_after(user,40)) + if(!src || !S || S.amount < 2) return + S.use(2) + user << "\blue You added the plating!" + var/turf/Tsrc = get_turf(src) + Tsrc.ChangeTurf(text2path("/turf/simulated/wall/mineral/[M]")) + for(var/turf/simulated/wall/mineral/X in Tsrc.loc) + if(X) X.add_hiddenprint(usr) + del(src) + return + + add_hiddenprint(usr) + + else if(istype(W, /obj/item/pipe)) + var/obj/item/pipe/P = W + if (P.pipe_type in list(0, 1, 5)) //simple pipes, simple bends, and simple manifolds. + user.drop_item() + P.loc = src.loc + user << "\blue You fit the pipe into the [src]!" + else + ..() + +/obj/structure/girder/blob_act() + if(prob(40)) + del(src) + +/obj/structure/girder/bullet_act(var/obj/item/projectile/Proj) + if(istype(Proj, /obj/item/projectile/beam)) + health -= Proj.damage + ..() + if(health <= 0) + new /obj/item/stack/sheet/metal(get_turf(src)) + del(src) + + if(istype(Proj ,/obj/item/projectile/beam/pulse)) + src.ex_act(2) + ..() + return 0 + +/obj/structure/girder/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + if (prob(75)) + var/remains = pick(/obj/item/stack/rods,/obj/item/stack/sheet/metal) + new remains(loc) + qdel(src) + return + if(3.0) + if (prob(30)) + var/remains = pick(/obj/item/stack/rods,/obj/item/stack/sheet/metal) + new remains(loc) + qdel(src) + return + else + return /obj/structure/girder/displaced icon_state = "displaced" anchored = 0 + health = 50 /obj/structure/girder/reinforced icon_state = "reinforced" state = 2 + health = 500 /obj/structure/cultgirder icon= 'icons/obj/cult.dmi' @@ -226,47 +229,55 @@ anchored = 1 density = 1 layer = 2 + var/health = 250 - attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/weapon/wrench)) - playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) - user << "\blue Now disassembling the girder" - if(do_after(user,40)) - user << "\blue You dissasembled the girder!" - new /obj/effect/decal/remains/human(get_turf(src)) - del(src) +/obj/structure/cultgirder/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/weapon/wrench)) + playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1) + user << "\blue Now disassembling the girder" + if(do_after(user,40)) + user << "\blue You dissasembled the girder!" + dismantle() - else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) - user << "\blue Now slicing apart the girder" - if(do_after(user,30)) - user << "\blue You slice apart the girder!" - new /obj/effect/decal/remains/human(get_turf(src)) - del(src) + else if(istype(W, /obj/item/weapon/pickaxe/plasmacutter)) + user << "\blue Now slicing apart the girder" + if(do_after(user,30)) + user << "\blue You slice apart the girder!" + dismantle() - else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) - user << "\blue You drill through the girder!" - new /obj/effect/decal/remains/human(get_turf(src)) - del(src) + else if(istype(W, /obj/item/weapon/pickaxe/diamonddrill)) + user << "\blue You drill through the girder!" + dismantle() + +/obj/structure/cultgirder/proc/dismantle() + new /obj/effect/decal/remains/human(get_turf(src)) + qdel(src) - blob_act() - if(prob(40)) - del(src) +/obj/structure/cultgirder/blob_act() + if(prob(40)) + dismantle() + +/obj/structure/cultgirder/bullet_act(var/obj/item/projectile/Proj) //No beam check- How else will you destroy the cult girder with silver bullets????? + health -= Proj.damage + ..() + if(health <= 0) + dismantle() + return - - ex_act(severity) - switch(severity) - if(1.0) +/obj/structure/cultgirder/ex_act(severity) + switch(severity) + if(1.0) + qdel(src) + return + if(2.0) + if (prob(30)) + new /obj/effect/decal/remains/human(loc) qdel(src) - return - if(2.0) - if (prob(30)) - new /obj/effect/decal/remains/human(loc) - qdel(src) - return - if(3.0) - if (prob(5)) - new /obj/effect/decal/remains/human(loc) - qdel(src) - return - else - return \ No newline at end of file + return + if(3.0) + if (prob(5)) + new /obj/effect/decal/remains/human(loc) + qdel(src) + return + else + return \ No newline at end of file diff --git a/code/game/vehicles/vehicle.dm b/code/game/vehicles/vehicle.dm index 9924fea07d0..a61015cf6d6 100644 --- a/code/game/vehicles/vehicle.dm +++ b/code/game/vehicles/vehicle.dm @@ -182,9 +182,9 @@ mecha.loc.assume_air(leaked_gas) else del(leaked_gas) - if(mecha.hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) + /*if(mecha.hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) // LAG INDUCING - it'll keep on creating sparks if(mecha.get_charge()) mecha.spark_system.start() mecha.cell.charge -= min(20,mecha.cell.charge) - mecha.cell.maxcharge -= min(20,mecha.cell.maxcharge) + mecha.cell.maxcharge -= min(20,mecha.cell.maxcharge) */ return \ No newline at end of file diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index ce3ca49fd22..e7ab656bfb4 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -42,7 +42,7 @@ var/list/admin_verbs_admin = list( /client/proc/admin_memo, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/ /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/ /client/proc/toggleprayers, /*toggles prayers on/off*/ -// /client/proc/toggle_hear_radio, /*toggles whether we hear the radio*/ + /client/proc/toggle_hear_radio, /*toggles whether we hear the radio*/ /client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/ /datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/ /datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/ diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index f62cef10073..7fa81ce358e 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -35,7 +35,6 @@ /obj/effect/landmark/corpse/proc/createCorpse() //Creates a mob and checks for gear in each slot before attempting to equip it. var/mob/living/carbon/human/human/M = new /mob/living/carbon/human/human (src.loc) - M.dna.mutantrace = mutantrace M.real_name = src.name M.death(1) //Kills the new mob M.timeofdeath = timeofdeath diff --git a/code/modules/awaymissions/mission_code/academy.dm b/code/modules/awaymissions/mission_code/academy.dm index 997276e74b8..16af3141647 100644 --- a/code/modules/awaymissions/mission_code/academy.dm +++ b/code/modules/awaymissions/mission_code/academy.dm @@ -3,6 +3,7 @@ /area/awaymission/academy name = "\improper Academy Asteroids" icon_state = "away" + report_alerts = 0 /area/awaymission/academy/headmaster name = "\improper Academy Fore Block" diff --git a/code/modules/awaymissions/mission_code/blackmarketpackers.dm b/code/modules/awaymissions/mission_code/blackmarketpackers.dm index d2d215053af..468863f3026 100644 --- a/code/modules/awaymissions/mission_code/blackmarketpackers.dm +++ b/code/modules/awaymissions/mission_code/blackmarketpackers.dm @@ -4,7 +4,7 @@ name = "\improper BMP Asteroids" icon_state = "away" luminosity = 0 - + report_alerts = 0 /area/awaymission/BMPship/Aft name = "\improper Aft Block" diff --git a/code/modules/awaymissions/mission_code/centcomAway.dm b/code/modules/awaymissions/mission_code/centcomAway.dm index 70be2fa7202..06f36c5bf44 100644 --- a/code/modules/awaymissions/mission_code/centcomAway.dm +++ b/code/modules/awaymissions/mission_code/centcomAway.dm @@ -4,7 +4,8 @@ name = "XCC-P5831" icon_state = "away" requires_power = 0 - + report_alerts = 0 + /area/awaymission/centcomAway/general name = "XCC-P5831" music = "music/ambigen3.ogg" diff --git a/code/modules/awaymissions/mission_code/challenge.dm b/code/modules/awaymissions/mission_code/challenge.dm index f7aa861eb80..966ce6053f6 100644 --- a/code/modules/awaymissions/mission_code/challenge.dm +++ b/code/modules/awaymissions/mission_code/challenge.dm @@ -1,5 +1,10 @@ //Challenge Areas +/area/awaymission/challenge + name = "Challenge" + icon_state = "away" + report_alerts = 0 + /area/awaymission/challenge/start name = "Where Am I?" icon_state = "away" diff --git a/code/modules/awaymissions/mission_code/clownplanet.dm b/code/modules/awaymissions/mission_code/clownplanet.dm index 45b0dc1c7de..359037ada0a 100644 --- a/code/modules/awaymissions/mission_code/clownplanet.dm +++ b/code/modules/awaymissions/mission_code/clownplanet.dm @@ -6,6 +6,7 @@ luminosity = 1 lighting_use_dynamic = 0 requires_power = 0 + report_alerts = 0 /area/awaymission/clownplanet/miningtown name = "\improper Clown Planet - Bananium-o-Rama" diff --git a/code/modules/awaymissions/mission_code/spacebattle.dm b/code/modules/awaymissions/mission_code/spacebattle.dm index dc86365f4ee..3f9c9925772 100644 --- a/code/modules/awaymissions/mission_code/spacebattle.dm +++ b/code/modules/awaymissions/mission_code/spacebattle.dm @@ -4,6 +4,7 @@ name = "\improper Space Battle" icon_state = "away" requires_power = 0 + report_alerts = 0 /area/awaymission/spacebattle/cruiser name = "\improper Nanotrasen Cruiser" diff --git a/code/modules/awaymissions/mission_code/stationCollision.dm b/code/modules/awaymissions/mission_code/stationCollision.dm index fdfd016e40a..dee5ae20af6 100644 --- a/code/modules/awaymissions/mission_code/stationCollision.dm +++ b/code/modules/awaymissions/mission_code/stationCollision.dm @@ -19,6 +19,9 @@ * Areas */ //Gateroom gets its own APC specifically for the gate + /area/awaymission + report_alerts = 0 + /area/awaymission/gateroom //Library, medbay, storage room diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index 8b4b300a6b0..2960f159546 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -25,6 +25,16 @@ src << "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTRADIO) ? "hear all radio chat in the world" : "only hear from nearby speakers"]." prefs.save_preferences(src) feedback_add_details("admin_verb","TGR") + +/client/proc/toggle_hear_radio() + set name = "Show/Hide RadioChatter" + set category = "Preferences" + set desc = "Toggle seeing radiochatter from radios and speakers" + if(!holder) return + prefs.toggles ^= CHAT_RADIO + prefs.save_preferences(src) + usr << "You will [(prefs.toggles & CHAT_RADIO) ? "no longer" : "now"] see radio chatter from radios or speakers" + feedback_add_details("admin_verb","THR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/verb/toggleadminhelpsound() set name = "Hear/Silence Adminhelps" diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index c728f0d88bb..dc584be1e73 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -51,44 +51,6 @@ siemens_coefficient = 0.7 species_fit = list("Vox") -/obj/item/clothing/mask/gas/voice - name = "gas mask" - //desc = "A face-covering mask that can be connected to an air supply. It seems to house some odd electronics." - var/mode = 0// 0==Scouter | 1==Night Vision | 2==Thermal | 3==Meson - var/voice = "Unknown" - var/vchange = 0//This didn't do anything before. It now checks if the mask has special functions/N - origin_tech = "syndicate=4" - species_fit = list("Vox") - -/obj/item/clothing/mask/gas/voice/space_ninja - name = "ninja mask" - desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement." - icon_state = "s-ninja(norm)" - item_state = "s-ninja_mask" - vchange = 1 - unacidable = 1 - siemens_coefficient = 0.2 - species_fit = list("Vox") - -/obj/item/clothing/mask/gas/voice/space_ninja/scar - name = "ninja mask" - desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement. This mask appears to have already seen battle." - icon_state = "s-ninja(scar)" - item_state = "s-ninja_mask" - -/obj/item/clothing/mask/gas/voice/space_ninja/visor - name = "ninja mask" - desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement. This variant appears to have a visor to increase vision." - icon_state = "s-ninja(visor)" - item_state = "s-ninja_mask" - -/obj/item/clothing/mask/gas/voice/space_ninja/monocular - name = "ninja mask" - desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement. This variant appears to focus the user's vision out of a single port." - icon_state = "s-ninja(mon)" - item_state = "s-ninja_mask" - - /obj/item/clothing/mask/gas/clown_hat name = "clown wig and mask" desc = "A true prankster's facial attire. A clown is incomplete without his wig and mask." diff --git a/code/modules/clothing/masks/voice.dm b/code/modules/clothing/masks/voice.dm new file mode 100644 index 00000000000..3d28b720d79 --- /dev/null +++ b/code/modules/clothing/masks/voice.dm @@ -0,0 +1,59 @@ +/obj/item/voice_changer + name = "voice changer" + desc = "A voice scrambling module. If you can see this, report it as a bug on the tracker." + var/voice //If set and item is present in mask/suit, this name will be used for the wearer's speech. + var/active + +/obj/item/clothing/mask/gas/voice + name = "gas mask" + desc = "A face-covering mask that can be connected to an air supply. It seems to house some odd electronics." + var/obj/item/voice_changer/changer + origin_tech = "syndicate=4" + +/obj/item/clothing/mask/gas/voice/verb/Toggle_Voice_Changer() + set category = "Object" + set src in usr + + changer.active = !changer.active + usr << "You [changer.active ? "enable" : "disable"] the voice-changing module in \the [src]." + +/obj/item/clothing/mask/gas/voice/verb/Set_Voice(name as text) + set category = "Object" + set src in usr + + var/voice = sanitize(copytext(name,1,MAX_MESSAGE_LEN)) + if(!voice || !length(voice)) return + changer.voice = voice + usr << "You are now mimicking [changer.voice]." + +/obj/item/clothing/mask/gas/voice/New() + ..() + changer = new(src) + +/obj/item/clothing/mask/gas/voice/space_ninja + name = "ninja mask" + desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement." + icon_state = "s-ninja(norm)" + item_state = "s-ninja_mask" + unacidable = 1 + siemens_coefficient = 0.2 + species_fit = list("Vox") + var/mode = 0// 0==Scouter | 1==Night Vision | 2==Thermal | 3==Meson + +/obj/item/clothing/mask/gas/voice/space_ninja/scar + name = "ninja mask" + desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement. This mask appears to have already seen battle." + icon_state = "s-ninja(scar)" + item_state = "s-ninja_mask" + +/obj/item/clothing/mask/gas/voice/space_ninja/visor + name = "ninja mask" + desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement. This variant appears to have a visor to increase vision." + icon_state = "s-ninja(visor)" + item_state = "s-ninja_mask" + +/obj/item/clothing/mask/gas/voice/space_ninja/monocular + name = "ninja mask" + desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement. This variant appears to focus the user's vision out of a single port." + icon_state = "s-ninja(mon)" + item_state = "s-ninja_mask" \ No newline at end of file diff --git a/code/modules/karma/karma.dm b/code/modules/karma/karma.dm index 0b97d51c8b3..38c101b4370 100644 --- a/code/modules/karma/karma.dm +++ b/code/modules/karma/karma.dm @@ -345,8 +345,8 @@ You've gained [totalkarma] total karma in your time here.
"} message_admins("SQL ERROR during karmaspent updating (updating existing entry). Error: \[[err]\]\n") return else - usr << "You have been charged [cost] karma." - message_admins("[key_name(usr)] has been charged [cost] karma.") + usr << "You have been [refund ? "refunded" : "charged"] [cost] karma." + message_admins("[key_name(usr)] has been [refund ? "refunded" : "charged"] [cost] karma.") return /client/proc/karmarefund(var/type,var/name,var/cost) diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index ed588a76482..facaf65c5da 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -12,14 +12,15 @@ var/obj/item/weapon/card/id/prisoner/inserted_id var/obj/machinery/door/airlock/release_door var/door_tag = "prisonshuttle" - var/obj/item/device/radio/Radio //needed to send messages to sec radio var/use_release_door = 0 + var/obj/item/device/radio/intercom/announcer /obj/machinery/mineral/labor_claim_console/New() ..() - Radio = new/obj/item/device/radio(src) - Radio.listening = 0 + announcer = new /obj/item/device/radio/intercom(null) + announcer.config(list("Security" = 0)) + spawn(7) src.machine = locate(/obj/machinery/mineral/stacking_machine, get_step(src, machinedir)) var/t @@ -103,8 +104,8 @@ else if(shuttle.location == 1) if (shuttle.moving_status == SHUTTLE_IDLE) - Radio.set_frequency(SEC_FREQ) - Radio.talk_into(src, "[inserted_id.registered_name] has returned to the station. Minerals and Prisoner ID card ready for retrieval.", SEC_FREQ) + var/message = "[inserted_id.registered_name] has returned to the station. Minerals and Prisoner ID card ready for retrieval." + announcer.autosay(message, "Labor Camp Controller", "Security") usr << "Shuttle received message and will be sent shortly." shuttle.launch() else @@ -131,7 +132,7 @@ /obj/machinery/mineral/stacking_machine/laborstacker var/points = 0 //The unclaimed value of ore stacked. Value for each ore loosely relative to its rarity. - var/list/ore_values = list(("glass" = 1), ("metal" = 2), ("solid plasma" = 20), ("plasteel" = 23), ("reinforced glass" = 4), ("gold" = 20), ("silver" = 20), ("uranium" = 20), ("diamond" = 25), ("bananium" = 50)) + var/list/ore_values = list(("glass" = 1), ("metal" = 2), ("iron" = 3), ("solid plasma" = 20), ("plasteel" = 23), ("reinforced glass" = 4), ("gold" = 20), ("silver" = 20), ("uranium" = 20), ("diamond" = 25), ("bananium" = 50)) /obj/machinery/mineral/stacking_machine/laborstacker/proc/get_ore_values() var/dat = "
Transmit:[(!radio.isWireCut(WIRE_TRANSMIT)) ? "En" : "Dis" ]abled + [radio.broadcasting ? "En" : "Dis" ]abled
Receive:[(!radio.isWireCut(WIRE_RECEIVE)) ? "En" : "Dis" ]abled - -
Signal Pulser:[(!radio.isWireCut(WIRE_SIGNAL)) ? "En" : "Dis" ]abled + [radio.listening ? "En" : "Dis" ]abled
" @@ -155,6 +156,17 @@ S.loc = output.loc else O.loc = output.loc + + //Output amounts that are past stack_amt. + for(var/sheet in stack_storage) + if(stack_storage[sheet] >= stack_amt) + var/stacktype = stack_paths[sheet] + var/obj/item/stack/sheet/S = new stacktype (get_turf(output)) + S.amount = stack_amt + stack_storage[sheet] -= stack_amt + + console.updateUsrDialog() + return /**********************Point Lookup Console**************************/ diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 4be7623c8ff..e999f919091 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -24,6 +24,7 @@ var/atom/movable/following = null var/medHUD = 0 var/secHUD = 0 + var/anonsay = 0 /mob/dead/observer/New(var/mob/body=null, var/flags=1) sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF @@ -490,6 +491,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(usr != src) return ..() + + if (href_list["track"]) + var/mob/target = locate(href_list["track"]) in mob_list + if(target) + ManualFollow(target) if (href_list["follow"]) var/mob/target = locate(href_list["follow"]) in mob_list @@ -532,3 +538,14 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp following = null ..() //END TELEPORT HREF CODE + +/mob/dead/observer/verb/toggle_anonsay() + set category = "Ghost" + set name = "Toggle Anonymous Chat" + set desc = "Toggles showing your key in dead chat." + + src.anonsay = !src.anonsay + if(anonsay) + src << "Your key won't be shown when you speak in dead chat." + else + src << "Your key will be publicly visible again." diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm new file mode 100644 index 00000000000..f63566a0483 --- /dev/null +++ b/code/modules/mob/hear_say.dm @@ -0,0 +1,215 @@ +// At minimum every mob has a hear_say proc. + +/mob/proc/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) + if(!client) + return + + if(speaker && !speaker.client && istype(src,/mob/dead/observer) && client.prefs.toggles & CHAT_GHOSTEARS && !speaker in view(src)) + //Does the speaker have a client? It's either random stuff that observers won't care about (Experiment 97B says, 'EHEHEHEHEHEHEHE') + //Or someone snoring. So we make it where they won't hear it. + return + + //make sure the air can transmit speech - hearer's side + var/turf/T = get_turf(src) + if ((T) && (!(istype(src, /mob/dead/observer)))) //Ghosts can hear even in vacuum. + var/datum/gas_mixture/environment = T.return_air() + var/pressure = (environment)? environment.return_pressure() : 0 + if(pressure < SOUND_MINIMUM_PRESSURE && get_dist(speaker, src) > 1) + return + + if (pressure < ONE_ATMOSPHERE*0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet + italics = 1 + sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact + + if(sleeping || stat == 1) + hear_sleep(message) + return + + //non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet. + if (language && (language.flags & NONVERBAL)) + if (!speaker || (src.sdisabilities & BLIND || src.blinded) || !(speaker in view(src))) + message = stars(message) + + if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages + if(!say_understands(speaker,language)) + if(istype(speaker,/mob/living/simple_animal)) + var/mob/living/simple_animal/S = speaker + message = pick(S.speak) + else + if(language) + message = language.scramble(message) + else + message = stars(message) + + var/speaker_name = speaker.name + if(istype(speaker, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = speaker + speaker_name = H.GetVoice() + + if(italics) + message = "[message]" + + var/track = null + if(istype(src, /mob/dead/observer)) + if(italics && client.prefs.toggles & CHAT_GHOSTRADIO) + return + if(speaker_name != speaker.real_name && speaker.real_name) + speaker_name = "[speaker.real_name] ([speaker_name])" + track = "(follow) " + if(client.prefs.toggles & CHAT_GHOSTEARS && speaker in view(src)) + message = "[message]" + + if(sdisabilities & DEAF || ear_deaf) + if(!language || !(language.flags & INNATE)) // INNATE is the flag for audible-emote-language, so we don't want to show an "x talks but you cannot hear them" message if it's set + if(speaker == src) + src << "You cannot hear yourself speak!" + else + src << "[speaker_name][alt_name] talks but you cannot hear \him." + else + if(language) + src << "[speaker_name][alt_name] [track][language.format_message(message, verb)]" + else + src << "[speaker_name][alt_name] [track][verb], \"[message]\"" + if (speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z)) + var/turf/source = speaker? get_turf(speaker) : get_turf(src) + src.playsound_local(source, speech_sound, sound_vol, 1) + + +/mob/proc/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="") + + if(!client) + return + + if(sleeping || stat==1) //If unconscious or sleeping + hear_sleep(message) + return + + var/track = null + + //non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet. + if (language && (language.flags & NONVERBAL)) + if (!speaker || (src.sdisabilities & BLIND || src.blinded) || !(speaker in view(src))) + message = stars(message) + + if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages + if(!say_understands(speaker,language)) + if(istype(speaker,/mob/living/simple_animal)) + var/mob/living/simple_animal/S = speaker + if(S.speak && S.speak.len) + message = pick(S.speak) + else + return + else + if(language) + message = language.scramble(message) + else + message = stars(message) + + if(hard_to_hear) + message = stars(message) + + var/speaker_name = speaker.name + + if(vname) + speaker_name = vname + + if(istype(speaker, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = speaker + if(H.voice) + speaker_name = H.voice + + if(hard_to_hear) + speaker_name = "unknown" + + var/changed_voice + + if(istype(src, /mob/living/silicon/ai) && !hard_to_hear) + var/jobname // the mob's "job" + var/mob/living/carbon/human/impersonating //The crewmember being impersonated, if any. + + if (ishuman(speaker)) + var/mob/living/carbon/human/H = speaker + + if((H.wear_id && istype(H.wear_id,/obj/item/weapon/card/id/syndicate)) && (H.wear_mask && istype(H.wear_mask,/obj/item/clothing/mask/gas/voice))) + + changed_voice = 1 + var/mob/living/carbon/human/I = locate(speaker_name) + + if(I) + impersonating = I + jobname = impersonating.get_assignment() + else + jobname = "Unknown" + else + jobname = H.get_assignment() + + else if (iscarbon(speaker)) // Nonhuman carbon mob + jobname = "No id" + else if (isAI(speaker)) + jobname = "AI" + else if (isrobot(speaker)) + jobname = "Cyborg" + else if (istype(speaker, /mob/living/silicon/pai)) + jobname = "Personal AI" + else + jobname = "Unknown" + + if(changed_voice) + if(impersonating) + track = "[speaker_name] ([jobname])" + else + track = "[speaker_name] ([jobname])" + else + track = "[speaker_name] ([jobname])" + + if(istype(src, /mob/dead/observer)) + if(speaker_name != speaker.real_name && !isAI(speaker)) //Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs. + speaker_name = "[speaker.real_name] ([speaker_name])" + track = "[speaker_name] (follow)" + + var/formatted + if(language) + formatted = language.format_message_radio(message, verb) + else + formatted = "[verb], \"[message]\"" + if(sdisabilities & DEAF || ear_deaf) + if(prob(20)) + src << "You feel your headset vibrate but can hear nothing from it!" + else if(track) + src << "[part_a][track][part_b][formatted]" + else + src << "[part_a][speaker_name][part_b][formatted]" + +/mob/proc/hear_signlang(var/message, var/verb = "gestures", var/datum/language/language, var/mob/speaker = null) + if(!client) + return + + if(say_understands(speaker, language)) + message = "[src] [verb], \"[message]\"" + else + message = "[src] [verb]." + + if(src.status_flags & PASSEMOTES) + for(var/obj/item/weapon/holder/H in src.contents) + H.show_message(message) + for(var/mob/living/M in src.contents) + M.show_message(message) + src.show_message(message) + +/mob/proc/hear_sleep(var/message) + var/heard = "" + if(prob(15)) + var/list/punctuation = list(",", "!", ".", ";", "?") + var/list/messages = text2list(message, " ") + var/R = rand(1, messages.len) + var/heardword = messages[R] + if(copytext(heardword,1, 1) in punctuation) + heardword = copytext(heardword,2) + if(copytext(heardword,-1) in punctuation) + heardword = copytext(heardword,1,lentext(heardword)) + heard = "...You hear something about...[heardword]" + + else + heard = "...You almost hear someone talking..." + + src << heard diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm index 0a043db41ef..3b4f1cc6f39 100644 --- a/code/modules/mob/language.dm +++ b/code/modules/mob/language.dm @@ -1,3 +1,5 @@ +#define SCRAMBLE_CACHE_LEN 20 + /* Datum based languages. Easily editable and modular. */ @@ -5,13 +7,106 @@ /datum/language var/name = "an unknown language" // Fluff name of language if any. var/desc = "A language." // Short description for 'Check Languages'. - var/speech_verb = "says" // 'says', 'hisses'. - var/ask_verb = "asks" // Used when sentence ends in a ? + var/speech_verb = "says" // 'says', 'hisses', 'farts'. + var/ask_verb = "asks" // Used when sentence ends in a ? var/exclaim_verb = "exclaims" // Used when sentence ends in a ! - var/colour = "say_quote" // CSS style to use for strings in this language. + var/whisper_verb // Optional. When not specified speech_verb + quietly/softly is used instead. + var/signlang_verb = list() // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags + var/colour = "body" // CSS style to use for strings in this language. var/key = "x" // Character used to speak in language eg. :o for Unathi. var/flags = 0 // Various language flags. var/native // If set, non-native speakers will have trouble speaking. + var/list/syllables // Used when scrambling text for a non-speaker. + var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string. + +/datum/language/proc/get_random_name(var/gender, name_count=2, syllable_count=4) + if(!syllables || !syllables.len) + if(gender==FEMALE) + return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names)) + else + return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names)) + + var/full_name = "" + var/new_name = "" + + for(var/i = 0;i0;x--) + new_name += pick(syllables) + full_name += " [capitalize(lowertext(new_name))]" + + return "[trim(full_name)]" + +/datum/language + var/list/scramble_cache = list() + +/datum/language/proc/scramble(var/input) + + if(!syllables || !syllables.len) + return stars(input) + + // If the input is cached already, move it to the end of the cache and return it + if(input in scramble_cache) + var/n = scramble_cache[input] + scramble_cache -= input + scramble_cache[input] = n + return n + + var/input_size = length(input) + var/scrambled_text = "" + var/capitalize = 1 + + while(length(scrambled_text) < input_size) + var/next = pick(syllables) + if(capitalize) + next = capitalize(next) + capitalize = 0 + scrambled_text += next + var/chance = rand(100) + if(chance <= 5) + scrambled_text += ". " + capitalize = 1 + else if(chance > 5 && chance <= space_chance) + scrambled_text += " " + + scrambled_text = trim(scrambled_text) + var/ending = copytext(scrambled_text, length(scrambled_text)) + if(ending == ".") + scrambled_text = copytext(scrambled_text,1,length(scrambled_text)-1) + var/input_ending = copytext(input, input_size) + if(input_ending in list("!","?",".")) + scrambled_text += input_ending + + // Add it to cache, cutting old entries if the list is too long + scramble_cache[input] = scrambled_text + if(scramble_cache.len > SCRAMBLE_CACHE_LEN) + scramble_cache.Cut(1, scramble_cache.len-SCRAMBLE_CACHE_LEN-1) + + + return scrambled_text + +/datum/language/proc/format_message(message, verb) + return "[verb], \"[capitalize(message)]\"" + +/datum/language/proc/format_message_radio(message, verb) + return "[verb], \"[capitalize(message)]\"" + +/datum/language/proc/get_talkinto_msg_range(message) + // if you yell, you'll be heard from two tiles over instead of one + return (copytext(message, length(message)) == "!") ? 2 : 1 + +/datum/language/proc/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) + log_say("[key_name(speaker)] : ([name]) [message]") + + if(!speaker_mask) speaker_mask = speaker.name + var/msg = "[name], [speaker_mask] [format_message(message, get_spoken_verb(message))]" + + for(var/mob/player in player_list) + if(istype(player,/mob/dead) || ((src in player.languages) && check_special_condition(player))) + player << msg + +/datum/language/proc/check_special_condition(var/mob/other) + return 1 /datum/language/proc/get_spoken_verb(var/msg_end) switch(msg_end) @@ -19,8 +114,25 @@ return exclaim_verb if("?") return ask_verb - return speech_verb - + return speech_verb + +// Noise "language", for audible emotes. +/datum/language/noise + name = "Noise" + desc = "Noises" + key = "" + flags = RESTRICTED|NONGLOBAL|INNATE|NO_TALK_MSG + +/datum/language/noise/format_message(message, verb) + return "[message]" + +/datum/language/noise/format_message_radio(message, verb) + return "[message]" + +/datum/language/noise/get_talkinto_msg_range(message) + // if you make a loud noise (screams etc), you'll be heard from 4 tiles over instead of two + return (copytext(message, length(message)) == "!") ? 4 : 2 + /datum/language/unathi name = "Sinta'unathi" desc = "The common language of Moghes, composed of sibilant hisses and rattles. Spoken natively by Unathi." @@ -29,17 +141,38 @@ exclaim_verb = "roars" colour = "soghun" key = "o" - flags = WHITELISTED + flags = RESTRICTED + syllables = list("ss","ss","ss","ss","skak","seeki","resh","las","esi","kor","sh") + +/datum/language/unathi/get_random_name() + + var/new_name = ..() + while(findtextEx(new_name,"sss",1,null)) + new_name = replacetext(new_name, "sss", "ss") + return capitalize(new_name) /datum/language/tajaran name = "Siik'tajr" - desc = "An expressive language that combines yowls and chirps with posture, tail and ears. Native to the Tajaran." + desc = "The traditionally employed tongue of Ahdomai, composed of expressive yowls and chirps. Native to the Tajaran." speech_verb = "mrowls" ask_verb = "mrowls" exclaim_verb = "yowls" colour = "tajaran" key = "j" - flags = WHITELISTED + flags = RESTRICTED + syllables = list("rr","rr","tajr","kir","raj","kii","mir","kra","ahk","nal","vah","khaz","jri","ran","darr", \ + "mi","jri","dynh","manq","rhe","zar","rrhaz","kal","chur","eech","thaa","dra","jurl","mah","sanu","dra","ii'r", \ + "ka","aasi","far","wa","baq","ara","qara","zir","sam","mak","hrar","nja","rir","khan","jun","dar","rik","kah", \ + "hal","ket","jurl","mah","tul","cresh","azu","ragh") + +/datum/language/tajaran/get_random_name(var/gender) + + var/new_name = ..(gender,1) + if(prob(80)) + new_name += " [pick(list("Hadii","Kaytam","Zhan-Khazan","Hharar","Njarir'Akhan"))]" + else + new_name += ..(gender,1) + return new_name /datum/language/skrell name = "Skrellian" @@ -49,7 +182,8 @@ exclaim_verb = "warbles" colour = "skrell" key = "k" - flags = WHITELISTED + flags = RESTRICTED + syllables = list("qr","qrr","xuq","qil","quum","xuqm","vol","xrim","zaoo","qu-uu","qix","qoo","zix","*","!") /datum/language/vox name = "Vox-pidgin" @@ -59,7 +193,12 @@ exclaim_verb = "SHRIEKS" colour = "vox" key = "v" - flags = RESTRICTED + flags = RESTRICTED | WHITELISTED + syllables = list("ti","ti","ti","hi","hi","ki","ki","ki","ki","ya","ta","ha","ka","ya","chi","cha","kah", \ + "SKRE","AHK","EHK","RAWK","KRA","AAA","EEE","KI","II","KRI","KA") + +/datum/language/vox/get_random_name() + return ..(FEMALE,1,6) /datum/language/diona name = "Rootspeak" @@ -67,79 +206,258 @@ speech_verb = "creaks and rustles" ask_verb = "creaks" exclaim_verb = "rustles" - colour = "soghun" + colour = "diona" key = "q" flags = RESTRICTED + syllables = list("hs","zt","kr","st","sh") +/datum/language/diona/get_random_name() + var/new_name = "[pick(list("To Sleep Beneath","Wind Over","Embrace of","Dreams of","Witnessing","To Walk Beneath","Approaching the"))]" + new_name += " [pick(list("the Void","the Sky","Encroaching Night","Planetsong","Starsong","the Wandering Star","the Empty Day","Daybreak","Nightfall","the Rain"))]" + return new_name + +/datum/language/trinary + name = "Trinary" + desc = "A modification of binary to allow fuzzy logic. 0 is no, 1 is maybe, 2 is yes. Credited with giving Machine People the ability to think creatively." + speech_verb = "states" + ask_verb = "queries" + exclaim_verb = "exclaims" + colour = "trinary" + key = "5" + flags = RESTRICTED | WHITELISTED + syllables = list("02011","01222","10100","10210","21012","02011","21200","1002","2001","0002","0012","0012","000","120","121","201","220","10","11","0") + /datum/language/kidan name = "Chittin" - desc = "The noise made by rubbing it's antennae together is actually a complex form of communication for Kidan." - speech_verb = "rubs its antenna together" - ask_verb = "rubs its antenna together" - exclaim_verb = "rubs its antenna together" - colour = "tajaran" + desc = "The noise made by rubbing its antennae together is actually a complex form of communication for Kidan." + speech_verb = "rubs its antennae together" + ask_verb = "rubs its antennae together" + exclaim_verb = "rubs its antennae together" + colour = "kidan" key = "4" - flags = RESTRICTED - + flags = RESTRICTED | WHITELISTED + syllables = list("click","clack") + /datum/language/slime name = "Bubblish" - desc = "Languages of slimes, a mixture of bubbling noises and pops. Almost impossible to speak without mechanical aid for non slime people." + desc = "The language of slimes. It's a mixture of bubbling noises and pops. Very difficult to speak without mechanical aid for humans." speech_verb = "bubbles and pops" ask_verb = "bubbles and pops" exclaim_verb = "bubbles and pops" - colour = "skrell" + colour = "slime" key = "f" + flags = RESTRICTED | WHITELISTED + syllables = list("blob","plop","pop","bop","boop") + +/datum/language/common + name = "Galactic Common" + desc = "The common galactic tongue." + speech_verb = "says" + whisper_verb = "whispers" + key = "0" flags = RESTRICTED + syllables = list("blah","blah","blah","bleh","meh","neh","nah","wah") + +//TODO flag certain languages to use the mob-type specific say_quote and then get rid of these. +/datum/language/common/get_spoken_verb(var/msg_end) + switch(msg_end) + if("!") + return pick("exclaims","shouts","yells") //TODO: make the basic proc handle lists of verbs. + if("?") + return ask_verb + return speech_verb /datum/language/human name = "Sol Common" desc = "A bastardized hybrid of informal English and elements of Mandarin Chinese; the common language of the Sol system." + speech_verb = "says" + whisper_verb = "whispers" + colour = "solcom" key = "1" flags = RESTRICTED + syllables = list("tao","shi","tzu","yi","com","be","is","i","op","vi","ed","lec","mo","cle","te","dis","e") -/datum/language/trinary - name = "Trinary" - desc = "A modification of binary to allow fuzzy logic. 0 is no, 1 maybe, 2 yes. Credited with giving Machine People the ability to think creatively." - key = "5" - flags = RESTRICTED - +/datum/language/human/get_spoken_verb(var/msg_end) + switch(msg_end) + if("!") + return pick("exclaims","shouts","yells") //TODO: make the basic proc handle lists of verbs. + if("?") + return ask_verb + return speech_verb // Galactic common languages (systemwide accepted standards). /datum/language/trader name = "Tradeband" desc = "Maintained by the various trading cartels in major systems, this elegant, structured language is used for bartering and bargaining." speech_verb = "enunciates" + colour = "say_quote" key = "2" + space_chance = 100 + syllables = list("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit", + "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore", + "magna", "aliqua", "ut", "enim", "ad", "minim", "veniam", "quis", "nostrud", + "exercitation", "ullamco", "laboris", "nisi", "ut", "aliquip", "ex", "ea", "commodo", + "consequat", "duis", "aute", "irure", "dolor", "in", "reprehenderit", "in", + "voluptate", "velit", "esse", "cillum", "dolore", "eu", "fugiat", "nulla", + "pariatur", "excepteur", "sint", "occaecat", "cupidatat", "non", "proident", "sunt", + "in", "culpa", "qui", "officia", "deserunt", "mollit", "anim", "id", "est", "laborum") /datum/language/gutter name = "Gutter" desc = "Much like Standard, this crude pidgin tongue descended from numerous languages and serves as Tradeband for criminal elements." speech_verb = "growls" + colour = "rough" key = "3" + syllables = list ("gra","ba","ba","breh","bra","rah","dur","ra","ro","gro","go","ber","bar","geh","heh", "gra") + +/datum/language/xenocommon + name = "Xenomorph" + colour = "alien" + desc = "The common tongue of the xenomorphs." + speech_verb = "hisses" + ask_verb = "hisses" + exclaim_verb = "hisses" + key = "6" + flags = RESTRICTED + syllables = list("sss","sSs","SSS") + +/datum/language/xenos + name = "Hivemind" + desc = "Xenomorphs have the strange ability to commune over a psychic hivemind." + speech_verb = "hisses" + ask_verb = "hisses" + exclaim_verb = "hisses" + colour = "alien" + key = "a" + flags = RESTRICTED | HIVEMIND + +/datum/language/xenos/check_special_condition(var/mob/other) + var/mob/living/carbon/M = other + if(!istype(M)) + return 1 + return 0 + +/datum/language/ling + name = "Changeling" + desc = "Although they are normally wary and suspicious of each other, changelings can commune over a distance." + speech_verb = "says" + colour = "changeling" + key = "g" + flags = RESTRICTED | HIVEMIND + +/datum/language/ling/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) + + if(speaker.mind && speaker.mind.changeling) + ..(speaker,message,speaker.mind.changeling.changelingID) + else + ..(speaker,message) + +/datum/language/corticalborer + name = "Cortical Link" + desc = "Cortical borers possess a strange link between their tiny minds." + speech_verb = "sings" + ask_verb = "sings" + exclaim_verb = "sings" + colour = "alien" + key = "x" + flags = RESTRICTED | HIVEMIND + +/datum/language/corticalborer/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) + + var/mob/living/simple_animal/borer/B + + if(istype(speaker,/mob/living/carbon)) + var/mob/living/carbon/M = speaker + B = M.has_brain_worms() + else if(istype(speaker,/mob/living/simple_animal/borer)) + B = speaker + + if(B) + speaker_mask = B.truename + ..(speaker,message,speaker_mask) + +/datum/language/binary + name = "Robot Talk" + desc = "Most human stations support free-use communications protocols and routing hubs for synthetic use." + colour = "say_quote" + speech_verb = "states" + ask_verb = "queries" + exclaim_verb = "declares" + key = "b" + flags = RESTRICTED | HIVEMIND + var/drone_only + +/datum/language/binary/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) + + if(!speaker.binarycheck()) + return + + if (!message) + return + + var/message_start = "[name], [speaker.name]" + var/message_body = "[speaker.say_quote(message)], \"[message]\"" + + 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("[message_start] [message_body]", 2) + + for (var/mob/living/S in living_mob_list) + + if(drone_only && !istype(S,/mob/living/silicon/robot/drone)) + continue + else if(istype(S , /mob/living/silicon/ai)) + message_start = "[name], [speaker.name]" + else if (!S.binarycheck()) + continue + + S.show_message("[message_start] [message_body]", 2) + + var/list/listening = hearers(1, src) + listening -= src + + for (var/mob/living/M in listening) + if(istype(M, /mob/living/silicon) || M.binarycheck()) + continue + M.show_message("synthesised voice beeps, \"beep beep beep\"",2) + + //robot binary xmitter component power usage + if (isrobot(speaker)) + var/mob/living/silicon/robot/R = speaker + var/datum/robot_component/C = R.components["comms"] + R.use_power(C.energy_consumption) + +/datum/language/binary/drone + name = "Drone Talk" + desc = "A heavily encoded damage control coordination stream." + speech_verb = "transmits" + ask_verb = "transmits" + exclaim_verb = "transmits" + colour = "say_quote" + key = "d" + flags = RESTRICTED | HIVEMIND + drone_only = 1 // Language handling. /mob/proc/add_language(var/language) - for(var/datum/language/L in languages) - if(L && L.name == language) - return 0 - var/datum/language/new_language = all_languages[language] - if(!istype(new_language,/datum/language)) + if(!istype(new_language) || new_language in languages) return 0 - languages += new_language + languages |= new_language return 1 /mob/proc/remove_language(var/rem_language) + var/datum/language/L = all_languages[rem_language] + . = (L in languages) + languages.Remove(L) - for(var/datum/language/L in languages) - if(L && L.name == rem_language) - languages -= L - return 1 +// Can we speak this language, as opposed to just understanding it? +/mob/proc/can_speak(datum/language/speaking) - return 0 + return (universal_speak || (speaking && speaking.flags & INNATE) || speaking in src.languages) //TBD /mob/verb/check_languages() @@ -150,7 +468,10 @@ var/dat = "Known Languages

" for(var/datum/language/L in languages) - dat += "[L.name] (:[L.key])
[L.desc]

" + if(!(L.flags & NONGLOBAL)) + dat += "[L.name] (:[L.key])
[L.desc]

" src << browse(dat, "window=checklanguage") - return \ No newline at end of file + return + +#undef SCRAMBLE_CACHE_LEN diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm index 0eb6fe764ee..721c2a4b3fb 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm @@ -21,6 +21,8 @@ if(name == "alien") name = text("alien ([rand(1, 1000)])") real_name = name + add_language("Xenomorph") + add_language("Hivemind") ..() //This is fine, works the same as a human diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index 5515c951514..a918e069bb7 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -23,6 +23,8 @@ name = "alien larva ([rand(1, 1000)])" real_name = name regenerate_icons() + add_language("Xenomorph") + add_language("Hivemind") ..() //This is fine, works the same as a human diff --git a/code/modules/mob/living/carbon/alien/say.dm b/code/modules/mob/living/carbon/alien/say.dm index 9604a50559d..8b4177470a4 100644 --- a/code/modules/mob/living/carbon/alien/say.dm +++ b/code/modules/mob/living/carbon/alien/say.dm @@ -1,69 +1,28 @@ /mob/living/carbon/alien/say(var/message) + var/verb = "says" + var/message_range = world.view - if (silent) - return + if(client) + if(client.prefs.muted & MUTE_IC) + src << "\red You cannot speak in IC (Muted)." + return - if (length(message) >= 2) - if (department_radio_keys[copytext(message, 1, 3)] == "alientalk") - 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) - else + message = trim_strip_html_properly(message) -/mob/living/proc/alien_talk(var/message) + if(stat == 2) + return say_dead(message) + + if(copytext(message,1,2) == "*") + return emote(copytext(message,2)) + + var/datum/language/speaking = parse_language(message) + + if(speaking) + message = copytext(message, 2+length(speaking.key)) - log_say("[key_name(src)] : [message]") message = trim(message) - if (!message) + if(!message || stat) return - 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) - - var/list/listening = hearers(1, src) - listening -= src - listening += src - - 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]" - var/rendered2 = null - for (var/mob/M in player_list) - if (istype(M, /mob/new_player)) - continue - if (M.stat > 1 && istype(M, /mob/dead/observer)) - rendered2 = "Hivemind, [name] (Follow) [message_a]" - M.show_message(rendered2, 2) \ No newline at end of file + ..(message, speaking, verb, null, null, message_range, null) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index f181cc05464..bbcd527386b 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -55,4 +55,7 @@ canmove = 1 use_me = 1 //If it can move, let it emote else canmove = 0 - return canmove \ No newline at end of file + return canmove + +/mob/living/carbon/brain/binarycheck() + return istype(loc, /obj/item/device/mmi/posibrain) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/brain/say.dm b/code/modules/mob/living/carbon/brain/say.dm index 1e7a9da9a76..26dc7b30f3f 100644 --- a/code/modules/mob/living/carbon/brain/say.dm +++ b/code/modules/mob/living/carbon/brain/say.dm @@ -1,3 +1,4 @@ +//TODO: Convert this over for languages. /mob/living/carbon/brain/say(var/message) if (silent) return @@ -8,18 +9,31 @@ if(!(container && (istype(container, /obj/item/device/mmi) || istype(container, /obj/item/device/mmi/posibrain)))) return //No MMI, can't speak, bucko./N else - if ((department_radio_keys[copytext(message, 1, 3)] == "binary") && (container && istype(container, /obj/item/device/mmi/posibrain))) - message = copytext(message, 3) - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - robot_talk(message) - return + var/datum/language/speaking = parse_language(message) + if(speaking) + message = copytext(message, 2+length(speaking.key)) + var/verb = "says" + var/ending = copytext(message, length(message)) + if (speaking) + verb = speaking.get_spoken_verb(ending) + else + if(ending=="!") + verb=pick("exclaims","shouts","yells") + if(ending=="?") + verb="asks" + if(prob(emp_damage*4)) if(prob(10))//10% chane to drop the message entirely return else message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher + + if(speaking && speaking.flags & HIVEMIND) + speaking.broadcast(src,trim(message)) + return + 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 + spawn(0) R.radio.hear_talk(src, trim(sanitize(message)), verb, speaking) + ..(trim(message), speaking, verb) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 5f3a0cca482..ff64560fbbe 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -9,6 +9,40 @@ var/datum/species/species //Contains icon generation and language information, set during New(). var/embedded_flag //To check if we've need to roll for damage on movement while an item is imbedded in us. +/mob/living/carbon/human/New(var/new_loc, var/new_species = null, var/delay_ready_dna=0) + if(!species) + if(new_species) + set_species(new_species,null,1) + else + set_species() + + var/datum/reagents/R = new/datum/reagents(1000) + reagents = R + R.my_atom = src + + hud_list[HEALTH_HUD] = image('icons/mob/hud.dmi', src, "hudhealth100") + hud_list[STATUS_HUD] = image('icons/mob/hud.dmi', src, "hudhealthy") + hud_list[ID_HUD] = image('icons/mob/hud.dmi', src, "hudunknown") + hud_list[WANTED_HUD] = image('icons/mob/hud.dmi', src, "hudblank") + hud_list[IMPLOYAL_HUD] = image('icons/mob/hud.dmi', src, "hudblank") + hud_list[IMPCHEM_HUD] = image('icons/mob/hud.dmi', src, "hudblank") + hud_list[IMPTRACK_HUD] = image('icons/mob/hud.dmi', src, "hudblank") + hud_list[SPECIALROLE_HUD] = image('icons/mob/hud.dmi', src, "hudblank") + hud_list[STATUS_HUD_OOC] = image('icons/mob/hud.dmi', src, "hudhealthy") + hud_list[NATIONS_HUD] = image('icons/mob/hud.dmi', src, "hudblank") + + ..() + + if(dna) + dna.real_name = real_name + + prev_gender = gender // Debug for plural genders + make_blood() + + // Set up DNA. + if(!delay_ready_dna) + dna.ready_dna(src) + /mob/living/carbon/human/dummy real_name = "Test Dummy" status_flags = GODMODE|CANPUSH @@ -49,8 +83,6 @@ /mob/living/carbon/human/grey/New(var/new_loc) ..(new_loc, "Grey") - spell_list += new /obj/effect/proc_holder/spell/wizard/targeted/remotetalk - /mob/living/carbon/human/human/New(var/new_loc) ..(new_loc, "Human") @@ -63,50 +95,6 @@ h_style = "blue IPC screen" ..(new_loc, "Machine") -/mob/living/carbon/human/New(var/new_loc, var/new_species = null, var/delay_ready_dna=0) - if(!species) - if(new_species) - set_species(new_species,null,1) - else - set_species() - - if(species.language) - var/datum/language/L = all_languages[species.language] - if(L) - languages += L - - var/datum/reagents/R = new/datum/reagents(1000) - reagents = R - R.my_atom = src - - if(!dna) - dna = new /datum/dna(null) - dna.species=species.name - - hud_list[HEALTH_HUD] = image('icons/mob/hud.dmi', src, "hudhealth100") - hud_list[STATUS_HUD] = image('icons/mob/hud.dmi', src, "hudhealthy") - hud_list[ID_HUD] = image('icons/mob/hud.dmi', src, "hudunknown") - hud_list[WANTED_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPLOYAL_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPCHEM_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPTRACK_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - hud_list[SPECIALROLE_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - hud_list[STATUS_HUD_OOC] = image('icons/mob/hud.dmi', src, "hudhealthy") - hud_list[NATIONS_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - - ..() - - if(dna) - dna.real_name = real_name - - prev_gender = gender // Debug for plural genders - make_blood() - - // Set up DNA. - if(!delay_ready_dna) - dna.ready_dna(src) - - /mob/living/carbon/human/Bump(atom/movable/AM as mob|obj, yes) if ((!( yes ) || now_pushing)) return @@ -1313,7 +1301,8 @@ usr << "\blue [self ? "Your" : "[src]'s"] pulse is [src.get_pulse(GETPULSE_HAND)]." /mob/living/carbon/human/proc/set_species(var/new_species, var/force_organs, var/default_colour) - + + var/datum/species/oldspecies = species if(!dna) if(!new_species) new_species = "Human" @@ -1323,19 +1312,34 @@ else dna.species = new_species - if(species && (species.name && species.name == new_species)) - return - - if(species && species.language) - remove_language(species.language) + if(species) + if(species.name && species.name == new_species) + return + if(species.language) + remove_language(species.language) + + if(species.default_language) + remove_language(species.default_language) + species = all_species[new_species] + + if(oldspecies) + if(oldspecies.default_genes.len) + oldspecies.handle_dna(src,1) // Remove any genes that belong to the old species if(force_organs || !organs || !organs.len) species.create_organs(src) + + if(vessel) + vessel = null + make_blood() if(species.language) add_language(species.language) + + if(species.default_language) + add_language(species.default_language) see_in_dark = species.darksight if(see_in_dark > 2) @@ -1343,9 +1347,6 @@ else see_invisible = SEE_INVISIBLE_LIVING - if(species.default_mutations.len>0 || species.default_blocks.len>0) - do_deferred_species_setup=1 - spawn(0) update_icons() @@ -1358,21 +1359,18 @@ r_skin = 0 g_skin = 0 b_skin = 0 + + if(!dna) + dna = new /datum/dna(null) + dna.species = species.name + dna.real_name = real_name + dna.mutantrace = null species.handle_post_spawn(src) spawn(0) - update_icons() - if(species && species.flags & NO_BLOOD) //We want the var for safety but we can do without the actual blood. - if(vessel.has_reagent("water") || vessel.has_reagent("blood")) - var/water = vessel.get_reagent_amount("water") - var/blood = vessel.get_reagent_amount("blood") - vessel.remove_reagent("water",water) - vessel.remove_reagent("blood",blood) - if(species.bloodflags & BLOOD_SLIME) - vessel.add_reagent("water",560-vessel.total_volume) - else - vessel.add_reagent("blood",560-vessel.total_volume) + overlays.Cut() + update_mutantrace(1) fixblood() if(species) diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 4db067181cb..49b6a73143c 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -32,9 +32,12 @@ timeofdeath = 0 /mob/living/carbon/human/getBrainLoss() - if(species && species.flags & NO_INTORGANS) return + if(species && species.flags & NO_INTORGANS) + return var/res = brainloss var/datum/organ/internal/brain/sponge = internal_organs_by_name["brain"] + if(!sponge) + return if (sponge.is_bruised()) res += 20 if (sponge.is_broken()) @@ -197,6 +200,7 @@ UpdateDamageIcon() hud_updateflag |= 1 << HEALTH_HUD updatehealth() + speech_problem_flag = 1 //Heal MANY external organs, in random order @@ -218,6 +222,7 @@ parts -= picked updatehealth() hud_updateflag |= 1 << HEALTH_HUD + speech_problem_flag = 1 if(update) UpdateDamageIcon() // damage MANY external organs, in random order diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index b98ea2c790f..4e373c7cdb8 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -50,6 +50,10 @@ var/icon/stand_icon = null var/icon/lying_icon = null + + var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life() + + var/speech_problem_flag = 0 var/miming = null //Toggle for the mime's abilities. var/special_voice = "" // For changing our voice. Used by a symptom. diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 8319046d5d5..0f32d9d187e 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -33,27 +33,9 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc var/prev_gender = null // Debug for plural genders var/temperature_alert = 0 var/in_stasis = 0 - var/do_deferred_species_setup=0 var/exposedtimenow = 0 var/firstexposed = 0 -// Doing this during species init breaks shit. -/mob/living/carbon/human/proc/DeferredSpeciesSetup() - var/mut_update=0 - if(species.default_mutations.len>0) - for(var/mutation in species.default_mutations) - if(!(mutation in mutations)) - mutations.Add(mutation) - mut_update=1 - if(species.default_blocks.len>0) - for(var/block in species.default_blocks) - if(!dna.GetSEState(block)) - dna.SetSEState(block,1) - mut_update=1 - if(mut_update) - domutcheck(src,null,MUTCHK_FORCED) - update_mutations() - /mob/living/carbon/human/Life() set invisibility = 0 //set background = 1 @@ -63,10 +45,6 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc ..() - if(do_deferred_species_setup) - DeferredSpeciesSetup() - do_deferred_species_setup=0 - //Apparently, the person who wrote this code designed it so that //blinded get reset each cycle and then get activated later in the //code. Very ugly. I dont care. Moving this stuff here so its easy @@ -84,6 +62,8 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc if(life_tick%30==15) hud_updateflag = 1022 + + voice = GetVoice() //No need to update all of these procs if the guy is dead. if(stat != DEAD && !in_stasis) @@ -252,22 +232,26 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc drop_item() emote("cough") if (disabilities & TOURETTES) + speech_problem_flag = 1 if ((prob(10) && paralysis <= 1)) Stun(10) switch(rand(1, 3)) if(1) emote("twitch") if(2 to 3) - say("[prob(50) ? ";" : ""][pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS")]") + var/tourettes = pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS") + say("[prob(50) ? ";" : ""][tourettes]") var/x_offset = pixel_x + rand(-2,2) //Should probably be moved into the twitch emote at some point. var/y_offset = pixel_y + rand(-1,1) animate(src, pixel_x = pixel_x + x_offset, pixel_y = pixel_y + y_offset, time = 1) animate(pixel_x = pixel_x - x_offset, pixel_y = pixel_y - y_offset, time = 1) if (disabilities & NERVOUS) + speech_problem_flag = 1 if (prob(10)) stuttering = max(10, stuttering) if (getBrainLoss() >= 60 && stat != 2) + speech_problem_flag = 1 if (prob(3)) switch(pick(1,2,3)) if(1) @@ -329,6 +313,7 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc if(gene.is_active(src)) /* if (prob(10) && prob(gene.instability)) adjustCloneLoss(1) */ + speech_problem_flag = 1 gene.OnMobLife(src) if (radiation) @@ -1091,6 +1076,7 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc if(halloss > 0) adjustHalLoss(-3) else if(sleeping) + speech_problem_flag = 1 handle_dreams() adjustStaminaLoss(-10) adjustHalLoss(-3) @@ -1944,7 +1930,29 @@ var/global/list/brutefireloss_overlays = list("1" = image("icon" = 'icons/mob/sc var/client/C = client for(var/mob/living/carbon/human/H in view(src, 14)) C.images += H.hud_list[NATIONS_HUD] + +/mob/living/carbon/human/handle_silent() + if(..()) + speech_problem_flag = 1 + return silent +/mob/living/carbon/human/handle_slurring() + if(..()) + speech_problem_flag = 1 + return slurring + +/mob/living/carbon/human/handle_stunned() + if(species.flags & NO_PAIN) + stunned = 0 + return 0 + if(..()) + speech_problem_flag = 1 + return stunned + +/mob/living/carbon/human/handle_stuttering() + if(..()) + speech_problem_flag = 1 + return stuttering // Need this in species. //#undef HUMAN_MAX_OXYLOSS diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 028f0efe755..4cc61b36290 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -1,81 +1,152 @@ /mob/living/carbon/human/say(var/message) - if(miming) - if(length(message) >= 2) - if(mind && mind.changeling) - if(copytext(message, 1, 2) != "*" && department_radio_keys[copytext(message, 1, 3)] != "changeling") - 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 + var/verb = "says" + var/alt_name = "" + var/message_range = world.view + var/italics = 0 - if(dna) - if(length(message) >= 2) - if (copytext(message, 1, 2) != "*" && department_radio_keys[copytext(message, 1, 3)] != "changeling") - for(var/datum/dna/gene/gene in dna_genes) - if(!gene.block) - continue - if(gene.is_active(src)) - message = gene.OnSay(src,message) - /* - if(dna.mutantrace == "lizard") - if(copytext(message, 1, 2) != "*") - message = replacetext(message, "s", stutter("ss")) + if(client) + if(client.prefs.muted & MUTE_IC) + src << "\red You cannot speak in IC (Muted)." + return - if(dna.mutantrace == "slime" && prob(5)) - if(copytext(message, 1, 2) != "*") - if(copytext(message, 1, 2) == ";") - message = ";" - else - message = "" - message += "SKR" - var/imax = rand(5,20) - for(var/i = 0,iYou're muzzled and cannot speak!
" + return + + var/message_mode = parse_message_mode(message, "headset") + + if(copytext(message,1,2) == "*") + return emote(copytext(message,2)) + + if(name != GetVoice()) + alt_name = "(as [get_id_name("Unknown")])" + + //parse the radio code and consume it + if (message_mode) + if (message_mode == "headset") + message = copytext(message,2) //it would be really nice if the parse procs could do this for us. + else + message = copytext(message,3) + + //parse the language code and consume it + var/datum/language/speaking = parse_language(message) + if(speaking) + message = copytext(message,2+length(speaking.key)) + else if(species.default_language) + speaking = all_languages[species.default_language] + + var/ending = copytext(message, length(message)) + if (speaking) + // This is broadcast to all mobs with the language, + // irrespective of distance or anything else. + if(speaking.flags & HIVEMIND) + speaking.broadcast(src,trim(message)) + return + //If we've gotten this far, keep going! + verb = speaking.get_spoken_verb(ending) + else + if(ending=="!") + verb=pick("exclaims","shouts","yells") + if(ending=="?") + verb="asks" + + message = trim(message) + + if(speech_problem_flag) + var/list/handle_r = handle_speech_problems(message) + message = handle_r[1] + verb = handle_r[2] + speech_problem_flag = handle_r[3] + + if(!message || message == "") + return + + var/list/obj/item/used_radios = new + + switch (message_mode) + if("headset") + if(l_ear && istype(l_ear,/obj/item/device/radio)) + var/obj/item/device/radio/R = l_ear + R.talk_into(src,message,null,verb,speaking) + used_radios += l_ear + else if(r_ear && istype(r_ear,/obj/item/device/radio)) + var/obj/item/device/radio/R = r_ear + R.talk_into(src,message,null,verb,speaking) + used_radios += r_ear + + if("right ear") + var/obj/item/device/radio/R + var/has_radio = 0 + if(r_ear && istype(r_ear,/obj/item/device/radio)) + R = r_ear + has_radio = 1 + if(r_hand && istype(r_hand, /obj/item/device/radio)) + R = r_hand + has_radio = 1 + if(has_radio) + R.talk_into(src,message,null,verb,speaking) + used_radios += R - if(wear_mask) - if(istype(wear_mask, /obj/item/clothing/mask/gas/voice/space_ninja) && wear_mask:voice == "Unknown") - if(copytext(message, 1, 2) != "*") - var/list/temp_message = text2list(message, " ") - var/list/pick_list = list() - for(var/i = 1, i <= temp_message.len, i++) - pick_list += i - for(var/i=1, i <= abs(temp_message.len/3), i++) - var/H = pick(pick_list) - if(findtext(temp_message[H], "*") || findtext(temp_message[H], ";") || findtext(temp_message[H], ":")) continue - temp_message[H] = ninjaspeak(temp_message[H]) - pick_list -= H - message = list2text(temp_message, " ") - message = replacetext(message, "o", "¤") - message = replacetext(message, "p", "þ") - message = replacetext(message, "l", "£") - message = replacetext(message, "s", "§") - message = replacetext(message, "u", "µ") - message = replacetext(message, "b", "ß") + if("left ear") + var/obj/item/device/radio/R + var/has_radio = 0 + if(l_ear && istype(l_ear,/obj/item/device/radio)) + R = l_ear + has_radio = 1 + if(l_hand && istype(l_hand,/obj/item/device/radio)) + R = l_hand + has_radio = 1 + if(has_radio) + R.talk_into(src,message,null,verb,speaking) + used_radios += R - else if(istype(wear_mask, /obj/item/clothing/mask/horsehead)) - var/obj/item/clothing/mask/horsehead/hoers = wear_mask - if(hoers.voicechange) - if(!(copytext(message, 1, 2) == "*" || (mind && mind.changeling && department_radio_keys[copytext(message, 1, 3)] != "changeling"))) - message = pick("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") + if("intercom") + for(var/obj/item/device/radio/intercom/I in view(1, null)) + I.talk_into(src, message, verb, speaking) + used_radios += I + if("whisper") + whisper_say(message, speaking, alt_name) + return + else + if(message_mode) + if(l_ear && istype(l_ear,/obj/item/device/radio)) + l_ear.talk_into(src,message, message_mode, verb, speaking) + used_radios += l_ear + else if(r_ear && istype(r_ear,/obj/item/device/radio)) + r_ear.talk_into(src,message, message_mode, verb, speaking) + used_radios += r_ear - if ((M_HULK in mutations) && health >= 25 && length(message)) - if(copytext(message, 1, 2) != "*") - message = "[uppertext(message)]!!" //because I don't know how to code properly in getting vars from other files -Bro + var/sound/speech_sound + var/sound_vol + if(species.speech_sounds && prob(species.speech_chance)) + speech_sound = sound(pick(species.speech_sounds)) + sound_vol = 50 - if (src.slurring) - if(copytext(message, 1, 2) != "*") - message = slur(message) + //speaking into radios + if(used_radios.len) + italics = 1 + message_range = 1 + if(speaking) + message_range = speaking.get_talkinto_msg_range(message) + var/msg + if(!speaking || !(speaking.flags & NO_TALK_MSG)) + msg = "\The [src] talks into \the [used_radios[1]]" + for(var/mob/living/M in hearers(5, src)) + if((M != src) && msg) + M.show_message(msg) + if (speech_sound) + sound_vol *= 0.5 - if((species.name == "Vox" || species.name == "Vox Armalis") && prob(20)) - playsound(src.loc, 'sound/voice/shriek1.ogg', 50, 1) - - ..(message) + ..(message, speaking, verb, alt_name, italics, message_range, speech_sound, sound_vol) //ohgod we should really be passing a datum here. /mob/living/carbon/human/proc/forcesay(list/append) if(stat == CONSCIOUS) @@ -100,6 +171,7 @@ if(findtext(temp, "*", 1, 2)) //emotes return temp = copytext(trim_left(temp), 1, rand(5,8)) + var/trimmed = trim_left(temp) if(length(trimmed)) if(append) @@ -108,25 +180,45 @@ say(temp) winset(client, "input", "text=[null]") -/mob/living/carbon/human/say_understands(var/other,var/datum/language/speaking = null) +/mob/living/carbon/human/say_understands(var/mob/other,var/datum/language/speaking = null) if(has_brain_worms()) //Brain worms translate everything. Even mice and alien speak. return 1 - if (istype(other, /mob/living/silicon)) - return 1 - if (istype(other, /mob/living/carbon/brain)) - return 1 - if (istype(other, /mob/living/carbon/slime)) + + if(species.can_understand(other)) return 1 + + //These only pertain to common. Languages are handled by mob/say_understands() + if (!speaking) + if (istype(other, /mob/living/carbon/monkey/diona)) + if(other.languages.len >= 2) //They've sucked down some blood and can speak common now. + return 1 + if (istype(other, /mob/living/silicon)) + return 1 + if (istype(other, /mob/living/carbon/brain)) + return 1 + if (istype(other, /mob/living/carbon/slime)) + return 1 + + //This is already covered by mob/say_understands() + //if (istype(other, /mob/living/simple_animal)) + // if((other.universal_speak && !speaking) || src.universal_speak || src.universal_understand) + // return 1 + // return 0 + return ..() /mob/living/carbon/human/GetVoice() - if(istype(src.wear_mask, /obj/item/clothing/mask/gas/voice)) - var/obj/item/clothing/mask/gas/voice/V = src.wear_mask - if(V.vchange) - return V.voice - else - return name + + var/voice_sub + for(var/obj/item/gear in list(wear_mask,wear_suit,head)) + if(!gear) + continue + var/obj/item/voice_changer/changer = locate() in gear + if(changer && changer.active && changer.voice) + voice_sub = changer.voice + if(voice_sub) + return voice_sub if(mind && mind.changeling && mind.changeling.mimicing) return mind.changeling.mimicing if(GetSpecialVoice()) @@ -143,4 +235,81 @@ return /mob/living/carbon/human/proc/GetSpecialVoice() - return special_voice \ No newline at end of file + return special_voice + + +/* + ***Deprecated*** + let this be handled at the hear_say or hear_radio proc + This is left in for robot speaking when humans gain binary channel access until I get around to rewriting + robot_talk() proc. + There is no language handling build into it however there is at the /mob level so we accept the call + for it but just ignore it. +*/ + +/mob/living/carbon/human/say_quote(var/message, var/datum/language/speaking = null) + var/verb = "says" + var/ending = copytext(message, length(message)) + + if(speaking) + verb = speaking.get_spoken_verb(ending) + else + if(ending == "!") + verb = "exclaims" + else if(ending == "?") + verb = "asks" + + return verb + +/mob/living/carbon/human/proc/handle_speech_problems(var/message) + + var/list/returns[3] + var/verb = "says" + var/handled = 0 + if(silent || (sdisabilities & MUTE)) + message = "" + handled = 1 + if(istype(wear_mask, /obj/item/clothing/mask/horsehead)) + var/obj/item/clothing/mask/horsehead/hoers = wear_mask + if(hoers.voicechange) + if(mind && mind.changeling && department_radio_keys[copytext(message, 1, 3)] != "changeling") + message = pick("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") + verb = pick("whinnies","neighs", "says") + handled = 1 + + if(dna) + for(var/datum/dna/gene/gene in dna_genes) + if(!gene.block) + continue + if(gene.is_active(src)) + message = gene.OnSay(src,message) + handled = 1 + + if(message != "") + if((M_HULK in mutations) && health >= 25 && length(message)) + message = "[uppertext(message)]!!!" + verb = pick("yells","roars","hollers") + handled = 1 + if(slurring) + message = slur(message) + verb = "slurs" + handled = 1 + if(stuttering) + message = stutter(message) + verb = "stammers" + handled = 1 + + var/braindam = getBrainLoss() + if(braindam >= 60) + handled = 1 + if(prob(braindam/4)) + message = stutter(message) + verb = "gibbers" + if(prob(braindam)) + message = uppertext(message) + verb = "yells loudly" + + returns[1] = message + returns[2] = verb + returns[3] = handled + return returns diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 9a47536c759..937c85bce8e 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -290,11 +290,12 @@ proc/get_damage_icon_part(damage_state, body_part) //Robotic limbs are handled in get_icon() so all we worry about are missing or dead limbs. //No icon stored, so we need to start with a basic one. var/datum/organ/external/chest = get_organ("chest") - base_icon = chest.get_icon(g,fat) + if(chest) + base_icon = chest.get_icon(g,fat) - if(chest.status & ORGAN_DEAD) - base_icon.ColorTone(necrosis_color_mod) - base_icon.SetIntensity(0.7) + if(chest.status & ORGAN_DEAD) + base_icon.ColorTone(necrosis_color_mod) + base_icon.SetIntensity(0.7) for(var/datum/organ/external/part in organs) diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm index 58bbfc54dec..43e8af94f50 100644 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ b/code/modules/mob/living/carbon/human/whisper.dm @@ -1,11 +1,8 @@ //Lallander was here /mob/living/carbon/human/whisper(message as text) + var/alt_name = "" - message = trim(copytext(strip_html_simple(message), 1, MAX_MESSAGE_LEN)) - - if (!message || silent || miming) - return - + message = trim_strip_html_properly(message) log_whisper("[src.name]/[src.key] : [message]") if (src.client) @@ -16,28 +13,74 @@ if (src.client.handle_spam_prevention(message,MUTE_IC)) return - if (src.stat == 2) return src.say_dead(message) if (src.stat) return - var/alt_name = "" - if (istype(src, /mob/living/carbon/human) && src.name != GetVoice()) - var/mob/living/carbon/human/H = src - alt_name = " (as [H.get_id_name("Unknown")])" - // Mute disability - if (src.sdisabilities & MUTE) - return + if(name != GetVoice()) + alt_name = "(as [get_id_name("Unknown")])" + + //parse the language code and consume it + var/datum/language/speaking = parse_language(message) + if (speaking) + message = copytext(message,2+length(speaking.key)) + + whisper_say(message, speaking, alt_name) + + +//This is used by both the whisper verb and human/say() to handle whispering +/mob/living/carbon/human/proc/whisper_say(var/message, var/datum/language/speaking = null, var/alt_name="", var/verb="whispers") if (istype(src.wear_mask, /obj/item/clothing/mask/muzzle)) + src << "You're muzzled and cannot speak!" return - var/italics = 1 var/message_range = 1 + var/eavesdropping_range = 2 + var/watching_range = 5 + var/italics = 1 - if(istype(src.wear_mask, /obj/item/clothing/mask/gas/voice/space_ninja)&&src.wear_mask:voice=="Unknown") + var/not_heard //the message displayed to people who could not hear the whispering + if (speaking) + if (speaking.whisper_verb) + verb = speaking.whisper_verb + not_heard = "[verb] something" + else + var/adverb = pick("quietly", "softly") + verb = "[speaking.speech_verb] [adverb]" + not_heard = "[speaking.speech_verb] something [adverb]" + else + not_heard = "[verb] something" //TODO get rid of the null language and just prevent speech if language is null + + message = capitalize(trim(message)) + + if(speech_problem_flag) + var/list/handle_r = handle_speech_problems(message) + message = handle_r[1] + verb = handle_r[2] + if(verb == "yells loudly") + verb = "slurs emphatically" + else + var/adverb = pick("quietly", "softly") + verb = "[verb] [adverb]" + + speech_problem_flag = handle_r[3] + + if(!message || message=="") + return + + //looks like this only appears in whisper. Should it be elsewhere as well? Maybe handle_speech_problems? + var/voice_sub + for(var/obj/item/gear in list(wear_mask,wear_suit,head)) + if(!gear) + continue + var/obj/item/voice_changer/changer = locate() in gear + if(changer && changer.active && changer.voice) + voice_sub = changer.voice + + if(voice_sub == "Unknown") if(copytext(message, 1, 2) != "*") var/list/temp_message = text2list(message, " ") var/list/pick_list = list() @@ -49,97 +92,60 @@ temp_message[H] = ninjaspeak(temp_message[H]) pick_list -= H message = list2text(temp_message, " ") - message = replacetext(message, "o", "?") - message = replacetext(message, "p", "?") - message = replacetext(message, "l", "?") - message = replacetext(message, "s", "?") - message = replacetext(message, "u", "?") - message = replacetext(message, "b", "?") - - if (src.stuttering) - message = stutter(message) - - for (var/obj/O in view(message_range, src)) - spawn (0) - if (O) - O.hear_talk(src, message) + message = replacetext(message, "o", "�") + message = replacetext(message, "p", "�") + message = replacetext(message, "l", "�") + message = replacetext(message, "s", "�") + message = replacetext(message, "u", "�") + message = replacetext(message, "b", "�") var/list/listening = hearers(message_range, src) listening |= src + //ghosts + for (var/mob/M in dead_mob_list) //does this include players who joined as observers as well? + if (!(M.client)) + continue + if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS)) + listening |= M + //Pass whispers on to anything inside the immediate listeners. for(var/mob/L in listening) for(var/mob/C in L.contents) if(istype(C,/mob/living)) listening += C - var/list/eavesdropping = hearers(2, src) + //pass on the message to objects that can hear us. + for (var/obj/O in view(message_range, src)) + spawn (0) + if (O) + O.hear_talk(src, message, verb, speaking) + + var/list/eavesdropping = hearers(eavesdropping_range, src) eavesdropping -= src eavesdropping -= listening - var/list/watching = hearers(5, src) + var/list/watching = hearers(watching_range, src) watching -= src watching -= listening watching -= eavesdropping - var/list/heard_a = list() // understood us - var/list/heard_b = list() // didn't understand us + //now mobs + var/speech_bubble_test = say_test(message) + var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]") + spawn(30) del(speech_bubble) - for (var/mob/M in listening) - if (M.say_understands(src)) - heard_a += M - else - heard_b += M + for(var/mob/M in listening) + M << speech_bubble + M.hear_say(message, verb, speaking, alt_name, italics, src) - var/rendered = null + if (eavesdropping.len) + var/new_message = stars(message) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less. + for(var/mob/M in eavesdropping) + M << speech_bubble + M.hear_say(new_message, verb, speaking, alt_name, italics, src) - for (var/mob/M in watching) - if (M.say_understands(src)) - rendered = "[src.name] whispers something." - else - rendered = "[src.voice_name] whispers something." - 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 - - 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.client.prefs.toggles & CHAT_GHOSTEARS)) + if (watching.len) + var/rendered = "[src.name] [not_heard]." + for (var/mob/M in watching) M.show_message(rendered, 2) diff --git a/code/modules/mob/living/carbon/metroid/say.dm b/code/modules/mob/living/carbon/metroid/say.dm index 89708595da4..f4de10ceb74 100644 --- a/code/modules/mob/living/carbon/metroid/say.dm +++ b/code/modules/mob/living/carbon/metroid/say.dm @@ -21,3 +21,16 @@ return 1 return ..() +/mob/living/carbon/slime/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) + if (speaker in Friends) + speech_buffer = list() + speech_buffer.Add(speaker) + speech_buffer.Add(lowertext(html_decode(message))) + ..() + +/mob/living/carbon/slime/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="") + if (speaker in Friends) + speech_buffer = list() + speech_buffer.Add(speaker) + speech_buffer.Add(lowertext(html_decode(message))) + ..() diff --git a/code/modules/mob/living/carbon/species.dm b/code/modules/mob/living/carbon/species.dm index 6512ee05247..02587d04994 100644 --- a/code/modules/mob/living/carbon/species.dm +++ b/code/modules/mob/living/carbon/species.dm @@ -8,10 +8,10 @@ var/icobase = 'icons/mob/human_races/r_human.dmi' // Normal icon set. var/deform = 'icons/mob/human_races/r_def_human.dmi' // Mutated icon set. var/eyes = "eyes_s" // Icon for eyes. + var/blurb = "A completely nondescript species." // A brief lore summary for use in the chargen screen. var/primitive // Lesser form, if any (ie. monkey for humans) var/tail // Name of tail image in species effects icon file. - var/language // Default racial language, if any. var/unarmed //For empty hand harm-intent attack var/unarmed_type = /datum/unarmed_attack var/mutantrace // Safeguard due to old code. @@ -41,11 +41,8 @@ var/brute_mod = null // Physical damage reduction/malus. var/burn_mod = null // Burn damage reduction/malus. - // For grays var/max_hurt_damage = 9 // Max melee damage dealt + 5 if hulk - var/list/default_mutations = list() - var/list/default_blocks = list() // Don't touch. - var/list/default_block_names = list() // Use this instead, using the names from setupgame.dm + var/list/default_genes = list() var/flags = 0 // Various specific features. var/bloodflags=0 @@ -60,12 +57,21 @@ //Used in icon caching. var/race_key = 0 var/icon/icon_template - + + // Language/culture vars. + var/default_language = "Galactic Common" // Default language is used when 'say' is used without modifiers. + var/language = "Galactic Common" // Default racial language, if any. + var/secondary_langs = list() // The names of secondary languages that are available to this species. + var/list/speech_sounds // A list of sounds to potentially play when speaking. + var/list/speech_chance // The likelihood of a speech sound playing. /datum/species/New() unarmed = new unarmed_type() - +/datum/species/proc/get_random_name(var/gender) + var/datum/language/species_language = all_languages[language] + return species_language.get_random_name(gender) + /datum/species/proc/create_organs(var/mob/living/carbon/human/H) //Handles creation of mob organs. //This is a basic humanoid limb setup. H.organs = list() @@ -81,7 +87,7 @@ H.organs_by_name["l_foot"] = new/datum/organ/external/l_foot(H.organs_by_name["l_leg"]) H.organs_by_name["r_foot"] = new/datum/organ/external/r_foot(H.organs_by_name["r_leg"]) - if (name!="Slime People") + if (name != "Slime People") new/datum/organ/internal/heart(H) new/datum/organ/internal/lungs(H) new/datum/organ/internal/liver(H) @@ -247,6 +253,10 @@ return 1 /datum/species/proc/handle_post_spawn(var/mob/living/carbon/C) //Handles anything not already covered by basic species assignment. + handle_dna(C) + return + +/datum/species/proc/handle_dna(var/mob/living/carbon/C, var/remove) //Handles DNA mutations, as that doesn't work at init. return // Used for species-specific names (Vox, etc) @@ -262,6 +272,10 @@ return message /datum/species/proc/equip(var/mob/living/carbon/human/H) + return + +/datum/species/proc/can_understand(var/mob/other) + return /datum/species/human name = "Human" @@ -272,17 +286,29 @@ flags = HAS_LIPS | HAS_UNDERWEAR | CAN_BE_FAT bodyflags = HAS_SKIN_TONE unarmed_type = /datum/unarmed_attack/punch + blurb = "Humanity originated in the Sol system, and over the last five centuries has spread \ + colonies across a wide swathe of space. They hold a wide range of forms and creeds.

\ + While the central Sol government maintains control of its far-flung people, powerful corporate \ + interests, rampant cyber and bio-augmentation and secretive factions make life on most human \ + worlds tumultous at best." /datum/species/unathi name = "Unathi" icobase = 'icons/mob/human_races/r_lizard.dmi' deform = 'icons/mob/human_races/r_def_lizard.dmi' path = /mob/living/carbon/human/unathi + default_language = "Galactic Common" language = "Sinta'unathi" tail = "sogtail" unarmed_type = /datum/unarmed_attack/claws primitive = /mob/living/carbon/monkey/unathi darksight = 3 + + blurb = "A heavily reptillian species, Unathi (or 'Sinta as they call themselves) hail from the \ + Uuosa-Eso system, which roughly translates to 'burning mother'.

Coming from a harsh, radioactive \ + desert planet, they mostly hold ideals of honesty, virtue, martial combat and bravery above all \ + else, frequently even their own lives. They prefer warmer temperatures than most species and \ + their native tongue is a heavy hissing laungage called Sinta'Unathi." flags = HAS_LIPS | HAS_UNDERWEAR bodyflags = FEET_CLAWS | HAS_TAIL | HAS_SKIN_COLOR @@ -305,10 +331,17 @@ icobase = 'icons/mob/human_races/r_tajaran.dmi' deform = 'icons/mob/human_races/r_def_tajaran.dmi' path = /mob/living/carbon/human/tajaran + default_language = "Galactic Common" language = "Siik'tajr" tail = "tajtail" unarmed_type = /datum/unarmed_attack/claws darksight = 8 + + blurb = "The Tajaran race is a species of feline-like bipeds hailing from the planet of Ahdomai in the \ + S'randarr system. They have been brought up into the space age by the Humans and Skrell, and have been \ + influenced heavily by their long history of Slavemaster rule. They have a structured, clan-influenced way \ + of family and politics. They prefer colder environments, and speak a variety of languages, mostly Siik'Maas, \ + using unique inflections their mouths form." cold_level_1 = 200 cold_level_2 = 140 @@ -331,9 +364,16 @@ icobase = 'icons/mob/human_races/r_skrell.dmi' deform = 'icons/mob/human_races/r_def_skrell.dmi' path = /mob/living/carbon/human/skrell + default_language = "Galactic Common" language = "Skrellian" primitive = /mob/living/carbon/monkey/skrell unarmed_type = /datum/unarmed_attack/punch + + blurb = "An amphibious species, Skrell come from the star system known as Qerr'Vallis, which translates to 'Star of \ + the royals' or 'Light of the Crown'.

Skrell are a highly advanced and logical race who live under the rule \ + of the Qerr'Katish, a caste within their society which keeps the empire of the Skrell running smoothly. Skrell are \ + herbivores on the whole and tend to be co-operative with the other species of the galaxy, although they rarely reveal \ + the secrets of their empire to their allies." flags = HAS_LIPS | HAS_UNDERWEAR bloodflags = BLOOD_GREEN @@ -348,8 +388,20 @@ icobase = 'icons/mob/human_races/r_vox.dmi' deform = 'icons/mob/human_races/r_def_vox.dmi' path = /mob/living/carbon/human/vox - language = "Vox-pidgin" + + default_language = "Galactic Common" + language = "Vox-pidgin" + speech_sounds = list('sound/voice/shriek1.ogg') + speech_chance = 20 + unarmed_type = /datum/unarmed_attack/claws //I dont think it will hurt to give vox claws too. + + blurb = "The Vox are the broken remnants of a once-proud race, now reduced to little more than \ + scavenging vermin who prey on isolated stations, ships or planets to keep their own ancient arkships \ + alive. They are four to five feet tall, reptillian, beaked, tailed and quilled; human crews often \ + refer to them as 'shitbirds' for their violent and offensive nature, as well as their horrible \ + smell.

Most humans will never meet a Vox raider, instead learning of this insular species through \ + dealing with their traders and merchants; those that do rarely enjoy the experience." warning_low_pressure = 50 hazard_low_pressure = 0 @@ -393,7 +445,6 @@ name = "Vox Armalis" icobase = 'icons/mob/human_races/r_armalis.dmi' deform = 'icons/mob/human_races/r_armalis.dmi' - language = "Vox-pidgin" path = /mob/living/carbon/human/voxarmalis unarmed_type = /datum/unarmed_attack/claws/armalis @@ -454,20 +505,19 @@ icobase = 'icons/mob/human_races/r_kidan.dmi' deform = 'icons/mob/human_races/r_def_kidan.dmi' path = /mob/living/carbon/human/kidan + default_language = "Galactic Common" language = "Chittin" unarmed_type = /datum/unarmed_attack/claws - brute_mod = 0.8 flags = IS_WHITELISTED bloodflags = BLOOD_GREEN bodyflags = FEET_CLAWS - - /datum/species/slime name = "Slime People" + default_language = "Galactic Common" language = "Bubblish" path = /mob/living/carbon/human/slime primitive = /mob/living/carbon/slime @@ -478,37 +528,48 @@ /datum/species/slime/handle_post_spawn(var/mob/living/carbon/human/H) H.dna = new /datum/dna(null) - H.dna.species=H.species.name + H.dna.real_name = H.real_name + H.dna.species = H.species.name H.dna.mutantrace = "slime" - H.update_mutantrace() - return ..() + ..() -/datum/species/grey // /vg/ +/datum/species/grey name = "Grey" icobase = 'icons/mob/human_races/r_grey.dmi' deform = 'icons/mob/human_races/r_def_grey.dmi' - language = "Grey" + default_language = "Galactic Common" + //language = "Grey" // Perhaps if they ever get a hivemind unarmed_type = /datum/unarmed_attack/punch darksight = 5 // BOOSTED from 2 eyes = "grey_eyes_s" brute_mod = 1.25 //greys are fragile + + default_genes = list(M_REMOTE_TALK) primitive = /mob/living/carbon/monkey // TODO flags = IS_WHITELISTED | HAS_LIPS | HAS_UNDERWEAR | CAN_BE_FAT - - // Both must be set or it's only a 45% chance of manifesting. - default_mutations=list(M_REMOTE_TALK) - default_block_names=list("REMOTETALK") - + +/datum/species/grey/handle_dna(var/mob/living/carbon/C, var/remove) + if(!remove) + C.dna.SetSEState(REMOTETALKBLOCK,1,1) + C.mutations |= M_REMOTE_TALK + genemutcheck(C,REMOTETALKBLOCK,null,MUTCHK_FORCED) + else + C.dna.SetSEState(REMOTETALKBLOCK,0,1) + C.mutations -= M_REMOTE_TALK + genemutcheck(C,REMOTETALKBLOCK,null,MUTCHK_FORCED) + C.update_mutations() + ..() /datum/species/diona name = "Diona" icobase = 'icons/mob/human_races/r_diona.dmi' deform = 'icons/mob/human_races/r_def_plant.dmi' path = /mob/living/carbon/human/diona + default_language = "Galactic Common" language = "Rootspeak" unarmed_type = /datum/unarmed_attack/diona primitive = /mob/living/carbon/monkey/diona @@ -523,6 +584,14 @@ heat_level_1 = 300 heat_level_2 = 350 heat_level_3 = 700 + + blurb = "Commonly referred to (erroneously) as 'plant people', the Dionaea are a strange space-dwelling collective \ + species hailing from Epsilon Ursae Minoris. Each 'diona' is a cluster of numerous cat-sized organisms called nymphs; \ + there is no effective upper limit to the number that can fuse in gestalt, and reports exist of the Epsilon Ursae \ + Minoris primary being ringed with a cloud of singing space-station-sized entities.

The Dionaea coexist peacefully with \ + all known species, especially the Skrell. Their communal mind makes them slow to react, and they have difficulty understanding \ + even the simplest concepts of other minds. Their alien physiology allows them survive happily off a diet of nothing but light, \ + water and other radiation." flags = NO_BREATHE | REQUIRE_LIGHT | IS_PLANT | RAD_ABSORB | NO_BLOOD | IS_SLOW | NO_PAIN @@ -532,6 +601,12 @@ flesh_color = "#907E4A" reagent_tag = IS_DIONA + +/datum/species/diona/can_understand(var/mob/other) + var/mob/living/carbon/monkey/diona/D = other + if(istype(D)) + return 1 + return 0 /datum/species/diona/handle_post_spawn(var/mob/living/carbon/human/H) H.gender = NEUTER @@ -561,6 +636,7 @@ icobase = 'icons/mob/human_races/r_machine.dmi' deform = 'icons/mob/human_races/r_machine.dmi' path = /mob/living/carbon/human/machine + default_language = "Galactic Common" language = "Trinary" unarmed_type = /datum/unarmed_attack/punch diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index bad5c10d9e0..51988feaf22 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -1,25 +1,22 @@ var/list/department_radio_keys = list( - ":r" = "right ear", "#r" = "right ear", ".r" = "right ear", "!r" = "fake right ear", - ":l" = "left ear", "#l" = "left ear", ".l" = "left ear", "!l" = "fake left ear", + ":r" = "right ear", "#r" = "right ear", ".r" = "right ear", + ":l" = "left ear", "#l" = "left ear", ".l" = "left ear", ":i" = "intercom", "#i" = "intercom", ".i" = "intercom", ":h" = "department", "#h" = "department", ".h" = "department", + ":+" = "special", "#+" = "special", ".+" = "special", //activate radio-specific special functions ":c" = "Command", "#c" = "Command", ".c" = "Command", ":n" = "Science", "#n" = "Science", ".n" = "Science", ":m" = "Medical", "#m" = "Medical", ".m" = "Medical", ":e" = "Engineering", "#e" = "Engineering", ".e" = "Engineering", ":s" = "Security", "#s" = "Security", ".s" = "Security", ":w" = "whisper", "#w" = "whisper", ".w" = "whisper", - ":b" = "binary", "#b" = "binary", ".b" = "binary", - ":d" = "drone", "#d" = "drone", ".d" = "drone", - ":a" = "alientalk", "#a" = "alientalk", ".a" = "alientalk", ":t" = "Syndicate", "#t" = "Syndicate", ".t" = "Syndicate", ":u" = "Supply", "#u" = "Supply", ".u" = "Supply", ":z" = "Service", "#z" = "Service", ".z" = "Service", - ":g" = "changeling", "#g" = "changeling", ".g" = "changeling", + ":p" = "AI Private", "#p" = "AI Private", ".p" = "AI Private", - - ":R" = "right ear", "#R" = "right ear", ".R" = "right ear", "!R" = "fake right ear", - ":L" = "left ear", "#L" = "left ear", ".L" = "left ear", "!L" = "fake left ear", + ":R" = "right ear", "#R" = "right ear", ".R" = "right ear", + ":L" = "left ear", "#L" = "left ear", ".L" = "left ear", ":I" = "intercom", "#I" = "intercom", ".I" = "intercom", ":H" = "department", "#H" = "department", ".H" = "department", ":C" = "Command", "#C" = "Command", ".C" = "Command", @@ -28,40 +25,35 @@ var/list/department_radio_keys = list( ":E" = "Engineering", "#E" = "Engineering", ".E" = "Engineering", ":S" = "Security", "#S" = "Security", ".S" = "Security", ":W" = "whisper", "#W" = "whisper", ".W" = "whisper", - ":D" = "drone", "#D" = "drone", ".D" = "drone", - ":B" = "binary", "#B" = "binary", ".B" = "binary", - ":A" = "alientalk", "#A" = "alientalk", ".A" = "alientalk", ":T" = "Syndicate", "#T" = "Syndicate", ".T" = "Syndicate", ":U" = "Supply", "#U" = "Supply", ".U" = "Supply", ":Z" = "Service", "#Z" = "Service", ".Z" = "Service", - ":G" = "changeling", "#G" = "changeling", ".G" = "changeling" - - /* //kinda localization -- rastaf0 - //same keys as above, but on russian keyboard layout. This file uses cp1251 as encoding. - ":ê" = "right hand", "#ê" = "right hand", ".ê" = "right hand", - ":ä" = "left hand", "#ä" = "left hand", ".ä" = "left hand", - ":ø" = "intercom", "#ø" = "intercom", ".ø" = "intercom", - ":ð" = "department", "#ð" = "department", ".ð" = "department", - ":ñ" = "Command", "#ñ" = "Command", ".ñ" = "Command", - ":ò" = "Science", "#ò" = "Science", ".ò" = "Science", - ":ü" = "Medical", "#ü" = "Medical", ".ü" = "Medical", - ":ó" = "Engineering", "#ó" = "Engineering", ".ó" = "Engineering", - ":û" = "Security", "#û" = "Security", ".û" = "Security", - ":ö" = "whisper", "#ö" = "whisper", ".ö" = "whisper", - ":è" = "binary", "#è" = "binary", ".è" = "binary", - ":ô" = "alientalk", "#ô" = "alientalk", ".ô" = "alientalk", - ":å" = "Syndicate", "#å" = "Syndicate", ".å" = "Syndicate", - ":é" = "Supply", "#é" = "Supply", ".é" = "Supply", - ":ï" = "changeling", "#ï" = "changeling", ".ï" = "changeling" */ + ":P" = "AI Private", "#P" = "AI Private", ".P" = "AI Private" ) + +var/list/channel_to_radio_key = new +proc/get_radio_key_from_channel(var/channel) + var/key = channel_to_radio_key[channel] + if(!key) + for(var/radio_key in department_radio_keys) + if(department_radio_keys[radio_key] == channel) + key = radio_key + break + if(!key) + key = "" + channel_to_radio_key[channel] = key + + return key + /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.l_ear || H.r_ear) var/obj/item/device/radio/headset/dongle @@ -72,446 +64,78 @@ var/list/department_radio_keys = list( if(!istype(dongle)) return if(dongle.translate_binary) return 1 -/mob/living/proc/hivecheck() - if (isalien(src)) return 1 - if (!ishuman(src)) return - var/mob/living/carbon/human/H = src - if (H.l_ear || H.r_ear) - var/obj/item/device/radio/headset/dongle - if(istype(H.l_ear,/obj/item/device/radio/headset)) - dongle = H.l_ear - else - dongle = H.r_ear - if(!istype(dongle)) return - if(dongle.translate_hive) return 1 - -/mob/living/say(var/message) - - /* - Formatting and sanitizing. - */ - - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - - - /* - Sanity checking and speech failure. - */ - - if (!message) - return - - if(silent) - return - - if (stat == 2) // Dead. - return say_dead(message) - else if (stat) // Unconcious. - return - - if (src.client) - if(client.prefs.muted & MUTE_IC) - src << "\red You cannot speak in IC (muted)." - return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return - // undo last word status. - - if(ishuman(src)) - var/mob/living/carbon/human/H=src - if(H.said_last_words) - H.said_last_words=0 - - // Mute disability - if (sdisabilities & MUTE) - return - - // Muzzled. - if (istype(wear_mask, /obj/item/clothing/mask/muzzle)) - return - - // Emotes. - if (copytext(message, 1, 2) == "*" && !stat) - return emote(copytext(message, 2)) - - /* - Identity hiding. - */ - 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")])" - - /* - Now we get into the real meat of the say processing. Determining the message mode. - */ - - var/italics = 0 - var/message_range = null - var/message_mode = null - - var/datum/language/speaking = null //For use if a specific language is being spoken. - - var/braindam = getBrainLoss() - if (braindam >= 60) - if(prob(braindam/4)) - message = stutter(message) - if(prob(braindam)) - message = uppertext(message) - - // General public key. Special message handling - var/mmode - var/cprefix = "" - if(length(message) >= 2) - cprefix = copytext(message, 1, 3) - if(cprefix in department_radio_keys) - mmode = department_radio_keys[cprefix] - if (copytext(message, 1, 2) == ";" || (prob(braindam/2) && !mmode)) - message_mode = "headset" - message = copytext(message, 2) - // Begin checking for either a message mode or a language to speak. - else if (length(message) >= 2) - var/channel_prefix = copytext(message, 1, 3) - - //Check if the person is speaking a language that they know. - if(languages.len) - for(var/datum/language/L in languages) - if(lowertext(channel_prefix) == ":[L.key]") - speaking = L - break - message_mode = department_radio_keys[channel_prefix] - if (message_mode || speaking || copytext(message,1,2) == ":") - message = trim(copytext(message, 3)) - if (!(istype(src,/mob/living/carbon/human) && !isAI(src) && !isrobot(src) || istype(src,/mob/living/carbon/monkey) || istype(src, /mob/living/simple_animal/parrot) || isrobot(src) && (message_mode=="department") || isAI(src) && (message_mode=="department") || (message_mode in radiochannels))) - message_mode = null //only humans can use headsets - - if(traumatic_shock > 61 && prob(50)) - if(message_mode) - src << "You try to use your radio, but you are in too much pain." - message_mode = null //people in shock will have a high chance of not being able to speak on radio; needed since people don't instantly pass out at 100 damage. - - message = capitalize(message) - - if (!message) - return - - if (stuttering) - message = stutter(message) - - /////////////////////////////////////////////////////////// - // VIDEO KILLED THE RADIO STAR V2.0 - // - // EXPERIMENTAL CODE BY YOUR PALS AT /vg/ - /////////////////////////////////////////////////////////// - - var/list/obj/item/used_radios = new - - // Actually speaking on the radio? - var/is_speaking_radio = 0 - - // Devices selected - var/list/devices=list() - - // Select all always_talk devices - // Carbon lifeforms - //if(istype(src, /mob/living/carbon)) - for(var/obj/item/device/radio/R in contents) - if(R.always_talk) - devices += R - - //src << "Speaking on [message_mode]: [message]" - if(message_mode) - switch (message_mode) - if ("right ear") - if(iscarbon(src)) - var/mob/living/carbon/C=src - if(C:r_ear) devices += C:r_ear - message_mode="headset" - message_range = 1 - italics = 1 - - if ("left ear") - if(iscarbon(src)) - var/mob/living/carbon/C=src - if(C:l_ear) devices += C:l_ear - message_mode="headset" - message_range = 1 - italics = 1 - - // Select a headset and speak into it without actually sending a message - if ("fake") - if(iscarbon(src)) - var/mob/living/carbon/C=src - if(C:l_ear) used_radios += C:l_ear - if(C:r_ear) used_radios += C:r_ear - if(issilicon(src)) - var/mob/living/silicon/Ro=src - if(Ro:radio) devices += Ro:radio - message_range = 1 - italics = 1 - if ("fake left ear") - if(iscarbon(src)) - var/mob/living/carbon/C=src - if(C:l_ear) used_radios += C:l_ear - message_range = 1 - italics = 1 - if ("fake right ear") - if(iscarbon(src)) - var/mob/living/carbon/C=src - if(C:r_ear) used_radios += C:r_ear - message_range = 1 - italics = 1 - - if ("intercom") - for (var/obj/item/device/radio/intercom/I in view(1, null)) - devices += I - message_mode=null - 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 ("alientalk") - if(alien_talk_understand || hivecheck()) - //message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) //seems redundant - alien_talk(message) - return - - if ("pAI") - message_range = 1 - italics = 1 - - if("changeling") - if(mind && mind.changeling) - log_say("[key_name(src)] ([mind.changeling.changelingID]): [message]") - for(var/mob/Changeling in mob_list) - if(istype(Changeling, /mob/living/silicon)) continue //WHY IS THIS NEEDED? - if((Changeling.mind && Changeling.mind.changeling) || istype(Changeling, /mob/dead/observer)) - Changeling << "[mind.changeling.changelingID]: [message]" - else if(istype(Changeling,/mob/dead/observer) && (Changeling.client && Changeling.client.prefs.toggles & CHAT_GHOSTEARS)) - Changeling << "[mind.changeling.changelingID] (: (Follow) [message]" - return - else // headset, department channels. - if(iscarbon(src)) - var/mob/living/carbon/C=src - if(C:l_ear) devices += C:l_ear - if(C:r_ear) devices += C:r_ear - if(issilicon(src)) - if(isAI(src))//for the AI's radio. This can't be with the borg thing above due to typecasting. - var/mob/living/silicon/ai/A = src - if(A.aiRadio) - A.aiRadio.talk_into(src, message, message_mode) - used_radios += A.aiRadio - else - var/mob/living/silicon/Ro=src - if(!isAI(Ro)) - if(Ro:radio) - devices += Ro:radio - else - warning("[src] has no radio!") - message_range = 1 - italics = 1 - if(devices.len>0) - for(var/obj/item/device/radio/R in devices) - if(istype(R)) - R.talk_into(src, message, message_mode) - used_radios += R - is_speaking_radio = 1 - - ///////////////////////////////////////////////////////////////// - // - ///////////////////////////////////////////////////////////////// - - var/datum/gas_mixture/environment = loc.return_air() - if(environment) - var/pressure = environment.return_pressure() - if(pressure < SOUND_MINIMUM_PRESSURE) - italics = 1 - message_range = 1 - - var/list/listening - - listening = get_mobs_in_view(message_range, src) - var/list/onscreen = viewers() - 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 +/mob/living/say(var/message, var/datum/language/speaking = null, var/verb="says", var/alt_name="", var/italics=0, var/message_range = world.view, var/sound/speech_sound, var/sound_vol) var/turf/T = get_turf(src) - var/list/W = hear(message_range, T) - for (var/obj/O in ((W | contents)-used_radios)) - W |= O + //handle nonverbal and sign languages here + if (speaking) + if (speaking.flags & NONVERBAL) + if (prob(30)) + src.custom_emote(1, "[pick(speaking.signlang_verb)].") - for (var/mob/M in W) - W |= M.contents + if (speaking.flags & SIGNLANG) + return say_signlang(message, pick(speaking.signlang_verb), speaking) - 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/list/listening = list() + var/list/listening_obj = list() - 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(message) + if(T) + //make sure the air can transmit speech - speaker's side + var/datum/gas_mixture/environment = T.return_air() + var/pressure = (environment)? environment.return_pressure() : 0 + if(pressure < SOUND_MINIMUM_PRESSURE) + message_range = 1 - if (isslime(A)) - 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 (pressure < ONE_ATMOSPHERE*0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet + italics = 1 + sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact - 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) + var/list/hear = hear(message_range, T) + var/list/hearturfs = list() + for(var/I in hear) + if(istype(I, /mob/)) + var/mob/M = I + listening += M + hearturfs += M.locs[1] + for(var/obj/O in M.contents) + listening_obj |= O + else if(istype(I, /obj/)) + var/obj/O = I + hearturfs += O.locs[1] + listening_obj |= O - 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,speaking)) - heard_a += M - else - heard_b += M - else - heard_a += M + for(var/mob/M in player_list) + if(M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS)) + listening |= M + continue + if(M.loc && M.locs[1] in hearturfs) + listening |= M var/speech_bubble_test = say_test(message) var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]") spawn(30) del(speech_bubble) - for(var/mob/M in hearers(5, src)) - if(M != src && is_speaking_radio) - M:show_message("[src] talks into [used_radios.len ? used_radios[1] : "radio"]") + for(var/mob/M in listening) + M << speech_bubble + M.hear_say(message, verb, speaking, alt_name, italics, src, speech_sound, sound_vol) -/* if(message_mode == null) - var/accent = "en-us" - var/voice = "m7" - var/speed = 175 - var/pitch = 0 - var/echo = 10 - if(istype(src, /mob/living/silicon/ai)) - echo = 90 - if(istype(src, /mob/living/silicon/robot)) - echo = 60 - if(src.client && src.client.prefs) - accent = src.client.prefs.accent - voice = src.client.prefs.voice - speed = src.client.prefs.talkspeed - pitch = src.client.prefs.pitch - src:texttospeech(message, speed, pitch, accent, "+[voice]", echo)*/ - - var/rendered = null - - if (length(heard_a)) - var/message_a = say_quote(message,speaking) - - if (italics) - message_a = "[message_a]" - - var/message_ghost = "[message_a]" // bold so ghosts know the person is in view. - rendered = "[GetVoice()][alt_name] [message_a]" - var/rendered2 = null - - for (var/mob/M in heard_a) - //BEGIN TELEPORT CHANGES -/* if(message_mode == null && fexists("sound/playervoices/[src.ckey].ogg")) - if(M.client) - if(M.client.prefs) - if(M.client.prefs.sound & SOUND_VOICES) - M.playsound_local(get_turf(src), "sound/playervoices/[src.ckey].ogg", 70, 0, 5, 1)*/ - if(!istype(M, /mob/new_player)) - if(M && M.stat == DEAD) - if (M.client && M.client.prefs && (M.client.prefs.toggles & CHAT_GHOSTEARS) && M in onscreen) - rendered2 = "[GetVoice()] [alt_name] (Follow) [message_ghost]" - else - rendered2 = "[GetVoice()] [alt_name] (Follow) [message_a]" - M:show_message(rendered2, 2) - continue - //END CHANGES - - 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) - M << speech_bubble - - if (length(heard_b)) - - var/message_b - message_b = stars(message) - message_b = say_quote(message_b,speaking) - - if (italics) - message_b = "[message_b]" - - rendered = "[name][alt_name] [message_b]" //Voice_name isn't too useful. You'd be able to tell who was talking presumably. - var/rendered2 = null - - for (var/M in heard_b) - var/mob/MM - if(istype(M, /mob)) - MM = M - if(!istype(MM, /mob/new_player) && MM) - if(MM && MM.stat == DEAD) - rendered2 = "[voice_name] (Follow) [message_b]" - MM:show_message(rendered2, 2) - continue - if(hascall(M,"show_message")) - M:show_message(rendered, 2) - M << speech_bubble - - /* - if(M.client) - - if(!M.client.bubbles || M == src) - var/image/I = image('icons/effects/speechbubble.dmi', B, "override") - I.override = 1 - M << I - */ /* - - flick("[presay]say", B) - - if(istype(loc, /turf)) - B.loc = loc - else - B.loc = loc.loc - - spawn() - sleep(11) - del(B) - */ + for(var/obj/O in listening_obj) + spawn(0) + if(O) //It's possible that it could be deleted in the meantime. + O.hear_talk(src, message, verb, speaking) log_say("[name]/[key] : [message]") + return 1 + +/mob/living/proc/say_signlang(var/message, var/verb="gestures", var/datum/language/language) + for (var/mob/O in viewers(src, null)) + O.hear_signlang(message, verb, language, src) + return 1 /obj/effect/speech_bubble var/mob/parent /mob/living/proc/GetVoice() return name - - diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 117a2d127d5..a7fb2ada618 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -112,6 +112,20 @@ var/list/ai_list = list() /mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \ /mob/living/silicon/ai/proc/toggle_camera_light, /mob/living/silicon/ai/proc/botcall, /mob/living/silicon/ai/proc/control_integrated_radio, /mob/living/silicon/ai/proc/control_hud, /mob/living/silicon/ai/proc/change_arrival_message, /mob/living/silicon/ai/proc/ai_store_location, /mob/living/silicon/ai/proc/ai_goto_location, /mob/living/silicon/ai/proc/ai_remove_location, /mob/living/silicon/ai/proc/nano_crew_monitor, /mob/living/silicon/ai/proc/ai_cancel_call) + //Languages + add_language("Robot Talk", 1) + add_language("Sol Common", 1) + add_language("Tradeband", 1) + add_language("Sinta'unathi", 0) + add_language("Siik'tajr", 0) + add_language("Skrellian", 0) + add_language("Vox-pidgin", 0) + add_language("Rootspeak", 0) + add_language("Trinary", 1) + add_language("Chittin", 0) + add_language("Bubblish", 0) + add_language("Gutter", 0) + if(!safety)//Only used by AIize() to successfully spawn an AI. if (!B)//If there is no player/brain inside. empty_playable_ai_cores += new/obj/structure/AIcore/deactivated(loc)//New empty terminal. @@ -127,6 +141,7 @@ var/list/ai_list = list() /mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \ /mob/living/silicon/ai/proc/toggle_camera_light,/mob/living/silicon/ai/verb/pick_icon,/mob/living/silicon/ai/proc/control_hud, /mob/living/silicon/ai/proc/change_arrival_message, /mob/living/silicon/ai/proc/ai_cancel_call) laws = new /datum/ai_laws/alienmov + add_language("xenocommon", 1) else B.brainmob.mind.transfer_to(src) diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index ec02433c46a..833d08e7cb4 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -5,7 +5,9 @@ /mob/aiEye name = "Inactive AI Eye" - icon = 'icons/obj/status_display.dmi' // For AI friend secret shh :o + icon = 'icons/mob/AI.dmi' + icon_state = "eye" + alpha = 127 var/list/visibleCameraChunks = list() var/mob/living/silicon/ai/ai = null density = 0 diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index b596fd53541..5a4398485ed 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -1,32 +1,8 @@ /mob/living/silicon/ai/say(var/message) if(parent && istype(parent) && parent.stat != 2) - parent.say(message) //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. - return - - ..(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/say_quote(var/text) - var/ending = copytext(text, length(text)) - - if (ending == "?") - return "queries, \"[text]\""; - else if (ending == "!") - return "declares, \"[text]\""; - - return "states, \"[text]\""; + return parent.say(message) + //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. + return ..(message) /mob/living/silicon/ai/proc/IsVocal() diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 48a34265d43..d62c5f544d4 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -71,6 +71,7 @@ var/obj/item/radio/integrated/signal/sradio // AI's signaller + var/translator_on = 0 // keeps track of the translator module /mob/living/silicon/pai/New(var/obj/item/device/paicard) canmove = 0 @@ -81,6 +82,13 @@ if(!card.radio) card.radio = new /obj/item/device/radio(src.card) radio = card.radio + + //Default languages without universal translator software + add_language("Sol Common", 1) + add_language("Tradeband", 1) + add_language("Gutter", 1) + add_language("Trinary", 1) + //Verbs for pAI mobile form, chassis and Say flavor text verbs += /mob/living/silicon/pai/proc/choose_chassis verbs += /mob/living/silicon/pai/proc/choose_verbs @@ -334,7 +342,7 @@ //I'm not sure how much of this is necessary, but I would rather avoid issues. if(istype(card.loc,/mob)) var/mob/holder = card.loc - holder.unEquip(card) + holder.drop_from_inventory(card) else if(istype(card.loc,/obj/item/clothing/suit/space/space_ninja)) var/obj/item/clothing/suit/space/space_ninja/holder = card.loc holder.pai = null @@ -503,3 +511,7 @@ spawn(1) close_up() return 2 + +// No binary for pAIs. +/mob/living/silicon/pai/binarycheck() + return 0 \ No newline at end of file diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 4f7bd1a1c61..ff9b343a7b1 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -268,7 +268,7 @@ src.medHUD = !src.medHUD if("translator") if(href_list["toggle"]) - src.universal_speak = !src.universal_speak + src.translator_toggle() if("doorjack") if(href_list["jack"]) if(src.cable && src.cable.machine) @@ -687,4 +687,36 @@ else dat += addtext("
") dat += "
From", index["owner"],": ", index["message"], "
" - return dat \ No newline at end of file + return dat + +/mob/living/silicon/pai/proc/translator_toggle() + + // Sol Common, Tradeband, Gutter and Trinary are added with New() and are therefore the current default, always active languages + + if(translator_on) + translator_on = 0 + + remove_language("Sinta'unathi") + remove_language("Siik'tajr") + remove_language("Skrellian") + remove_language("Vox-pidgin") + remove_language("Rootspeak") + remove_language("Trinary") + remove_language("Chittin") + remove_language("Bubblish") + + src << "\blue Translator Module toggled OFF." + + else + translator_on = 1 + + add_language("Sinta'unathi") + add_language("Siik'tajr") + add_language("Skrellian") + add_language("Vox-pidgin") + add_language("Rootspeak") + add_language("Trinary") + add_language("Chittin") + add_language("Bubblish") + + src << "\blue Translator Module toggled ON." \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index 5817715ff99..e2d6c31e8f6 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -121,6 +121,11 @@ /mob/living/silicon/robot/proc/is_component_functioning(module_name) var/datum/robot_component/C = components[module_name] return C && C.installed == 1 && C.toggled && C.is_powered() + +// Returns component by it's string name +/mob/living/silicon/robot/proc/get_component(var/component_name) + var/datum/robot_component/C = components[component_name] + return C /obj/item/broken_device name = "broken component" diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index e3a04f2cd0f..e83298a68eb 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -13,7 +13,8 @@ lawupdate = 0 density = 1 req_access = list(access_engine, access_robotics) - + local_transmit = 1 + // We need to keep track of a few module items so we don't need to do list operations // every time we need them. These get set in New() after the module is chosen. var/obj/item/stack/sheet/metal/cyborg/stack_metal = null @@ -25,11 +26,13 @@ //Used for self-mailing. var/mail_destination = 0 - //Used for pulling. /mob/living/silicon/robot/drone/New() ..() + + remove_language("Robot Talk") + add_language("Drone Talk", 1) if(camera && "Robots" in camera.network) camera.network.Add("Engineering") @@ -88,79 +91,6 @@ /mob/living/silicon/robot/drone/pick_module() return -//Drones can only use binary and say emotes. NOTHING else. -//TBD, fix up boilerplate. ~ Z -/mob/living/silicon/robot/drone/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(copytext(message,1,2) == "*") - return emote(copytext(message,2)) - - if (length(message) >= 2) - var/prefix = copytext(message, 1, 3) - if (department_radio_keys[prefix] == "drone") - message = copytext(message, 3) - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - - if(istype(src, /mob/living/silicon/robot/drone)) - var/mob/living/silicon/robot/drone/R = src - if(!R.is_component_functioning("comms")) - src << "\red Your drone communications component isn't functional." - return - drone_talk(message) - else - var/list/listeners = hearers(5,src) - listeners |= src - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - for(var/mob/living/silicon/D in listeners) - if(D.client) - D << "[src] transmits, \"[message]\"" - - for (var/mob/M in player_list) - if (!M.client) - continue - if (istype(M, /mob/new_player)) - continue - else if(M.stat == 2 && (M.client.prefs.toggles & CHAT_GHOSTEARS) && src.client) - M << "[src] (Follow) transmits, \"[message]\"" - else if(M.stat == 2 && src.client && M in listeners) - M << "[src] (Follow) transmits, \"[message]\"" - -/mob/living/proc/drone_talk(var/message) - log_say("[key_name(src)] : [message]") - message = trim(message) - - if (!message) - return - - var/message_a = say_quote(message) - var/rendered = "Drone Talk, [name] [message_a]" - - for (var/mob/living/S in living_mob_list) - if(istype(S, /mob/living/silicon/robot/drone)) - S.show_message(rendered, 2) - - for (var/mob/S in dead_mob_list) - if(!istype(S,/mob/new_player) && !istype(S,/mob/living/carbon/brain)) - var/rendered2 = "Drone Talk, [name] (Follow) [message_a]" - S.show_message(rendered2, 2) - //Drones cannot be upgraded with borg modules so we need to catch some items before they get used in ..(). /mob/living/silicon/robot/drone/attackby(obj/item/weapon/W as obj, mob/user as mob) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index e28f9ac8b7d..ae42fbc4697 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -80,6 +80,8 @@ spark_system = new /datum/effect/effect/system/spark_spread() spark_system.set_up(5, 0, src) spark_system.attach(src) + + add_language("Robot Talk", 1) robot_modules_background = new() robot_modules_background.icon_state = "block" @@ -174,6 +176,13 @@ rbPDA.set_name_and_job(custom_name,braintype) if(hiddenborg) rbPDA.hidden = 1 + +/mob/living/silicon/robot/binarycheck() + if(is_component_functioning("comms")) + var/datum/robot_component/RC = get_component("comms") + use_power(RC.energy_consumption) + return 1 + return 0 //If there's an MMI in the robot, have it ejected when the mob goes away. --NEO //Improved /N @@ -197,7 +206,6 @@ modtype = input("Please, select a module!", "Robot", null, null) in modules designation = modtype var/module_sprites[0] //Used to store the associations between sprite names and sprite index. - var/channels = list() if(module) return @@ -205,14 +213,14 @@ switch(modtype) if("Standard") module = new /obj/item/weapon/robot_module/standard(src) - channels = list("Service" = 1) + module.channels = list("Service" = 1) module_sprites["Basic"] = "robot_old" module_sprites["Android"] = "droid" module_sprites["Default"] = "robot" if("Service") module = new /obj/item/weapon/robot_module/butler(src) - channels = list("Service" = 1) + module.channels = list("Service" = 1) module_sprites["Waitress"] = "Service" module_sprites["Kent"] = "toiletbot" module_sprites["Bro"] = "Brobot" @@ -221,7 +229,7 @@ /* if("Clerical") module = new /obj/item/weapon/robot_module/clerical(src) - channels = list("Service" = 1) + module.channels = list("Service" = 1) module_sprites["Waitress"] = "Service" module_sprites["Kent"] = "toiletbot" module_sprites["Bro"] = "Brobot" @@ -230,7 +238,7 @@ */ if("Miner") module = new /obj/item/weapon/robot_module/miner(src) - channels = list("Supply" = 1) + module.channels = list("Supply" = 1) if(camera && "Robots" in camera.network) camera.network.Add("MINE") module_sprites["Basic"] = "Miner_old" @@ -239,7 +247,7 @@ if("Medical") module = new /obj/item/weapon/robot_module/medical(src) - channels = list("Medical" = 1) + module.channels = list("Medical" = 1) if(camera && "Robots" in camera.network) camera.network.Add("Medical") module_sprites["Basic"] = "Medbot" @@ -249,7 +257,7 @@ if("Security") module = new /obj/item/weapon/robot_module/security(src) - channels = list("Security" = 1) + module.channels = list("Security" = 1) module_sprites["Basic"] = "secborg" module_sprites["Red Knight"] = "Security" module_sprites["Black Knight"] = "securityrobot" @@ -257,7 +265,7 @@ if("Engineering") module = new /obj/item/weapon/robot_module/engineering(src) - channels = list("Engineering" = 1) + module.channels = list("Engineering" = 1) if(camera && "Robots" in camera.network) camera.network.Add("Engineering") module_sprites["Basic"] = "Engineering" @@ -266,14 +274,15 @@ if("Janitor") module = new /obj/item/weapon/robot_module/janitor(src) + module.channels = list("Service" = 1) module_sprites["Basic"] = "JanBot2" module_sprites["Mopbot"] = "janitorrobot" module_sprites["Mop Gear Rex"] = "mopgearrex" if("Combat") module = new /obj/item/weapon/robot_module/combat(src) + module.channels = list("Security" = 1) module_sprites["Combat Android"] = "droidcombat" - channels = list("Security" = 1) if("Hunter") updatename(module) @@ -283,7 +292,9 @@ icon_state = "xenoborg-state-a" modtype = "Xeno-Hu" feedback_inc("xeborg_hunter",1) - + + //languages + module.add_languages(src) //Custom_sprite check and entry if (custom_sprite == 1) @@ -297,7 +308,7 @@ status_flags &= ~CANPUSH choose_icon(6,module_sprites) - radio.config(channels) + radio.config(module.channels) notify_ai(2) /mob/living/silicon/robot/proc/updatename(var/prefix as text) @@ -1467,6 +1478,16 @@ laws = new /datum/ai_laws/syndicate_override() Namepick() + +/mob/living/silicon/robot/syndicate/canUseTopic(atom/movable/M) + if(stat || lockcharge || stunned || weakened) + return + if(z in config.admin_levels) + return 1 + /*if(istype(M, /obj/machinery)) + var/obj/machinery/Machine = M + return Machine.emagged*/ + return 1 /mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/oldname, var/newname) if(!connected_ai) diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index a2c1fcb7347..a6b9d46f19a 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -10,6 +10,7 @@ var/obj/item/emag = null var/obj/item/borg/upgrade/jetpack = null var/list/stacktypes + var/channels = list() emp_act(severity) @@ -55,6 +56,20 @@ for(var/obj/O in temp_list) if(O) modules += O + +/obj/item/weapon/robot_module/proc/add_languages(var/mob/living/silicon/robot/R) + //full set of languages + R.add_language("Sol Common", 1) + R.add_language("Tradeband", 1) + R.add_language("Sinta'unathi", 0) + R.add_language("Siik'tajr", 0) + R.add_language("Skrellian", 0) + R.add_language("Vox-pidgin", 0) + R.add_language("Rootspeak", 0) + R.add_language("Trinary", 1) + R.add_language("Chittin", 0) + R.add_language("Bubblish", 0) + R.add_language("Gutter", 0) /obj/item/weapon/robot_module/standard name = "standard robot module" @@ -248,6 +263,20 @@ if(src.emag) var/obj/item/weapon/reagent_containers/food/drinks/cans/beer/B = src.emag B.reagents.add_reagent("beer2", 2) + +/obj/item/weapon/robot_module/butler/add_languages(var/mob/living/silicon/robot/R) + //full set of languages + R.add_language("Sol Common", 1) + R.add_language("Tradeband", 1) + R.add_language("Sinta'unathi", 1) + R.add_language("Siik'tajr", 1) + R.add_language("Skrellian", 1) + R.add_language("Vox-pidgin", 1) + R.add_language("Rootspeak", 1) + R.add_language("Trinary", 1) + R.add_language("Chittin", 1) + R.add_language("Bubblish", 1) + R.add_language("Gutter", 1) /* /obj/item/weapon/robot_module/clerical //Whyyyyy? @@ -341,6 +370,10 @@ src.emag = new /obj/item/weapon/reagent_containers/spray/alien/acid(src) src.emag.reagents.add_reagent("pacid", 125) src.emag.reagents.add_reagent("sacid", 125) + +/obj/item/weapon/robot_module/butler/add_languages(var/mob/living/silicon/robot/R) + ..() + R.add_language("xenocommon", 1) /obj/item/weapon/robot_module/drone name = "drone module" diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 35a31a8dbe0..ba5b0ad4f82 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -2,79 +2,154 @@ var/ending = copytext(text, length(text)) if (ending == "?") - return "[speak_query], \"[text]\""; + return speak_query else if (ending == "!") - return "[speak_exclamation], \"[text]\""; + return speak_exclamation - return "[speak_statement], \"[text]\""; + return speak_statement + +#define IS_AI 1 +#define IS_ROBOT 2 +#define IS_PAI 3 + +/mob/living/silicon/say_understands(var/other,var/datum/language/speaking = null) + //These only pertain to common. Languages are handled by mob/say_understands() + if (!speaking) + if (istype(other, /mob/living/carbon)) + return 1 + if (istype(other, /mob/living/silicon)) + return 1 + if (istype(other, /mob/living/carbon/brain)) + return 1 + return ..() /mob/living/silicon/say(var/message) if (!message) - return + return 0 if (src.client) if(client.prefs.muted & MUTE_IC) src << "You cannot send IC messages (muted)." - return + return 0 if (src.client.handle_spam_prevention(message,MUTE_IC)) - return + return 0 + + message = trim_strip_html_properly(message) if (stat == 2) - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) return say_dead(message) + if(copytext(message,1,2) == "*") + return emote(copytext(message,2)) + + var/bot_type = 0 //Let's not do a fuck ton of type checks, thanks. + if(istype(src, /mob/living/silicon/ai)) + bot_type = IS_AI + else if(istype(src, /mob/living/silicon/robot)) + bot_type = IS_ROBOT + else if(istype(src, /mob/living/silicon/pai)) + bot_type = IS_PAI + + var/mob/living/silicon/ai/AI = src //and let's not declare vars over and over and over for these guys. + var/mob/living/silicon/robot/R = src + var/mob/living/silicon/pai/P = src + //Must be concious to speak if (stat) - return + return 0 - if (length(message) >= 2) - var/prefix = copytext(message, 1, 3) - if (department_radio_keys[prefix] == "binary") - if(istype(src, /mob/living/silicon/pai)) - return ..(message) - message = copytext(message, 3) - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) + var/verb = say_quote(message) - // TODO: move the component system up to silicon so we don't have to use this ugly hack.. - if(istype(src, /mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = src - if(!R.is_component_functioning("comms")) - src << "\red Your binary communications component isn't functional." - return - robot_talk(message) - - else if (department_radio_keys[prefix] == "alientalk") - if(!alien_talk_understand) return - message = copytext(message, 3) - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - - alien_talk(message) - else if (department_radio_keys[prefix] == "department") - 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 + //parse radio key and consume it + var/message_mode = parse_message_mode(message, "general") + if (message_mode) + if (message_mode == "general") + message = trim(copytext(message,2)) else - var/mob/living/silicon/ai/AI = src - if (isAI(src) && AI.aiRadio.disabledAi) - src << "\red System Error - Transceiver Disabled" - return - else - return ..(message) - else - var/mob/living/silicon/ai/AI = src - if (isAI(src) && AI.aiRadio.disabledAi) - src << "\red System Error - Transceiver Disabled" - return + message = trim(copytext(message,3)) + + //parse language key and consume it + var/datum/language/speaking = parse_language(message) + if (speaking) + verb = speaking.speech_verb + message = trim(copytext(message,2+length(speaking.key))) + + if(speaking.flags & HIVEMIND) + speaking.broadcast(src,trim(message)) + return 1 + + // Currently used by drones. + if(local_transmit) + var/list/listeners = hearers(5,src) + listeners |= src + + for(var/mob/living/silicon/D in listeners) + if(D.client && istype(D,src.type)) + D << "[src] transmits, \"[message]\"" + + for (var/mob/M in player_list) + if (istype(M, /mob/new_player)) + continue + else if(M.stat == 2 && M.client.prefs.toggles & CHAT_GHOSTEARS) + if(M.client) M << "[src] transmits, \"[message]\"" + return 1 + + if(message_mode && bot_type == IS_ROBOT && !R.is_component_functioning("radio")) + src << "\red Your radio isn't functional at this time." + return 0 + + switch(message_mode) + if("department") + switch(bot_type) + if(IS_AI) + return AI.holopad_talk(message, verb, speaking) + if(IS_ROBOT) + log_say("[key_name(src)] : [message]") + return R.radio.talk_into(src,message,message_mode,verb,speaking) + if(IS_PAI) + log_say("[key_name(src)] : [message]") + return P.radio.talk_into(src,message,message_mode,verb,speaking) + return 0 + + if("general") + switch(bot_type) + if(IS_AI) + if (AI.aiRadio.disabledAi || AI.aiRestorePowerRoutine || AI.stat) + src << "\red System Error - Transceiver Disabled" + return 0 + else + log_say("[key_name(src)] : [message]") + return AI.aiRadio.talk_into(src,message,null,verb,speaking) + if(IS_ROBOT) + log_say("[key_name(src)] : [message]") + return R.radio.talk_into(src,message,null,verb,speaking) + if(IS_PAI) + log_say("[key_name(src)] : [message]") + return P.radio.talk_into(src,message,null,verb,speaking) + return 0 + else - return ..(message) + if(message_mode) + switch(bot_type) + if(IS_AI) + if (AI.aiRadio.disabledAi || AI.aiRestorePowerRoutine || AI.stat) + src << "\red System Error - Transceiver Disabled" + return 0 + else + log_say("[key_name(src)] : [message]") + return AI.aiRadio.talk_into(src,message,message_mode,verb,speaking) + if(IS_ROBOT) + log_say("[key_name(src)] : [message]") + return R.radio.talk_into(src,message,message_mode,verb,speaking) + if(IS_PAI) + log_say("[key_name(src)] : [message]") + return P.radio.talk_into(src,message,message_mode,verb,speaking) + return 0 + + return ..(message,speaking,verb) //For holopads only. Usable by AI. -/mob/living/silicon/ai/proc/holopad_talk(var/message) +/mob/living/silicon/ai/proc/holopad_talk(var/message, verb, datum/language/speaking) log_say("[key_name(src)] : [message]") @@ -84,18 +159,23 @@ return var/obj/machinery/hologram/holopad/T = src.holo - 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) + if(T && T.hologram && T.master == src)//If there is a hologram and its master is the user. //Human-like, sorta, heard by those who understand humans. - var/rendered_a = "[name] [message_a]" - + var/rendered_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]" + var/message_stars = stars(message) + var/rendered_b + + if(speaking) + rendered_a = "[name] [speaking.format_message(message, verb)]" + rendered_b = "[voice_name] [speaking.format_message(message_stars, verb)]" + src << "Holopad transmitted, [real_name] [speaking.format_message(message, verb)]"//The AI can "hear" its own message. + else + rendered_a = "[name] [verb], \"[message]\"" + rendered_b = "[voice_name] [verb], \"[message_stars]\"" + src << "Holopad transmitted, [real_name] [verb], \"[message]\""//The AI can "hear" its own message. - src << "Holopad transmitted, [real_name] [message_a]"//The AI can "hear" its own message. 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) @@ -105,79 +185,37 @@ This is another way of saying that we won't bother dealing with them.*/ else src << "No holopad connected." - return + return + return 1 -/mob/living/proc/robot_talk(var/message) +/mob/living/silicon/ai/proc/holopad_emote(var/message) //This is called when the AI uses the 'me' verb while using a holopad. - log_say("[key_name(src)] : [message]") - - var/desig = "Unknown" //ezmode for taters - if(isrobot(src)) - var/mob/living/silicon/S = src - if(S.designation) - desig = trim_left(S.designation) - else - desig = "Default" - else if(isAI(src)) - desig = "AI" + log_emote("[key_name(src)] : [message]") message = trim(message) if (!message) return - var/message_a = say_quote(message) - var/rendered = "Robotic Talk, [name] ([desig]) [message_a]" + var/obj/machinery/hologram/holopad/T = src.holo + if(T && T.hologram && T.master == src) + var/rendered = "[name] [message]" + src << "Holopad action relayed, [real_name] [message]" - 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(istype(S , /mob/living/silicon/ai)) - var/renderedAI = "Robotic Talk, [name] ([desig]) [message_a]" - S.show_message(renderedAI, 2) - else if(istype(S , /mob/dead/observer) && S.stat == DEAD) - var/rendered2 = "Robotic Talk, [name] ([desig]) (Follow) [message_a]" - S.show_message(rendered2, 2) - else - S.show_message(rendered, 2) - - - else if (S.binarycheck()) - if(istype(S , /mob/living/silicon/ai)) - var/renderedAI = "Robotic Talk, [name] [message_a]" - S.show_message(renderedAI, 2) - else if(istype(S , /mob/dead/observer) && S.stat == DEAD) - var/rendered2 = "Robotic Talk, [name] (Follow) [message_a]" - S.show_message(rendered2, 2) - else - S.show_message(rendered, 2) - - var/list/listening = hearers(1, src) - listening -= src - listening += src - - var/list/heard = list() - for (var/mob/M in listening) - if(!istype(M, /mob/living/silicon) && !M.robot_talk_understand) - heard += M - - if (length(heard)) - var/message_b - - message_b = "beep beep beep" - message_b = say_quote(message_b) - message_b = "[message_b]" - - rendered = "[voice_name] [message_b]" - - for (var/mob/M in heard) + for(var/mob/M in viewers(T.loc)) M.show_message(rendered, 2) + else //This shouldn't occur, but better safe then sorry. + src << "No holopad connected." + return + return 1 - message = say_quote(message) +/mob/living/silicon/ai/emote(var/act, var/type, var/message) + var/obj/machinery/hologram/holopad/T = src.holo + if(T && T.hologram && T.master == src) //Is the AI using a holopad? + src.holopad_emote(message) + else //Emote normally, then. + ..() -// rendered = "Robotic Talk, [name] [message_a]" - rendered = null - - for (var/mob/M in dead_mob_list) - if(!istype(M,/mob/new_player) && !istype(M,/mob/living/carbon/brain)) //No meta-evesdropping - rendered = "Robotic Talk, [name] (Follow) [message_a]" - M.show_message(rendered, 2) \ No newline at end of file +#undef IS_AI +#undef IS_ROBOT +#undef IS_PAI diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 6f88c99c71e..86da14df0e7 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -8,6 +8,7 @@ var/list/alarms_to_clear = list() immune_to_ssd = 1 var/list/hud_list[10] + var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer 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) var/designation = "" @@ -20,6 +21,7 @@ var/sensor_mode = 0 //Determines the current HUD. #define SEC_HUD 1 //Security HUD mode #define MED_HUD 2 //Medical HUD mode + var/local_transmit //If set, can only speak to others of the same type within a short range. /mob/living/silicon/proc/cancelAlarm() return @@ -205,6 +207,37 @@ show_emergency_shuttle_eta() show_system_integrity() show_malf_ai() + +//Silicon mob language procs + +/mob/living/silicon/can_speak(datum/language/speaking) + return universal_speak || (speaking in src.speech_synthesizer_langs) //need speech synthesizer support to vocalize a language + +/mob/living/silicon/add_language(var/language, var/can_speak=1) + if (..(language) && can_speak) + speech_synthesizer_langs.Add(all_languages[language]) + return 1 + +/mob/living/silicon/remove_language(var/rem_language) + ..(rem_language) + + for (var/datum/language/L in speech_synthesizer_langs) + if (L.name == rem_language) + speech_synthesizer_langs -= L + +/mob/living/silicon/check_languages() + set name = "Check Known Languages" + set category = "IC" + set src = usr + + var/dat = "Known Languages

" + + for(var/datum/language/L in languages) + if(!(L.flags & NONGLOBAL)) + dat += "[L.name] (:[L.key])
Speech Synthesizer: [(L in speech_synthesizer_langs)? "YES":"NOT SUPPORTED"]
[L.desc]

" + + src << browse(dat, "window=checklanguage") + return // this function displays the stations manifest in a separate window /mob/living/silicon/proc/show_station_manifest() @@ -257,6 +290,9 @@ set category = "IC" flavor_text = copytext(sanitize(input(usr, "Please enter your new flavour text.", "Flavour text", null) as text), 1) + +/mob/living/silicon/binarycheck() + return 1 /mob/living/silicon/proc/toggle_sensor_mode() var/sensor_type = input("Please select sensor type.", "Sensor Integration", null) in list("Security", "Medical","Disable") diff --git a/code/modules/mob/living/simple_animal/borer.dm b/code/modules/mob/living/simple_animal/borer.dm index 8dae4be093b..50334dbdf27 100644 --- a/code/modules/mob/living/simple_animal/borer.dm +++ b/code/modules/mob/living/simple_animal/borer.dm @@ -100,51 +100,12 @@ /mob/living/simple_animal/borer/New(var/by_gamemode=0) ..() + add_language("Cortical Link") truename = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]" if(!by_gamemode) request_player() - -/mob/living/simple_animal/borer/say(var/message) - - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - message = capitalize(message) - - if(!message) - return - - if (stat == 2) - return say_dead(message) - - if (stat) - return - - if (src.client) - if(client.prefs.muted & MUTE_IC) - src << "\red You cannot speak in IC (muted)." - return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return - - if (copytext(message, 1, 2) == "*") - return emote(copytext(message, 2)) - - if (copytext(message, 1, 2) == ";") //Brain borer hivemind. - return borer_speak(message) - - if(!host) - src << "You have no host to speak to." - return //No host, no audible speech. - - src << "You drop words into [host]'s mind: \"[message]\"" - host << "Your own thoughts speak: \"[message]\"" - - for(var/mob/M in mob_list) - if(M.mind && (istype(M, /mob/dead/observer))) - M << "Thought-speech, [truename] -> [host]: [copytext(message, 2)]" - - /mob/living/simple_animal/borer/Stat() ..() statpanel("Status") diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 48fd6943658..a62f0640a54 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -48,6 +48,7 @@ response_disarm = "gently moves aside the" response_harm = "swats the" stop_automated_movement = 1 + universal_speak = 1 var/parrot_state = PARROT_WANDER //Hunt for a perch when created var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in life() being run every single tick. @@ -61,7 +62,7 @@ var/list/available_channels = list() //Headset for Poly to yell at engineers :) - var/obj/item/device/radio/headset/l_ear = null + var/obj/item/device/radio/headset/ears = null //The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, //mobs it wants to attack or mobs that have attacked it @@ -84,13 +85,13 @@ /mob/living/simple_animal/parrot/New() ..() - if(!l_ear) + if(!ears) var/headset = pick(/obj/item/device/radio/headset/headset_sec, \ /obj/item/device/radio/headset/headset_eng, \ /obj/item/device/radio/headset/headset_med, \ /obj/item/device/radio/headset/headset_sci, \ /obj/item/device/radio/headset/headset_cargo) - l_ear = new headset(src) + ears = new headset(src) parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var @@ -100,7 +101,7 @@ /mob/living/simple_animal/parrot/proc/perch_player) -/mob/living/simple_animal/parrot/Die() +/mob/living/simple_animal/parrot/death() if(held_item) held_item.loc = src.loc held_item = null @@ -119,10 +120,10 @@ if(user.stat) return var/dat = "
Inventory of [name]

" - if(l_ear) - dat += "
Headset: [l_ear] (Remove)" + if(ears) + dat += "
Headset: [ears] (Remove)" else - dat += "
Headset: Nothing" + dat += "
Headset: Nothing" user << browse(dat, text("window=mob[];size=325x500", name)) onclose(user, "mob[real_name]") @@ -134,21 +135,21 @@ if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) return - //Is the usr's mob type able to do this? (lolaliens) - if(ishuman(usr) || ismonkey(usr) || isrobot(usr) || isalienadult(usr)) + //Is the usr's mob type able to do this? + if(ishuman(usr) || ismonkey(usr) || isrobot(usr)) //Removing from inventory if(href_list["remove_inv"]) var/remove_from = href_list["remove_inv"] switch(remove_from) - if("l_ear") - if(l_ear) + if("ears") + if(ears) if(available_channels.len) src.say("[pick(available_channels)] BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") else src.say("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") - l_ear.loc = src.loc - l_ear = null + ears.loc = src.loc + 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)) @@ -163,8 +164,8 @@ usr << "\red You have nothing in your hand to put on its [add_to]." return switch(add_to) - if("l_ear") - if(l_ear) + if("ears") + if(ears) usr << "\red It's already wearing something." return else @@ -180,7 +181,7 @@ usr.drop_item() headset_to_add.loc = src - src.l_ear = headset_to_add + src.ears = headset_to_add usr << "You fit the headset onto [src]." clearlist(available_channels) @@ -214,7 +215,7 @@ /mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/M as mob) ..() if(client) return - if(!stat && M.a_intent == "harm") + if(!stat && M.a_intent == "hurt") icon_state = "parrot_fly" //It is going to be flying regardless of whether it flees or attacks @@ -231,25 +232,6 @@ drop_held_item(0) return -/mob/living/simple_animal/parrot/attack_paw(mob/living/carbon/monkey/M as mob) - attack_hand(M) - -/mob/living/simple_animal/parrot/attack_alien(mob/living/carbon/monkey/M as mob) - attack_hand(M) - -//Simple animals -/mob/living/simple_animal/parrot/attack_animal(mob/living/simple_animal/M as mob) - if(client) return - - - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - if(M.melee_damage_upper > 0) - parrot_interest = M - parrot_state = PARROT_SWOOP | PARROT_ATTACK //Attack other animals regardless - icon_state = "parrot_fly" - //Mobs with objects /mob/living/simple_animal/parrot/attackby(var/obj/item/O as obj, var/mob/user as mob) ..() @@ -335,7 +317,7 @@ if(speak.len) var/list/newspeak = list() - if(available_channels.len && src.l_ear) + if(available_channels.len && src.ears) for(var/possible_phrase in speak) //50/50 chance to not use the radio at all @@ -496,11 +478,11 @@ var/datum/organ/external/affecting = H.get_organ(ran_zone(pick(parrot_dam_zone))) H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp=1) - emote(pick("pecks [H]'s [affecting]", "cuts [H]'s [affecting] with its talons")) + emote(pick("pecks [H]'s [affecting].", "cuts [H]'s [affecting] with its talons.")) else L.adjustBruteLoss(damage) - emote(pick("pecks at [L]", "claws [L]")) + emote(pick("pecks at [L].", "claws [L].")) return //Otherwise, fly towards the mob! @@ -700,6 +682,71 @@ speak = list("Poly wanna cracker!", ":e Check the singlo, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN HARDSUITS?",":e OH GOD ITS FREE CALL THE SHUTTLE") /mob/living/simple_animal/parrot/Poly/New() - l_ear = new /obj/item/device/radio/headset/headset_eng(src) + ears = new /obj/item/device/radio/headset/headset_eng(src) available_channels = list(":e") ..() + +/mob/living/simple_animal/parrot/say(var/message) + + if(stat) + return + + var/verb = "says" + if(speak_emote.len) + verb = pick(speak_emote) + + var/message_mode = null + if(copytext(message,1,2) == ";") + message_mode = "headset" + message = copytext(message,2) + + if(copytext(message,1,2) == ":") + var/channel_prefix = copytext(message, 1 ,3) + message_mode = department_radio_keys[channel_prefix] + var/positioncut = 3 + message = trim(copytext(message,positioncut)) + + message = capitalize(trim_left(message)) + if(message_mode) + if(message_mode in radiochannels) + if(ears && istype(ears,/obj/item/device/radio)) + ears.talk_into(src, message, message_mode, verb, null) + + ..(message) + + +/mob/living/simple_animal/parrot/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null) + if(prob(50)) + parrot_hear(message) + ..(message,verb,language,alt_name,italics,speaker) + + + +/mob/living/simple_animal/parrot/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0) + if(prob(50)) + parrot_hear("[pick(available_channels)] [message]") + ..(message,verb,language,part_a,part_b,speaker,hard_to_hear) + + +/mob/living/simple_animal/parrot/proc/parrot_hear(var/message="") + if(!message || stat) + return + speech_buffer.Add(message) + +/mob/living/simple_animal/parrot/attack_paw(var/mob/user, var/damage, var/attack_message) + + var/success = ..() + + if(client) + return success + + if(parrot_state == PARROT_PERCH) + parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched + + if(!success) + return 0 + + parrot_interest = user + parrot_state = PARROT_SWOOP | PARROT_ATTACK //Attack other animals regardless + icon_state = "parrot_fly" + return success diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 800d9967ca7..6d0694c3073 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -269,6 +269,7 @@ for(var/mob/O in viewers(src, null)) if ((O.client && !( O.blinded ))) O.show_message("\blue [M] [response_help] [src].") + playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) if("grab") if (M == src || anchored) @@ -288,12 +289,14 @@ for(var/mob/O in viewers(src, null)) if ((O.client && !( O.blinded ))) O.show_message(text("\red [] has grabbed [] passively!", M, src), 1) + playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) if("harm", "disarm") adjustBruteLoss(harm_intent_damage) for(var/mob/O in viewers(src, null)) if ((O.client && !( O.blinded ))) O.show_message("\red [M] [response_harm] [src]!") + playsound(loc, "punch", 25, 1, -1) return @@ -537,3 +540,22 @@ if (S.occupant || S.occupant2) return 0 return 1 + +/mob/living/simple_animal/say(var/message) + if(stat) + return + + if(copytext(message,1,2) == "*") + return emote(copytext(message,2)) + + if(stat) + return + + var/verb = "says" + + if(speak_emote.len) + verb = pick(speak_emote) + + message = capitalize(trim_left(message)) + + ..(message, null, verb) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 7b197aafcb2..a3d5932e10b 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -93,6 +93,7 @@ return 0 /mob/proc/Life() + handle_typing_indicator() // if(organStructure) // organStructure.ProcessOrgans() return diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index e23014f9f52..d3abbfc1776 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -446,3 +446,49 @@ var/list/intents = list("help","disarm","grab","harm") /proc/get_both_hands(mob/living/carbon/M) var/list/hands = list(M.l_hand, M.r_hand) return hands + + +//Direct dead say used both by emote and say +//It is somewhat messy. I don't know what to do. +//I know you can't see the change, but I rewrote the name code. It is significantly less messy now +/proc/say_dead_direct(var/message, var/mob/subject = null) + var/name + var/keyname + if(subject && subject.client) + var/client/C = subject.client + keyname = (C.holder && C.holder.fakekey) ? C.holder.fakekey : C.key + if(C.mob) //Most of the time this is the dead/observer mob; we can totally use him if there is no better name + var/mindname + var/realname = C.mob.real_name + if(C.mob.mind) + mindname = C.mob.mind.name + if(C.mob.mind.original && C.mob.mind.original.real_name) + realname = C.mob.mind.original.real_name + if(mindname && mindname != realname) + name = "[realname] died as [mindname]" + else + name = realname + + for(var/mob/M in player_list) + if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights == R_MOD)) && (M.client.prefs.toggles & CHAT_DEAD)) + var/follow + var/lname + if(subject) + if(subject != M) + follow = "(follow) " + if(M.stat != DEAD && M.client.holder) + follow = "(JMP) " + var/mob/dead/observer/DM + if(istype(subject, /mob/dead/observer)) + DM = subject + if(M.client.holder) // What admins see + lname = "[keyname][(DM && DM.anonsay) ? "*" : (DM ? "" : "^")] ([name])" + else + if(DM && DM.anonsay) // If the person is actually observer they have the option to be anonymous + lname = "Ghost of [name]" + else if(DM) // Non-anons + lname = "[keyname] ([name])" + else // Everyone else (dead people who didn't ghost yet, etc.) + lname = name + lname = "[lname] " + M << "[lname][follow][message]" diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 6ecdfdc4e67..4810c0b10f7 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -444,46 +444,22 @@ proc/create_character() spawning = 1 close_spawn_windows() + var/mob/living/carbon/human/new_character + var/datum/species/chosen_species if(client.prefs.species) chosen_species = all_species[client.prefs.species] if(chosen_species) // Have to recheck admin due to no usr at roundstart. Latejoins are fine though. if(is_species_whitelisted(chosen_species) || has_admin_rights()) - switch(chosen_species.name) - if("Slime People") - new_character = new /mob/living/carbon/human/slime(loc) - if("Tajaran") - new_character = new /mob/living/carbon/human/tajaran(loc) - if("Unathi") - new_character = new /mob/living/carbon/human/unathi(loc) - if("Skrell") - new_character = new /mob/living/carbon/human/skrell(loc) - if("Diona") - new_character = new /mob/living/carbon/human/diona(loc) - if("Vox") - new_character = new /mob/living/carbon/human/vox(loc) - if("Vox Armalis") - new_character = new /mob/living/carbon/human/voxarmalis(loc) - if("Kidan") - new_character = new /mob/living/carbon/human/kidan(loc) - if("Grey") - new_character = new /mob/living/carbon/human/grey(loc) - if("Machine") - new_character = new /mob/living/carbon/human/machine(loc) - if("Plasmaman") - new_character = new /mob/living/carbon/human/plasma(loc) - if("Human") - new_character = new /mob/living/carbon/human/human(loc) -// new_character.set_species(client.prefs.species) - if(chosen_species.language) - new_character.add_language(chosen_species.language) - else - new_character = new /mob/living/carbon/human(loc) + new_character = new(loc, client.prefs.species) + + if(!new_character) + new_character = new(loc) + new_character.lastarea = get_area(loc) - var/datum/language/chosen_language if(client.prefs.language) chosen_language = all_languages[client.prefs.language] @@ -529,6 +505,8 @@ if(client.prefs.disabilities & DISABILITY_FLAG_DEAF) new_character.dna.SetSEState(DEAFBLOCK,1,1) new_character.sdisabilities |= DEAF + + chosen_species.handle_dna(new_character) domutcheck(new_character) new_character.dna.UpdateSE() diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 41612819446..38328b16bb7 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -9,114 +9,97 @@ /mob/verb/say_verb(message as text) set name = "Say" set category = "IC" + + //Let's try to make users fix their errors - we try to detect single, out-of-place letters and 'unintended' words + /* + var/first_letter = copytext(message,1,2) + if((copytext(message,2,3) == " " && first_letter != "I" && first_letter != "A" && first_letter != ";") || cmptext(copytext(message,1,5), "say ") || cmptext(copytext(message,1,4), "me ") || cmptext(copytext(message,1,6), "looc ") || cmptext(copytext(message,1,5), "ooc ") || cmptext(copytext(message,2,6), "say ")) + var/response = alert(usr, "Do you really want to say this using the *say* verb?\n\n[message]\n", "Confirm your message", "Yes", "Edit message", "No") + if(response == "Edit message") + message = input(usr, "Please edit your message carefully:", "Edit message", message) + if(!message) + return + else if(response == "No") + return + */ + + set_typing_indicator(0) usr.say(message) /mob/verb/me_verb(message as text) set name = "Me" set category = "IC" - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) + message = strip_html_properly(message) + set_typing_indicator(0) if(use_me) usr.emote("me",usr.emote_type,message) else usr.emote(message) /mob/proc/say_dead(var/message) - var/name = src.real_name - var/alt_name = "" - - if(!src.client.holder) if(!dsay_allowed) - src << "\red Deadchat is globally muted" + src << "Deadchat is globally muted." return if(client && !(client.prefs.toggles & CHAT_DEAD)) - usr << "\red You have deadchat muted." + usr << "You have deadchat muted." return - if(mind && mind.name) - name = "[mind.name]" - else - name = real_name - if(name != real_name) - alt_name = " (died as [real_name])" - - message = src.say_quote(message) - //var/rendered = "DEAD: [name][alt_name] [message]" - var/rendered2 = null//edited - for(var/mob/M in player_list) - rendered2 = "DEAD: [name][alt_name] (Follow) [message]"//edited - if(istype(M, /mob/new_player)) - continue - if(M.client && M.client.holder && (M.client.holder.rights & R_ADMIN|R_MOD) && (M.client.prefs.toggles & CHAT_DEAD)) // Show the message to admins/mods with deadchat toggled on - M << rendered2 //Admins can hear deadchat, if they choose to, no matter if they're blind/deaf or not. - - else if(M.client && M.stat == DEAD && (M.client.prefs.toggles & CHAT_DEAD)) // Show the message to regular ghosts with deadchat toggled on. - M.show_message(rendered2, 2) //Takes into account blindness and such. - return + say_dead_direct("[pick("complains","moans","whines","laments","blubbers")], \"[message]\"", src) /mob/proc/say_understands(var/mob/other,var/datum/language/speaking = null) - if(!other) - return 1 - //Universal speak makes everything understandable, for obvious reasons. - if(other.universal_speak || src.universal_speak || src.universal_understand) - return 1 - if (src.stat == 2) + if (src.stat == 2) //Dead return 1 - if(!speaking) //Handle languages later - if(other.universal_speak || src.universal_speak) + //Universal speak makes everything understandable, for obvious reasons. + else if(src.universal_speak || src.universal_understand) + return 1 + + //Languages are handled after. + if (!speaking) + if(!other) + return 1 + if(other.universal_speak) return 1 if(isAI(src) && ispAI(other)) return 1 if (istype(other, src.type) || istype(src, other.type)) return 1 - if (istype(other, /mob/living/carbon/human) && (istype(src, /mob/living/carbon/human) || istype(src, /mob/living/silicon/pai))) - return 1 - if(istype(other, /mob/living/carbon/alien) && istype(src, /mob/living/carbon/alien)) - return 1 return 0 - for(var/datum/language/L in src.languages) //Handling languages + if(speaking.flags & INNATE) + return 1 + + //Language check. + for(var/datum/language/L in src.languages) if(speaking.name == L.name) return 1 return 0 -/mob/proc/say_quote(var/text,var/datum/language/speaking) +/* + ***Deprecated*** + let this be handled at the hear_say or hear_radio proc + This is left in for robot speaking when humans gain binary channel access until I get around to rewriting + robot_talk() proc. + There is no language handling build into it however there is at the /mob level so we accept the call + for it but just ignore it. +*/ - 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 - //tcomms code is still runtiming somewhere here - var/ending = copytext(text, length(text)) +/mob/proc/say_quote(var/message, var/datum/language/speaking = null) + var/verb = "says" + var/ending = copytext(message, length(message)) + if(ending=="!") + verb=pick("exclaims","shouts","yells") + else if(ending=="?") + verb="asks" - var/speechverb = "" + return verb - if (speaking) - speechverb = "[speaking.speech_verb], \"" - else if(speak_emote && speak_emote.len) - speechverb = "[pick(speak_emote)], \"" - else if (src.stuttering) - speechverb = "stammers, \"" - else if (src.slurring) - speechverb = "slurrs, \"" - else if (ending == "?") - speechverb = "asks, \"" - else if (ending == "!") - speechverb = "exclaims, \"" - else if(isliving(src)) - var/mob/living/L = src - if (L.getBrainLoss() >= 60) - speechverb = "gibbers, \"" - else - speechverb = "says, \"" - else - speechverb = "says, \"" - - return "[speechverb][text]\"" /mob/proc/emote(var/act, var/type, var/message) if(act == "me") @@ -133,21 +116,34 @@ /mob/proc/say_test(var/text) var/ending = copytext(text, length(text)) if (ending == "?") - if(isalien(src)) - return "A1" - if(isrobot(src)) - return "R1" - else - return "1" + return "1" else if (ending == "!") - if(isalien(src)) - return "A2" - if(isrobot(src)) - return "R2" - else - return "2" - else if(isalien(src)) - return "A0" - else if(isrobot(src)) - return "R0" + return "2" return "0" + +//parses the message mode code (e.g. :h, :w) from text, such as that supplied to say. +//returns the message mode string or null for no message mode. +//standard mode is the mode returned for the special ';' radio code. +/mob/proc/parse_message_mode(var/message, var/standard_mode="headset") + if(length(message) >= 1 && copytext(message,1,2) == ";") + return standard_mode + + if(length(message) >= 2) + var/channel_prefix = copytext(message, 1 ,3) + return department_radio_keys[channel_prefix] + + return null + +//parses the language code (e.g. :j) from text, such as that supplied to say. +//returns the language object only if the code corresponds to a language that src can speak, otherwise null. +/mob/proc/parse_language(var/message) + if(length(message) >= 1 && copytext(message,1,2) == "!") + return all_languages["Noise"] + + if(length(message) >= 2) + var/language_prefix = lowertext(copytext(message, 1 ,3)) + var/datum/language/L = language_keys[language_prefix] + if (can_speak(L)) + return L + + return null diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm new file mode 100644 index 00000000000..c104aa839a4 --- /dev/null +++ b/code/modules/mob/typing_indicator.dm @@ -0,0 +1,85 @@ +#define TYPING_INDICATOR_LIFETIME 30 * 10 //grace period after which typing indicator disappears regardless of text in chatbar + +mob/var/hud_typing = 0 //set when typing in an input window instead of chatline +mob/var/typing +mob/var/last_typed +mob/var/last_typed_time + +var/global/image/typing_indicator + +/mob/proc/set_typing_indicator(var/state) + + if(!typing_indicator) + typing_indicator = image('icons/mob/talk.dmi',null,"typing") + + if(client) + if(client.prefs.toggles & SHOW_TYPING) + overlays -= typing_indicator + else + if(state) + if(!typing) + overlays += typing_indicator + typing = 1 + else + if(typing) + overlays -= typing_indicator + typing = 0 + return state + +/mob/verb/say_wrapper() + set name = ".Say" + set hidden = 1 + + set_typing_indicator(1) + hud_typing = 1 + var/message = input("","say (text)") as text + hud_typing = 0 + set_typing_indicator(0) + if(message) + say_verb(message) + +/mob/verb/me_wrapper() + set name = ".Me" + set hidden = 1 + + set_typing_indicator(1) + hud_typing = 1 + var/message = input("","me (text)") as text + hud_typing = 0 + set_typing_indicator(0) + if(message) + me_verb(message) + +/mob/proc/handle_typing_indicator() + if(client) + if(!(client.prefs.toggles & SHOW_TYPING) && !hud_typing) + var/temp = winget(client, "input", "text") + + if (temp != last_typed) + last_typed = temp + last_typed_time = world.time + + if (world.time > last_typed_time + TYPING_INDICATOR_LIFETIME) + set_typing_indicator(0) + return + if(length(temp) > 5 && findtext(temp, "Say \"", 1, 7)) + set_typing_indicator(1) + else if(length(temp) > 3 && findtext(temp, "Me ", 1, 5)) + set_typing_indicator(1) + + else + set_typing_indicator(0) + +/client/verb/typing_indicator() + set name = "Show/Hide Typing Indicator" + set category = "Preferences" + set desc = "Toggles showing an indicator when you are typing emote or say message." + prefs.toggles ^= SHOW_TYPING + prefs.save_preferences(src) + src << "You will [(prefs.toggles & SHOW_TYPING) ? "no longer" : "now"] display a typing indicator." + + // Clear out any existing typing indicator. + if(prefs.toggles & SHOW_TYPING) + if(istype(mob)) mob.set_typing_indicator(0) + + feedback_add_details("admin_verb","TID") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! \ No newline at end of file diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 98726004ea4..7beff751760 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -21,8 +21,6 @@ var/list/alldepartments = list() var/department = "Unknown" // our department var/destination = "Central Command" // the department we're sending to - - var/data[0] /obj/machinery/photocopier/faxmachine/New() ..() @@ -51,6 +49,7 @@ var/list/alldepartments = list() user << "You swipe the card through [src], but nothing happens." /obj/machinery/photocopier/faxmachine/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] if(scan) data["scan_name"] = scan.name else @@ -142,8 +141,7 @@ var/list/alldepartments = list() else if(istype(copyitem, /obj/item/weapon/photo)) copyitem.name = "[(n_name ? text("[n_name]") : "photo")]" else if(istype(copyitem, /obj/item/weapon/paper_bundle)) - copyitem.name = "[(n_name ? text("[n_name]") : "paper")]" - data["name"] = copyitem.name + copyitem.name = "[(n_name ? text("[n_name]") : "paper")]" nanomanager.update_uis(src) diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index 4567d00bfde..ab5653e0d1a 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -62,7 +62,7 @@ build_path = /obj/item/weapon/circuitboard/bodyscanner category = list("Medical Machinery") -/datum/design/sleep_console +/datum/design/bodyscanner_console name = "Machine Board (Body Scanner Console)" desc = "Allows for the construction of circuit boards used to build a Body Scanner Console." id = "bodyscanner_console" diff --git a/code/modules/surgery/headreattach.dm b/code/modules/surgery/headreattach.dm index 3c49a9e8c0a..c493607c52c 100644 --- a/code/modules/surgery/headreattach.dm +++ b/code/modules/surgery/headreattach.dm @@ -196,7 +196,7 @@ user.visible_message("\blue [user] has attached [target]'s head to the body.", \ "\blue You have attached [target]'s head to the body.") affected.status = 0 - if(istype(target,/mob/living/carbon/human/machine)) + if(target.species && (target.species.flags & IS_SYNTHETIC)) affected.status = 128 affected.amputated = 0 affected.destspawn = 0 diff --git a/code/setup.dm b/code/setup.dm index ca1c664815a..c2981325856 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -711,11 +711,12 @@ var/list/TAGGERLOCATIONS = list("Disposals", #define CHAT_GHOSTEARS 4 #define CHAT_GHOSTSIGHT 8 #define CHAT_PRAYER 16 -//#define CHAT_RADIO 32 +#define CHAT_RADIO 32 #define CHAT_ATTACKLOGS 64 #define CHAT_DEBUGLOGS 128 #define CHAT_LOOC 256 #define CHAT_GHOSTRADIO 512 +#define SHOW_TYPING 1024 #define SOUND_ADMINHELP 1 #define SOUND_MIDI 2 @@ -843,12 +844,15 @@ var/list/restricted_camera_networks = list( //Those networks can only be accesse #define HAS_SKIN_TONE 16 #define HAS_SKIN_COLOR 32 - //Language flags. -#define WHITELISTED 1 // Language is available if the speaker is whitelisted. -#define RESTRICTED 2 // Language can only be accquired by spawning or an admin. -#define NONVERBAL 4 // Language has a significant non-verbal component. Speech is garbled without line-of-sight -#define SIGNLANG 8 // Language is completely non-verbal. Speech is displayed through emotes for those who can understand. +#define WHITELISTED 1 // Language is available if the speaker is whitelisted. +#define RESTRICTED 2 // Language can only be accquired by spawning or an admin. +#define NONVERBAL 4 // Language has a significant non-verbal component. Speech is garbled without line-of-sight +#define SIGNLANG 8 // Language is completely non-verbal. Speech is displayed through emotes for those who can understand. +#define HIVEMIND 16 // Broadcast to all mobs with this language. +#define NONGLOBAL 32 // Do not add to general languages list +#define INNATE 64 // All mobs can be assumed to speak and understand this language (audible emotes) +#define NO_TALK_MSG 128 // Do not show the "\The [speaker] talks into \the [radio]" message //Flags for zone sleeping #define ZONE_ACTIVE 1 diff --git a/code/stylesheet.dm b/code/stylesheet.dm index 441ffc17000..b7111e1e5a1 100644 --- a/code/stylesheet.dm +++ b/code/stylesheet.dm @@ -67,9 +67,18 @@ h1.alert, h2.alert {color: #000000;} .noticealien {color: #00c000;} .alertalien {color: #00c000; font-weight: bold;} .tajaran {color: #803B56;} +.tajaran_signlang {color: #941C1C;} .skrell {color: #00CED1;} .soghun {color: #228B22;} +.solcom {color: #22228B;} +.changeling {color: #800080;} .vox {color: #AA00AA;} +.diona {color: #007200;} +.trinary {color: #727272;} +.kidan {color: #664205;} +.slime {color: #0077AA;} +.rough {font-family: "Trebuchet MS", cursive, sans-serif;} +.say_quote {font-family: Georgia, Verdana, sans-serif;} .say_quote {font-family: Georgia, Verdana, sans-serif;} diff --git a/config/example/config.txt b/config/example/config.txt index a26aa4575d6..454dcca2d0c 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -256,7 +256,7 @@ ADMIN_LEVELS 2 CONTACT_LEVELS 1;5 ## Defines all Z-levels a character can typically reach -PLAYER_LEVELS 1;3;4;5;6 +PLAYER_LEVELS 1;3;4;5;6;7 ## Expected round length in minutes EXPECTED_ROUND_LENGTH 120 diff --git a/icons/mob/human_races/r_golem.dmi b/icons/mob/human_races/r_golem.dmi new file mode 100644 index 00000000000..f9cfb8e157c Binary files /dev/null and b/icons/mob/human_races/r_golem.dmi differ diff --git a/icons/mob/human_races/r_shadow.dmi b/icons/mob/human_races/r_shadow.dmi new file mode 100644 index 00000000000..70450af6902 Binary files /dev/null and b/icons/mob/human_races/r_shadow.dmi differ diff --git a/icons/mob/human_races/r_slime.dmi b/icons/mob/human_races/r_slime.dmi new file mode 100644 index 00000000000..287779d3c7b Binary files /dev/null and b/icons/mob/human_races/r_slime.dmi differ diff --git a/icons/mob/talk.dmi b/icons/mob/talk.dmi index 2cd0b40bb18..86534d162d0 100644 Binary files a/icons/mob/talk.dmi and b/icons/mob/talk.dmi differ diff --git a/icons/obj/robot_storage.dmi b/icons/obj/robot_storage.dmi index 5a996c01e7e..2382586eee7 100644 Binary files a/icons/obj/robot_storage.dmi and b/icons/obj/robot_storage.dmi differ diff --git a/interface/interface.dm b/interface/interface.dm index e4d6a082d8e..4362cad34d2 100644 --- a/interface/interface.dm +++ b/interface/interface.dm @@ -124,7 +124,7 @@ Admin: //adv. hotkey mode verbs, vars located in /code/modules/client/client defines.dm /client/verb/hotkey_toggle()//toggles hotkey mode between on and off, respects selected type - set name = ".Toggle hotkey mode" + set name = ".Toggle Hotkey Mode" hotkeyon = !hotkeyon//toggle the var diff --git a/interface/skin.dmf b/interface/skin.dmf index 96ab4aea9bc..52f9a07309c 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -149,11 +149,11 @@ macro "AZERTYoff" is-disabled = false elem name = "F3" - command = "say" + command = ".say" is-disabled = false elem name = "F4" - command = "me" + command = ".me" is-disabled = false elem name = "F5" @@ -347,7 +347,7 @@ macro "AZERTYon" is-disabled = false elem name = "T" - command = "say" + command = ".say" is-disabled = false elem name = "W" @@ -403,11 +403,11 @@ macro "AZERTYon" is-disabled = false elem name = "F3" - command = "say" + command = ".say" is-disabled = false elem name = "F4" - command = "me" + command = ".me" is-disabled = false elem name = "F5" @@ -705,6 +705,10 @@ macro "hotkeymode" name = "CTRL+4" command = "a-intent harm" is-disabled = false + elem + name = "5" + command = ".me" + is-disabled = false elem name = "A+REP" command = ".west" diff --git a/maps/cyberiad.dmm b/maps/cyberiad.dmm index 75a770d8900..77c8ce078cd 100644 --- a/maps/cyberiad.dmm +++ b/maps/cyberiad.dmm @@ -10987,7 +10987,7 @@ "edo" = (/obj/machinery/door/airlock/external{frequency = 1379; icon_state = "door_locked"; id_tag = "labor_camp_north_outer"; locked = 1; name = "Labor Camp Airlock"; opacity = 1; req_access_txt = "2"},/turf/simulated/floor{icon_state = "floorgrime"; temperature = 273.15},/area/mine/laborcamp) "edp" = (/obj/machinery/light/small{dir = 1},/obj/machinery/airlock_sensor{frequency = 1379; id_tag = "labor_camp_north_sensor"; pixel_x = -8; pixel_y = 30},/obj/machinery/atmospherics/unary/vent_pump/high_volume{dir = 4; frequency = 1379; id_tag = "labor_camp_north_pump"},/obj/machinery/door_control{id = "Labor"; name = "Labor Camp Lockdown"; pixel_x = 0; pixel_y = -28; req_access_txt = "2"},/obj/machinery/embedded_controller/radio/airlock/docking_port_multi{id_tag = "labor_camp_north_airlock"; master_tag = "labor_camp_dock"; pixel_y = 30; req_access_txt = "2"; req_one_access_txt = "0"; tag_airpump = "labor_camp_north_pump"; tag_chamber_sensor = "labor_camp_north_sensor"; tag_exterior_door = "labor_camp_north_outer"; tag_interior_door = "labor_camp_north_inner"},/turf/simulated/floor{icon_state = "floorgrime"; temperature = 273.15},/area/mine/laborcamp) "edq" = (/obj/machinery/door/airlock/external{frequency = 1379; icon_state = "door_locked"; id_tag = "labor_camp_north_inner"; locked = 1; name = "Labor Camp Airlock"; opacity = 1; req_access_txt = "2"},/obj/machinery/atmospherics/pipe/simple/hidden{dir = 4},/turf/simulated/floor{icon_state = "floorgrime"; temperature = 273.15},/area/mine/laborcamp) -"edr" = (/obj/machinery/door/poddoor/preopen{id = "Labor"; name = "labor camp blast door"},/obj/machinery/access_button{command = "cycle_interior"; frequency = 1379; master_tag = "labor_camp_north_airlock"; name = "interior access button"; pixel_x = -25; pixel_y = 25; req_access_txt = "2"},/obj/machinery/atmospherics/pipe/simple/hidden{dir = 10; initialize_directions = 10},/turf/simulated/floor{icon_state = "floorgrime"; temperature = 273.15},/area/mine/laborcamp) +"edr" = (/obj/machinery/door/poddoor/preopen{id = "Labor"; name = "labor camp blast door"},/obj/machinery/access_button{command = "cycle_interior"; frequency = 1379; master_tag = "labor_camp_north_airlock"; name = "interior access button"; pixel_x = -25; pixel_y = 25; req_access_txt = "2"},/obj/machinery/atmospherics/pipe/simple/hidden{dir = 10; initialize_directions = 10},/obj/machinery/door/airlock/glass_security{name = "Labor Camp Shuttle Cockpit"; req_access_txt = "2"},/turf/simulated/floor{icon_state = "floorgrime"; temperature = 273.15},/area/mine/laborcamp) "eds" = (/turf/simulated/floor,/area/mine/laborcamp) "edt" = (/obj/machinery/mineral/processing_unit_console{machinedir = 6},/turf/simulated/wall,/area/mine/laborcamp) "edu" = (/obj/structure/plasticflaps,/obj/machinery/conveyor{dir = 2; id = "gulag"},/obj/machinery/mineral/input,/turf/simulated/floor{icon_state = "floorgrime"; temperature = 273.15},/area/mine/laborcamp) diff --git a/nano/images/uiBackground-Syndicate.png b/nano/images/uiBackground-Syndicate.png index 241de891ece..3ca932cb835 100644 Binary files a/nano/images/uiBackground-Syndicate.png and b/nano/images/uiBackground-Syndicate.png differ diff --git a/nano/images/uiTitleFluff-Syndicate.png b/nano/images/uiTitleFluff-Syndicate.png index b2a32013f86..b09f5ba6b29 100644 Binary files a/nano/images/uiTitleFluff-Syndicate.png and b/nano/images/uiTitleFluff-Syndicate.png differ diff --git a/paradise.dme b/paradise.dme index fa33cb1324b..3b01e089723 100644 --- a/paradise.dme +++ b/paradise.dme @@ -239,6 +239,7 @@ #include "code\defines\procs\AStar.dm" #include "code\defines\procs\dbcore.dm" #include "code\defines\procs\hud.dm" +#include "code\defines\procs\radio.dm" #include "code\defines\procs\sd_Alert.dm" #include "code\defines\procs\statistics.dm" #include "code\game\asteroid.dm" @@ -959,6 +960,7 @@ #include "code\modules\clothing\masks\breath.dm" #include "code\modules\clothing\masks\gasmask.dm" #include "code\modules\clothing\masks\miscellaneous.dm" +#include "code\modules\clothing\masks\voice.dm" #include "code\modules\clothing\shoes\colour.dm" #include "code\modules\clothing\shoes\magboots.dm" #include "code\modules\clothing\shoes\miscellaneous.dm" @@ -1159,6 +1161,7 @@ #include "code\modules\mob\abilities.dm" #include "code\modules\mob\death.dm" #include "code\modules\mob\emote.dm" +#include "code\modules\mob\hear_say.dm" #include "code\modules\mob\holder.dm" #include "code\modules\mob\inventory.dm" #include "code\modules\mob\language.dm" @@ -1172,6 +1175,7 @@ #include "code\modules\mob\mob_transformation_simple.dm" #include "code\modules\mob\say.dm" #include "code\modules\mob\transform_procs.dm" +#include "code\modules\mob\typing_indicator.dm" #include "code\modules\mob\update_icons.dm" #include "code\modules\mob\camera\camera.dm" #include "code\modules\mob\dead\death.dm"