"
@@ -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/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm
index 342ece56272..4440c825215 100644
--- a/code/game/objects/effects/effect_system.dm
+++ b/code/game/objects/effects/effect_system.dm
@@ -1149,13 +1149,13 @@ steam.start() -- spawns the effect
// Clamp all values to MAX_EXPLOSION_RANGE
if (round(amount/12) > 0)
- devastation = min (MAX_EXPLOSION_RANGE, devastation + round(amount/12))
+ devastation = min (MAX_EX_DEVESTATION_RANGE, devastation + round(amount/12))
if (round(amount/6) > 0)
- heavy = min (MAX_EXPLOSION_RANGE, heavy + round(amount/6))
+ heavy = min (MAX_EX_HEAVY_RANGE, heavy + round(amount/6))
if (round(amount/3) > 0)
- light = min (MAX_EXPLOSION_RANGE, light + round(amount/3))
+ light = min (MAX_EX_LIGHT_RANGE, light + round(amount/3))
if (flash && flashing_factor)
flash += (round(amount/4) * flashing_factor)
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 @@
| 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
|
@@ -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/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm
index 0f5f9f64ac1..567dc49bf4f 100644
--- a/code/game/objects/items/devices/radio/intercom.dm
+++ b/code/game/objects/items/devices/radio/intercom.dm
@@ -4,7 +4,7 @@
icon_state = "intercom"
anchored = 1
w_class = 4.0
- canhear_range = 5
+ canhear_range = 2
flags = CONDUCT | NOBLOODY
var/number = 0
var/anyai = 1
@@ -15,7 +15,7 @@
..()
processing_objects += src
-/obj/item/device/radio/intercom/Destroy()
+/obj/item/device/radio/intercom/Del()
processing_objects -= src
..()
@@ -24,10 +24,6 @@
spawn (0)
attack_self(user)
-/obj/item/device/radio/intercom/attack_paw(mob/user as mob)
- return src.attack_hand(user)
-
-
/obj/item/device/radio/intercom/attack_hand(mob/user as mob)
src.add_fingerprint(user)
spawn (0)
@@ -36,15 +32,13 @@
/obj/item/device/radio/intercom/receive_range(freq, level)
if (!on)
return -1
- if (isWireCut(WIRE_RECEIVE))
- return -1
if(!(0 in level))
var/turf/position = get_turf(src)
if(isnull(position) || !(position.z in level))
return -1
if (!src.listening)
return -1
- if(freq == SYND_FREQ)
+ if(freq in ANTAG_FREQS)
if(!(src.syndie))
return -1//Prevents broadcast of messages over devices lacking the encryption
@@ -72,4 +66,4 @@
if(!on)
icon_state = "intercom-p"
else
- icon_state = "intercom"
\ No newline at end of file
+ icon_state = "intercom"
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 b4d24b90ec1..b2fbda4d76c 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/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index 082553f3092..49274720ed1 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -455,6 +455,16 @@
dat += "[mob_loc.loc] | "
else
dat += "| Head not found! |
"
+ if(ticker.mode.num_players_started() >= 30)
+ for(var/datum/mind/N in ticker.mode.get_extra_living_heads())
+ var/mob/M = N.current
+ if(M)
+ dat += "| [M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""] | "
+ dat += "PM | "
+ var/turf/mob_loc = get_turf_loc(M)
+ dat += "[mob_loc.loc] |
"
+ else
+ dat += "| Head not found! |
"
dat += "
"
if(ticker.mode.name == "nations")
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/computer3/computers/security.dm b/code/modules/computer3/computers/security.dm
index f193b071396..477f7c7f972 100644
--- a/code/modules/computer3/computers/security.dm
+++ b/code/modules/computer3/computers/security.dm
@@ -38,7 +38,7 @@
proc/authenticate()
- if(access_security in scan.access || access_forensics_lockers in scan.access )
+ if((access_security in scan.access) || (access_forensics_lockers in scan.access) )
return 1
if(istype(usr,/mob/living/silicon/ai))
return 1
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 = ""
@@ -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 6f99995aba4..409954cb719 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 9d8958d62a5..780b43ba3ea 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
@@ -504,3 +512,6 @@
close_up()
return 2
+// No binary for pAIs.
+/mob/living/silicon/pai/binarycheck()
+ return 0
diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm
index 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("| From | → | ", index["owner"],": ", index["message"], "
|
")
dat += "
"
- 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 d422fe644e6..132a4c28d32 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/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm
index 10ca1d5f5a6..0cc8356150c 100644
--- a/code/modules/reagents/Chemistry-Recipes.dm
+++ b/code/modules/reagents/Chemistry-Recipes.dm
@@ -31,12 +31,6 @@ datum
on_reaction(var/datum/reagents/holder, var/created_volume)
var/datum/effect/effect/system/reagents_explosion/e = new()
e.set_up(round (created_volume/10, 1), holder.my_atom, 0, 0)
- e.holder_damage(holder.my_atom)
- if(isliving(holder.my_atom))
- e.amount *= 0.5
- var/mob/living/L = holder.my_atom
- if(L.stat!=DEAD)
- e.amount *= 0.5
e.start()
holder.clear_reagents()
return
@@ -356,12 +350,6 @@ datum
on_reaction(var/datum/reagents/holder, var/created_volume)
var/datum/effect/effect/system/reagents_explosion/e = new()
e.set_up(round (created_volume/2, 1), holder.my_atom, 0, 0)
- e.holder_damage(holder.my_atom)
- if(isliving(holder.my_atom))
- e.amount *= 0.5
- var/mob/living/L = holder.my_atom
- if(L.stat!=DEAD)
- e.amount *= 0.5
e.start()
holder.clear_reagents()
@@ -385,23 +373,14 @@ datum
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(2, 1, location)
s.start()
- for(var/mob/living/carbon/M in viewers(world.view, location))
- switch(get_dist(M, location))
- if(0 to 3)
- if(hasvar(M, "glasses"))
- if(istype(M:glasses, /obj/item/clothing/glasses/sunglasses))
- continue
-
- flick("e_flash", M.flash)
- M.Weaken(15)
-
- if(4 to 5)
- if(hasvar(M, "glasses"))
- if(istype(M:glasses, /obj/item/clothing/glasses/sunglasses))
- continue
-
- flick("e_flash", M.flash)
- M.Stun(5)
+ for(var/mob/living/carbon/C in hearers(5, location))
+ if(C.eyecheck())
+ continue
+ flick("e_flash", C.flash)
+ if(get_dist(C, location) < 4)
+ C.Weaken(5)
+ continue
+ C.Stun(5)
napalm
name = "Napalm"
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 35052478851..c2981325856 100644
--- a/code/setup.dm
+++ b/code/setup.dm
@@ -156,6 +156,10 @@ var/turf/space/Space_Tile = locate(/turf/space) // A space tile to reference whe
//This was a define, but I changed it to a variable so it can be changed in-game.(kept the all-caps definition because... code...) -Errorage
var/MAX_EXPLOSION_RANGE = 14
+var/MAX_EX_DEVESTATION_RANGE = 3
+var/MAX_EX_HEAVY_RANGE = 7
+var/MAX_EX_LIGHT_RANGE = 14
+var/MAX_EX_FLASH_RANGE = 14
//#define MAX_EXPLOSION_RANGE 14 // Defaults to 12 (was 8) -- TLE
#define HUMAN_STRIP_DELAY 40 //takes 40ds = 4s to strip someone.
@@ -707,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
@@ -839,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 b77e1e61f50..0f2ec8b4b3f 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"
@@ -958,6 +959,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"
@@ -1158,6 +1160,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"
@@ -1171,6 +1174,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"