Merge pull request #780 from Citadel-Station-13/upstream-merge-26776

[MIRROR] Moves languages to a holder datum, gives mind its own language holder
This commit is contained in:
LetterJay
2017-05-17 05:44:08 -04:00
committed by GitHub
51 changed files with 1551 additions and 1317 deletions
+3
View File
@@ -2,3 +2,6 @@
#define TONGUELESS_SPEECH 2
#define LANGUAGE_HIDE_ICON_IF_UNDERSTOOD 4
#define LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD 8
#define LANGUAGE_KNOWN "language_known"
#define LANGUAGE_SHADOWED "language_shadowed"
+1
View File
@@ -46,6 +46,7 @@
#define INIT_ORDER_TICKER 13
#define INIT_ORDER_MAPPING 12
#define INIT_ORDER_ATOMS 11
#define INIT_ORDER_LANGUAGE 10
#define INIT_ORDER_MACHINES 9
#define INIT_ORDER_SHUTTLE 3
#define INIT_ORDER_TIMER 1
+4 -2
View File
@@ -19,5 +19,7 @@ GLOBAL_LIST_EMPTY(silicon_mobs) //all silicon mobs
GLOBAL_LIST_EMPTY(ai_list)
GLOBAL_LIST_EMPTY(pai_list)
GLOBAL_LIST_EMPTY(available_ai_shells)
GLOBAL_LIST_EMPTY(language_datums)
GLOBAL_LIST_EMPTY(simple_animals)
GLOBAL_LIST_EMPTY(simple_animals)
GLOBAL_LIST_EMPTY(language_datum_instances)
GLOBAL_LIST_EMPTY(all_languages)
+4
View File
@@ -68,6 +68,10 @@
using.screen_loc = ui_ghost_pai
static_inventory += using
using = new /obj/screen/language_menu
using.icon = ui_style
static_inventory += using
/datum/hud/ghost/show_hud(version = 0, mob/viewmob)
..()
if(!mymob.client.prefs.ghost_hud)
+3 -4
View File
@@ -91,10 +91,9 @@
screen_loc = ui_language_menu
/obj/screen/language_menu/Click()
var/mob/living/L = usr
if(!istype(L))
return
L.open_language_menu(usr)
var/mob/M = usr
var/datum/language_holder/H = M.get_language_holder()
H.open_language_menu(usr)
/obj/screen/inventory
var/slot_id // The indentifier for the slot. It has nothing to do with ID cards.
+18
View File
@@ -0,0 +1,18 @@
SUBSYSTEM_DEF(language)
name = "Language"
init_order = INIT_ORDER_LANGUAGE
flags = SS_NO_FIRE
/datum/controller/subsystem/language/Initialize(timeofday)
for(var/L in subtypesof(/datum/language))
var/datum/language/language = L
if(!initial(language.key))
continue
GLOB.all_languages += language
var/datum/language/instance = new language
GLOB.language_datum_instances[language] = instance
return ..()
+4 -3
View File
@@ -482,6 +482,7 @@
/datum/action/language_menu/Trigger()
if(!..())
return FALSE
if(isliving(owner))
var/mob/living/L = owner
L.open_language_menu(usr)
if(ismob(owner))
var/mob/M = owner
var/datum/language_holder/H = M.get_language_holder()
H.open_language_menu(usr)
+160 -159
View File
@@ -1,159 +1,160 @@
#define HOLOPAD_MAX_DIAL_TIME 200
/mob/camera/aiEye/remote/holo/setLoc()
. = ..()
var/obj/machinery/holopad/H = origin
H.move_hologram(eye_user, loc)
//this datum manages it's own references
/datum/holocall
var/mob/living/user //the one that called
var/obj/machinery/holopad/calling_holopad //the one that sent the call
var/obj/machinery/holopad/connected_holopad //the one that answered the call (may be null)
var/list/dialed_holopads //all things called, will be cleared out to just connected_holopad once answered
var/mob/camera/aiEye/remote/holo/eye //user's eye, once connected
var/obj/effect/overlay/holo_pad_hologram/hologram //user's hologram, once connected
var/call_start_time
//creates a holocall made by `caller` from `calling_pad` to `callees`
/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees)
call_start_time = world.time
user = caller
calling_pad.outgoing_call = src
calling_holopad = calling_pad
dialed_holopads = list()
for(var/I in callees)
var/obj/machinery/holopad/H = I
if(!QDELETED(H) && H.is_operational())
dialed_holopads += H
LAZYADD(H.holo_calls, src)
if(!dialed_holopads.len)
calling_pad.say("Connection failure.")
qdel(src)
return
testing("Holocall started")
//cleans up ALL references :)
/datum/holocall/Destroy()
QDEL_NULL(eye)
user.reset_perspective()
user = null
hologram.HC = null
hologram = null
calling_holopad.outgoing_call = null
for(var/I in dialed_holopads)
var/obj/machinery/holopad/H = I
LAZYREMOVE(H.holo_calls, src)
dialed_holopads.Cut()
if(calling_holopad)
calling_holopad.SetLightsAndPower()
calling_holopad = null
if(connected_holopad)
connected_holopad.SetLightsAndPower()
connected_holopad = null
testing("Holocall destroyed")
return ..()
//Gracefully disconnects a holopad `H` from a call. Pads not in the call are ignored. Notifies participants of the disconnection
/datum/holocall/proc/Disconnect(obj/machinery/holopad/H)
testing("Holocall disconnect")
if(H == connected_holopad)
calling_holopad.say("[usr] disconnected.")
else if(H == calling_holopad && connected_holopad)
connected_holopad.say("[usr] disconnected.")
ConnectionFailure(H, TRUE)
//Forcefully disconnects a holopad `H` from a call. Pads not in the call are ignored.
/datum/holocall/proc/ConnectionFailure(obj/machinery/holopad/H, graceful = FALSE)
testing("Holocall connection failure: graceful [graceful]")
if(H == connected_holopad || H == calling_holopad)
if(!graceful)
calling_holopad.say("Connection failure.")
qdel(src)
return
LAZYREMOVE(H.holo_calls, src)
dialed_holopads -= H
if(!dialed_holopads.len)
if(graceful)
calling_holopad.say("Call rejected.")
testing("No recipients, terminating")
qdel(src)
//Answers a call made to a holopad `H` which cannot be the calling holopad. Pads not in the call are ignored
/datum/holocall/proc/Answer(obj/machinery/holopad/H)
testing("Holocall answer")
if(H == calling_holopad)
CRASH("How cute, a holopad tried to answer itself.")
if(!(H in dialed_holopads))
return
if(connected_holopad)
CRASH("Multi-connection holocall")
for(var/I in dialed_holopads)
if(I == H)
continue
Disconnect(I)
for(var/I in H.holo_calls)
var/datum/holocall/HC = I
if(HC != src)
HC.Disconnect(H)
connected_holopad = H
if(!Check())
return
hologram = H.activate_holo(user)
hologram.HC = src
//eyeobj code is horrid, this is the best copypasta I could make
eye = new
eye.origin = H
eye.eye_initialized = TRUE
eye.eye_user = user
eye.name = "Camera Eye ([user.name])"
user.remote_control = eye
user.reset_perspective(eye)
eye.setLoc(H.loc)
//Checks the validity of a holocall and qdels itself if it's not. Returns TRUE if valid, FALSE otherwise
/datum/holocall/proc/Check()
for(var/I in dialed_holopads)
var/obj/machinery/holopad/H = I
if(!H.is_operational())
ConnectionFailure(H)
if(QDELETED(src))
return FALSE
. = !QDELETED(user) && !user.incapacitated() && !QDELETED(calling_holopad) && calling_holopad.is_operational() && user.loc == calling_holopad.loc
if(.)
if(connected_holopad)
. = !QDELETED(connected_holopad) && connected_holopad.is_operational()
else
. = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME)
if(!.)
calling_holopad.say("No answer recieved.")
calling_holopad.temp = ""
if(!.)
testing("Holocall Check fail")
qdel(src)
#define HOLOPAD_MAX_DIAL_TIME 200
/mob/camera/aiEye/remote/holo/setLoc()
. = ..()
var/obj/machinery/holopad/H = origin
H.move_hologram(eye_user, loc)
//this datum manages it's own references
/datum/holocall
var/mob/living/user //the one that called
var/obj/machinery/holopad/calling_holopad //the one that sent the call
var/obj/machinery/holopad/connected_holopad //the one that answered the call (may be null)
var/list/dialed_holopads //all things called, will be cleared out to just connected_holopad once answered
var/mob/camera/aiEye/remote/holo/eye //user's eye, once connected
var/obj/effect/overlay/holo_pad_hologram/hologram //user's hologram, once connected
var/call_start_time
//creates a holocall made by `caller` from `calling_pad` to `callees`
/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees)
call_start_time = world.time
user = caller
calling_pad.outgoing_call = src
calling_holopad = calling_pad
dialed_holopads = list()
for(var/I in callees)
var/obj/machinery/holopad/H = I
if(!QDELETED(H) && H.is_operational())
dialed_holopads += H
LAZYADD(H.holo_calls, src)
if(!dialed_holopads.len)
calling_pad.say("Connection failure.")
qdel(src)
return
testing("Holocall started")
//cleans up ALL references :)
/datum/holocall/Destroy()
QDEL_NULL(eye)
user.reset_perspective()
user = null
hologram.HC = null
hologram = null
calling_holopad.outgoing_call = null
for(var/I in dialed_holopads)
var/obj/machinery/holopad/H = I
LAZYREMOVE(H.holo_calls, src)
dialed_holopads.Cut()
if(calling_holopad)
calling_holopad.SetLightsAndPower()
calling_holopad = null
if(connected_holopad)
connected_holopad.SetLightsAndPower()
connected_holopad = null
testing("Holocall destroyed")
return ..()
//Gracefully disconnects a holopad `H` from a call. Pads not in the call are ignored. Notifies participants of the disconnection
/datum/holocall/proc/Disconnect(obj/machinery/holopad/H)
testing("Holocall disconnect")
if(H == connected_holopad)
calling_holopad.say("[usr] disconnected.")
else if(H == calling_holopad && connected_holopad)
connected_holopad.say("[usr] disconnected.")
ConnectionFailure(H, TRUE)
//Forcefully disconnects a holopad `H` from a call. Pads not in the call are ignored.
/datum/holocall/proc/ConnectionFailure(obj/machinery/holopad/H, graceful = FALSE)
testing("Holocall connection failure: graceful [graceful]")
if(H == connected_holopad || H == calling_holopad)
if(!graceful)
calling_holopad.say("Connection failure.")
qdel(src)
return
LAZYREMOVE(H.holo_calls, src)
dialed_holopads -= H
if(!dialed_holopads.len)
if(graceful)
calling_holopad.say("Call rejected.")
testing("No recipients, terminating")
qdel(src)
//Answers a call made to a holopad `H` which cannot be the calling holopad. Pads not in the call are ignored
/datum/holocall/proc/Answer(obj/machinery/holopad/H)
testing("Holocall answer")
if(H == calling_holopad)
CRASH("How cute, a holopad tried to answer itself.")
if(!(H in dialed_holopads))
return
if(connected_holopad)
CRASH("Multi-connection holocall")
for(var/I in dialed_holopads)
if(I == H)
continue
Disconnect(I)
for(var/I in H.holo_calls)
var/datum/holocall/HC = I
if(HC != src)
HC.Disconnect(H)
connected_holopad = H
if(!Check())
return
hologram = H.activate_holo(user)
hologram.HC = src
//eyeobj code is horrid, this is the best copypasta I could make
eye = new
eye.origin = H
eye.eye_initialized = TRUE
eye.eye_user = user
eye.name = "Camera Eye ([user.name])"
user.remote_control = eye
user.reset_perspective(eye)
eye.setLoc(H.loc)
//Checks the validity of a holocall and qdels itself if it's not. Returns TRUE if valid, FALSE otherwise
/datum/holocall/proc/Check()
for(var/I in dialed_holopads)
var/obj/machinery/holopad/H = I
if(!H.is_operational())
ConnectionFailure(H)
if(QDELETED(src))
return FALSE
. = !QDELETED(user) && !user.incapacitated() && !QDELETED(calling_holopad) && calling_holopad.is_operational() && user.loc == calling_holopad.loc
if(.)
if(connected_holopad)
. = !QDELETED(connected_holopad) && connected_holopad.is_operational()
else
. = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME)
if(!.)
calling_holopad.say("No answer recieved.")
calling_holopad.temp = ""
if(!.)
testing("Holocall Check fail")
qdel(src)
+12
View File
@@ -63,6 +63,8 @@
var/mob/living/enslaved_to //If this mind's master is another mob (i.e. adamantine golems)
var/datum/language_holder
/datum/mind/New(var/key)
src.key = key
soulOwner = src
@@ -75,11 +77,21 @@
antag_datums = null
return ..()
/datum/mind/proc/get_language_holder()
if(!language_holder)
var/datum/language_holder/L = current.get_language_holder(shadow=FALSE)
language_holder = L.copy(src)
return language_holder
/datum/mind/proc/transfer_to(mob/new_character, var/force_key_move = 0)
if(current) // remove ourself from our old body's mind variable
current.mind = null
SStgui.on_transfer(current, new_character)
if(!language_holder)
language_holder = new_character.language_holder.copy(src)
if(key)
if(new_character.key != key) //if we're transfering into a body with a key associated which is not ours
new_character.ghostize(1) //we'll need to ghostize so that key isn't mobless.
+1 -1
View File
@@ -43,7 +43,7 @@
if(SSatoms.InitAtom(src, args))
//we were deleted
return
var/list/created = SSatoms.created_atoms
if(created)
created += src
+51 -26
View File
@@ -13,9 +13,9 @@
var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported
var/throw_range = 7
var/mob/pulledby = null
var/list/languages
var/list/initial_languages = list(/datum/language/common)
var/only_speaks_language = null
var/initial_language_holder = /datum/language_holder
var/datum/language_holder/language_holder
var/datum/language_menu/language_menu = null
var/verb_say = "says"
var/verb_ask = "asks"
var/verb_exclaim = "exclaims"
@@ -44,10 +44,6 @@
return FALSE
return ..()
/atom/movable/Initialize(mapload)
. = ..()
for(var/L in initial_languages)
grant_language(L)
/atom/movable/Move(atom/newloc, direct = 0)
if(!loc || !newloc) return 0
@@ -177,6 +173,7 @@
STOP_PROCESSING(SSinbounds, src)
QDEL_NULL(proximity_monitor)
QDEL_NULL(language_holder)
. = ..()
if(loc)
@@ -576,44 +573,70 @@
/* Language procs */
/atom/movable/proc/get_language_holder(shadow=TRUE)
if(language_holder)
return language_holder
else
language_holder = new initial_language_holder(src)
return language_holder
/atom/movable/proc/grant_language(datum/language/dt)
LAZYINITLIST(languages)
languages[dt] = TRUE
var/datum/language_holder/H = get_language_holder()
H.grant_language(dt)
/atom/movable/proc/grant_all_languages(omnitongue=FALSE)
for(var/la in subtypesof(/datum/language))
grant_language(la)
if(omnitongue)
SET_SECONDARY_FLAG(src, OMNITONGUE)
var/datum/language_holder/H = get_language_holder()
H.grant_all_languages(omnitongue)
/atom/movable/proc/get_random_understood_language()
var/list/possible = list()
for(var/dt in languages)
possible += dt
. = safepick(possible)
var/datum/language_holder/H = get_language_holder()
. = H.get_random_understood_language()
/atom/movable/proc/remove_language(datum/language/dt)
LAZYREMOVE(languages, dt)
var/datum/language_holder/H = get_language_holder()
H.remove_language(dt)
/atom/movable/proc/remove_all_languages()
LAZYCLEARLIST(languages)
var/datum/language_holder/H = get_language_holder()
H.remove_all_languages()
/atom/movable/proc/has_language(datum/language/dt)
. = is_type_in_typecache(dt, languages)
var/datum/language_holder/H = get_language_holder()
. = H.has_language(dt)
// Whether an AM can speak in a language or not, independent of whether
// it KNOWS the language
/atom/movable/proc/could_speak_in_language(datum/language/dt)
. = TRUE
/atom/movable/proc/can_speak_in_language(datum/language/dt)
. = has_language(dt)
if(only_speaks_language && !HAS_SECONDARY_FLAG(src, OMNITONGUE))
. = . && ispath(only_speaks_language, dt)
var/datum/language_holder/H = get_language_holder()
if(!H.has_language(dt))
return FALSE
else if(H.omnitongue || could_speak_in_language(dt))
return TRUE
else
return FALSE
/atom/movable/proc/get_default_language()
// if no language is specified, and we want to say() something, which
// language do we use?
var/datum/language_holder/H = get_language_holder()
if(H.selected_default_language)
if(H.has_language(H.selected_default_language))
return H.selected_default_language
else
H.selected_default_language = null
var/datum/language/chosen_langtype
var/highest_priority
for(var/lt in languages)
for(var/lt in H.languages)
var/datum/language/langtype = lt
if(!can_speak_in_language(langtype))
continue
@@ -622,8 +645,10 @@
if(!highest_priority || (pri > highest_priority))
chosen_langtype = langtype
highest_priority = pri
H.selected_default_language = .
. = chosen_langtype
/* End language procs */
/atom/movable/proc/ConveyorMove(movedir)
set waitfor = FALSE
if(!anchored && has_gravity())
+10
View File
@@ -0,0 +1,10 @@
diff a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm (rejected hunks)
@@ -571,7 +571,7 @@
/* Language procs */
-/atom/movable/proc/get_language_holder()
+/atom/movable/proc/get_language_holder(shadow=TRUE)
if(language_holder)
return language_holder
else
+1 -2
View File
@@ -14,8 +14,7 @@
verb_ask = "requests"
verb_exclaim = "proclaims"
verb_yell = "harangues"
initial_languages = list(/datum/language/common, /datum/language/ratvar)
only_speaks_language = /datum/language/ratvar
initial_language_holder = /datum/language_holder/clockmob
bubble_icon = "clock"
light_color = "#E42742"
death_sound = 'sound/magic/clockwork/anima_fragment_death.ogg'
@@ -62,7 +62,7 @@
icon = 'icons/mob/swarmer.dmi'
desc = "A robot of unknown design, they seek only to consume materials and replicate themselves indefinitely."
speak_emote = list("tones")
initial_languages = list(/datum/language/swarmer)
initial_language_holder = /datum/language_holder/swarmer
bubble_icon = "swarmer"
health = 40
maxHealth = 40
+3 -2
View File
@@ -61,7 +61,6 @@ GLOBAL_LIST_EMPTY(holopads)
/obj/machinery/holopad/Destroy()
if(outgoing_call)
LAZYADD(holo_calls, outgoing_call)
outgoing_call = null
for(var/I in holo_calls)
var/datum/holocall/HC = I
@@ -295,7 +294,9 @@ GLOBAL_LIST_EMPTY(holopads)
Hologram.add_atom_colour("#77abff", FIXED_COLOUR_PRIORITY)
Hologram.Impersonation = user
Hologram.languages = user.languages
Hologram.language_holder = user.get_language_holder()
Hologram.mouse_opacity = 0//So you can't click on it.
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
Hologram.anchored = 1//So space wind cannot drag it.
+17
View File
@@ -0,0 +1,17 @@
diff a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm (rejected hunks)
@@ -294,7 +294,7 @@ GLOBAL_LIST_EMPTY(holopads)
Hologram.add_atom_colour("#77abff", FIXED_COLOUR_PRIORITY)
Hologram.Impersonation = user
- Hologram.languages = user.languages
+ Hologram.language_holder = user.get_language_holder()
Hologram.mouse_opacity = 0//So you can't click on it.
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
Hologram.anchored = 1//So space wind cannot drag it.
@@ -392,4 +392,4 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
req_components = list(/obj/item/weapon/stock_parts/capacitor = 1)
#undef HOLOPAD_PASSIVE_POWER_USAGE
-#undef HOLOGRAM_POWER_USAGE
\ No newline at end of file
+#undef HOLOGRAM_POWER_USAGE
+1 -1
View File
@@ -23,7 +23,7 @@
var/persistence_replacement //have something WAY too amazing to live to the next round? Set a new path here. Overuse of this var will make me upset.
var/unique_rename = FALSE // can you customize the description/name of the thing?
var/dangerous_possession = FALSE //Admin possession yes/no
/obj/vv_edit_var(vname, vval)
+2 -2
View File
@@ -61,7 +61,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
var/messagepart = " <span class='message'>[lang_treat(speaker, message_language, raw_message, spans)]</span></span>"
var/languageicon = ""
var/datum/language/D = get_language_instance(message_language)
var/datum/language/D = GLOB.language_datum_instances[message_language]
if(D.display_icon(src))
languageicon = "[D.get_icon()] "
@@ -103,7 +103,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
return speaker.say_quote(raw_message, spans, message_mode)
else if(language)
var/atom/movable/AM = speaker.GetSource()
var/datum/language/D = get_language_instance(language)
var/datum/language/D = GLOB.language_datum_instances[language]
raw_message = D.scramble(raw_message)
if(AM)
return AM.say_quote(raw_message, spans, message_mode)
+5 -5
View File
@@ -1799,12 +1799,12 @@
if(!check_rights(R_ADMIN))
return
var/mob/living/L = locate(href_list["languagemenu"]) in GLOB.mob_list
if(!isliving(L))
to_chat(usr, "This can only be used on instances of type /mob/living.")
var/mob/M = locate(href_list["languagemenu"]) in GLOB.mob_list
if(!ismob(M))
to_chat(usr, "This can only be used on instances of type /mob.")
return
L.open_language_menu(usr)
var/datum/language_holder/H = M.get_language_holder()
H.open_language_menu(usr)
else if(href_list["traitor"])
if(!check_rights(R_ADMIN))
+2 -11
View File
@@ -12,7 +12,8 @@
var/exclaim_verb = "exclaims" // Used when sentence ends in a !
var/whisper_verb = "whispers" // Optional. When not specified speech_verb + quietly/softly is used instead.
var/list/signlang_verb = list("signs", "gestures") // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags
var/key = "x" // Character used to speak in language
var/key // Character used to speak in language
// If key is null, then the language isn't real or learnable.
var/flags // Various language flags.
var/list/syllables // Used when scrambling text for a non-speaker.
var/list/sentence_chance = 5 // Likelihood of making a new sentence after each syllable.
@@ -108,13 +109,3 @@
return speech_verb
#undef SCRAMBLE_CACHE_LEN
/proc/get_language_instance(langtype)
if(!ispath(langtype, /datum/language))
return
if(!GLOB.language_datums[langtype])
var/datum/language/langdatum = new langtype
GLOB.language_datums[langtype] = langdatum
. = GLOB.language_datums[langtype]
+110
View File
@@ -0,0 +1,110 @@
/datum/language_holder
var/list/languages = list(/datum/language/common)
var/list/shadow_languages = list()
var/only_speaks_language = null
var/selected_default_language = null
var/datum/language_menu/language_menu
var/omnitongue = FALSE
var/owner
/datum/language_holder/New(owner)
src.owner = owner
languages = typecacheof(languages)
shadow_languages = typecacheof(shadow_languages)
/datum/language_holder/Destroy()
owner = null
QDEL_NULL(language_menu)
/datum/language_holder/proc/copy(newowner)
var/datum/language_holder/copy = new(newowner)
copy.languages = src.languages.Copy()
// shadow languages are not copied.
copy.only_speaks_language = src.only_speaks_language
copy.selected_default_language = src.selected_default_language
// language menu is not copied, that's tied to the holder.
copy.omnitongue = src.omnitongue
return copy
/datum/language_holder/proc/grant_language(datum/language/dt)
languages[dt] = TRUE
/datum/language_holder/proc/grant_all_languages(omnitongue=FALSE)
for(var/la in GLOB.all_languages)
grant_language(la)
if(omnitongue)
src.omnitongue = TRUE
/datum/language_holder/proc/get_random_understood_language()
var/list/possible = list()
for(var/dt in languages)
possible += dt
. = safepick(possible)
/datum/language_holder/proc/remove_language(datum/language/dt)
languages -= dt
/datum/language_holder/proc/remove_all_languages()
languages.Cut()
/datum/language_holder/proc/has_language(datum/language/dt)
if(is_type_in_typecache(dt, languages))
return LANGUAGE_KNOWN
else
var/atom/movable/AM = get_atom()
var/datum/language_holder/L = AM.get_language_holder(shadow=FALSE)
if(L != src)
if(is_type_in_typecache(dt, L.shadow_languages))
return LANGUAGE_SHADOWED
return FALSE
/datum/language_holder/proc/open_language_menu(mob/user)
if(!language_menu)
language_menu = new(src)
language_menu.ui_interact(user)
/datum/language_holder/proc/get_atom()
if(istype(owner, /atom/movable))
. = owner
else if(istype(owner, /datum/mind))
var/datum/mind/M = owner
if(M.current)
. = M.current
/datum/language_holder/alien
languages = list(/datum/language/xenocommon)
/datum/language_holder/monkey
languages = list(/datum/language/monkey)
/datum/language_holder/swarmer
languages = list(/datum/language/swarmer)
/datum/language_holder/clockmob
languages = list(/datum/language/common, /datum/language/ratvar)
only_speaks_language = /datum/language/ratvar
/datum/language_holder/drone
languages = list(/datum/language/common, /datum/language/drone, /datum/language/machine)
only_speaks_language = /datum/language/drone
/datum/language_holder/drone/syndicate
only_speaks_language = null
/datum/language_holder/slime
languages = list(/datum/language/common, /datum/language/slime)
/datum/language_holder/lightbringer
// TODO change to a lightbringer specific sign language
languages = list(/datum/language/slime)
/datum/language_holder/synthetic
languages = list(/datum/language/common)
shadow_languages = list(/datum/language/machine, /datum/language/draconic)
/datum/language_holder/universal/New()
..()
grant_all_languages(omnitongue=TRUE)
+40 -26
View File
@@ -1,11 +1,11 @@
/datum/language_menu
var/mob/living/owner
var/datum/language_holder/language_holder
/datum/language_menu/New(new_owner)
owner = new_owner
/datum/language_menu/New(language_holder)
src.language_holder = language_holder
/datum/language_menu/Destroy()
owner = null
language_holder = null
. = ..()
/datum/language_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.language_menu_state)
@@ -17,28 +17,38 @@
/datum/language_menu/ui_data(mob/user)
var/list/data = list()
var/datum/language/mob_default_language = owner.get_default_language()
var/atom/movable/AM = language_holder.get_atom()
if(isliving(AM))
data["is_living"] = TRUE
else
data["is_living"] = FALSE
data["languages"] = list()
for(var/ld in owner.languages)
for(var/ld in GLOB.all_languages)
var/result = language_holder.has_language(ld)
if(!result)
continue
var/shadow = result == LANGUAGE_SHADOWED
var/datum/language/LD = ld
var/list/L = list()
L["name"] = initial(LD.name)
L["desc"] = initial(LD.desc)
L["key"] = initial(LD.key)
L["is_default"] = (LD == mob_default_language)
L["can_speak"] = owner.can_speak_in_language(LD)
L["is_default"] = (LD == language_holder.selected_default_language)
L["shadow"] = shadow
if(AM)
L["can_speak"] = AM.can_speak_in_language(LD)
data["languages"] += list(L)
if(check_rights_for(user.client, R_ADMIN))
if(check_rights_for(user.client, R_ADMIN) || isobserver(AM))
data["admin_mode"] = TRUE
data["omnitongue"] = HAS_SECONDARY_FLAG(owner, OMNITONGUE)
data["omnitongue"] = language_holder.omnitongue
data["unknown_languages"] = list()
for(var/ld in subtypesof(/datum/language))
if(owner.has_language(ld))
for(var/ld in GLOB.all_languages)
if(language_holder.has_language(ld))
continue
var/datum/language/LD = ld
var/list/L = list()
@@ -54,10 +64,11 @@
if(..())
return
var/mob/user = usr
var/atom/movable/AM = language_holder.get_atom()
var/language_name = params["language_name"]
var/datum/language/language_datum
for(var/ld in subtypesof(/datum/language))
for(var/ld in GLOB.all_languages)
var/datum/language/LD = ld
if(language_name == initial(LD.name))
language_datum = LD
@@ -66,23 +77,26 @@
switch(action)
if("select_default")
if(language_datum)
owner.selected_default_language = language_datum
language_holder.selected_default_language = language_datum
. = TRUE
if("grant_language")
if(is_admin && language_datum)
owner.grant_language(language_datum)
message_admins("[key_name_admin(user)] granted the [language_name] language to [key_name_admin(owner)].")
log_admin("[key_name(user)] granted the language [language_name] to [key_name(owner)].")
if((is_admin || isobserver(AM)) && language_datum)
language_holder.grant_language(language_datum)
if(is_admin)
message_admins("[key_name_admin(user)] granted the [language_name] language to [key_name_admin(AM)].")
log_admin("[key_name(user)] granted the language [language_name] to [key_name(AM)].")
. = TRUE
if("remove_language")
if(is_admin && language_datum)
owner.remove_language(language_datum)
message_admins("[key_name_admin(user)] removed the [language_name] language to [key_name_admin(owner)].")
log_admin("[key_name(user)] removed the language [language_name] to [key_name(owner)].")
if((is_admin || isobserver(AM)) && language_datum)
language_holder.remove_language(language_datum)
if(is_admin)
message_admins("[key_name_admin(user)] removed the [language_name] language to [key_name_admin(AM)].")
log_admin("[key_name(user)] removed the language [language_name] to [key_name(AM)].")
. = TRUE
if("toggle_omnitongue")
if(is_admin)
TOGGLE_SECONDARY_FLAG(owner, OMNITONGUE)
message_admins("[key_name_admin(user)] [HAS_SECONDARY_FLAG(owner, OMNITONGUE) ? "enabled" : "disabled"] the ability to speak all languages (that they know) of [key_name_admin(owner)].")
log_admin("[key_name(user)] [HAS_SECONDARY_FLAG(owner, OMNITONGUE) ? "enabled" : "disabled"] the ability to speak all languages (that_they know) of [key_name(owner)].")
if(is_admin || isobserver(AM))
language_holder.omnitongue = !language_holder.omnitongue
if(is_admin)
message_admins("[key_name_admin(user)] [language_holder.omnitongue ? "enabled" : "disabled"] the ability to speak all languages (that they know) of [key_name_admin(AM)].")
log_admin("[key_name(user)] [language_holder.omnitongue ? "enabled" : "disabled"] the ability to speak all languages (that_they know) of [key_name(AM)].")
. = TRUE
+18 -19
View File
@@ -117,10 +117,13 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
animate(src, pixel_y = 2, time = 10, loop = -1)
grant_all_languages()
GLOB.dead_mob_list += src
..()
grant_all_languages()
/mob/dead/observer/narsie_act()
var/old_color = color
color = "#960000"
@@ -270,25 +273,24 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
/mob/dead/observer/Move(NewLoc, direct)
if(updatedir)
setDir(direct )//only update dir if we actually need it, so overlays won't spin on base sprites that don't have directions of their own
setDir(direct)//only update dir if we actually need it, so overlays won't spin on base sprites that don't have directions of their own
var/oldloc = loc
if(NewLoc)
loc = NewLoc
for(var/obj/effect/step_trigger/S in NewLoc)
S.Crossed(src)
update_parallax_contents()
return
loc = get_turf(src) //Get out of closets and such as a ghost
if((direct & NORTH) && y < world.maxy)
y++
else if((direct & SOUTH) && y > 1)
y--
if((direct & EAST) && x < world.maxx)
x++
else if((direct & WEST) && x > 1)
x--
else
loc = get_turf(src) //Get out of closets and such as a ghost
if((direct & NORTH) && y < world.maxy)
y++
else if((direct & SOUTH) && y > 1)
y--
if((direct & EAST) && x < world.maxx)
x++
else if((direct & WEST) && x > 1)
x--
for(var/obj/effect/step_trigger/S in locate(x, y, z)) //<-- this is dumb
S.Crossed(src)
Moved(oldloc, direct)
/mob/dead/observer/is_active()
return 0
@@ -297,9 +299,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
..()
if(statpanel("Status"))
if(SSticker.HasRoundStarted())
for(var/datum/gang/G in SSticker.mode.gangs)
if(G.is_dominating)
stat(null, "[G.name] Gang Takeover: [max(G.domination_time_remaining(), 0)]")
if(istype(SSticker.mode, /datum/game_mode/blob))
var/datum/game_mode/blob/B = SSticker.mode
if(B.message_sent)
@@ -0,0 +1,15 @@
diff a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm (rejected hunks)
@@ -117,10 +117,12 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
animate(src, pixel_y = 2, time = 10, loop = -1)
- grant_all_languages()
GLOB.dead_mob_list += src
+
..()
+ grant_all_languages()
+
/mob/dead/observer/narsie_act()
var/old_color = color
color = "#960000"
+4 -5
View File
@@ -27,10 +27,9 @@
message = capitalize(message)
return message
/mob/living/brain/can_speak_in_language(datum/language/dt)
if(HAS_SECONDARY_FLAG(src, OMNITONGUE))
. = has_language(dt)
else if(istype(container, /obj/item/device/mmi/posibrain/soul_vessel))
. = has_language(dt) && ispath(dt, /datum/language/ratvar)
/mob/living/brain/could_speak_in_language(datum/language/dt)
if(istype(container, /obj/item/device/mmi/posibrain/soul_vessel))
// soul vessels can only speak ratvarian.
. = ispath(dt, /datum/language/ratvar)
else
. = ..()
@@ -14,7 +14,7 @@
sight = SEE_MOBS
see_in_dark = 4
verb_say = "hisses"
initial_languages = list(/datum/language/xenocommon)
initial_language_holder = /datum/language_holder/alien
bubble_icon = "alien"
type_of_meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/xeno
var/nightvision = 1
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,4 @@
/mob/living/carbon/human
initial_languages = list(/datum/language/common)
hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD,ANTAG_HUD)
possible_a_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB, INTENT_HARM)
pressure_resistance = 25
@@ -49,4 +48,4 @@
var/datum/personal_crafting/handcrafting
can_buckle = TRUE
buckle_lying = FALSE
can_ride_typecache = list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/simple_animal/parrot)
can_ride_typecache = list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/simple_animal/parrot)
@@ -2,7 +2,7 @@
name = "monkey"
voice_name = "monkey"
verb_say = "chimpers"
initial_languages = list(/datum/language/monkey)
initial_language_holder = /datum/language_holder/monkey
icon = 'icons/mob/monkey.dmi'
icon_state = ""
gender = NEUTER
+5 -10
View File
@@ -29,14 +29,9 @@
if(I)
. |= I.get_held_item_speechspans(src)
/mob/living/carbon/can_speak_in_language(datum/language/dt)
if(HAS_SECONDARY_FLAG(src, OMNITONGUE))
. = has_language(dt)
else if(has_language(dt))
var/obj/item/organ/tongue/T = getorganslot("tongue")
if(T)
. = T.can_speak_in_language(dt)
else
. = initial(dt.flags) & TONGUELESS_SPEECH
/mob/living/carbon/could_speak_in_language(datum/language/dt)
var/obj/item/organ/tongue/T = getorganslot("tongue")
if(T)
. = T.could_speak_in_language(dt)
else
. = FALSE
. = initial(dt.flags) & TONGUELESS_SPEECH
-4
View File
@@ -17,8 +17,6 @@
medhud.add_to_hud(src)
faction += "\ref[src]"
language_menu = new(src)
/mob/living/prepare_huds()
..()
@@ -50,8 +48,6 @@
staticOverlays.len = 0
remove_from_all_data_huds()
QDEL_NULL(language_menu)
return ..()
/mob/living/ghostize(can_reenter_corpse = 1)
@@ -74,4 +74,3 @@
var/datum/riding/riding_datum
var/datum/language/selected_default_language
var/datum/language_menu/language_menu
+11 -22
View File
@@ -167,11 +167,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
spans += get_spans()
if(language)
var/datum/language/L = GLOB.language_datums[language]
if(!istype(L))
L = new language
GLOB.language_datums[language] = L
var/datum/language/L = GLOB.language_datum_instances[language]
spans |= L.spans
//Log what we've said with an associated timestamp, using the list's len for safety/to prevent overwriting messages
@@ -308,13 +304,9 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
return GLOB.department_radio_keys[key_symbol]
/mob/living/proc/get_message_language(message)
var/static/list/langlist
if(!langlist)
langlist = subtypesof(/datum/language)
if(copytext(message, 1, 2) == ",")
var/key = copytext(message, 2, 3)
for(var/ld in langlist)
for(var/ld in GLOB.all_languages)
var/datum/language/LD = ld
if(initial(LD.key) == key)
return LD
@@ -435,17 +427,14 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
else
. = ..()
/mob/living/get_default_language()
if(selected_default_language)
if(has_language(selected_default_language))
return selected_default_language
else
selected_default_language = null
. = ..()
/mob/living/proc/open_language_menu(mob/user)
language_menu.ui_interact(user)
/mob/living/whisper(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null)
say("#[message]", bubble_type, spans, sanitize, language)
/mob/living/get_language_holder(shadow=TRUE)
if(mind && shadow)
// Mind language holders shadow mob holders.
. = mind.get_language_holder()
if(.)
return .
. = ..()
+3 -5
View File
@@ -164,11 +164,9 @@
#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))
/mob/living/silicon/ai/could_speak_in_language(datum/language/dt)
if(is_servant_of_ratvar(src))
// Ratvarian AIs can only speak Ratvarian
. = ispath(dt, /datum/language/ratvar) && has_language(dt)
. = ispath(dt, /datum/language/ratvar)
else
. = ..()
+1 -1
View File
@@ -6,7 +6,7 @@
verb_ask = "queries"
verb_exclaim = "declares"
verb_yell = "alarms"
initial_languages = list(/datum/language/common, /datum/language/machine)
initial_language_holder = /datum/language_holder/synthetic
see_in_dark = 8
bubble_icon = "machine"
weather_immunities = list("ash")
@@ -20,7 +20,7 @@
verb_ask = "queries"
verb_exclaim = "declares"
verb_yell = "alarms"
initial_languages = list(/datum/language/common, /datum/language/machine)
initial_language_holder = /datum/language_holder/synthetic
bubble_icon = "machine"
faction = list("neutral", "silicon" , "turret")
@@ -37,8 +37,7 @@
voice_name = "synthesized chirp"
speak_emote = list("chirps")
bubble_icon = "machine"
initial_languages = list(/datum/language/common, /datum/language/machine, /datum/language/drone)
only_speaks_language = /datum/language/drone
initial_language_holder = /datum/language_holder/drone
mob_size = MOB_SIZE_SMALL
has_unlimited_silicon_privilege = 1
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
@@ -17,6 +17,7 @@
picked = TRUE //the appearence of syndrones is static, you don't get to change it.
health = 30
maxHealth = 120 //If you murder other drones and cannibalize them you can get much stronger
initial_language_holder = /datum/language_holder/drone/syndicate
faction = list("syndicate")
speak_emote = list("hisses")
bubble_icon = "syndibot"
@@ -110,8 +111,7 @@
verb_exclaim = "proclaims"
verb_yell = "harangues"
bubble_icon = "clock"
initial_languages = list(/datum/language/common, /datum/language/ratvar)
only_speaks_language = /datum/language/ratvar
initial_language_holder = /datum/language_holder/clockmob
light_color = "#E42742"
heavy_emp_damage = 0
laws = "0. Purge all untruths and honor Ratvar."
@@ -647,8 +647,7 @@ Difficulty: Very Hard
verb_ask = "floats inquisitively"
verb_exclaim = "zaps"
verb_yell = "bangs"
initial_languages = list(/datum/language/common, /datum/language/slime)
only_speaks_language = /datum/language/slime
initial_language_holder = /datum/language_holder/lightbringer
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
light_range = 4
faction = list("neutral")
@@ -7,9 +7,6 @@
status_flags = CANPUSH
// goats bray, cows go moo, and the fox says Geckers
initial_languages = list(/datum/language/common)
var/icon_living = ""
var/icon_dead = "" //icon when the animal is dead. Don't use animated icons for this.
var/icon_gib = null //We only try to show a gibbing animation if this exists.
@@ -18,7 +18,7 @@
emote_see = list("jiggles", "bounces in place")
speak_emote = list("blorbles")
bubble_icon = "slime"
initial_languages = list(/datum/language/common, /datum/language/slime)
initial_language_holder = /datum/language_holder/slime
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+7 -1
View File
@@ -754,7 +754,6 @@
client.move_delay += movement_delay()
return 1
/mob/proc/IsAdvancedToolUser()//This might need a rename but it should replace the can this mob use things check
return 0
@@ -1017,3 +1016,10 @@
if("logging")
return debug_variable(var_name, logging, 0, src, FALSE)
. = ..()
/mob/verb/open_language_menu()
set name = "Open Language Menu"
set category = "IC"
var/datum/language_holder/H = get_language_holder()
H.open_language_menu(usr)
+2 -4
View File
@@ -60,7 +60,6 @@
obj_integrity = 1000
max_integrity = 1000
verb_say = "chants"
initial_languages = list(/datum/language/common)
var/obj/machinery/power/emitter/energycannon/magical/our_statue
var/list/mob/living/sleepers = list()
var/never_spoken = TRUE
@@ -148,11 +147,11 @@
3. Don't get messed up in their affairs."
status_flags = GODMODE // Please don't punch the barkeeper
unique_name = FALSE // disables the (123) number suffix
initial_language_holder = /datum/language_holder/universal
/mob/living/simple_animal/drone/snowflake/bardrone/Initialize()
. = ..()
access_card.access |= GLOB.access_cent_bar
grant_all_languages(omnitongue=TRUE)
/mob/living/simple_animal/hostile/alien/maid/barmaid
gold_core_spawnable = 0
@@ -163,6 +162,7 @@
unique_name = FALSE
AIStatus = AI_OFF
stop_automated_movement = TRUE
initial_language_holder = /datum/language_holder/universal
/mob/living/simple_animal/hostile/alien/maid/barmaid/Initialize()
. = ..()
@@ -172,8 +172,6 @@
access_card.access |= GLOB.access_cent_bar
access_card.flags |= NODROP
grant_all_languages(omnitongue=TRUE)
/mob/living/simple_animal/hostile/alien/maid/barmaid/Destroy()
qdel(access_card)
. = ..()
+1 -1
View File
@@ -34,7 +34,7 @@
if(say_mod && M.dna && M.dna.species)
M.dna.species.say_mod = initial(M.dna.species.say_mod)
/obj/item/organ/tongue/can_speak_in_language(datum/language/dt)
/obj/item/organ/tongue/could_speak_in_language(datum/language/dt)
. = is_type_in_typecache(dt, languages_possible)
/obj/item/organ/tongue/lizard
+1 -1
View File
@@ -10,5 +10,5 @@ GLOBAL_DATUM_INIT(language_menu_state, /datum/ui_state/language_menu, new)
. = UI_INTERACTIVE
else if(istype(src_object, /datum/language_menu))
var/datum/language_menu/LM = src_object
if(LM.owner == user)
if(LM.language_holder.get_atom() == user)
. = UI_INTERACTIVE
+2
View File
@@ -202,6 +202,7 @@
#include "code\controllers\subsystem\inbounds.dm"
#include "code\controllers\subsystem\ipintel.dm"
#include "code\controllers\subsystem\job.dm"
#include "code\controllers\subsystem\language.dm"
#include "code\controllers\subsystem\lighting.dm"
#include "code\controllers\subsystem\machines.dm"
#include "code\controllers\subsystem\mapping.dm"
@@ -1426,6 +1427,7 @@
#include "code\modules\language\draconic.dm"
#include "code\modules\language\drone.dm"
#include "code\modules\language\language.dm"
#include "code\modules\language\language_holder.dm"
#include "code\modules\language\language_menu.dm"
#include "code\modules\language\machine.dm"
#include "code\modules\language\monkey.dm"
+9
View File
@@ -0,0 +1,9 @@
diff a/tgstation.dme b/tgstation.dme (rejected hunks)
@@ -1373,6 +1373,7 @@
#include "code\modules\language\draconic.dm"
#include "code\modules\language\drone.dm"
#include "code\modules\language\language.dm"
+#include "code\modules\language\language_holder.dm"
#include "code\modules\language\language_menu.dm"
#include "code\modules\language\machine.dm"
#include "code\modules\language\monkey.dm"
File diff suppressed because one or more lines are too long
+18 -16
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+22 -15
View File
@@ -3,29 +3,36 @@
<ui-section label={{name}}>
<span>{{desc}}</span>
<span>Key: ,{{key}}</span>
{{#if shadow}}
<span>(gained from mob)</span>
{{/if}}
<span>{{can_speak ? "Can Speak" : "Cannot Speak"}}</span>
<ui-button
action='select_default'
params='{"language_name":"{{name}}"}'
style='{{is_default ? "selected" : can_speak ? null : "disabled"}}'
>{{is_default ? "Default Language" : "Select as Default"}}
</ui-button>
{{#if data.admin_mode}}
{{#if data.is_living}}
<ui-button
action='remove_language'
action='select_default'
params='{"language_name":"{{name}}"}'
>Remove
</ui-button>
style='{{is_default ? "selected" : can_speak ? null : "disabled"}}'
>{{is_default ? "Default Language" : "Select as Default"}}
</ui-button>
{{/if}}
{{#if data.admin_mode}}
{{#if shadow}}
<ui-button action='grant_language' params='{"language_name":"{{name}}"}'>Grant</ui-button>
{{else}}
<ui-button action='remove_language' params='{"language_name":"{{name}}"}'>Remove</ui-button>
{{/if}}
{{/if}}
</ui-section>
{{/each}}
</ui-display>
{{#if data.admin_mode}}
<ui-button
action='toggle_omnitongue'
style='{{data.omnitongue ? "selected" : null}}'
>Omnitongue {{data.omnitongue ? "Enabled" : "Disabled"}}
</ui-button>
{{#if data.is_living}}
<ui-button
action='toggle_omnitongue'
style='{{data.omnitongue ? "selected" : null}}'
>Omnitongue {{data.omnitongue ? "Enabled" : "Disabled"}}
</ui-button>
{{/if}}
<ui-display title="Unknown Languages">
{{#each data.unknown_languages}}
<ui-section label={{name}}>