April sync (#360)

* Maps and things no code/icons

* helpers defines globalvars

* Onclick world.dm orphaned_procs

* subsystems

Round vote and shuttle autocall done here too

* datums

* Game folder

* Admin - chatter modules

* clothing - mining

* modular computers - zambies

* client

* mob level 1

* mob stage 2 + simple_animal

* silicons n brains

* mob stage 3 + Alien/Monkey

* human mobs

* icons updated

* some sounds

* emitter y u no commit

* update tgstation.dme

* compile fixes

* travis fixes

Also removes Fast digest mode, because reasons.

* tweaks for travis Mentors are broke again

Also fixes Sizeray guns

* oxygen loss fix for vore code.

* removes unused code

* some code updates

* bulk fixes

* further fixes

* outside things

* whoops.

* Maint bar ported

* GLOBs.
This commit is contained in:
Poojawa
2017-04-13 23:37:00 -05:00
committed by GitHub
parent cdc32c98fa
commit 7e9b96a00f
1322 changed files with 174827 additions and 23888 deletions
+98 -27
View File
@@ -1,11 +1,10 @@
#define CALL_BOT_COOLDOWN 900
var/list/ai_list = list()
//Not sure why this is necessary...
/proc/AutoUpdateAI(obj/subject)
var/is_in_use = 0
if (subject!=null)
for(var/A in ai_list)
for(var/A in GLOB.ai_list)
var/mob/living/silicon/ai/M = A
if ((M.client && M.machine == subject))
is_in_use = 1
@@ -22,7 +21,6 @@ var/list/ai_list = list()
canmove = 0
status_flags = CANSTUN|CANPUSH
a_intent = INTENT_HARM //so we always get pushed instead of trying to swap
force_compose = 1 //This ensures that the AI always composes it's own hear message. Needed for hrefs and job display.
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS
see_in_dark = 8
med_hud = DATA_HUD_MEDICAL_BASIC
@@ -84,6 +82,10 @@ var/list/ai_list = list()
var/obj/machinery/camera/portable/builtInCamera
var/obj/structure/AIcore/deactivated/linked_core //For exosuit control
var/mob/living/silicon/robot/deployed_shell = null //For shell control
var/datum/action/innate/deploy_shell/deploy_action = new
var/datum/action/innate/deploy_last_shell/redeploy_action = new
var/chnotify = 0
/mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai)
..()
@@ -138,22 +140,24 @@ var/list/ai_list = list()
radio = new /obj/item/device/radio/headset/ai(src)
aicamera = new/obj/item/device/camera/siliconcam/ai_camera(src)
deploy_action.Grant(src)
if(isturf(loc))
verbs.Add(/mob/living/silicon/ai/proc/ai_network_change, \
/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/set_automatic_say_channel)
ai_list += src
shuttle_caller_list += src
GLOB.ai_list += src
GLOB.shuttle_caller_list += src
builtInCamera = new /obj/machinery/camera/portable(src)
builtInCamera.network = list("SS13")
/mob/living/silicon/ai/Destroy()
ai_list -= src
shuttle_caller_list -= src
GLOB.ai_list -= src
GLOB.shuttle_caller_list -= src
SSshuttle.autoEvac()
qdel(eyeobj) // No AI, no Eye
malfhack = null
@@ -247,13 +251,16 @@ var/list/ai_list = list()
for(var/mob/living/silicon/robot/R in connected_robots)
borg_area = get_area(R)
var/robot_status = "Nominal"
if(R.stat || !R.client)
if(R.shell)
robot_status = "AI SHELL"
else if(R.stat || !R.client)
robot_status = "OFFLINE"
else if(!R.cell || R.cell.charge <= 0)
robot_status = "DEPOWERED"
//Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "Empty"] | \
Module: [R.designation] | Loc: [borg_area.name] | Status: [robot_status]"))
Module: [R.designation] | Loc: [borg_area.name] | Status: [robot_status]"))
stat(null, text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]")) //Count of total AI shells
else
stat(null, text("Systems nonfunctional"))
@@ -293,7 +300,7 @@ var/list/ai_list = list()
/mob/living/silicon/ai/proc/ai_roster()
var/dat = "<html><head><title>Crew Roster</title></head><body><b>Crew Roster:</b><br><br>"
dat += data_core.get_manifest()
dat += GLOB.data_core.get_manifest()
dat += "</body></html>"
src << browse(dat, "window=airoster")
@@ -315,7 +322,7 @@ var/list/ai_list = list()
// hack to display shuttle timer
if(!EMERGENCY_IDLE_OR_RECALLED)
var/obj/machinery/computer/communications/C = locate() in machines
var/obj/machinery/computer/communications/C = locate() in GLOB.machines
if(C)
C.post_status("shuttle")
@@ -363,7 +370,7 @@ var/list/ai_list = list()
unset_machine()
src << browse(null, t1)
if (href_list["switchcamera"])
switchCamera(locate(href_list["switchcamera"])) in cameranet.cameras
switchCamera(locate(href_list["switchcamera"])) in GLOB.cameranet.cameras
if (href_list["showalerts"])
ai_alerts()
#ifdef AI_VOX
@@ -403,14 +410,14 @@ var/list/ai_list = list()
if(call_bot_cooldown > world.time)
to_chat(src, "<span class='danger'>Error: Your last call bot command is still processing, please wait for the bot to finish calculating a route.</span>")
return
Bot = locate(href_list["callbot"]) in living_mob_list
Bot = locate(href_list["callbot"]) in GLOB.living_mob_list
if(!Bot || Bot.remote_disabled || src.control_disabled)
return //True if there is no bot found, the bot is manually emagged, or the AI is carded with wireless off.
waypoint_mode = 1
to_chat(src, "<span class='notice'>Set your waypoint by clicking on a valid location free of obstructions.</span>")
return
if(href_list["interface"]) //Remotely connect to a bot!
Bot = locate(href_list["interface"]) in living_mob_list
Bot = locate(href_list["interface"]) in GLOB.living_mob_list
if(!Bot || Bot.remote_disabled || src.control_disabled)
return
Bot.attack_ai(src)
@@ -423,7 +430,7 @@ var/list/ai_list = list()
if(controlled_mech)
to_chat(src, "<span class='warning'>You are already loaded into an onboard computer!</span>")
return
if(!cameranet.checkCameraVis(M))
if(!GLOB.cameranet.checkCameraVis(M))
to_chat(src, "<span class='warning'>Exosuit is no longer near active cameras.</span>")
return
if(lacks_power())
@@ -470,7 +477,7 @@ var/list/ai_list = list()
d += "<A HREF=?src=\ref[src];botrefresh=1>Query network status</A><br>"
d += "<table width='100%'><tr><td width='40%'><h3>Name</h3></td><td width='30%'><h3>Status</h3></td><td width='30%'><h3>Location</h3></td><td width='10%'><h3>Control</h3></td></tr>"
for (Bot in living_mob_list)
for (Bot in GLOB.living_mob_list)
if(Bot.z == ai_Zlevel && !Bot.remote_disabled) //Only non-emagged bots on the same Z-level are detected!
bot_area = get_area(Bot)
var/bot_mode = Bot.get_mode()
@@ -492,7 +499,7 @@ var/list/ai_list = list()
//The target must be in view of a camera or near the core.
if(turf_check in range(get_turf(src)))
call_bot(turf_check)
else if(cameranet && cameranet.checkTurfVis(turf_check))
else if(GLOB.cameranet && GLOB.cameranet.checkTurfVis(turf_check))
call_bot(turf_check)
else
to_chat(src, "<span class='danger'>Selected location is not visible.</span>")
@@ -582,7 +589,7 @@ var/list/ai_list = list()
var/mob/living/silicon/ai/U = usr
for (var/obj/machinery/camera/C in cameranet.cameras)
for (var/obj/machinery/camera/C in GLOB.cameranet.cameras)
if(!C.can_use())
continue
@@ -601,7 +608,7 @@ var/list/ai_list = list()
if(isnull(network))
network = old_network // If nothing is selected
else
for(var/obj/machinery/camera/C in cameranet.cameras)
for(var/obj/machinery/camera/C in GLOB.cameranet.cameras)
if(!C.can_use())
continue
if(network in C.network)
@@ -625,7 +632,7 @@ var/list/ai_list = list()
return //won't work if dead
var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Friend Computer", "Dorfy", "Blue Glow", "Red Glow")
var/emote = input("Please, select a status!", "AI Status", null, null) in ai_emotions
for (var/obj/machinery/M in machines) //change status
for (var/obj/machinery/M in GLOB.machines) //change status
if(istype(M, /obj/machinery/ai_status_display))
var/obj/machinery/ai_status_display/AISD = M
AISD.emotion = emote
@@ -652,7 +659,7 @@ var/list/ai_list = list()
if("Crew Member")
var/list/personnel_list = list()
for(var/datum/data/record/t in data_core.locked)//Look in data core locked.
for(var/datum/data/record/t in GLOB.data_core.locked)//Look in data core locked.
personnel_list["[t.fields["name"]]: [t.fields["rank"]]"] = t.fields["image"]//Pull names, rank, and image.
if(personnel_list.len)
@@ -791,12 +798,13 @@ var/list/ai_list = list()
if(!..())
return
if(interaction == AI_TRANS_TO_CARD)//The only possible interaction. Upload AI mob to a card.
if(!mind)
to_chat(user, "<span class='warning'>No intelligence patterns detected.</span>" )
return
if(!can_be_carded)
to_chat(user, "<span class='boldwarning'>Transfer failed.</span>")
return
disconnect_shell() //If the AI is controlling a borg, force the player back to core!
if(!mind)
to_chat(user, "<span class='warning'>No intelligence patterns detected.</span>" )
return
ShutOffDoomsdayDevice()
new /obj/structure/AIcore/deactivated(loc)//Spawns a deactivated terminal at AI location.
ai_restore_power()//So the AI initially has power.
@@ -818,12 +826,12 @@ var/list/ai_list = list()
//stop AIs from leaving windows open and using then after they lose vision
//apc_override is needed here because AIs use their own APC when powerless
//get_turf_pixel() is because APCs in maint aren't actually in view of the inner camera
if(M && cameranet && !cameranet.checkTurfVis(get_turf_pixel(M)) && !apc_override)
if(M && GLOB.cameranet && !GLOB.cameranet.checkTurfVis(get_turf_pixel(M)) && !apc_override)
return
return 1
/mob/living/silicon/ai/proc/relay_speech(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans)
raw_message = lang_treat(speaker, message_langs, raw_message, spans)
/mob/living/silicon/ai/proc/relay_speech(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
raw_message = lang_treat(speaker, message_language, raw_message, spans, message_mode)
var/name_used = speaker.GetVoice()
var/rendered = "<i><span class='game say'>Relayed Speech: <span class='name'>[name_used]</span> <span class='message'>[raw_message]</span></span></i>"
show_message(rendered, 2)
@@ -907,6 +915,69 @@ var/list/ai_list = list()
to_chat(src, "Hack complete. \The [apc] is now under your exclusive control.")
apc.update_icon()
/mob/living/silicon/ai/verb/deploy_to_shell(var/mob/living/silicon/robot/target)
set category = "AI Commands"
set name = "Deploy to Shell"
if(stat || lacks_power() || control_disabled)
to_chat(src, "<span class='danger'>Wireless networking module is offline.</span>")
return
var/list/possible = list()
for(var/borgie in GLOB.available_ai_shells)
var/mob/living/silicon/robot/R = borgie
if(R.shell && !R.deployed && (R.stat != DEAD) && (!R.connected_ai ||(R.connected_ai == src)))
possible += R
if(!LAZYLEN(possible))
to_chat(src, "No usable AI shell beacons detected.")
if(!target || !(target in possible)) //If the AI is looking for a new shell, or its pre-selected shell is no longer valid
target = input(src, "Which body to control?") as null|anything in possible
if (!target || target.stat == DEAD || target.deployed || !(!target.connected_ai ||(target.connected_ai == src)))
return
else if(mind)
soullink(/datum/soullink/sharedbody, src, target)
deployed_shell = target
target.deploy_init(src)
mind.transfer_to(target)
diag_hud_set_deployed()
/datum/action/innate/deploy_shell
name = "Deploy to AI Shell"
desc = "Wirelessly control a specialized cyborg shell."
button_icon_state = "ai_shell"
/datum/action/innate/deploy_shell/Trigger()
var/mob/living/silicon/ai/AI = owner
if(!AI)
return
AI.deploy_to_shell()
/datum/action/innate/deploy_last_shell
name = "Reconnect to shell"
desc = "Reconnect to the most recently used AI shell."
button_icon_state = "ai_last_shell"
var/mob/living/silicon/robot/last_used_shell
/datum/action/innate/deploy_last_shell/Trigger()
if(!owner)
return
if(last_used_shell)
var/mob/living/silicon/ai/AI = owner
AI.deploy_to_shell(last_used_shell)
else
Remove(owner) //If the last shell is blown, destroy it.
/mob/living/silicon/ai/proc/disconnect_shell()
if(deployed_shell) //Forcibly call back AI in event of things such as damage, EMP or power loss.
to_chat(src, "<span class='danger'>Your remote connection has been reset!</span>")
deployed_shell.undeploy()
diag_hud_set_deployed()
/mob/living/silicon/ai/resist()
return
@@ -6,7 +6,7 @@
/mob/living/silicon/ai/attack_alien(mob/living/carbon/alien/humanoid/M)
if(!ticker || !ticker.mode)
if(!SSticker || !SSticker.mode)
to_chat(M, "You cannot attack people before the game has started.")
return
..()
@@ -22,6 +22,7 @@
return 0
/mob/living/silicon/ai/emp_act(severity)
disconnect_shell()
if (prob(30))
switch(pick(1,2))
if(1)
+2 -2
View File
@@ -16,7 +16,7 @@
if(eyeobj)
eyeobj.setLoc(get_turf(src))
shuttle_caller_list -= src
GLOB.shuttle_caller_list -= src
SSshuttle.autoEvac()
ShutOffDoomsdayDevice()
@@ -35,7 +35,7 @@
if(nuking)
set_security_level("red")
nuking = FALSE
for(var/obj/item/weapon/pinpointer/P in pinpointer_list)
for(var/obj/item/weapon/pinpointer/P in GLOB.pinpointer_list)
P.switch_mode_to(TRACK_NUKE_DISK) //Party's over, back to work, everyone
P.nuke_warning = FALSE
@@ -1,21 +1,23 @@
/mob/living/silicon/ai/examine(mob/user)
var/msg = "<span class='info'>*---------*\nThis is \icon[src] <EM>[src]</EM>!\n"
if (src.stat == DEAD)
if (stat == DEAD)
msg += "<span class='deadsay'>It appears to be powered-down.</span>\n"
else
msg += "<span class='warning'>"
if (src.getBruteLoss())
if (src.getBruteLoss() < 30)
if (getBruteLoss())
if (getBruteLoss() < 30)
msg += "It looks slightly dented.\n"
else
msg += "<B>It looks severely dented!</B>\n"
if (src.getFireLoss())
if (src.getFireLoss() < 30)
if (getFireLoss())
if (getFireLoss() < 30)
msg += "It looks slightly charred.\n"
else
msg += "<B>Its casing is melted and heat-warped!</B>\n"
msg += "</span>"
if (shunted == 0 && !src.client)
if(deployed_shell)
msg += "The wireless networking light is blinking.\n"
else if (!shunted && !client)
msg += "[src]Core.exe has stopped responding! NTOS is searching for a solution to the problem...\n"
msg += "*---------*</span>"
@@ -2,9 +2,9 @@
//
// The datum containing all the chunks.
var/const/CHUNK_SIZE = 16 // Only chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc..
#define CHUNK_SIZE 16 // Only chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc..
var/datum/cameranet/cameranet = new()
GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
/datum/cameranet
var/name = "Camera Net" // Name to show for VV and stat()
@@ -66,7 +66,7 @@ var/datum/cameranet/cameranet = new()
/datum/cameranet/proc/updateVisibility(atom/A, opacity_check = 1)
if(!ticker || (opacity_check && !A.opacity))
if(!SSticker || (opacity_check && !A.opacity))
return
majorChunkChange(A, 2)
@@ -151,7 +151,7 @@ var/datum/cameranet/cameranet = new()
if(!statclick)
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
stat(name, statclick.update("Cameras: [cameranet.cameras.len] | Chunks: [cameranet.chunks.len]"))
stat(name, statclick.update("Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]"))
// Debug verb for VVing the chunk that the turf is in.
/*
@@ -173,4 +173,5 @@
t.obscured.plane = LIGHTING_PLANE+1
obscured += t.obscured
#undef UPDATE_BUFFER
#undef UPDATE_BUFFER
#undef CHUNK_SIZE
@@ -21,7 +21,7 @@
return
T = get_turf(T)
loc = T
cameranet.visibility(src)
GLOB.cameranet.visibility(src)
if(ai.client)
ai.client.eye = src
update_parallax_contents()
@@ -104,6 +104,6 @@
acceleration = !acceleration
to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].")
/mob/camera/aiEye/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans)
/mob/camera/aiEye/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
if(relay_speech && speaker && ai && !radio_freq && speaker != ai && near_camera(speaker))
ai.relay_speech(message, speaker, message_langs, raw_message, radio_freq, spans)
ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans)
+4 -1
View File
@@ -65,6 +65,7 @@
health = maxHealth - getOxyLoss() - getToxLoss() - getBruteLoss() - getFireLoss()
update_stat()
diag_hud_set_health()
disconnect_shell()
/mob/living/silicon/ai/update_stat()
if(status_flags & GODMODE)
@@ -90,6 +91,7 @@
if(see_override)
see_invisible = see_override
sync_lighting_plane_alpha()
/mob/living/silicon/ai/proc/start_RestorePowerRoutine()
@@ -146,7 +148,7 @@
to_chat(src, "Receiving control information from APC.")
sleep(2)
apc_override = 1
theAPC.ui_interact(src, state = conscious_state)
theAPC.ui_interact(src, state = GLOB.conscious_state)
apc_override = 0
aiRestorePowerRoutine = POWER_RESTORATION_APC_FOUND
sleep(50)
@@ -163,6 +165,7 @@
update_sight()
/mob/living/silicon/ai/proc/ai_lose_power()
disconnect_shell()
aiRestorePowerRoutine = POWER_RESTORATION_START
blind_eyes(1)
update_sight()
+1 -1
View File
@@ -6,7 +6,7 @@
client.images += blood
if(stat != DEAD)
for(var/obj/machinery/ai_status_display/O in machines) //change status
for(var/obj/machinery/ai_status_display/O in GLOB.machines) //change status
O.mode = 1
O.emotion = "Neutral"
view_core()
+25 -20
View File
@@ -1,6 +1,6 @@
/mob/living/silicon/ai/say(message)
/mob/living/silicon/ai/say(message, language)
if(parent && istype(parent) && parent.stat != 2) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead.
parent.say(message)
parent.say(message, language)
return
..(message)
@@ -17,7 +17,7 @@
/mob/living/silicon/ai/IsVocal()
return !config.silent_ai
/mob/living/silicon/ai/radio(message, message_mode, list/spans)
/mob/living/silicon/ai/radio(message, message_mode, list/spans, language)
if(!radio_enabled || aiRestorePowerRoutine || stat) //AI cannot speak if radio is disabled (via intellicard) or depowered.
to_chat(src, "<span class='danger'>Your radio transmitter is offline!</span>")
return 0
@@ -29,17 +29,17 @@
else
return ..()
/mob/living/silicon/ai/handle_inherent_channels(message, message_mode)
/mob/living/silicon/ai/handle_inherent_channels(message, message_mode, language)
. = ..()
if(.)
return .
if(message_mode == MODE_HOLOPAD)
holopad_talk(message)
holopad_talk(message, language)
return 1
//For holopads only. Usable by AI.
/mob/living/silicon/ai/proc/holopad_talk(message)
/mob/living/silicon/ai/proc/holopad_talk(message, language)
log_say("[key_name(src)] : [message]")
message = trim(message)
@@ -49,20 +49,15 @@
var/obj/machinery/holopad/T = current
if(istype(T) && T.masters[src])//If there is a hologram and its master is the user.
send_speech(message, 7, T, "robot", get_spans())
send_speech(message, 7, T, "robot", get_spans(), language)
to_chat(src, "<i><span class='game say'>Holopad transmitted, <span class='name'>[real_name]</span> <span class='message robot'>\"[message]\"</span></span></i>")
else
to_chat(src, "No holopad connected.")
return
// Make sure that the code compiles with AI_VOX undefined
#ifdef AI_VOX
var/announcing_vox = 0 // Stores the time of the last announcement
var/const/VOX_CHANNEL = 200
var/const/VOX_DELAY = 600
#define VOX_DELAY 600
/mob/living/silicon/ai/verb/announcement_help()
set name = "Announcement Help"
@@ -79,10 +74,10 @@ var/const/VOX_DELAY = 600
<font class='bad'>WARNING:</font><BR>Misuse of the announcement system will get you job banned.<HR>"
var/index = 0
for(var/word in vox_sounds)
for(var/word in GLOB.vox_sounds)
index++
dat += "<A href='?src=\ref[src];say_word=[word]'>[capitalize(word)]</A>"
if(index != vox_sounds.len)
if(index != GLOB.vox_sounds.len)
dat += " / "
var/datum/browser/popup = new(src, "announce_help", "Announcement Help", 500, 400)
@@ -91,6 +86,7 @@ var/const/VOX_DELAY = 600
/mob/living/silicon/ai/proc/announcement()
var/static/announcing_vox = 0 // Stores the time of the last announcement
if(announcing_vox > world.time)
to_chat(src, "<span class='notice'>Please wait [round((announcing_vox - world.time) / 10)] seconds.</span>")
return
@@ -120,7 +116,7 @@ var/const/VOX_DELAY = 600
if(!word)
words -= word
continue
if(!vox_sounds[word])
if(!GLOB.vox_sounds[word])
incorrect_words += word
if(incorrect_words.len)
@@ -147,16 +143,16 @@ var/const/VOX_DELAY = 600
word = lowertext(word)
if(vox_sounds[word])
if(GLOB.vox_sounds[word])
var/sound_file = vox_sounds[word]
var/sound/voice = sound(sound_file, wait = 1, channel = VOX_CHANNEL)
var/sound_file = GLOB.vox_sounds[word]
var/sound/voice = sound(sound_file, wait = 1, channel = CHANNEL_VOX)
voice.status = SOUND_STREAM
// If there is no single listener, broadcast to everyone in the same z level
if(!only_listener)
// Play voice for all mobs in the z level
for(var/mob/M in player_list)
for(var/mob/M in GLOB.player_list)
if(M.client && !M.ear_deaf && (M.client.prefs.toggles & SOUND_ANNOUNCEMENTS))
var/turf/T = get_turf(M)
if(T.z == z_level)
@@ -167,3 +163,12 @@ var/const/VOX_DELAY = 600
return 0
#endif
/mob/living/silicon/ai/can_speak_in_language(datum/language/dt)
if(HAS_SECONDARY_FLAG(src, OMNITONGUE))
. = has_language(dt)
else if(is_servant_of_ratvar(src))
// Ratvarian AIs can only speak Ratvarian
. = ispath(dt, /datum/language/ratvar) && has_language(dt)
else
. = ..()
@@ -2,7 +2,7 @@
// Dynamically loading it has bad results with sounds overtaking each other, even with the wait variable.
#ifdef AI_VOX
var/list/vox_sounds = list("," = 'sound/vox_fem/,.ogg',
GLOBAL_LIST_INIT(vox_sounds, list("," = 'sound/vox_fem/,.ogg',
"." = 'sound/vox_fem/..ogg',
"a" = 'sound/vox_fem/a.ogg',
"abortions" = 'sound/vox_fem/abortions.ogg',
@@ -713,6 +713,5 @@ var/list/vox_sounds = list("," = 'sound/vox_fem/,.ogg',
"z" = 'sound/vox_fem/z.ogg',
"zero" = 'sound/vox_fem/zero.ogg',
"zone" = 'sound/vox_fem/zone.ogg',
"zulu" = 'sound/vox_fem/zulu.ogg',
)
"zulu" = 'sound/vox_fem/zulu.ogg'))
#endif