@@ -307,6 +419,9 @@ datumrefresh=[refid];[HrefToken()]'>Refresh
"}
+
+ // Resets vv_spectre, and shows it to user
+ vv_spectre.reset()
src << browse(html, "window=variables[refid];size=475x650")
/client/proc/vv_update_display(datum/thing, span, content)
@@ -314,3 +429,9 @@ datumrefresh=[refid];[HrefToken()]'>Refresh
#undef ICON_STATE_CHECKED
#undef ICON_STATE_NULL
+
+#undef STYLE_DATUM
+#undef STYLE_APPEARANCE
+#undef STYLE_READ_ONLY_LIST
+#undef STYLE_LIST
+#undef STYLE_SPECIAL_LIST
diff --git a/code/modules/admin/view_variables/vv_ghost.dm b/code/modules/admin/view_variables/vv_ghost.dm
new file mode 100644
index 0000000000000..580b3cb07756b
--- /dev/null
+++ b/code/modules/admin/view_variables/vv_ghost.dm
@@ -0,0 +1,104 @@
+GLOBAL_PROTECT(vv_ghost)
+GLOBAL_DATUM_INIT(vv_ghost, /datum/vv_ghost, new) // Fake datum for vv debug_variables() proc. Am I real?
+
+/*
+ < What the hell is this vv_ghost? >
+ Our view-variables client proc doesn't like to investigate list() instances.
+ This means `debug_variables(list_reference)` won't work, because it only wants /datum - but /list is not /datum
+ vv_ghost exists to trick the proc, by storing values to bypass /datum restriction of the proc.
+ but also, it exists to deliever some special list that isn't possible to get through locate().
+ < Can you just do `locate([0x0_list_ref_id])`? >
+ Only an ordinary /list is possible to be located through locate()
+ First, vv takes values from 'href' texts.
+ Of course, we can get ref_id of a special list,
+ BUT `locate(ref_id_of_special_list)` returns null. (only ordinary /list works this)
+ This is why we need to store 'dmlist_origin_ref', and 'dmlist_varname'
+ We locate(dmlist_origin_ref), then access their special list from their var list.
+ => dmlist_holder.vars[dmlist_varname]
+ < Summary >
+ Two usages exist:
+ 1. Store a list_ref into this datum, to deliver the list into the vv debugging system.
+ 2. Store a datum's ref_id with target varname, to deliver the special list into the vv debugging system.
+*/
+
+/datum/vv_ghost
+ // --- variables for vv special list ---
+ /// Ref ID of a thing.
+ var/dmlist_origin_ref
+ /// which var of the reference you're s eeing
+ var/dmlist_varname
+ /// instance holder for special list
+ var/datum/dmlist_holder
+
+ // --- variable for ordinary lists ---
+ /// instance holder for normal list
+ var/list_holder
+
+ // --- variable for internal use only ---
+ /// a failsafe variable
+ var/ready_to_del
+
+/datum/vv_ghost/New()
+ var/static/creation_count = 2 // to prevent something bullshit
+
+ if(creation_count)
+ creation_count--
+ ..()
+ return
+
+ else
+ stack_trace("vv_ghost is not meant to be created more than 2 in the current logic. One for GLOB, one for vv internal")
+ ready_to_del = TRUE
+ qdel(src)
+
+/datum/vv_ghost/Destroy(force = FALSE)
+ if(ready_to_del || force)
+ reset()
+ return ..()
+
+ stack_trace("Something breaks view-variables debugging tool... Check something.")
+ return QDEL_HINT_LETMELIVE
+
+/datum/vv_ghost/proc/mark_special(origin_ref, varname)
+ if(dmlist_origin_ref)
+ CRASH("vv_ghost has dmlist_origin_ref already: [dmlist_origin_ref]. It can be async issue.")
+ if(dmlist_varname)
+ CRASH("vv_ghost has dmlist_varname already: [dmlist_varname]. It can be async issue.")
+ dmlist_origin_ref = origin_ref
+ dmlist_varname = varname
+
+/datum/vv_ghost/proc/mark_list(actual_list)
+ if(list_holder)
+ CRASH("vv_ghost has list_ref already: [list_holder]. It can be async issue.")
+ list_holder = actual_list
+
+/// a proc that delivers values to vv_spectre (internal static one).
+/// vv_spectre exists to prevent async error, just in case
+/datum/vv_ghost/proc/deliver_special()
+ if(GLOB.vv_ghost == src)
+ CRASH("This proc isn't meant be called from GLOB one.")
+
+ dmlist_origin_ref = GLOB.vv_ghost.dmlist_origin_ref // = [0x123456]
+ dmlist_varname = GLOB.vv_ghost.dmlist_varname // = "vis_contents"
+ GLOB.vv_ghost.dmlist_origin_ref = null
+ GLOB.vv_ghost.dmlist_varname = null
+
+ var/datum/located = locate(dmlist_origin_ref) // = Clown [0x123456]
+ dmlist_holder = located.vars[dmlist_varname] // = Clown.vis_contents
+ return dmlist_holder
+
+/// a proc that delivers values to vv_spectre (internal static one).
+/// vv_spectre exists to prevent async error, just in case
+/datum/vv_ghost/proc/deliver_list()
+ if(GLOB.vv_ghost == src)
+ CRASH("This proc isn't meant be called from GLOB one.")
+
+ var/return_target = GLOB.vv_ghost.list_holder
+ GLOB.vv_ghost.list_holder = null
+ return return_target
+
+/datum/vv_ghost/proc/reset()
+ dmlist_origin_ref = null
+ dmlist_varname = null
+ dmlist_holder = null
+ list_holder = null
diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm
index 403ea59a3a549..c75541bd0e1b2 100644
--- a/code/modules/antagonists/abductor/equipment/gland.dm
+++ b/code/modules/antagonists/abductor/equipment/gland.dm
@@ -135,12 +135,12 @@
/obj/item/organ/heart/gland/slime/Insert(mob/living/carbon/M, special = 0, pref_load = FALSE)
..()
owner.faction |= "slime"
- owner.grant_language(/datum/language/slime, TRUE, TRUE, LANGUAGE_GLAND)
+ owner.grant_language(/datum/language/slime, source = LANGUAGE_GLAND)
/obj/item/organ/heart/gland/slime/Remove(mob/living/carbon/M, special = 0, pref_load = FALSE)
..()
owner.faction -= "slime"
- owner.remove_language(/datum/language/slime, TRUE, TRUE, LANGUAGE_GLAND)
+ owner.remove_language(/datum/language/slime, source = LANGUAGE_GLAND)
/obj/item/organ/heart/gland/slime/activate()
to_chat(owner, "You feel nauseated!")
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index f37fc8cd758d8..5b042e87c0201 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -96,7 +96,8 @@
if(give_objectives)
forge_objectives()
handle_clown_mutation(owner.current, "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself.")
- owner.current.grant_all_languages(FALSE, FALSE, TRUE) //Grants omnitongue. We are able to transform our body after all.
+ owner.current.get_language_holder().omnitongue = TRUE
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ling_aler.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)
. = ..()
/datum/antagonist/changeling/on_removal()
diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm
index c11aa30ed52e1..d379047ad2497 100644
--- a/code/modules/antagonists/changeling/powers/mutations.dm
+++ b/code/modules/antagonists/changeling/powers/mutations.dm
@@ -160,7 +160,7 @@
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
- sharpness = IS_SHARP
+ sharpness = SHARP_DISMEMBER_EASY
bleed_force = BLEED_DEEP_WOUND
var/can_drop = FALSE
var/fake = FALSE
@@ -175,6 +175,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/obj/item/melee/arm_blade)
loc.visible_message("A grotesque blade forms around [loc.name]\'s arm!", "Our arm twists and mutates, transforming it into a deadly blade.", "You hear organic matter ripping and tearing!")
if(synthetic)
can_drop = TRUE
+ sharpness = SHARP_DISMEMBER
AddComponent(/datum/component/butchering, 60, 80)
/obj/item/melee/arm_blade/afterattack(atom/target, mob/user, proximity)
diff --git a/code/modules/antagonists/changeling/powers/tiny_prick.dm b/code/modules/antagonists/changeling/powers/tiny_prick.dm
index 44ff428fdaff9..a56a3e5a4a38e 100644
--- a/code/modules/antagonists/changeling/powers/tiny_prick.dm
+++ b/code/modules/antagonists/changeling/powers/tiny_prick.dm
@@ -128,6 +128,7 @@
/obj/item/melee/arm_blade/false
desc = "A grotesque mass of flesh that used to be your arm. Although it looks dangerous at first, you can tell it's actually quite dull and useless."
+ sharpness = BLUNT //Not actually sharp
force = 5 //Basically as strong as a punch
fake = TRUE
diff --git a/code/modules/antagonists/clock_cult/items/clockwork_weapon.dm b/code/modules/antagonists/clock_cult/items/clockwork_weapon.dm
index 97c61732910c2..9caee25a385a9 100644
--- a/code/modules/antagonists/clock_cult/items/clockwork_weapon.dm
+++ b/code/modules/antagonists/clock_cult/items/clockwork_weapon.dm
@@ -19,7 +19,7 @@
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb_continuous = list("attacks", "pokes", "jabs", "tears", "lacerates", "gores")
attack_verb_simple = list("attack", "poke", "jab", "tear", "lacerate", "gore")
- sharpness = IS_SHARP_ACCURATE
+ sharpness = SHARP
bleed_force = BLEED_CUT
max_integrity = 200
var/clockwork_hint = ""
@@ -99,7 +99,7 @@
worn_icon_state = "mining_hammer1"
throwforce = 25
armour_penetration = 6
- sharpness = IS_BLUNT
+ sharpness = BLUNT
attack_verb_continuous = list("bashes", "bludgeons", "thrashes", "whacks")
attack_verb_simple = list("bash", "bludgeon", "thrash", "whack")
clockwork_hint = "Enemies hit by this will be flung back while on Reebe."
diff --git a/code/modules/antagonists/clock_cult/servant_of_ratvar.dm b/code/modules/antagonists/clock_cult/servant_of_ratvar.dm
index 155e456db44e9..ca40507c138f2 100644
--- a/code/modules/antagonists/clock_cult/servant_of_ratvar.dm
+++ b/code/modules/antagonists/clock_cult/servant_of_ratvar.dm
@@ -74,7 +74,7 @@
owner.current.throw_alert("clockinfo", /atom/movable/screen/alert/clockwork/clocksense)
SSticker.mode.update_clockcult_icons_added(owner)
var/datum/language_holder/LH = owner.current.get_language_holder()
- LH.grant_language(/datum/language/ratvar, TRUE, TRUE, LANGUAGE_CULTIST)
+ LH.grant_language(/datum/language/ratvar, source = LANGUAGE_CULTIST)
/datum/antagonist/servant_of_ratvar/remove_innate_effects(mob/living/M)
owner.current.faction -= "ratvar"
@@ -86,7 +86,7 @@
owner_mob.remove_overlay(forbearance)
qdel(forbearance)
var/datum/language_holder/LH = owner.current.get_language_holder()
- LH.remove_language(/datum/language/ratvar, TRUE, TRUE, LANGUAGE_CULTIST)
+ LH.remove_language(/datum/language/ratvar, source = LANGUAGE_CULTIST)
. = ..()
/datum/antagonist/servant_of_ratvar/proc/equip_servant_conversion()
diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm
index 0f5fe03d59f8a..a778d6cf0bd65 100644
--- a/code/modules/antagonists/cult/cult.dm
+++ b/code/modules/antagonists/cult/cult.dm
@@ -114,7 +114,7 @@
if(mob_override)
current = mob_override
current.faction |= "cult"
- current.grant_language(/datum/language/narsie, TRUE, TRUE, LANGUAGE_CULTIST)
+ current.grant_language(/datum/language/narsie, source = LANGUAGE_CULTIST)
if(!cult_team.cult_master)
vote.Grant(current)
communion.Grant(current)
@@ -142,7 +142,7 @@
if(mob_override)
current = mob_override
current.faction -= "cult"
- current.remove_language(/datum/language/narsie, TRUE, TRUE, LANGUAGE_CULTIST)
+ current.remove_language(/datum/language/narsie, source = LANGUAGE_CULTIST)
vote.Remove(current)
communion.Remove(current)
magic.Remove(current)
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index cf05b79bfbcaf..ae43b26f59559 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -47,7 +47,7 @@ Striking a noncultist, however, will tear their flesh."}
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
flags_1 = CONDUCT_1
- sharpness = IS_SHARP
+ sharpness = SHARP_DISMEMBER
bleed_force = BLEED_CUT
w_class = WEIGHT_CLASS_BULKY
block_level = 1
@@ -576,7 +576,7 @@ Striking a noncultist, however, will tear their flesh."}
block_upgrade_walk = 1
attack_verb_continuous = list("attacks", "impales", "stabs", "tears", "lacerates", "gores")
attack_verb_simple = list("attack", "impale", "stab", "tear", "lacerate", "gore")
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_CUT
hitsound = 'sound/weapons/bladeslice.ogg'
var/datum/action/innate/cult/spear/spear_act
diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm
index 1f3385c65b49d..cbdca6a8bf1f3 100644
--- a/code/modules/antagonists/heretic/items/heretic_blades.dm
+++ b/code/modules/antagonists/heretic/items/heretic_blades.dm
@@ -9,7 +9,7 @@
inhand_x_dimension = 64
inhand_y_dimension = 64
flags_1 = CONDUCT_1
- sharpness = IS_SHARP
+ sharpness = SHARP_DISMEMBER_EASY
bleed_force = BLEED_CUT
w_class = WEIGHT_CLASS_LARGE
force = 24
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm
index cadfaff47ef48..357d568717197 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm
@@ -88,7 +88,7 @@ GLOBAL_LIST_EMPTY(heretic_sacrifice_landmarks)
/area/heretic_sacrifice
name = "Mansus"
icon_state = "away"
- has_gravity = STANDARD_GRAVITY
+ default_gravity = STANDARD_GRAVITY
ambience_index = AMBIENCE_SPOOKY
sound_environment = SOUND_ENVIRONMENT_CAVE
area_flags = UNIQUE_AREA | HIDDEN_AREA | BLOCK_SUICIDE
diff --git a/code/modules/antagonists/heretic/structures/carving_knife.dm b/code/modules/antagonists/heretic/structures/carving_knife.dm
index eed18614df296..516c3f914a763 100644
--- a/code/modules/antagonists/heretic/structures/carving_knife.dm
+++ b/code/modules/antagonists/heretic/structures/carving_knife.dm
@@ -6,7 +6,7 @@
icon = 'icons/obj/heretic.dmi'
icon_state = "rune_carver"
flags_1 = CONDUCT_1
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_CUT
w_class = WEIGHT_CLASS_SMALL
force = 10
diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm
index 4c43bd564af0d..ad245568a6d72 100644
--- a/code/modules/antagonists/pirate/pirate.dm
+++ b/code/modules/antagonists/pirate/pirate.dm
@@ -65,12 +65,12 @@
. = ..()
var/mob/living/owner_mob = mob_override || owner.current
var/datum/language_holder/holder = owner_mob.get_language_holder()
- holder.grant_language(/datum/language/piratespeak, TRUE, TRUE, LANGUAGE_PIRATE)
+ holder.grant_language(/datum/language/piratespeak, source = LANGUAGE_PIRATE)
holder.selected_language = /datum/language/piratespeak
/datum/antagonist/pirate/remove_innate_effects(mob/living/mob_override)
var/mob/living/owner_mob = mob_override || owner.current
- owner_mob.remove_language(/datum/language/piratespeak, TRUE, TRUE, LANGUAGE_PIRATE)
+ owner_mob.remove_language(/datum/language/piratespeak, source = LANGUAGE_PIRATE)
return ..()
/datum/team/pirate
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index d420e06b81032..984ef90c9fb0d 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -84,7 +84,7 @@
check_rev_teleport() // they're spawned in non-station for some reason...
random_revenant_name()
AddComponent(/datum/component/tracking_beacon, "ghost", null, null, TRUE, "#9e4d91", TRUE, TRUE, "#490066")
- grant_all_languages(TRUE, FALSE, FALSE, LANGUAGE_REVENANT) // rev can understand every langauge
+ grant_all_languages(UNDERSTOOD_LANGUAGE, grant_omnitongue = FALSE, source = LANGUAGE_REVENANT) // rev can understand every langauge
ADD_TRAIT(src, TRAIT_FREE_HYPERSPACE_MOVEMENT, INNATE_TRAIT)
AddElement(/datum/element/movetype_handler)
ADD_TRAIT(src, TRAIT_MOVE_FLOATING, "ghost")
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index c90dbffffa045..8ffaca9fcd298 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -96,7 +96,7 @@
if(TRAITOR_AI)
add_law_zero()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', vol = 100, vary = FALSE, channel = CHANNEL_ANTAG_GREETING, pressure_affected = FALSE, use_reverb = FALSE)
- owner.current.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MALF)
+ owner.current.grant_language(/datum/language/codespeak, source = LANGUAGE_MALF)
if(TRAITOR_HUMAN)
ui_interact(owner.current)
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', vol = 100, vary = FALSE, channel = CHANNEL_ANTAG_GREETING, pressure_affected = FALSE, use_reverb = FALSE)
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index 411269dfffb60..4b72737714311 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -353,9 +353,9 @@
S.real_name = "Shade of [T.real_name]"
S.key = shade_controller.key
S.copy_languages(T, LANGUAGE_MIND)//Copies the old mobs languages into the new mob holder.
- S.copy_languages(user, LANGUAGE_MASTER)
- S.update_atom_languages()
- grant_all_languages(FALSE, FALSE, TRUE) //Grants omnitongue
+ if(user)
+ S.copy_languages(user, LANGUAGE_MASTER)
+ S.get_language_holder().omnitongue = TRUE //Grants omnitongue
if(user)
S.faction |= "[REF(user)]" //Add the master as a faction, allowing inter-mob cooperation
if(user && iscultist(user))
diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm
index efb313a639375..650e65709d65f 100644
--- a/code/modules/atmospherics/machinery/atmosmachinery.dm
+++ b/code/modules/atmospherics/machinery/atmosmachinery.dm
@@ -20,6 +20,7 @@
resistance_flags = FIRE_PROOF
max_integrity = 200
obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
+ flags_1 = STAT_UNIQUE_1
var/can_unwrench = 0
var/initialize_directions = 0
var/pipe_color
diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm
index be7bf34de53a6..128f6ce75e683 100644
--- a/code/modules/cargo/packs.dm
+++ b/code/modules/cargo/packs.dm
@@ -77,7 +77,7 @@
/obj/item/clothing/suit/jacket/leather/overcoat,
/obj/item/clothing/gloves/color/black,
/obj/item/clothing/head/soft/cargo,
- /obj/item/clothing/mask/bandana/skull)//so you can properly #cargoniabikergang
+ /obj/item/clothing/mask/bandana/skull/black)//so you can properly #cargoniabikergang
crate_name = "Biker Kit"
crate_type = /obj/structure/closet/crate/large
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 31802de2e8c0d..e936068946c1f 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -115,6 +115,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
cmd_mentor_pm(href_list["mentor_msg"], null)
return TRUE
+ if(href_list["commandbar_typing"])
+ handle_commandbar_typing(href_list)
+
switch(href_list["_src_"])
if("holder")
hsrc = holder
@@ -209,6 +212,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
tgui_say = new(src, "tgui_say")
tgui_asay = new(src, "tgui_asay")
+ initialize_commandbar_spy()
+
GLOB.ahelp_tickets.ClientLogin(src)
GLOB.mhelp_tickets.ClientLogin(src)
GLOB.interviews.client_login(src)
diff --git a/code/modules/client/loadout/loadout_accessories.dm b/code/modules/client/loadout/loadout_accessories.dm
index 60c18fcbc94be..2c6cf051a21d0 100644
--- a/code/modules/client/loadout/loadout_accessories.dm
+++ b/code/modules/client/loadout/loadout_accessories.dm
@@ -209,7 +209,7 @@
/datum/gear/accessory/bandana/skull
display_name = "skull bandana"
- path = /obj/item/clothing/mask/bandana/skull
+ path = /obj/item/clothing/mask/bandana/skull/black
cost = 2000
//LIPSTICK
diff --git a/code/modules/client/verbs/typing.dm b/code/modules/client/verbs/typing.dm
new file mode 100644
index 0000000000000..0fa583baa1cb6
--- /dev/null
+++ b/code/modules/client/verbs/typing.dm
@@ -0,0 +1,70 @@
+#define IC_VERBS list("say", "me", "whisper")
+
+/client/var/commandbar_thinking = FALSE
+/client/var/commandbar_typing = FALSE
+
+/client/proc/initialize_commandbar_spy()
+ src << output('html/typing_indicator.html', "commandbar_spy")
+
+/client/proc/handle_commandbar_typing(href_list)
+ //if (!typing_indicators) //check pref
+ // return
+ if (length(href_list["verb"]) < 1 || !(LOWER_TEXT(href_list["verb"]) in IC_VERBS) || text2num(href_list["argument_length"]) < 1)
+ if (commandbar_typing)
+ commandbar_typing = FALSE
+ stop_typing()
+
+ if (commandbar_thinking)
+ commandbar_thinking = FALSE
+ stop_thinking()
+ return
+
+ if (!commandbar_thinking)
+ commandbar_thinking = TRUE
+ start_thinking()
+
+ if (!commandbar_typing)
+ commandbar_typing = TRUE
+ start_typing()
+
+
+/** Sets the mob as "thinking" - with indicator and the TRAIT_THINKING_IN_CHARACTER trait */
+/client/proc/start_thinking()
+ //if(!typing_indicators)
+ // return FALSE
+ /// Special exemptions
+ if(isabductor(mob))
+ return FALSE
+ ADD_TRAIT(mob, TRAIT_THINKING_IN_CHARACTER, CURRENTLY_TYPING_TRAIT)
+ mob.create_thinking_indicator()
+
+/** Removes typing/thinking indicators and flags the mob as not thinking */
+/client/proc/stop_thinking()
+ mob?.remove_all_indicators()
+
+/**
+ * Handles the user typing. After a brief period of inactivity,
+ * signals the client mob to revert to the "thinking" icon.
+ */
+/client/proc/start_typing()
+ var/mob/client_mob = mob
+ client_mob.remove_thinking_indicator()
+ if(!HAS_TRAIT(client_mob, TRAIT_THINKING_IN_CHARACTER))
+ return FALSE
+ client_mob.create_typing_indicator()
+ addtimer(CALLBACK(src, PROC_REF(stop_typing)), 5 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_STOPPABLE)
+
+/**
+ * Callback to remove the typing indicator after a brief period of inactivity.
+ * If the user was typing IC, the thinking indicator is shown.
+ */
+/client/proc/stop_typing()
+ if(isnull(mob))
+ return FALSE
+ var/mob/client_mob = mob
+ client_mob.remove_typing_indicator()
+ if(!HAS_TRAIT(client_mob, TRAIT_THINKING_IN_CHARACTER))
+ return FALSE
+ client_mob.create_thinking_indicator()
+
+#undef IC_VERBS
diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm
index 5741630d0d3f4..a73b48b71a3e2 100644
--- a/code/modules/clothing/glasses/_glasses.dm
+++ b/code/modules/clothing/glasses/_glasses.dm
@@ -123,7 +123,7 @@
attack_verb_continuous = list("slices")
attack_verb_simple = list("slice")
hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_SURFACE
/obj/item/clothing/glasses/meson/prescription
@@ -244,7 +244,7 @@
attack_verb_continuous = list("slices")
attack_verb_simple = list("slice")
hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_SURFACE
glass_colour_type = /datum/client_colour/glass_colour/lightgreen
@@ -329,7 +329,7 @@
attack_verb_continuous = list("slices")
attack_verb_simple = list("slice")
hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_SURFACE
/obj/item/clothing/glasses/sunglasses/advanced/garb/supergarb
@@ -351,7 +351,7 @@
attack_verb_continuous = list("slices")
attack_verb_simple = list("slice")
hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_SURFACE
glass_colour_type = /datum/client_colour/glass_colour/orange
diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm
index b3beba00edd23..743fe43f953c1 100644
--- a/code/modules/clothing/glasses/hud.dm
+++ b/code/modules/clothing/glasses/hud.dm
@@ -293,7 +293,7 @@
attack_verb_continuous = list("slices")
attack_verb_simple = list("slice")
hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_SURFACE
/obj/item/clothing/glasses/hud/security/sunglasses/gars/supergars
diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm
index 8fc44b0c65e68..9e1f03a9daf06 100644
--- a/code/modules/clothing/head/misc_special.dm
+++ b/code/modules/clothing/head/misc_special.dm
@@ -148,17 +148,16 @@
/obj/item/clothing/head/costume/speedwagon
name = "hat of ultimate masculinity"
desc = "Even the mere act of wearing this makes you want to pose menacingly."
- worn_icon = 'icons/mob/large-worn-icons/64x64/head.dmi'
icon_state = "speedwagon"
item_state = "speedwagon"
- worn_x_dimension = 64
- worn_y_dimension = 64
+ worn_y_offset = 4
/obj/item/clothing/head/costume/speedwagon/cursed
name = "ULTIMATE HAT"
desc = "You feel weak and pathetic in comparison to this exceptionally beautiful hat."
- icon_state = "speedwagon_cursed"
- item_state = "speedwagon_cursed"
+ icon_state = "speedwagon"
+ item_state = "speedwagon"
+ worn_y_offset = 6
/obj/item/clothing/head/franks_hat
name = "Frank's Hat"
diff --git a/code/modules/clothing/head/pirate.dm b/code/modules/clothing/head/pirate.dm
index 5ddb96ae5ff77..bc377306b94bb 100644
--- a/code/modules/clothing/head/pirate.dm
+++ b/code/modules/clothing/head/pirate.dm
@@ -13,7 +13,7 @@
if(!ishuman(user))
return
if(slot == ITEM_SLOT_HEAD)
- user.grant_language(/datum/language/piratespeak/, TRUE, TRUE, LANGUAGE_HAT)
+ user.grant_language(/datum/language/piratespeak/, source = LANGUAGE_HAT)
to_chat(user, "You suddenly know how to speak like a pirate!")
/obj/item/clothing/head/costume/pirate/dropped(mob/user)
@@ -22,7 +22,7 @@
return
var/mob/living/carbon/human/H = user
if(H.get_item_by_slot(ITEM_SLOT_HEAD) == src && !QDELETED(src)) //This can be called as a part of destroy
- user.remove_language(/datum/language/piratespeak/, TRUE, TRUE, LANGUAGE_HAT)
+ user.remove_language(/datum/language/piratespeak/, source = LANGUAGE_HAT)
to_chat(user, "You can no longer speak like a pirate.")
/obj/item/clothing/head/costume/pirate/captain
diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm
index 48816303b8c96..50dc44d8e44f8 100644
--- a/code/modules/clothing/masks/_masks.dm
+++ b/code/modules/clothing/masks/_masks.dm
@@ -6,7 +6,7 @@
strip_delay = 40
equip_delay_other = 40
var/modifies_speech = FALSE
- var/mask_adjusted = 0
+ var/mask_adjusted = FALSE
var/adjusted_flags = null
var/voice_change = FALSE //Used to mask/change the user's voice, only specific masks can set this to TRUE
var/obj/item/organ/tongue/chosen_tongue = null
diff --git a/code/modules/clothing/masks/bandana.dm b/code/modules/clothing/masks/bandana.dm
new file mode 100644
index 0000000000000..f2a560ef4d234
--- /dev/null
+++ b/code/modules/clothing/masks/bandana.dm
@@ -0,0 +1,192 @@
+/obj/item/clothing/mask/bandana
+ w_class = WEIGHT_CLASS_TINY
+ flags_cover = MASKCOVERSMOUTH
+ flags_inv = HIDEFACE|HIDEFACIALHAIR|HIDESNOUT
+ visor_flags_inv = HIDEFACE|HIDEFACIALHAIR|HIDESNOUT
+ visor_flags_cover = MASKCOVERSMOUTH
+ slot_flags = ITEM_SLOT_MASK
+ adjusted_flags = ITEM_SLOT_HEAD
+ dying_key = DYE_REGISTRY_BANDANA
+ //flags_1 = IS_PLAYER_COLORABLE_1
+ name = "bandana"
+ desc = "A fine bandana with nanotech lining."
+ icon_state = "bandana"
+ worn_icon_state = "bandana_worn"
+ greyscale_config = /datum/greyscale_config/bandana
+ greyscale_config_worn = /datum/greyscale_config/bandana_worn
+ var/greyscale_config_up = /datum/greyscale_config/bandana_up
+ var/greyscale_config_worn_up = /datum/greyscale_config/bandana_worn_up
+ greyscale_colors = "#2e2e2e"
+
+/obj/item/clothing/mask/bandana/attack_self(mob/user)
+ if(slot_flags & ITEM_SLOT_NECK)
+ to_chat(user, "You must undo [src] in order to push it into a hat!")
+ return
+ adjustmask(user)
+ if(greyscale_config == initial(greyscale_config) && greyscale_config_worn == initial(greyscale_config_worn))
+ worn_icon_state += "_up"
+ undyeable = TRUE
+ set_greyscale(
+ new_config = greyscale_config_up,
+ new_worn_config = greyscale_config_worn_up
+ )
+ else
+ worn_icon_state = initial(worn_icon_state)
+ undyeable = initial(undyeable)
+ set_greyscale(
+ new_config = initial(greyscale_config),
+ new_worn_config = initial(greyscale_config_worn)
+ )
+
+/obj/item/clothing/mask/bandana/AltClick(mob/user)
+ . = ..()
+ if(iscarbon(user))
+ var/mob/living/carbon/C = user
+ var/matrix/widen = matrix()
+ if(!user.is_holding(src))
+ to_chat(user, "You must be holding [src] in order to tie it!")
+ return
+ if((C.get_item_by_slot(ITEM_SLOT_HEAD == src)) || (C.get_item_by_slot(ITEM_SLOT_MASK) == src))
+ to_chat(user, "You can't tie [src] while wearing it!")
+ return
+ if(slot_flags & ITEM_SLOT_HEAD)
+ to_chat(user, "You must undo [src] before you can tie it into a neckerchief!")
+ return
+ if(slot_flags & ITEM_SLOT_MASK)
+ undyeable = TRUE
+ slot_flags = ITEM_SLOT_NECK
+ worn_y_offset = -3
+ widen.Scale(1.25, 1)
+ transform = widen
+ user.visible_message("[user] ties [src] up like a neckerchief.", "You tie [src] up like a neckerchief.")
+ else
+ undyeable = initial(undyeable)
+ slot_flags = initial(slot_flags)
+ worn_y_offset = initial(worn_y_offset)
+ transform = initial(transform)
+ user.visible_message("[user] unties the neckerchief.", "You untie the neckerchief.")
+
+/obj/item/clothing/mask/bandana/red
+ name = "red bandana"
+ desc = "A fine red bandana with nanotech lining."
+ greyscale_colors = "#A02525"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/blue
+ name = "blue bandana"
+ desc = "A fine blue bandana with nanotech lining."
+ greyscale_colors = "#294A98"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/purple
+ name = "purple bandana"
+ desc = "A fine purple bandana with nanotech lining."
+ greyscale_colors = "#8019a0"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/green
+ name = "green bandana"
+ desc = "A fine green bandana with nanotech lining."
+ greyscale_colors = "#3D9829"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/gold
+ name = "gold bandana"
+ desc = "A fine gold bandana with nanotech lining."
+ greyscale_colors = "#DAC20E"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/orange
+ name = "orange bandana"
+ desc = "A fine orange bandana with nanotech lining."
+ greyscale_colors = "#da930e"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/black
+ name = "black bandana"
+ desc = "A fine black bandana with nanotech lining."
+ greyscale_colors = "#2e2e2e"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/white
+ name = "white bandana"
+ desc = "A fine white bandana with nanotech lining."
+ greyscale_colors = "#DCDCDC"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/durathread
+ name = "durathread bandana"
+ desc = "A bandana made from durathread, you wish it would provide some protection to its wearer, but it's far too thin..."
+ greyscale_colors = "#5c6d80"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/striped
+ name = "striped bandana"
+ desc = "A fine bandana with nanotech lining and a stripe across."
+ icon_state = "bandstriped"
+ worn_icon_state = "bandstriped_worn"
+ greyscale_config = /datum/greyscale_config/bandstriped
+ greyscale_config_worn = /datum/greyscale_config/bandstriped_worn
+ greyscale_config_up = /datum/greyscale_config/bandstriped_up
+ greyscale_config_worn_up = /datum/greyscale_config/bandstriped_worn_up
+ greyscale_colors = "#2e2e2e#C6C6C6"
+ undyeable = TRUE
+
+/obj/item/clothing/mask/bandana/striped/black
+ name = "striped bandana"
+ desc = "A fine black and white bandana with nanotech lining and a stripe across."
+ greyscale_colors = "#2e2e2e#C6C6C6"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/striped/security
+ name = "striped security bandana"
+ desc = "A fine bandana with nanotech lining, a stripe across and security colors."
+ greyscale_colors = "#A02525#2e2e2e"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/striped/science
+ name = "striped science bandana"
+ desc = "A fine bandana with nanotech lining, a stripe across and science colors."
+ greyscale_colors = "#DCDCDC#8019a0"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/striped/engineering
+ name = "striped engineering bandana"
+ desc = "A fine bandana with nanotech lining, a stripe across and engineering colors."
+ greyscale_colors = "#dab50e#ec7404"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/striped/medical
+ name = "striped medical bandana"
+ desc = "A fine bandana with nanotech lining, a stripe across and medical colors."
+ greyscale_colors = "#DCDCDC#5995BA"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/striped/cargo
+ name = "striped cargo bandana"
+ desc = "A fine bandana with nanotech lining, a stripe across and cargo colors."
+ greyscale_colors = "#967032#5F350B"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/striped/botany
+ name = "striped botany bandana"
+ desc = "A fine bandana with nanotech lining, a stripe across and botany colors."
+ greyscale_colors = "#3D9829#294A98"
+ flags_1 = NONE
+
+/obj/item/clothing/mask/bandana/skull
+ name = "skull bandana"
+ desc = "A fine bandana with nanotech lining and a skull emblem."
+ icon_state = "bandskull"
+ worn_icon_state = "bandskull_worn"
+ greyscale_config = /datum/greyscale_config/bandskull
+ greyscale_config_worn = /datum/greyscale_config/bandskull_worn
+ greyscale_config_up = /datum/greyscale_config/bandskull_up
+ greyscale_config_worn_up = /datum/greyscale_config/bandskull_worn_up
+ greyscale_colors = "#2e2e2e#C6C6C6"
+ undyeable = TRUE
+
+/obj/item/clothing/mask/bandana/skull/black
+ desc = "A fine black bandana with nanotech lining and a skull emblem."
+ greyscale_colors = "#2e2e2e#C6C6C6"
+ flags_1 = NONE
diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm
index 1cc8af863b241..d472999cf14df 100644
--- a/code/modules/clothing/masks/miscellaneous.dm
+++ b/code/modules/clothing/masks/miscellaneous.dm
@@ -255,90 +255,6 @@
icon_state = "bumba"
item_state = "bumba"
-/obj/item/clothing/mask/bandana
- name = "white bandana"
- desc = "A fine white bandana with nanotech lining."
- w_class = WEIGHT_CLASS_TINY
- flags_cover = MASKCOVERSMOUTH
- flags_inv = HIDEFACE|HIDEFACIALHAIR|HIDESNOUT
- visor_flags_inv = HIDEFACE|HIDEFACIALHAIR|HIDESNOUT
- visor_flags_cover = MASKCOVERSMOUTH
- slot_flags = ITEM_SLOT_MASK
- adjusted_flags = ITEM_SLOT_HEAD
- icon_state = "bandwhite"
-
-/obj/item/clothing/mask/bandana/attack_self(mob/user)
- adjustmask(user)
- if (mask_adjusted)
- worn_icon = 'icons/mob/clothing/head/costume.dmi'
- else
- worn_icon = 'icons/mob/clothing/mask.dmi'
-
-/obj/item/clothing/mask/bandana/AltClick(mob/user)
- if(!user.canUseTopic(src, BE_CLOSE))
- return
- if(iscarbon(user))
- var/mob/living/carbon/C = user
- if((C.get_item_by_slot(ITEM_SLOT_HEAD == src)) || (C.get_item_by_slot(ITEM_SLOT_MASK) == src))
- to_chat(user, "You can't tie [src] while wearing it!")
- return
- if(slot_flags & ITEM_SLOT_HEAD)
- to_chat(user, "You must undo [src] before you can tie it into a neckerchief!")
- else
- if(user.is_holding(src))
- var/obj/item/clothing/neck/neckerchief/nk = new(src)
- nk.name = "[name] neckerchief"
- nk.desc = "[desc] It's tied up like a neckerchief."
- nk.icon_state = icon_state
- nk.sourceBandanaType = src.type
- var/currentHandIndex = user.get_held_index_of_item(src)
- user.transferItemToLoc(src, null)
- user.put_in_hand(nk, currentHandIndex)
- user.visible_message("[user] ties [src] up like a neckerchief.", "You tie [src] up like a neckerchief.")
- qdel(src)
- else
- to_chat(user, "You must be holding [src] in order to tie it!")
-
-/obj/item/clothing/mask/bandana/red
- name = "red bandana"
- desc = "A fine red bandana with nanotech lining."
- icon_state = "bandred"
-
-/obj/item/clothing/mask/bandana/blue
- name = "blue bandana"
- desc = "A fine blue bandana with nanotech lining."
- icon_state = "bandblue"
-
-/obj/item/clothing/mask/bandana/green
- name = "green bandana"
- desc = "A fine green bandana with nanotech lining."
- icon_state = "bandgreen"
-
-/obj/item/clothing/mask/bandana/gold
- name = "gold bandana"
- desc = "A fine gold bandana with nanotech lining."
- icon_state = "bandgold"
-
-/obj/item/clothing/mask/bandana/black
- name = "black bandana"
- desc = "A fine black bandana with nanotech lining."
- icon_state = "bandblack"
-
-/obj/item/clothing/mask/bandana/skull
- name = "skull bandana"
- desc = "A fine black bandana with nanotech lining and a skull emblem."
- icon_state = "bandskull"
-
-/obj/item/clothing/mask/bandana/botany
- name = "botany bandana"
- desc = "A fine bandana with nanotech lining and a hydroponics pattern."
- icon_state = "bandbotany"
-
-/obj/item/clothing/mask/bandana/durathread
- name = "durathread bandana"
- desc = "A bandana made from durathread, you wish it would provide some protection to its wearer, but it's far too thin..."
- icon_state = "banddurathread"
-
/obj/item/clothing/mask/mummy
name = "mummy mask"
desc = "Ancient bandages."
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index 55505bbae412a..88a6144890a51 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -198,34 +198,6 @@
desc = "In nomine Patris, et Filii, et Spiritus Sancti."
icon_state = "cross"
-/obj/item/clothing/neck/neckerchief
- worn_icon = "empty_bandana"
- w_class = WEIGHT_CLASS_TINY
- var/sourceBandanaType
-
-/obj/item/clothing/neck/neckerchief/worn_overlays(mutable_appearance/standing, isinhands = FALSE, icon_file, item_layer, atom/origin)
- . = ..()
- if(!isinhands)
- var/mutable_appearance/realOverlay = mutable_appearance('icons/mob/clothing/mask.dmi', icon_state, item_layer)
- realOverlay.pixel_y = -3
- . += realOverlay
-
-/obj/item/clothing/neck/neckerchief/AltClick(mob/user)
- if(iscarbon(user))
- var/mob/living/carbon/C = user
- if(C.get_item_by_slot(ITEM_SLOT_NECK) == src)
- to_chat(user, "You can't untie [src] while wearing it!")
- return
- if(user.is_holding(src))
- var/obj/item/clothing/mask/bandana/newBand = new sourceBandanaType(user)
- var/currentHandIndex = user.get_held_index_of_item(src)
- var/oldName = src.name
- qdel(src)
- user.put_in_hand(newBand, currentHandIndex)
- user.visible_message("[user] unties [oldName] back into a [newBand.name]", "You untie [oldName] back into a [newBand.name]")
- else
- to_chat(user, "You must be holding [src] in order to untie it!")
-
/////////////////
//DONATOR ITEMS//
/////////////////
diff --git a/code/modules/clothing/suits/chaplainsuits.dm b/code/modules/clothing/suits/chaplainsuits.dm
index 8ac269fe03a6d..03bddfa26507d 100644
--- a/code/modules/clothing/suits/chaplainsuits.dm
+++ b/code/modules/clothing/suits/chaplainsuits.dm
@@ -156,6 +156,7 @@
icon = 'icons/obj/clothing/head/chaplain.dmi'
worn_icon = 'icons/mob/clothing/head/chaplain.dmi'
icon_state = "crusader"
+ item_state = null
w_class = WEIGHT_CLASS_NORMAL
flags_inv = HIDEHAIR|HIDEEARS|HIDEFACE
armor_type = /datum/armor/plate_crusader
@@ -185,12 +186,11 @@
name = "Prophet's Hat"
desc = "A religious-looking hat."
icon_state = null
- worn_icon = 'icons/mob/large-worn-icons/64x64/head.dmi'
+ worn_icon = 'icons/mob/clothing/head/helmet.dmi'
item_state = null
flags_1 = 0
armor_type = /datum/armor/crusader_prophet
- worn_x_dimension = 64
- worn_y_dimension = 64
+ worn_y_offset = 6
/datum/armor/crusader_prophet
@@ -218,12 +218,10 @@
name = "cage"
desc = "A cage that restrains the will of the self, allowing one to see the profane world for what it is."
flags_inv = NONE
- worn_icon = 'icons/mob/large-worn-icons/64x64/head.dmi'
icon_state = "cage"
item_state = null
- worn_x_dimension = 64
- worn_y_dimension = 64
dynamic_hair_suffix = ""
+ worn_y_offset = 7
/obj/item/clothing/head/helmet/chaplain/ancient
name = "ancient helmet"
diff --git a/code/modules/error_handler/error_viewer.dm b/code/modules/error_handler/error_viewer.dm
index 4197b5e5da6e2..a72e9d2e5c4e3 100644
--- a/code/modules/error_handler/error_viewer.dm
+++ b/code/modules/error_handler/error_viewer.dm
@@ -128,7 +128,7 @@ GLOBAL_DATUM(error_cache, /datum/error_viewer/error_cache)
var/const/viewtext = "\[view]" // Nesting these in other brackets went poorly
//log_debug("Runtime in [e.file], line [e.line]: [html_encode(e.name)] [error_entry.make_link(viewtext)]")
var/err_msg_delay
- if(config)
+ if(config?.loaded)
err_msg_delay = CONFIG_GET(number/error_msg_delay)
else
var/datum/config_entry/CE = /datum/config_entry/number/error_msg_delay
diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm
index 7139a65377a6d..940ddbef01ebe 100644
--- a/code/modules/events/sentience.dm
+++ b/code/modules/events/sentience.dm
@@ -82,7 +82,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list(
SA.key = SG.key
- SA.grant_all_languages(TRUE, FALSE, FALSE)
+ SA.grant_all_languages(UNDERSTOOD_LANGUAGE, grant_omnitongue = FALSE, source = LANGUAGE_ATOM)
SA.sentience_act()
diff --git a/code/modules/food_and_drinks/plate.dm b/code/modules/food_and_drinks/plate.dm
index d7a68c3567884..7bd30f14fea3a 100644
--- a/code/modules/food_and_drinks/plate.dm
+++ b/code/modules/food_and_drinks/plate.dm
@@ -91,7 +91,7 @@
base_icon_state = "plate_shard"
force = 5
throwforce = 5
- sharpness = IS_SHARP
+ sharpness = SHARP
/// How many variants of shard there are
var/variants = 5
diff --git a/code/modules/holoparasite/abilities/lesser/babel.dm b/code/modules/holoparasite/abilities/lesser/babel.dm
index 036e2c6f1c3fc..c785f72a1902d 100644
--- a/code/modules/holoparasite/abilities/lesser/babel.dm
+++ b/code/modules/holoparasite/abilities/lesser/babel.dm
@@ -6,7 +6,7 @@
/datum/holoparasite_ability/lesser/babelfish/apply()
..()
- owner.grant_all_languages(TRUE, TRUE, TRUE, LANGUAGE_HOLOPARA)
+ owner.grant_all_languages(source = LANGUAGE_HOLOPARA)
/datum/holoparasite_ability/lesser/babelfish/remove()
..()
diff --git a/code/modules/holoparasite/holoparasite_language.dm b/code/modules/holoparasite/holoparasite_language.dm
index b2fe09a276ad8..5c33ca28e2adc 100644
--- a/code/modules/holoparasite/holoparasite_language.dm
+++ b/code/modules/holoparasite/holoparasite_language.dm
@@ -7,36 +7,30 @@
/// The parent holder of the holoparasite, which is the holder of the summoner.
var/datum/holoparasite_holder/holder
-/datum/language_holder/holoparasite/New(atom/owner, datum/holoparasite_holder/_holder)
- if(!istype(_holder))
- CRASH("Attempted to create holoparasite language holder for [key_name(owner)] without a valid holoparasite holder!")
- holder = _holder
+/datum/language_holder/holoparasite/New(atom/owner, datum/holoparasite_holder/new_holder)
+ holder = new_holder
return ..()
/**
* Ensures that the holoparasite can understand any language that its summoner can understand.
*/
-/datum/language_holder/holoparasite/has_language(language, spoken)
- var/datum/language_holder/summoner_mind_language_holder = holder.owner.get_language_holder()
+/datum/language_holder/holoparasite/has_language(language, flags_to_check)
var/datum/language_holder/summoner_body_language_holder = holder.owner.current?.get_language_holder()
- return ..() || summoner_mind_language_holder.has_language(language, spoken) || summoner_body_language_holder?.has_language(language, spoken)
+ return ..() || summoner_body_language_holder?.has_language(language, flags_to_check)
/**
* Ensures that the holoparasite can speak any language that its summoner can understand.
*/
/datum/language_holder/holoparasite/can_speak_language(language)
- var/datum/language_holder/summoner_mind_language_holder = holder.owner.get_language_holder()
var/datum/language_holder/summoner_body_language_holder = holder.owner.current?.get_language_holder()
- return ..() || summoner_mind_language_holder.can_speak_language(language) || summoner_body_language_holder?.can_speak_language(language)
+ return ..() || summoner_body_language_holder?.can_speak_language(language)
/**
* Picks a random understood language, combining the holoparasite's understood languages with that of the summoner's mind and body.
*/
/datum/language_holder/holoparasite/get_random_understood_language()
- var/datum/language_holder/summoner_mind_language_holder = holder.owner.get_language_holder()
var/datum/language_holder/summoner_body_language_holder = holder.owner.current?.get_language_holder()
var/list/choices = understood_languages.Copy()
- choices |= summoner_mind_language_holder.understood_languages
if(summoner_body_language_holder)
choices |= summoner_body_language_holder.understood_languages
return pick(choices)
@@ -45,10 +39,8 @@
* Picks a random spoken language, combining the holoparasite's spoken languages with that of the summoner's mind and body.
*/
/datum/language_holder/holoparasite/get_random_spoken_language()
- var/datum/language_holder/summoner_mind_language_holder = holder.owner.get_language_holder()
var/datum/language_holder/summoner_body_language_holder = holder.owner.current?.get_language_holder()
var/list/choices = spoken_languages.Copy()
- choices |= summoner_mind_language_holder.spoken_languages
if(summoner_body_language_holder)
choices |= summoner_body_language_holder.spoken_languages
return pick(choices)
diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm
index 8b995fb82dafe..f213159854662 100644
--- a/code/modules/hydroponics/hydroitemdefines.dm
+++ b/code/modules/hydroponics/hydroitemdefines.dm
@@ -87,7 +87,7 @@
attack_verb_continuous = list("chops", "tears", "lacerates", "cuts")
attack_verb_simple = list("chop", "tear", "lacerate", "cut")
hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP
+ sharpness = SHARP_DISMEMBER
bleed_force = BLEED_CUT
/obj/item/hatchet/Initialize(mapload)
diff --git a/code/modules/jobs/job_types/curator.dm b/code/modules/jobs/job_types/curator.dm
index a50627fbe99a8..2fa8559a6db82 100644
--- a/code/modules/jobs/job_types/curator.dm
+++ b/code/modules/jobs/job_types/curator.dm
@@ -57,4 +57,4 @@
if(visualsOnly)
return
- H.grant_all_languages(TRUE, TRUE, TRUE, LANGUAGE_CURATOR)
+ H.grant_all_languages(source = LANGUAGE_CURATOR)
diff --git a/code/modules/language/codespeak.dm b/code/modules/language/codespeak.dm
index 0f1bd035a3a25..0b84b8f84027f 100644
--- a/code/modules/language/codespeak.dm
+++ b/code/modules/language/codespeak.dm
@@ -47,7 +47,7 @@
return
to_chat(user, "You start skimming through [src], and suddenly your mind is filled with codewords and responses.")
- user.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MIND)
+ user.grant_language(/datum/language/codespeak, source = LANGUAGE_MIND)
use_charge(user)
@@ -66,7 +66,7 @@
M.visible_message("[user] beats [M] over the head with [src]!", "[user] beats you over the head with [src]!", "You hear smacking.")
else
M.visible_message("[user] teaches [M] by beating [M.p_them()] over the head with [src]!", "As [user] hits you with [src], codewords and responses flow through your mind.", "You hear smacking.")
- M.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MIND)
+ M.grant_language(/datum/language/codespeak, source = LANGUAGE_MIND)
use_charge(user)
/obj/item/codespeak_manual/proc/use_charge(mob/user)
diff --git a/code/modules/language/language_holder.dm b/code/modules/language/language_holder.dm
index e5c6e65c03d49..d3c6a80640bb4 100644
--- a/code/modules/language/language_holder.dm
+++ b/code/modules/language/language_holder.dm
@@ -1,4 +1,4 @@
-/*!Language holders will either exist in an atom/movable or a mind. Creation of language holders happens
+/*!Language holders will either exist in an atom/movable. Creation of language holders happens
automatically when they are needed, for example when something tries to speak.
Where a mind is available, the mind language holder will be the one "in charge". The mind holder
will update its languages based on the atom holder, and will get updated as part of
@@ -33,35 +33,36 @@ Key procs
* [has_language](atom/movable.html#proc/has_language)
* [can_speak_language](atom/movable.html#proc/can_speak_language)
* [get_selected_language](atom/movable.html#proc/get_selected_language)
-* [update_atom_languages](atom/movable.html#proc/update_atom_languages)
*/
/datum/language_holder
- /// Understood languages.
- var/list/understood_languages = list(/datum/language/common = list(LANGUAGE_MIND))
- /// A list of languages that can be spoken. Tongue organ may also set limits beyond this list.
+ /// Lazyassoclist of all understood languages
+ var/list/understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
+ /// Lazyassoclist of languages that can be spoken.
+ /// Tongue organ may also set limits beyond this list.
var/list/spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
- /// A list of blocked languages. Used to prevent understanding and speaking certain languages, ie for certain mobs, mutations etc.
- var/list/blocked_languages = list()
- /// If true, overrides tongue limitations.
+ /// Lazyassoclist of blocked languages.
+ /// Used to prevent understanding and speaking certain languages, ie for certain mobs, mutations etc.
+ var/list/blocked_languages
+ /// If true, overrides tongue aforementioned limitations.
var/omnitongue = FALSE
/// Handles displaying the language menu UI.
var/datum/language_menu/language_menu
/// Currently spoken language
var/selected_language
/// Tracks the entity that owns the holder.
- var/atom/owner
+ var/atom/movable/owner
/// Initializes, and copies in the languages from the current atom if available.
-/datum/language_holder/New(atom/_owner)
- if(_owner && QDELING(_owner))
- CRASH("Langauge holder added to a qdeleting thing, what the fuck [REF(_owner)]")
- owner = _owner
- if(istype(owner, /datum/mind))
- var/datum/mind/M = owner
- if(M.current)
- update_atom_languages(M.current)
- grant_language(/datum/language/metalanguage, understood=TRUE, spoken=FALSE, source=LANGUAGE_MIND) // Gets metalanguage that you can only understand
+/datum/language_holder/New(atom/new_owner)
+ if(new_owner)
+ if(QDELETED(new_owner))
+ CRASH("Langauge holder added to a qdeleting thing, what the fuck [text_ref(new_owner)]")
+ if(!ismovable(new_owner))
+ CRASH("Language holder being added to a non-movable thing, this is invalid (was: [new_owner] / [new_owner.type])")
+
+ owner = new_owner
+ grant_language(/datum/language/metalanguage, language_flags = ALL, source = LANGUAGE_MIND) // Gets metalanguage that you can only understand
// If we have an owner, we'll set a default selected language
if(owner)
get_selected_language()
@@ -72,54 +73,50 @@ Key procs
return ..()
/// Grants the supplied language.
-/datum/language_holder/proc/grant_language(language, understood = TRUE, spoken = TRUE, source = LANGUAGE_MIND)
- if(understood)
- if(!understood_languages[language])
- understood_languages[language] = list()
- understood_languages[language] |= source
+/datum/language_holder/proc/grant_language(language, language_flags = ALL, source = LANGUAGE_MIND)
+ if(language_flags & UNDERSTOOD_LANGUAGE)
+ LAZYORASSOCLIST(understood_languages, language, source)
. = TRUE
- if(spoken)
- if(!spoken_languages[language])
- spoken_languages[language] = list()
- spoken_languages[language] |= source
+ if(language_flags & SPOKEN_LANGUAGE)
+ LAZYORASSOCLIST(spoken_languages, language, source)
. = TRUE
+ return .
+
/// Grants every language to understood and spoken, and gives omnitongue.
-/datum/language_holder/proc/grant_all_languages(understood = TRUE, spoken = TRUE, grant_omnitongue = TRUE, source = LANGUAGE_MIND)
+/datum/language_holder/proc/grant_all_languages(language_flags = ALL, grant_omnitongue = TRUE, source = LANGUAGE_MIND)
for(var/language in GLOB.all_languages)
if(language == /datum/language/metalanguage)
continue // metalanguage shouldn't be given by this method
- grant_language(language, understood, spoken, source)
- if(grant_omnitongue) // Overrides tongue limitations.
+ grant_language(language, language_flags, source)
+ if(grant_omnitongue) // Overrides tongue limitations.
omnitongue = TRUE
return TRUE
/// Removes a single language or source, removing all sources returns the pre-removal state of the language.
-/datum/language_holder/proc/remove_language(language, understood = TRUE, spoken = TRUE, source = LANGUAGE_ALL)
- if(understood && understood_languages[language])
+/datum/language_holder/proc/remove_language(language, language_flags = ALL, source = LANGUAGE_ALL)
+ if(language_flags & UNDERSTOOD_LANGUAGE)
if(source == LANGUAGE_ALL)
- understood_languages -= language
+ LAZYREMOVE(understood_languages, language)
else
- understood_languages[language] -= source
- if(!length(understood_languages[language]))
- understood_languages -= language
+ LAZYREMOVEASSOC(understood_languages, language, source)
. = TRUE
- if(spoken && spoken_languages[language])
+ if(language_flags & SPOKEN_LANGUAGE)
if(source == LANGUAGE_ALL)
- spoken_languages -= language
+ LAZYREMOVE(spoken_languages, language)
else
- spoken_languages[language] -= source
- if(!length(spoken_languages[language]))
- spoken_languages -= language
+ LAZYREMOVEASSOC(spoken_languages, language, source)
. = TRUE
+ return .
+
/// Removes every language and optionally sets omnitongue false. If a non default source is supplied, only removes that source.
/datum/language_holder/proc/remove_all_languages(source = LANGUAGE_ALL, remove_omnitongue = FALSE)
for(var/language in GLOB.all_languages)
if(language == /datum/language/metalanguage) // this language is important. Don't remove it by remove_all
continue
- remove_language(language, TRUE, TRUE, source)
+ remove_language(language, ALL, source)
if(remove_omnitongue)
omnitongue = FALSE
return TRUE
@@ -128,41 +125,41 @@ Key procs
/datum/language_holder/proc/add_blocked_language(languages, source = LANGUAGE_MIND)
if(!islist(languages))
languages = list(languages)
+
for(var/language in languages)
- if(!blocked_languages[language])
- blocked_languages[language] = list()
- blocked_languages[language] |= source
+ LAZYORASSOCLIST(blocked_languages, language, source)
return TRUE
/// Removes a single language or list of languages from the blocked language list.
/datum/language_holder/proc/remove_blocked_language(languages, source = LANGUAGE_MIND)
if(!islist(languages))
languages = list(languages)
+
for(var/language in languages)
- if(blocked_languages[language])
- if(source == LANGUAGE_ALL)
- blocked_languages -= language
- else
- blocked_languages[language] -= source
- if(!length(blocked_languages[language]))
- blocked_languages -= language
+ if(source == LANGUAGE_ALL)
+ LAZYREMOVE(blocked_languages, language)
+ else
+ LAZYREMOVEASSOC(blocked_languages, language, source)
+
return TRUE
-/// Checks if you have the language. If spoken is true, only checks if you can speak the language.
-/datum/language_holder/proc/has_language(language, spoken = FALSE)
+/// Checks if you have the language passed.
+/datum/language_holder/proc/has_language(language, flag_to_check = UNDERSTOOD_LANGUAGE)
if(language in blocked_languages)
return FALSE
- if(spoken)
- return language in spoken_languages
- return language in understood_languages
+
+ var/list/langs_to_check = list()
+ if(flag_to_check & SPOKEN_LANGUAGE)
+ langs_to_check |= spoken_languages
+ if(flag_to_check & UNDERSTOOD_LANGUAGE)
+ langs_to_check |= understood_languages
+
+ return language in langs_to_check
/// Checks if you can speak the language. Tongue limitations should be supplied as an argument.
/datum/language_holder/proc/can_speak_language(language)
- var/atom/movable/our_atom = get_atom()
- var/tongue = our_atom.could_speak_language(language)
- if((omnitongue || tongue) && has_language(language, TRUE))
- return TRUE
- return FALSE
+ var/can_speak_language_path = omnitongue || owner.could_speak_language(language)
+ return (can_speak_language_path && has_language(language, SPOKEN_LANGUAGE))
/// Returns selected language if it can be spoken, or decides, sets and returns a new selected language if possible.
/datum/language_holder/proc/get_selected_language()
@@ -195,40 +192,15 @@ Key procs
language_menu = new (src)
language_menu.ui_interact(user)
-/// Gets the atom, since we some times need to check if the tongue has limitations.
-/datum/language_holder/proc/get_atom()
- if(owner)
- if(istype(owner, /datum/mind))
- var/datum/mind/M = owner
- return M.current
- return owner
- return FALSE
-
-/// Empties out the atom specific languages and updates them according to the supplied atoms language holder.
-/datum/language_holder/proc/update_atom_languages(atom/movable/thing)
- var/datum/language_holder/from_atom = thing.get_language_holder(FALSE) //Gets the atoms language holder
- if(from_atom == src) //This could happen if called on an atom without a mind.
- return FALSE
- for(var/language in understood_languages)
- remove_language(language, TRUE, FALSE, LANGUAGE_ATOM)
- for(var/language in spoken_languages)
- remove_language(language, FALSE, TRUE, LANGUAGE_ATOM)
- for(var/language in blocked_languages)
- remove_blocked_language(language, LANGUAGE_ATOM)
-
- copy_languages(from_atom, blocked=TRUE) // full-copy
- get_selected_language()
- return TRUE
-
/// Copies all languages from the supplied atom/language holder. Source should be overridden when you
/// do not want the language overwritten by later atom updates or want to avoid blocked languages.
/datum/language_holder/proc/copy_languages(var/datum/language_holder/from_holder, source_override=FALSE, spoken=TRUE, understood=TRUE, blocked=FALSE)
if(understood)
for(var/language in from_holder.understood_languages)
- grant_language(language, TRUE, FALSE, source_override || from_holder.understood_languages[language]) // if you don't have 'source_override' argument, source from 'from_holder' will be used.
+ grant_language(language, UNDERSTOOD_LANGUAGE, source_override || from_holder.understood_languages[language]) // if you don't have 'source_override' argument, source from 'from_holder' will be used.
if(spoken)
for(var/language in from_holder.spoken_languages)
- grant_language(language, FALSE, TRUE, source_override || from_holder.spoken_languages[language])
+ grant_language(language, SPOKEN_LANGUAGE, source_override || from_holder.spoken_languages[language])
if(blocked)
// blocked is set to FALSE by default because there's no reason to copy blocked languages in standard situations.
// 'blocked=TRUE' is recommanded when 'source_override=FALSE' because it means full-copy
@@ -236,55 +208,117 @@ Key procs
add_blocked_language(language, source_override || from_holder.blocked_languages[language])
return TRUE
+/// Transfers all mind languages to the supplied language holder.
+/datum/language_holder/proc/transfer_mind_languages(datum/language_holder/to_holder)
+ for(var/language in understood_languages)
+ if(LANGUAGE_MIND in understood_languages[language])
+ remove_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_MIND)
+ to_holder.grant_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_MIND)
+ for(var/language in spoken_languages)
+ if(LANGUAGE_MIND in spoken_languages[language])
+ remove_language(language, SPOKEN_LANGUAGE, LANGUAGE_MIND)
+ to_holder.grant_language(language, SPOKEN_LANGUAGE, LANGUAGE_MIND)
+ for(var/language in blocked_languages)
+ if(LANGUAGE_MIND in blocked_languages[language])
+ remove_blocked_language(language, LANGUAGE_MIND)
+ to_holder.add_blocked_language(language, LANGUAGE_MIND)
+
+ if(owner)
+ get_selected_language()
+ if(to_holder.owner)
+ to_holder.get_selected_language()
+
+/// A global assoc list containing prototypes of all language holders
+/// [Language holder typepath] to [language holder instance]
+/// Used for easy reference of what can speak what without needing to constantly recreate language holders.
+GLOBAL_LIST_INIT(prototype_language_holders, init_language_holder_prototypes())
-//************************************************
-//* Specific language holders *
-//* Use atom language sources only. *
-//************************************************/
+/// Inits the global list of language holder prototypes.
+/proc/init_language_holder_prototypes()
+ var/list/prototypes = list()
+ for(var/holdertype in typesof(/datum/language_holder))
+ prototypes[holdertype] = new holdertype()
+ return prototypes
+
+/*
+ * Specific language holders presets
+ *
+ * Prefer to use [LANGUGAE_ATOM]. Atom languages will stick through species changes but not mindswaps.
+ */
/datum/language_holder/alien
- understood_languages = list(/datum/language/xenocommon = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/xenocommon = list(LANGUAGE_ATOM))
- blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/xenocommon = list(LANGUAGE_ATOM)
+ )
+ spoken_languages = list(
+ /datum/language/xenocommon = list(LANGUAGE_ATOM)
+ )
+ blocked_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM)
+ )
/datum/language_holder/apid
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/apidite = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/apidite = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/apidite = list(LANGUAGE_ATOM)
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/apidite = list(LANGUAGE_ATOM)
+ )
/datum/language_holder/clockmob
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/ratvar = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/ratvar = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/ratvar = list(LANGUAGE_ATOM)
+ )
+ spoken_languages = list(
+ /datum/language/ratvar = list(LANGUAGE_ATOM)
+ )
/datum/language_holder/construct
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/narsie = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/narsie = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/narsie = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/narsie = list(LANGUAGE_ATOM),
+ )
/datum/language_holder/drone
understood_languages = list(/datum/language/drone = list(LANGUAGE_ATOM),
- /datum/language/machine = list(LANGUAGE_ATOM))
+ /datum/language/machine = list(LANGUAGE_ATOM))
spoken_languages = list(/datum/language/drone = list(LANGUAGE_ATOM))
blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
/datum/language_holder/drone/syndicate
- blocked_languages = list()
+ blocked_languages = null
+
+/datum/language_holder/human_basic
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM)
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM)
+ )
/datum/language_holder/jelly
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/slime = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/slime = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/slime = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/slime = list(LANGUAGE_ATOM),
+ )
/datum/language_holder/oozeling
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/slime = list(LANGUAGE_ATOM))
+ /datum/language/slime = list(LANGUAGE_ATOM))
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/slime = list(LANGUAGE_ATOM))
+ /datum/language/slime = list(LANGUAGE_ATOM))
/datum/language_holder/lightbringer
understood_languages = list(/datum/language/slime = list(LANGUAGE_ATOM))
@@ -292,10 +326,14 @@ Key procs
blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
/datum/language_holder/lizard
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/draconic = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/draconic = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/draconic = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/draconic = list(LANGUAGE_ATOM),
+ )
/datum/language_holder/lizard/ash
understood_languages = list(/datum/language/draconic = list(LANGUAGE_ATOM))
@@ -303,19 +341,29 @@ Key procs
blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
/datum/language_holder/monkey
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/monkey = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/monkey = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/monkey = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/monkey = list(LANGUAGE_ATOM),
+ )
/datum/language_holder/mushroom
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/mushroom = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/mushroom = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/mushroom = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/mushroom = list(LANGUAGE_ATOM),
+ )
/datum/language_holder/slime
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/slime = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/slime = list(LANGUAGE_ATOM),
+ )
spoken_languages = list(/datum/language/slime = list(LANGUAGE_ATOM))
/datum/language_holder/swarmer
@@ -330,7 +378,7 @@ Key procs
/datum/language_holder/spider
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/buzzwords = list(LANGUAGE_ATOM))
+ /datum/language/buzzwords = list(LANGUAGE_ATOM))
spoken_languages = list(/datum/language/buzzwords = list(LANGUAGE_ATOM))
/datum/language_holder/venus
@@ -339,95 +387,143 @@ Key procs
blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
/datum/language_holder/synthetic
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/uncommon = list(LANGUAGE_ATOM),
- /datum/language/machine = list(LANGUAGE_ATOM),
- /datum/language/draconic = list(LANGUAGE_ATOM),
- /datum/language/moffic = list(LANGUAGE_ATOM),
- /datum/language/calcic = list(LANGUAGE_ATOM),
- /datum/language/voltaic = list(LANGUAGE_ATOM),
- /datum/language/apidite = list(LANGUAGE_ATOM),
- /datum/language/sonus = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/uncommon = list(LANGUAGE_ATOM),
- /datum/language/machine = list(LANGUAGE_ATOM),
- /datum/language/draconic = list(LANGUAGE_ATOM),
- /datum/language/moffic = list(LANGUAGE_ATOM),
- /datum/language/calcic = list(LANGUAGE_ATOM),
- /datum/language/voltaic = list(LANGUAGE_ATOM),
- /datum/language/apidite = list(LANGUAGE_ATOM),
- /datum/language/sonus = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/uncommon = list(LANGUAGE_ATOM),
+ /datum/language/machine = list(LANGUAGE_ATOM),
+ /datum/language/draconic = list(LANGUAGE_ATOM),
+ /datum/language/moffic = list(LANGUAGE_ATOM),
+ /datum/language/calcic = list(LANGUAGE_ATOM),
+ /datum/language/voltaic = list(LANGUAGE_ATOM),
+ /datum/language/apidite = list(LANGUAGE_ATOM),
+ /datum/language/sonus = list(LANGUAGE_ATOM)
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/uncommon = list(LANGUAGE_ATOM),
+ /datum/language/machine = list(LANGUAGE_ATOM),
+ /datum/language/draconic = list(LANGUAGE_ATOM),
+ /datum/language/moffic = list(LANGUAGE_ATOM),
+ /datum/language/calcic = list(LANGUAGE_ATOM),
+ /datum/language/voltaic = list(LANGUAGE_ATOM),
+ /datum/language/apidite = list(LANGUAGE_ATOM),
+ /datum/language/sonus = list(LANGUAGE_ATOM)
+ )
/datum/language_holder/moth
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/moffic = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/moffic = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/moffic = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/moffic = list(LANGUAGE_ATOM),
+ )
/datum/language_holder/skeleton
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/calcic = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/calcic = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/calcic = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/calcic = list(LANGUAGE_ATOM),
+ )
/datum/language_holder/ethereal
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/voltaic = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/voltaic = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/voltaic = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/voltaic = list(LANGUAGE_ATOM),
+ )
/datum/language_holder/golem
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/terrum = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/terrum = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/terrum = list(LANGUAGE_ATOM)
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/terrum = list(LANGUAGE_ATOM)
+ )
/datum/language_holder/golem/bone
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/terrum = list(LANGUAGE_ATOM),
- /datum/language/calcic = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/terrum = list(LANGUAGE_ATOM),
- /datum/language/calcic = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/terrum = list(LANGUAGE_ATOM),
+ /datum/language/calcic = list(LANGUAGE_ATOM)
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/terrum = list(LANGUAGE_ATOM),
+ /datum/language/calcic = list(LANGUAGE_ATOM)
+ )
/datum/language_holder/golem/runic
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/terrum = list(LANGUAGE_ATOM),
- /datum/language/narsie = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/terrum = list(LANGUAGE_ATOM),
- /datum/language/narsie = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/terrum = list(LANGUAGE_ATOM),
+ /datum/language/narsie = list(LANGUAGE_ATOM)
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/terrum = list(LANGUAGE_ATOM),
+ /datum/language/narsie = list(LANGUAGE_ATOM)
+ )
/datum/language_holder/fly
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/buzzwords = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/buzzwords = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/buzzwords = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/buzzwords = list(LANGUAGE_ATOM),
+ )
/datum/language_holder/diona
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/sylvan = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/sylvan = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/sylvan = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/sylvan = list(LANGUAGE_ATOM),
+ )
/datum/language_holder/shadowpeople
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/shadowtongue = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/shadowtongue = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/shadowtongue = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/shadowtongue = list(LANGUAGE_ATOM),
+ )
/datum/language_holder/psyphoza
- understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/sonus = list(LANGUAGE_ATOM),
- /datum/language/sylvan = list(LANGUAGE_ATOM))
- spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
- /datum/language/sonus = list(LANGUAGE_ATOM),
- /datum/language/sylvan = list(LANGUAGE_ATOM))
+ understood_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/sonus = list(LANGUAGE_ATOM),
+ /datum/language/sylvan = list(LANGUAGE_ATOM)
+ )
+ spoken_languages = list(
+ /datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/sonus = list(LANGUAGE_ATOM),
+ /datum/language/sylvan = list(LANGUAGE_ATOM)
+ )
/datum/language_holder/empty
- understood_languages = list()
- spoken_languages = list()
+ understood_languages = null
+ spoken_languages = null
+
+/datum/language_holder/universal
+ understood_languages = null
+ spoken_languages = null
/datum/language_holder/universal/New()
- ..()
+ . = ..()
grant_all_languages()
diff --git a/code/modules/language/language_menu.dm b/code/modules/language/language_menu.dm
index 47af82f578924..fbc9986768295 100644
--- a/code/modules/language/language_menu.dm
+++ b/code/modules/language/language_menu.dm
@@ -8,7 +8,6 @@
language_holder = null
. = ..()
-
/datum/language_menu/ui_state(mob/user)
return GLOB.language_menu_state
@@ -43,46 +42,40 @@
var/list/data = list()
var/is_admin = check_rights_for(user.client, R_ADMIN) || check_rights_for(user.client, R_DEBUG)
- var/atom/movable/AM = language_holder.get_atom()
- if(isliving(AM))
- data["is_living"] = TRUE
- else
- data["is_living"] = FALSE
+ var/atom/movable/speaker = language_holder.owner
+ data["is_living"] = isliving(speaker)
data["known_languages"] = list()
- for(var/lang in GLOB.all_languages)
- var/result = language_holder.has_language(lang) || language_holder.has_language(lang, TRUE)
+ for(var/datum/language/language as anything in GLOB.all_languages)
+ var/result = language_holder.has_language(language) || language_holder.has_language(language, SPOKEN_LANGUAGE)
if(!result)
continue
- var/datum/language/language = lang
- var/list/L = list()
+ var/list/lang_data = list()
- L["name"] = initial(language.name)
- L["is_default"] = (language == language_holder.selected_language)
- if(AM)
- L["can_speak"] = AM.can_speak_language(language)
- L["can_understand"] = AM.has_language(language)
+ lang_data["name"] = initial(language.name)
+ lang_data["is_default"] = (language == language_holder.selected_language)
+ if(speaker)
+ lang_data["can_speak"] = speaker.can_speak_language(language)
+ lang_data["can_understand"] = speaker.has_language(language)
- if(lang == /datum/language/metalanguage) // metalanguage is only visible to admins
+ if(language == /datum/language/metalanguage) // metalanguage is only visible to admins
if(!(is_admin || HAS_TRAIT(user, TRAIT_METALANGUAGE_KEY_ALLOWED)))
continue
- data["known_languages"] += list(L)
+ UNTYPED_LIST_ADD(data["known_languages"], lang_data)
- if(is_admin || isobserver(AM))
+ if(is_admin || isobserver(speaker))
data["admin_mode"] = TRUE
data["omnitongue"] = language_holder.omnitongue
-
data["unknown_languages"] = list()
- for(var/lang in GLOB.all_languages)
- if(language_holder.has_language(lang) || language_holder.has_language(lang, TRUE))
+ for(var/datum/language/language as anything in GLOB.all_languages)
+ if(language_holder.has_language(language) || language_holder.has_language(language, TRUE))
continue
- var/datum/language/language = lang
- var/list/L = list()
+ var/list/lang_data = list()
- L["name"] = initial(language.name)
+ lang_data["name"] = initial(language.name)
- data["unknown_languages"] += list(L)
+ UNTYPED_LIST_ADD(data["unknown_languages"], lang_data)
else
data["admin_mode"] = null
data["omnitongue"] = null
@@ -90,18 +83,17 @@
return data
/datum/language_menu/ui_act(action, params)
- if(..())
+ . = ..()
+ if(.)
return
var/mob/user = usr
- var/atom/movable/AM = language_holder.get_atom()
-
+ var/atom/movable/speaker = language_holder.owner
+ var/is_admin = check_rights_for(user.client, R_ADMIN) || check_rights_for(user.client, R_DEBUG)
var/language_name = params["language_name"]
var/datum/language/language_datum
- for(var/lang in GLOB.all_languages)
- var/datum/language/language = lang
+ for(var/datum/language/language as anything in GLOB.all_languages)
if(language_name == initial(language.name))
language_datum = language
- var/is_admin = check_rights_for(user.client, R_ADMIN) || check_rights_for(user.client, R_DEBUG)
switch(action)
if("select_default")
@@ -111,67 +103,68 @@
language_datum != /datum/language/metalanguage && \
!HAS_TRAIT(user, TRAIT_METALANGUAGE_KEY_ALLOWED) && \
!is_admin)
- var/no = alert(user, "You're giving up your power to speak in a powerful language that everyone understands. Do you really wish to do that?", "WARNING!", "Yes", "No")
- if(no != "Yes")
+ var/metachoice = tgui_alert(user, "You're giving up your power to speak in a powerful language that everyone understands. Do you really wish to do that?", "WARNING!", list("Yes", "No"))
+ if(metachoice != "Yes")
return
- if(AM.can_speak_language(language_datum))
+ if(speaker.can_speak_language(language_datum))
language_holder.selected_language = language_datum
. = TRUE
if("grant_language")
- if((is_admin || isobserver(AM)) && language_datum)
+ if((is_admin || isobserver(speaker)) && language_datum)
var/list/choices = list("Only Spoken", "Only Understood", "Both")
- var/choice = input(user,"How do you want to add this language?","[language_datum]",null) as null|anything in choices
- var/spoken = FALSE
- var/understood = FALSE
+ var/choice = tgui_input_list(user, "How do you want to add this language?", "[language_datum]", choices)
+ if(isnull(choice))
+ return
+ var/adding_flags = NONE
switch(choice)
if("Only Spoken")
- spoken = TRUE
+ adding_flags |= SPOKEN_LANGUAGE
if("Only Understood")
- understood = TRUE
+ adding_flags |= UNDERSTOOD_LANGUAGE
if("Both")
- spoken = TRUE
- understood = TRUE
- if(language_holder.blocked_languages.Find(language_datum))
- var/blocked_language_choice = alert(user,"The [language_name] language is in this mob's list of blocked languages. Do you wish to remove it so you may give the mob the [language_name] language?","[language_datum]", "Yes", "No")
- if(blocked_language_choice == "Yes")
- language_holder.remove_blocked_language(language_datum)
- message_admins("[key_name_admin(user)] removed the [language_name] language from [key_name_admin(AM)]'s blocked languages list.")
- log_admin("[key_name(user)] removed the language [language_name] from [key_name(AM)]'s blocked languages list.")
- language_holder.grant_language(language_datum, understood, spoken)
- if(spoken && language_datum == /datum/language/metalanguage)
- var/yes = alert(user, "You have added speakable Metalanguage. Do you wish to give them a trait that they can use language key(,`) to say that? Otherwise, they'll have no way to say that, or, instead, you should set their default language to metalanguage.", "Give Metalangauge trait?", "Yes", "No")
+ adding_flags |= ALL
+ if(LAZYACCESS(language_holder.blocked_languages, language_datum))
+ choice = tgui_alert(user, "Do you want to lift the blockage that's also preventing the language to be spoken or understood?", "[language_datum]", list("Yes", "No"))
+ if(choice == "Yes")
+ language_holder.remove_blocked_language(language_datum, LANGUAGE_ALL)
+ message_admins("[key_name_admin(user)] removed the [language_name] language from [key_name_admin(speaker)]'s blocked languages list.")
+ log_admin("[key_name(user)] removed the language [language_name] from [key_name(speaker)]'s blocked languages list.")
+ language_holder.grant_language(language_datum, adding_flags)
+ if((adding_flags & SPOKEN_LANGUAGE) && (language_datum == /datum/language/metalanguage))
+ var/yes = tgui_alert(user, "You have added speakable Metalanguage. Do you wish to give them a trait that they can use language key(,`) to say that? Otherwise, they'll have no way to say that, or, instead, you should set their default language to metalanguage.", "Give Metalangauge trait?", list("Yes", "No"))
if(yes == "Yes")
ADD_TRAIT(user, TRAIT_METALANGUAGE_KEY_ALLOWED, "lang_added_by_admin")
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)].")
+ message_admins("[key_name_admin(user)] granted the [language_name] language to [key_name_admin(speaker)].")
+ log_admin("[key_name(user)] granted the language [language_name] to [key_name(speaker)].")
. = TRUE
if("remove_language")
- if((is_admin || isobserver(AM)) && language_datum)
+ if((is_admin || isobserver(speaker)) && language_datum)
var/list/choices = list("Only Spoken", "Only Understood", "Both")
- var/choice = input(user,"Which part do you wish to remove?","[language_datum]",null) as null|anything in choices
- var/spoken = FALSE
- var/understood = FALSE
+ var/choice = tgui_input_list(user, "Which part do you wish to remove?", "[language_datum]", choices)
+ if(isnull(choice))
+ return
+ var/removing_flags = NONE
switch(choice)
if("Only Spoken")
- spoken = TRUE
+ removing_flags |= SPOKEN_LANGUAGE
if("Only Understood")
- understood = TRUE
+ removing_flags |= UNDERSTOOD_LANGUAGE
if("Both")
- spoken = TRUE
- understood = TRUE
- language_holder.remove_language(language_datum, understood, spoken)
- if(spoken && language_datum == /datum/language/metalanguage)
+ removing_flags |= ALL
+
+ language_holder.remove_language(language_datum, removing_flags)
+ if((removing_flags & SPOKEN_LANGUAGE) && (language_datum == /datum/language/metalanguage))
REMOVE_TRAIT(user, TRAIT_METALANGUAGE_KEY_ALLOWED, "lang_added_by_admin")
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)].")
+ message_admins("[key_name_admin(user)] removed the [language_name] language to [key_name_admin(speaker)].")
+ log_admin("[key_name(user)] removed the language [language_name] to [key_name(speaker)].")
. = TRUE
if("toggle_omnitongue")
- if(is_admin || isobserver(AM))
+ if(is_admin || isobserver(speaker))
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)].")
+ message_admins("[key_name_admin(user)] [language_holder.omnitongue ? "enabled" : "disabled"] the ability to speak all languages (that they know) of [key_name_admin(speaker)].")
+ log_admin("[key_name(user)] [language_holder.omnitongue ? "enabled" : "disabled"] the ability to speak all languages (that_they know) of [key_name(speaker)].")
. = TRUE
diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm
index 7199aa1e6b220..105deac6bf761 100644
--- a/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher.dm
@@ -18,7 +18,7 @@
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb_continuous = list("smashes", "crushes", "cleaves", "chops", "pulps")
attack_verb_simple = list("smash", "crush", "cleave", "chop", "pulp")
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_CUT
actions_types = list(/datum/action/item_action/toggle_light)
light_system = MOVABLE_LIGHT
diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm
index f2a5a5add17c3..e835264d1c353 100644
--- a/code/modules/mining/equipment/mining_tools.dm
+++ b/code/modules/mining/equipment/mining_tools.dm
@@ -128,7 +128,7 @@
custom_materials = list(/datum/material/iron=50)
attack_verb_continuous = list("bashes", "batters", "bludgeons", "whacks")
attack_verb_simple = list("bash", "batter", "bludgeon", "whack")
- sharpness = IS_SHARP
+ sharpness = SHARP
/obj/item/shovel/Initialize(mapload)
. = ..()
diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm
index ff5b9ac353ce1..8083967f78af0 100644
--- a/code/modules/mining/equipment/survival_pod.dm
+++ b/code/modules/mining/equipment/survival_pod.dm
@@ -4,7 +4,7 @@
icon_state = "away"
dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
requires_power = FALSE
- has_gravity = STANDARD_GRAVITY
+ default_gravity = STANDARD_GRAVITY
area_flags = BLOBS_ALLOWED | UNIQUE_AREA
//Survival Capsule
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index 2ec126b7947dd..c2c8abb594648 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -582,7 +582,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/obj/item/shared_storage/blue)
if(!user.can_read(src))
return FALSE
to_chat(user, "You flip through the pages of the book, quickly and conveniently learning every language in existence. Somewhat less conveniently, the aging book crumbles to dust in the process. Whoops.")
- user.grant_all_languages()
+ user.grant_all_languages(source = LANGUAGE_BABEL)
new /obj/effect/decal/cleanable/ash(get_turf(user))
qdel(src)
@@ -765,7 +765,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/obj/item/shared_storage/blue)
hitsound = 'sound/weapons/bladeslice.ogg'
hitsound_on = 'sound/weapons/bladeslice.ogg'
w_class = WEIGHT_CLASS_BULKY
- sharpness = IS_SHARP
+ sharpness = SHARP_DISMEMBER
bleed_force = BLEED_CUT
faction_bonus_force = 45
nemesis_factions = list("mining", "boss")
@@ -859,7 +859,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/obj/item/shared_storage/blue)
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
flags_1 = CONDUCT_1
- sharpness = IS_SHARP
+ sharpness = SHARP_DISMEMBER
bleed_force = BLEED_CUT
w_class = WEIGHT_CLASS_BULKY
force = 1
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 3b46d8299efa2..bd793766da4f6 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -78,6 +78,7 @@
var/hit_amount = (100 - armour_block) / 100
add_bleeding(I.bleed_force * hit_amount)
if(I.force)
+ var/limb_damage = affecting.get_damage() //We need to save this for later to simplify dismemberment
var/armour_block = run_armor_check(affecting, MELEE, armour_penetration = I.armour_penetration)
apply_damage(I.force, I.damtype, affecting, armour_block)
if(I.damtype == BRUTE && (IS_ORGANIC_LIMB(affecting)))
@@ -102,18 +103,26 @@
to_chat(src, "The heat from [I] cauterizes your bleeding!")
playsound(src, 'sound/surgery/cautery2.ogg', 70)
- //dismemberment
- var/dismemberthreshold = (((affecting.max_damage * 2) / max(I.is_sharp(), 0.5)) - (affecting.get_damage() + ((I.w_class - 3) * 10) + ((I.attack_weight - 1) * 15)))
- if(HAS_TRAIT(src, TRAIT_EASYDISMEMBER))
- dismemberthreshold -= 50
- if(I.is_sharp())
- dismemberthreshold = min(((affecting.max_damage * 2) - affecting.get_damage()), dismemberthreshold) //makes it so limbs wont become immune to being dismembered if the item is sharp
- if(stat == DEAD)
- dismemberthreshold = dismemberthreshold / 3
- if(I.force >= dismemberthreshold && I.force >= 10)
- if(affecting.dismember(I.damtype))
- I.add_mob_blood(src)
- playsound(get_turf(src), I.get_dismember_sound(), 80, 1)
+ var/dismember_limb = FALSE
+ var/weapon_sharpness = I.is_sharp()
+
+ if(((HAS_TRAIT(src, TRAIT_EASYDISMEMBER) && limb_damage) || (weapon_sharpness == SHARP_DISMEMBER_EASY)) && prob(I.force))
+ dismember_limb = TRUE
+ //Easy dismemberment on the mob allows even blunt weapons to potentially delimb, but only if the limb is already damaged
+ //Certain weapons are so sharp/strong they have a chance to cleave right through a limb without following the normal restrictions
+
+ else if(weapon_sharpness > SHARP || (weapon_sharpness == SHARP && stat == DEAD))
+ //Delimbing cannot normally occur with blunt weapons
+ //You also aren't cutting someone's arm off with a scalpel unless they're already dead
+
+ if(limb_damage >= affecting.max_damage)
+ dismember_limb = TRUE
+ //You can only cut a limb off if it is already damaged enough to be fully disabled
+
+ if(dismember_limb && (affecting.body_zone != BODY_ZONE_HEAD || stat != CONSCIOUS) && affecting.dismember(I.damtype))
+ I.add_mob_blood(src)
+ playsound(get_turf(src), I.get_dismember_sound(), 80, 1)
+
return TRUE //successful attack
/mob/living/carbon/send_item_attack_message(obj/item/I, mob/living/user, hit_area, obj/item/bodypart/hit_bodypart)
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 06d5bf93df7b0..57b1155aed6bf 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -32,7 +32,10 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/skinned_type
var/list/no_equip = list() // slots the race can't equip stuff to
var/nojumpsuit = 0 // this is sorta... weird. it basically lets you equip stuff that usually needs jumpsuits without one, like belts and pockets and ids
- var/species_language_holder = /datum/language_holder
+ /// What languages this species can understand and say.
+ /// Use a [language holder datum][/datum/language_holder] typepath in this var.
+ /// Should never be null.
+ var/datum/language_holder/species_language_holder = /datum/language_holder/human_basic
/**
* Visible CURRENT bodyparts that are unique to a species.
* DO NOT USE THIS AS A LIST OF ALL POSSIBLE BODYPARTS AS IT WILL FUCK
@@ -481,6 +484,16 @@ GLOBAL_LIST_EMPTY(features_by_species)
C.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/species, multiplicative_slowdown=speedmod)
+ // All languages associated with this language holder are added with source [LANGUAGE_SPECIES]
+ // rather than source [LANGUAGE_ATOM], so we can track what to remove if our species changes again
+ var/datum/language_holder/gaining_holder = GLOB.prototype_language_holders[species_language_holder]
+ for(var/language in gaining_holder.understood_languages)
+ C.grant_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_SPECIES)
+ for(var/language in gaining_holder.spoken_languages)
+ C.grant_language(language, SPOKEN_LANGUAGE, LANGUAGE_SPECIES)
+ for(var/language in gaining_holder.blocked_languages)
+ C.add_blocked_language(language, LANGUAGE_SPECIES)
+
SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species)
@@ -511,6 +524,16 @@ GLOBAL_LIST_EMPTY(features_by_species)
for(var/i in inherent_factions)
C.faction -= i
C.remove_movespeed_modifier(/datum/movespeed_modifier/species)
+
+ // Removes all languages previously associated with [LANGUAGE_SPECIES], gaining our new species will add new ones back
+ var/datum/language_holder/losing_holder = GLOB.prototype_language_holders[species_language_holder]
+ for(var/language in losing_holder.understood_languages)
+ C.remove_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_SPECIES)
+ for(var/language in losing_holder.spoken_languages)
+ C.remove_language(language, SPOKEN_LANGUAGE, LANGUAGE_SPECIES)
+ for(var/language in losing_holder.blocked_languages)
+ C.remove_blocked_language(language, LANGUAGE_SPECIES)
+
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
/datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour)
@@ -1773,6 +1796,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/armor_block = H.run_armor_check(affecting, MELEE, "Your armor has protected your [hit_area]!", "Your armor has softened a hit to your [hit_area]!",I.armour_penetration)
var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords)
+ var/limb_damage = affecting.get_damage() //We need to save this for later to simplify dismemberment
apply_damage(I.force, I.damtype, def_zone, armor_block, H)
if (I.bleed_force)
@@ -1808,17 +1832,25 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(!I.force)
return 0 //item force is zero
- //dismemberment
- var/dismemberthreshold = ((affecting.max_damage * 2) - affecting.get_damage()) //don't take the current hit into account.
- var/attackforce = (((I.w_class - 3) * 5) + ((I.attack_weight - 1) * 14) + ((I.is_sharp()-1) * 20)) //all the variables that go into ripping off a limb in one handy package. Force is absent because it's already been taken into account by the limb being damaged
- if(HAS_TRAIT(src, TRAIT_EASYDISMEMBER))
- dismemberthreshold -= 30
- if(I.is_sharp())
- attackforce = max(attackforce, I.force)
- if(attackforce >= dismemberthreshold && I.force >= 10)
- if(affecting.dismember(I.damtype))
- I.add_mob_blood(H)
- playsound(get_turf(H), I.get_dismember_sound(), 80, 1)
+ var/dismember_limb = FALSE
+ var/weapon_sharpness = I.is_sharp()
+
+ if(((HAS_TRAIT(H, TRAIT_EASYDISMEMBER) && limb_damage) || (weapon_sharpness == SHARP_DISMEMBER_EASY)) && prob(I.force))
+ dismember_limb = TRUE
+ //Easy dismemberment on the mob allows even blunt weapons to potentially delimb, but only if the limb is already damaged
+ //Certain weapons are so sharp/strong they have a chance to cleave right through a limb without following the normal restrictions
+
+ else if(weapon_sharpness > SHARP || (weapon_sharpness == SHARP && H.stat == DEAD))
+ //Delimbing cannot normally occur with blunt weapons
+ //You also aren't cutting someone's arm off with a scalpel unless they're already dead
+
+ if(limb_damage >= affecting.max_damage)
+ dismember_limb = TRUE
+ //You can only cut a limb off if it is already damaged enough to be fully disabled
+
+ if(dismember_limb && (affecting.body_zone != BODY_ZONE_HEAD || H.stat != CONSCIOUS) && affecting.dismember(I.damtype))
+ I.add_mob_blood(H)
+ playsound(get_turf(H), I.get_dismember_sound(), 80, 1)
if(I.damtype == BRUTE && (I.force >= max(10, armor_block) && hit_area == BODY_ZONE_HEAD))
if(!I.is_sharp() && H.mind && H.stat == CONSCIOUS && H != user && (H.health - (I.force * I.attack_weight)) <= 0) // rev deconversion through blunt trauma.
@@ -2937,21 +2969,23 @@ GLOBAL_LIST_EMPTY(features_by_species)
* Returns a list containing perks, or an empty list.
*/
/datum/species/proc/create_pref_language_perk()
- var/list/to_add = list()
// Grab galactic common as a path, for comparisons
var/datum/language/common_language = /datum/language/common
// Now let's find all the languages they can speak that aren't common
var/list/bonus_languages = list()
- var/datum/language_holder/temp_holder = new species_language_holder()
- for(var/datum/language/language_type as anything in temp_holder.spoken_languages)
+ var/datum/language_holder/basic_holder = GLOB.prototype_language_holders[species_language_holder]
+ for(var/datum/language/language_type as anything in basic_holder.spoken_languages)
if(ispath(language_type, common_language))
continue
bonus_languages += initial(language_type.name)
- // If we have any languages we can speak: create a perk for them all
- if(length(bonus_languages))
+ if(!length(bonus_languages))
+ return // You're boring
+
+ var/list/to_add = list()
+ if(common_language in basic_holder.spoken_languages)
to_add += list(list(
SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
SPECIES_PERK_ICON = "comment",
@@ -2959,7 +2993,13 @@ GLOBAL_LIST_EMPTY(features_by_species)
SPECIES_PERK_DESC = "Alongside [initial(common_language.name)], [plural_form] gain the ability to speak [english_list(bonus_languages)].",
))
- qdel(temp_holder)
+ else
+ to_add += list(list(
+ SPECIES_PERK_TYPE = SPECIES_NEUTRAL_PERK,
+ SPECIES_PERK_ICON = "comment",
+ SPECIES_PERK_NAME = "Foreign Speaker",
+ SPECIES_PERK_DESC = "[plural_form] may not speak [initial(common_language.name)], but they can speak [english_list(bonus_languages)].",
+ ))
return to_add
diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
index 17a6a73e58717..b32ddee29e193 100644
--- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
@@ -225,7 +225,7 @@
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
item_flags = ABSTRACT | DROPDEL | ISWEAPON
w_class = WEIGHT_CLASS_HUGE
- sharpness = IS_SHARP
+ sharpness = SHARP_DISMEMBER_EASY
bleed_force = BLEED_DEEP_WOUND
/obj/item/light_eater/Initialize(mapload)
diff --git a/code/modules/mob/living/carbon/human/status_procs.dm b/code/modules/mob/living/carbon/human/status_procs.dm
index 337cba0d5413e..66175a6d3eb75 100644
--- a/code/modules/mob/living/carbon/human/status_procs.dm
+++ b/code/modules/mob/living/carbon/human/status_procs.dm
@@ -42,12 +42,12 @@
/mob/living/carbon/human/set_drugginess(amount)
..()
if(!amount)
- remove_language(/datum/language/beachbum, TRUE, TRUE, LANGUAGE_DRUGGY)
+ remove_language(/datum/language/beachbum, source = LANGUAGE_DRUGGY)
/mob/living/carbon/human/adjust_drugginess(amount)
..()
if(!dna.check_mutation(STONER))
if(druggy)
- grant_language(/datum/language/beachbum, TRUE, TRUE, LANGUAGE_DRUGGY)
+ grant_language(/datum/language/beachbum, source = LANGUAGE_DRUGGY)
else
- remove_language(/datum/language/beachbum, TRUE, TRUE, LANGUAGE_DRUGGY)
+ remove_language(/datum/language/beachbum, source = LANGUAGE_DRUGGY)
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 9cd3a1486c1ba..8803ff19701e5 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -736,31 +736,36 @@ generate/load female uniform sprites matching all previously decided variables
standing = center_image(standing, isinhands ? inhand_x_dimension : worn_x_dimension, isinhands ? inhand_y_dimension : worn_y_dimension)
- //Handle held offsets
- var/mob/M = loc
- if(istype(M))
- var/list/L = get_held_offsets()
- if(L)
- standing.pixel_x += L["x"] //+= because of center()ing
- standing.pixel_y += L["y"]
+ //Worn offsets
+ var/list/offsets = get_worn_offsets(isinhands)
+ standing.pixel_x += offsets[1]
+ standing.pixel_y += offsets[2]
standing.alpha = alpha
standing.color = color
return standing
-/obj/item/proc/get_held_offsets()
- var/list/L
- if(ismob(loc))
- if(ishuman(loc))
- var/mob/living/carbon/human/H = loc
- L = H.dna?.species.get_item_offsets_for_index(src)
- if(L)
- return L
- var/mob/M = loc
- L = M.get_item_offsets_for_index(M.get_held_index_of_item(src))
-
- return L
+/// Returns offsets used for equipped item overlays in list(px_offset,py_offset) form.
+/obj/item/proc/get_worn_offsets(isinhands)
+ . = list(0,0) //(px,py)
+ if(isinhands)
+ //Handle held offsets
+ var/mob/holder = loc
+ var/list/offsets
+ if(ismob(loc))
+ if(ishuman(loc))
+ var/mob/living/carbon/human/H = loc
+ offsets = H.dna?.species.get_item_offsets_for_index(src)
+ if(offsets)
+ return offsets
+ if(istype(holder))
+ offsets = holder.get_item_offsets_for_index(holder.get_held_index_of_item(src))
+ if(offsets)
+ .[1] = offsets["x"]
+ .[2] = offsets["y"]
+ else
+ .[2] = worn_y_offset
//Can't think of a better way to do this, sadly
diff --git a/code/modules/mob/living/carbon/say.dm b/code/modules/mob/living/carbon/say.dm
index 398f794bac0da..18e96feca1a30 100644
--- a/code/modules/mob/living/carbon/say.dm
+++ b/code/modules/mob/living/carbon/say.dm
@@ -18,10 +18,10 @@
return FALSE
return ..()
-/mob/living/carbon/could_speak_language(datum/language/dt)
- if(CHECK_BITFIELD(initial(dt.flags), TONGUELESS_SPEECH))
+/mob/living/carbon/could_speak_language(datum/language/language_path)
+ if(CHECK_BITFIELD(initial(language_path.flags), TONGUELESS_SPEECH))
return TRUE
- var/obj/item/organ/tongue/T = getorganslot(ORGAN_SLOT_TONGUE)
- if(T)
- return T.could_speak_language(dt)
+ var/obj/item/organ/tongue/spoken_with = getorganslot(ORGAN_SLOT_TONGUE)
+ if(spoken_with)
+ return spoken_with.could_speak_language(language_path)
return FALSE
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index e182215eff8d8..d44cc72c47cd7 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1309,6 +1309,7 @@
buckled.unbuckle_mob(src, force = TRUE)
if(has_buckled_mobs())
unbuckle_all_mobs(force = TRUE)
+ refresh_gravity()
. = ..()
if(.)
if(client)
diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm
index 25b8815ef6b45..d8ef6435e45ab 100644
--- a/code/modules/mob/living/living_movement.dm
+++ b/code/modules/mob/living/living_movement.dm
@@ -24,7 +24,7 @@
var/area/old_area = get_area(old_turf)
var/area/new_area = get_area(new_turf)
// If the area gravity has changed, then it's possible that our state has changed, so update
- if(old_area.has_gravity != new_area.has_gravity)
+ if(old_area.default_gravity != new_area.default_gravity)
refresh_gravity()
/mob/living/onTransitZ(old_z, new_z, same_z_layer, notify_contents)
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index f53dccfad0991..78fcdc3b789c2 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -124,10 +124,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
return TRUE
if(!language) // get_message_mods() proc finds a language key, and add the language to LANGUAGE_EXTENSION
- language = message_mods[LANGUAGE_EXTENSION]
-
- if(!language) // there's no language argument and LANGUAGE_EXTENSION has no language. failsafe.
- language = get_selected_language()
+ language = message_mods[LANGUAGE_EXTENSION] || get_selected_language()
// if you add a new language that works like everyone doesn't understand (i.e. anti-metalanguage), add an additional condition after this
// i.e.) if(!language) language = /datum/language/nobody_understands
@@ -259,8 +256,10 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
var/eavesdrop_range = 0
if(message_mods[WHISPER_MODE]) //If we're whispering
eavesdrop_range = EAVESDROP_EXTRA_RANGE
+
var/list/listening = get_hearers_in_view(message_range+eavesdrop_range, source, SEE_INVISIBLE_MAXIMUM)
var/list/the_dead = list()
+
for(var/mob/M as() in GLOB.player_list)
if(!M) //yogs
continue //yogs | null in player_list for whatever reason :shrug:
@@ -434,12 +433,3 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
/mob/living/whisper(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
say("#[message]", bubble_type, spans, sanitize, language, ignore_spam, forced)
-
-/mob/living/get_language_holder(shadow=TRUE)
- if(mind && shadow)
- // Mind language holders shadow mob holders.
- . = mind.get_language_holder()
- if(.)
- return .
-
- . = ..()
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index d5ac9e90095f7..b355c0c9f4864 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -629,19 +629,21 @@ CREATION_TEST_IGNORE_SUBTYPES(/mob/living/silicon/ai)
if(incapacitated())
return
+ var/mob/user = src
var/input
- switch(alert("Would you like to select a hologram based on a crew member, an animal, or switch to a unique avatar?",,"Crew Member","Unique","Animal"))
+ var/list/hologram_choice = list("Crew Member","Unique","Animal")
+ switch(tgui_alert(user, "Would you like to select a hologram based on a crew member, an animal, or switch to a unique avatar?", "Choices", hologram_choice))
if("Crew Member")
var/list/personnel_list = list()
- for(var/datum/record/crew/record in GLOB.manifest.locked)//Look in data core locked.
+ for(var/datum/record/locked/record in GLOB.manifest.locked)//Look in data core locked.
personnel_list["[record.name]: [record.rank]"] = record.character_appearance//Pull names, rank, and image.
if(!length(personnel_list))
alert("No suitable records found. Aborting.")
return
- if(personnel_list.len)
- input = input("Select a crew member:") as null|anything in sort_list(personnel_list)
+ if(length(personnel_list))
+ input = tgui_input_list(user, "Select a crew member", "AI Hologram Selection", sort_list(personnel_list))
var/mutable_appearance/character_icon = personnel_list[input]
if(character_icon)
qdel(holo_icon)//Clear old icon so we're not storing it in memory.
@@ -667,7 +669,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/mob/living/silicon/ai)
"spider" = 'icons/mob/animal.dmi'
)
- input = input("Please select a hologram:") as null|anything in sort_list(icon_list)
+ input = tgui_input_list(user, "Please select a hologram:", "Animal Choice", sort_list(icon_list))
if(input)
qdel(holo_icon)
switch(input)
@@ -687,7 +689,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/mob/living/silicon/ai)
"horror" = 'icons/mob/ai.dmi'
)
- input = input("Please select a hologram:") as null|anything in sort_list(icon_list)
+ input = tgui_input_list(user, "Please select a hologram", "Hologram Choice", sort_list(icon_list))
if(input)
qdel(holo_icon)
switch(input)
diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm
index 5c498dd31ad53..66131fc677c49 100644
--- a/code/modules/mob/living/silicon/pai/software.dm
+++ b/code/modules/mob/living/silicon/pai/software.dm
@@ -180,7 +180,7 @@
sec.remove_hud_from(src)
if("universal_translator")
if(!languages_granted)
- grant_all_languages(TRUE, TRUE, TRUE, LANGUAGE_SOFTWARE)
+ grant_all_languages(source = LANGUAGE_SOFTWARE)
languages_granted = TRUE
if("wipe_core")
var/confirm = alert(src, "Are you certain you want to wipe yourself?", "Personality Wipe", "Yes", "No")
diff --git a/code/modules/mob/living/simple_animal/slime/powers.dm b/code/modules/mob/living/simple_animal/slime/powers.dm
index 131ac488b324d..588289cb1767a 100644
--- a/code/modules/mob/living/simple_animal/slime/powers.dm
+++ b/code/modules/mob/living/simple_animal/slime/powers.dm
@@ -244,7 +244,7 @@
M.maxHealth = round(M.maxHealth * 1.3)
M.health = M.maxHealth
if(transformeffects & SLIME_EFFECT_PINK)
- M.grant_language(/datum/language/common, TRUE, TRUE)
+ M.grant_language(/datum/language/common)
var/datum/language_holder/LH = M.get_language_holder()
LH.selected_language = /datum/language/common
if(transformeffects & SLIME_EFFECT_BLUESPACE)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 4ba48a2af5a5e..0b04e5ecbd70c 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1279,12 +1279,11 @@
fully_replace_character_name(real_name, new_name)
///Show the language menu for this mob
-/mob/verb/open_language_menu()
+/mob/verb/open_language_menu_verb()
set name = "Open Language Menu"
set category = "IC"
- var/datum/language_holder/H = get_language_holder()
- H.open_language_menu(usr)
+ get_language_holder().open_language_menu(usr)
///Adjust the nutrition of a mob
/mob/proc/adjust_nutrition(var/change) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 6161f9d1e4901..9969981ccadf7 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -233,9 +233,7 @@ CREATION_TEST_IGNORE_SELF(/mob)
///Is the mob actively shifting?
var/shifting
- ///Currently possesses a typing indicator icon
- var/typing_indicator = FALSE
- /// Thinking indicator - mob has input window open
- var/thinking_indicator = FALSE
- /// User is thinking in character. Used to revert to thinking state after stop_typing
- var/thinking_IC = FALSE
+ ///the icon currently used for the typing indicator's bubble
+ var/active_typing_indicator
+ ///the icon currently used for the thinking indicator's bubble
+ var/active_thinking_indicator
diff --git a/code/modules/mob/mob_stat.dm b/code/modules/mob/mob_stat.dm
index 604db92362d38..8f75fde9dc687 100644
--- a/code/modules/mob/mob_stat.dm
+++ b/code/modules/mob/mob_stat.dm
@@ -118,6 +118,7 @@
tab_data[REF(src)] = list(
text="[name]",
tag = STAT_PANEL_TAG(src),
+ image = FAST_REF(src),
type=STAT_ATOM
)
var/max_item_sanity = MAX_ITEMS_TO_READ
@@ -150,11 +151,13 @@
var/image/override_image = overrides[A]
atom_name = override_image.name
image_overrides[A] = override_image
- var/list/item_group = atom_count["[atom_type][atom_name]"]
+ // use the max item sanity as an extention if the unique flag is set, since its unique
+ var/extension = (A.flags_1 & STAT_UNIQUE_1) && max_item_sanity
+ var/list/item_group = atom_count["[atom_type][atom_name][extension]"]
if (item_group)
item_group += A
else
- atom_count["[A.type][A.name]"] = list(A)
+ atom_count["[A.type][A.name][extension]"] = list(A)
// To many icon types per tile
if (icon_count_sanity-- <= 0)
break
@@ -168,20 +171,26 @@
for (var/obj/item/stack/stack_item as() in atom_items)
item_count += stack_item.amount
var/atom_name = first_atom.name
+ var/image_icon
if (image_overrides[first_atom])
var/image/override_image = image_overrides[first_atom]
atom_name = override_image.name
+ image_icon = FAST_REF(override_image)
+ else
+ image_icon = FAST_REF(first_atom)
tab_data[REF(first_atom)] = list(
text = "[atom_name][item_count > 1 ? " (x[item_count])" : ""]",
tag = STAT_PANEL_TAG(first_atom),
+ image = image_icon,
type = STAT_ATOM
)
// Display self
tab_data[REF(client.mob)] = list(
- text = client.mob.name,
- tag = "You",
- type = STAT_ATOM
- )
+ text = client.mob.name,
+ tag = "You",
+ image = FAST_REF(client.mob),
+ type = STAT_ATOM
+ )
/mob/proc/get_all_verbs()
var/list/all_verbs = new
diff --git a/code/modules/ninja/energy_katana.dm b/code/modules/ninja/energy_katana.dm
index e1e7b7c15f154..fd52baf236c79 100644
--- a/code/modules/ninja/energy_katana.dm
+++ b/code/modules/ninja/energy_katana.dm
@@ -18,7 +18,7 @@
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
slot_flags = ITEM_SLOT_BACK|ITEM_SLOT_BELT
- sharpness = IS_SHARP
+ sharpness = SHARP_DISMEMBER_EASY
bleed_force = BLEED_DEEP_WOUND
max_integrity = 200
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 6d1b2b5d3e4fd..7b18ba27d539c 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -97,7 +97,7 @@
throw_speed = 4
colour = "crimson"
custom_materials = list(/datum/material/gold = 750)
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_SURFACE
resistance_flags = FIRE_PROOF
unique_reskin_icon = list("Oak" = "pen-fountain-o",
@@ -252,7 +252,7 @@
hitsound = 'sound/weapons/edagger.ogg'
embedding = list(embed_chance = 200, max_damage_mult = 15, armour_block = 40) //rule of cool
throwforce = 35
- sharpness = IS_SHARP
+ sharpness = SHARP_DISMEMBER
bleed_force = BLEED_CUT
playsound(user, 'sound/weapons/saberon.ogg', 5, 1)
to_chat(user, "[src] is now active.")
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index 0a28124b85fbf..77e0d572c62a6 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -42,6 +42,7 @@ By design, d1 is the smallest direction and d2 is the highest
layer = WIRE_LAYER //Above hidden pipes, GAS_PIPE_HIDDEN_LAYER
anchored = TRUE
obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
+ flags_1 = STAT_UNIQUE_1
var/d1 = 0 // cable direction 1 (see above)
var/d2 = 1 // cable direction 2 (see above)
var/datum/powernet/powernet
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index 0a1b5360dfd39..948273ea8c49f 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -28,6 +28,8 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/sprite_number = 0
var/ztrait //Set to a valid ZTRAIT define to have the gravgen provide gravity to all of the zlevels with said trait. Ex: ZTRAIT_STATION
+ /// The gravity field created by the generator.
+ var/datum/proximity_monitor/advanced/gravity/gravity_field
/obj/machinery/gravity_generator/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG)
return FALSE
@@ -97,12 +99,6 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
/obj/machinery/gravity_generator/main/station
ztrait = ZTRAIT_STATION
-/obj/machinery/gravity_generator/main/station/Initialize(mapload)
- . = ..()
- setup_parts()
- middle.add_overlay("activated")
- update_list()
-
//
// Generator an admin can spawn
//
@@ -131,6 +127,13 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
var/broken_state = 0
var/setting = 1 //Gravity value when on
+/obj/machinery/gravity_generator/main/Initialize(mapload)
+ . = ..()
+ setup_parts()
+ middle.add_overlay("activated")
+ update_list()
+ update_mobs()
+
/obj/machinery/gravity_generator/main/Destroy() // If we somehow get deleted, remove all of our other parts.
investigate_log("was destroyed!", INVESTIGATE_GRAVITY)
on = FALSE
@@ -173,7 +176,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
charge_count = 0
breaker = 0
set_power()
- set_state(0)
+ disable()
investigate_log("has broken down.", INVESTIGATE_GRAVITY)
/obj/machinery/gravity_generator/main/set_fix()
@@ -304,8 +307,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
investigate_log("is now [charging_state == POWER_UP ? "charging" : "discharging"].", INVESTIGATE_GRAVITY)
update_appearance()
-// Set the state of the gravity.
-/obj/machinery/gravity_generator/main/proc/set_state(new_state)
+/obj/machinery/gravity_generator/main/proc/enable()
charging_state = POWER_IDLE
on = new_state
update_use_power(on ? ACTIVE_POWER_USE : IDLE_POWER_USE)
@@ -323,11 +325,35 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
investigate_log("was brought offline and there is now no gravity for this level.", INVESTIGATE_GRAVITY)
message_admins("The gravity generator was brought offline with no backup generator. [ADMIN_VERBOSEJMP(src)]")
+ var/old_gravity = gravity_in_level()
+ complete_state_update()
+ gravity_field = new(src, 2, TRUE, 6)
+
+ if (!old_gravity)
+ if(SSticker.current_state == GAME_STATE_PLAYING)
+ investigate_log("was brought online and is now producing gravity for this level.", INVESTIGATE_GRAVITY)
+ message_admins("The gravity generator was brought online [ADMIN_VERBOSEJMP(src)]")
+ shake_everyone()
+
+/obj/machinery/gravity_generator/main/proc/disable()
+ charging_state = POWER_IDLE
+ on = FALSE
+ use_power = IDLE_POWER_USE
+
+ QDEL_NULL(gravity_field)
+ var/old_gravity = gravity_in_level()
+ complete_state_update()
+
+ if (old_gravity)
+ if(SSticker.current_state == GAME_STATE_PLAYING)
+ investigate_log("was brought offline and there is now no gravity for this level.", INVESTIGATE_GRAVITY)
+ message_admins("The gravity generator was brought offline with no backup generator. [ADMIN_VERBOSEJMP(src)]")
+ shake_everyone()
+
+/obj/machinery/gravity_generator/main/proc/complete_state_update()
update_appearance()
update_list()
ui_update()
- if(alert)
- shake_everyone()
// Charge/Discharge and turn on/off gravity when you reach 0/100 percent.
// Also emit radiation and handle the overlays.
@@ -336,9 +362,9 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
return
if(charging_state != POWER_IDLE)
if(charging_state == POWER_UP && charge_count >= 100)
- set_state(1)
+ enable()
else if(charging_state == POWER_DOWN && charge_count <= 0)
- set_state(0)
+ disable()
else
if(charging_state == POWER_UP)
charge_count += 2
@@ -417,11 +443,6 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
GLOB.gravity_generators["[theZ]"] -= src
SSmapping.calculate_z_level_gravity(z)
-/obj/machinery/gravity_generator/main/proc/change_setting(value)
- if(value != setting)
- setting = value
- shake_everyone()
-
/obj/machinery/gravity_generator/main/beforeShuttleMove(turf/newT, rotation, move_mode, obj/docking_port/mobile/moving_dock)
. = ..()
on = FALSE
@@ -433,6 +454,9 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
on = TRUE
update_list()
+/obj/machinery/gravity_generator/main/proc/update_mobs()
+ for(var/mob/living/animal in GLOB.mob_living_list)
+ animal.refresh_gravity()
// Misc
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index 6fa3812ad6a64..5a734b282a3f5 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -107,7 +107,7 @@
attack_verb_continuous = list("attacks", "slashes", "cuts", "slices")
attack_verb_simple = list("attack", "slash", "cut", "slice")
force = 12
- sharpness = IS_SHARP
+ sharpness = SHARP
can_charge = FALSE
dead_cell = TRUE
usesound = list('sound/items/welder.ogg', 'sound/items/welder2.ogg')
diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm
index e9b8d8b024021..bc911c1142cc9 100644
--- a/code/modules/projectiles/guns/magic/staff.dm
+++ b/code/modules/projectiles/guns/magic/staff.dm
@@ -107,7 +107,7 @@
force = 20
armour_penetration = 75
block_flags = BLOCKING_ACTIVE | BLOCKING_NASTY | BLOCKING_PROJECTILE
- sharpness = IS_SHARP
+ sharpness = SHARP_DISMEMBER
bleed_force = BLEED_DEEP_WOUND
max_charges = 4
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index eafa82b4c8f10..fe71fbc591037 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -2191,12 +2191,12 @@
/datum/reagent/consumable/ratlight/on_mob_metabolize(mob/living/L)
L.add_blocked_language(subtypesof(/datum/language/) - /datum/language/ratvar, LANGUAGE_REAGENT)
- L.grant_language(/datum/language/ratvar, TRUE, TRUE, LANGUAGE_REAGENT)
+ L.grant_language(/datum/language/ratvar, source = LANGUAGE_REAGENT)
..()
/datum/reagent/consumable/ratlight/on_mob_end_metabolize(mob/living/L)
L.remove_blocked_language(subtypesof(/datum/language/), LANGUAGE_REAGENT)
- L.remove_language(/datum/language/ratvar, TRUE, TRUE, LANGUAGE_REAGENT)
+ L.remove_language(/datum/language/ratvar, source = LANGUAGE_REAGENT)
L.set_light(-1)
..()
diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
index a9180e678bd38..186c650810fc3 100644
--- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm
+++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
@@ -126,7 +126,7 @@
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb_continuous = list("stabs", "slashes", "attacks")
attack_verb_simple = list("stab", "slash", "attack")
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_SURFACE
var/static/icon/broken_outline = icon('icons/obj/drinks.dmi', "broken")
diff --git a/code/modules/research/nanites/nanite_programs/buffing.dm b/code/modules/research/nanites/nanite_programs/buffing.dm
index aadebf25e1776..65cd5241e6642 100644
--- a/code/modules/research/nanites/nanite_programs/buffing.dm
+++ b/code/modules/research/nanites/nanite_programs/buffing.dm
@@ -177,6 +177,7 @@
name = "metallic armblade"
desc = "Nanites have formed this extremely sharp blade around your arm. Owie."
force = 15
+ sharpness = SHARP_DISMEMBER
icon = 'icons/obj/nanite.dmi'
icon_state = "nanite_blade"
item_state = "nanite_blade"
diff --git a/code/modules/research/xenobiology/crossbreeding/_weapons.dm b/code/modules/research/xenobiology/crossbreeding/_weapons.dm
index 62c196fb254e4..8e9bd675f5fd1 100644
--- a/code/modules/research/xenobiology/crossbreeding/_weapons.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_weapons.dm
@@ -9,6 +9,7 @@ Slimecrossing Weapons
name = "slimy boneblade"
desc = "What remains of the bones in your arm. Incredibly sharp, and painful for both you and your opponents."
force = 15
+ sharpness = SHARP_DISMEMBER
force_string = "painful"
/obj/item/melee/arm_blade/slime/attack(mob/living/L, mob/user)
diff --git a/code/modules/research/xenobiology/crossbreeding/transformative.dm b/code/modules/research/xenobiology/crossbreeding/transformative.dm
index 6f5ba4ce7ecdf..40b5cd0207658 100644
--- a/code/modules/research/xenobiology/crossbreeding/transformative.dm
+++ b/code/modules/research/xenobiology/crossbreeding/transformative.dm
@@ -128,7 +128,7 @@ transformative extracts:
/obj/item/slimecross/transformative/pink/do_effect(mob/living/simple_animal/slime/S)
..()
- S.grant_language(/datum/language/common, TRUE, TRUE)
+ S.grant_language(/datum/language/common)
var/datum/language_holder/LH = S.get_language_holder()
LH.selected_language = /datum/language/common
diff --git a/code/modules/research/xenobiology/crossbreeding/warping.dm b/code/modules/research/xenobiology/crossbreeding/warping.dm
index eff700d090aaf..81a2240be439c 100644
--- a/code/modules/research/xenobiology/crossbreeding/warping.dm
+++ b/code/modules/research/xenobiology/crossbreeding/warping.dm
@@ -825,7 +825,7 @@ GLOBAL_DATUM(warped_room, /datum/map_template/warped_room)
icon_state = "yellow"
dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
requires_power = FALSE
- has_gravity = TRUE
+ default_gravity = STANDARD_GRAVITY
teleport_restriction = TELEPORT_ALLOW_NONE
/area/warped_room/get_virtual_z(turf/T)
diff --git a/code/modules/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/ruins/spaceruin_code/hilbertshotel.dm
index ea4655efac38e..5e83faddef81d 100644
--- a/code/modules/ruins/spaceruin_code/hilbertshotel.dm
+++ b/code/modules/ruins/spaceruin_code/hilbertshotel.dm
@@ -341,7 +341,7 @@ GLOBAL_VAR_INIT(hhmysteryRoomNumber, 1337)
name = "Hilbert's Hotel Room"
icon_state = "hilbertshotel"
requires_power = FALSE
- has_gravity = TRUE
+ default_gravity = STANDARD_GRAVITY
teleport_restriction = TELEPORT_ALLOW_NONE
area_flags = HIDDEN_AREA
dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
@@ -426,7 +426,7 @@ GLOBAL_VAR_INIT(hhmysteryRoomNumber, 1337)
icon_state = "hilbertshotel"
requires_power = FALSE
area_flags = HIDDEN_AREA | UNIQUE_AREA
- has_gravity = TRUE
+ default_gravity = STANDARD_GRAVITY
teleport_restriction = TELEPORT_ALLOW_NONE
/obj/item/abstracthotelstorage
diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm
index 89c92e3f140e6..9c0ead370fe8a 100644
--- a/code/modules/security_levels/keycard_authentication.dm
+++ b/code/modules/security_levels/keycard_authentication.dm
@@ -22,6 +22,8 @@ GLOBAL_DATUM_INIT(keycard_events, /datum/events, new)
var/datum/callback/ev
var/event = ""
var/obj/machinery/keycard_auth/event_source
+ ///Triggering ID card relayed to auth devices to make sure two keycards are used.
+ var/obj/item/card/id/triggering_card
var/mob/triggerer = null
var/waiting = 0
@@ -67,24 +69,31 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/keycard_auth, 26)
/obj/machinery/keycard_auth/ui_act(action, params)
if(..() || waiting || !allowed(usr))
return
+ var/obj/item/card/swipe_id = usr.get_idcard()
+ if(!swipe_id || !istype(swipe_id))
+ to_chat(usr, "No ID card detected.")
+ return
switch(action)
if("red_alert")
if(!event_source)
- sendEvent(KEYCARD_RED_ALERT)
+ sendEvent(KEYCARD_RED_ALERT, swipe_id)
. = TRUE
if("emergency_maint")
if(!event_source)
- sendEvent(KEYCARD_EMERGENCY_MAINTENANCE_ACCESS)
+ sendEvent(KEYCARD_EMERGENCY_MAINTENANCE_ACCESS, swipe_id)
. = TRUE
if("auth_swipe")
if(event_source)
+ if(swipe_id == event_source.triggering_card)
+ to_chat(usr, "Invalid ID. Confirmation ID must not equal trigger ID.")
+ return
event_source.trigger_event(usr)
event_source = null
update_appearance()
. = TRUE
if("bsa_unlock")
if(!event_source)
- sendEvent(KEYCARD_BSA_UNLOCK)
+ sendEvent(KEYCARD_BSA_UNLOCK, swipe_id)
. = TRUE
/obj/machinery/keycard_auth/update_appearance(updates)
@@ -102,8 +111,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/keycard_auth, 26)
. += mutable_appearance(icon, "auth_on")
. += emissive_appearance(icon, "auth_on", alpha = src.alpha)
-/obj/machinery/keycard_auth/proc/sendEvent(event_type)
+/obj/machinery/keycard_auth/proc/sendEvent(event_type, obj/item/card/id/swipe_id)
triggerer = usr
+ triggering_card = swipe_id //Shouldn't need qdel registering due to very short time before this var resets.
event = event_type
waiting = 1
GLOB.keycard_events.fireEvent("triggerEvent", src)
@@ -111,6 +121,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/keycard_auth, 26)
/obj/machinery/keycard_auth/proc/eventSent()
triggerer = null
+ triggering_card = null
event = ""
waiting = 0
diff --git a/code/modules/shuttle/shuttle_creation/shuttle_creator.dm b/code/modules/shuttle/shuttle_creation/shuttle_creator.dm
index 5631e7fd3ed61..fa2ee82768ce6 100644
--- a/code/modules/shuttle/shuttle_creation/shuttle_creator.dm
+++ b/code/modules/shuttle/shuttle_creation/shuttle_creator.dm
@@ -334,7 +334,7 @@ GLOBAL_LIST_EMPTY(custom_shuttle_machines) //Machines that require updating (He
newS.setup(str)
newS.set_dynamic_lighting()
//Shuttles always have gravity
- newS.has_gravity = TRUE
+ newS.default_gravity = STANDARD_GRAVITY
newS.requires_power = TRUE
//Record the area for use when creating the docking port
recorded_shuttle_area = newS
diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/alien_artifact.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/alien_artifact.dm
index 761873d5532c7..f04fdbf4a7ea2 100644
--- a/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/alien_artifact.dm
+++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/loot/alien_artifact.dm
@@ -58,7 +58,7 @@
clockwork_warp_allowed = FALSE
requires_power = FALSE
mood_bonus = -999
- has_gravity = STANDARD_GRAVITY
+ default_gravity = STANDARD_GRAVITY
ambience_index = AMBIENCE_NONE
sound_environment = SOUND_ENVIRONMENT_DRUGGED
teleport_restriction = TELEPORT_ALLOW_NONE
diff --git a/code/modules/shuttle/super_cruise/orbital_poi_generator/ruin_generator/exoplanets/biomes/_biome.dm b/code/modules/shuttle/super_cruise/orbital_poi_generator/ruin_generator/exoplanets/biomes/_biome.dm
index 7708ca8473314..365e92ca4e0a8 100644
--- a/code/modules/shuttle/super_cruise/orbital_poi_generator/ruin_generator/exoplanets/biomes/_biome.dm
+++ b/code/modules/shuttle/super_cruise/orbital_poi_generator/ruin_generator/exoplanets/biomes/_biome.dm
@@ -18,7 +18,7 @@
/area/planet
icon_state = "Unknown Planet"
- has_gravity = STANDARD_GRAVITY
+ default_gravity = STANDARD_GRAVITY
flags_1 = NONE
sound_environment = SOUND_AREA_LAVALAND
always_unpowered = TRUE
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index 29b39806745f5..4523b3587140c 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -7,7 +7,15 @@
slot = ORGAN_SLOT_TONGUE
attack_verb_continuous = list("licks", "slobbers", "slaps", "frenches", "tongues")
attack_verb_simple = list("lick", "slobber", "slap", "french", "tongue")
- var/list/languages_possible
+ /**
+ * A cached list of paths of all the languages this tongue is capable of speaking
+ *
+ * Relates to a mob's ability to speak a language - a mob must be able to speak the language
+ * and have a tongue able to speak the language (or omnitongue) in order to actually speak said language
+ *
+ * To modify this list for subtypes, see [/obj/item/organ/internal/tongue/proc/get_possible_languages]. Do not modify directly.
+ */
+ VAR_PRIVATE/list/languages_possible
var/say_mod = "says"
var/ask_mod = "asks"
var/yell_mod = "yells"
@@ -15,9 +23,33 @@
var/liked_food = JUNKFOOD | FRIED
var/disliked_food = GROSS | RAW | CLOTH | GORE
var/toxic_food = TOXIC
- var/taste_sensitivity = 15 // lower is more sensitive.
+ // Determines how "sensitive" this tongue is to tasting things, lower is more sensitive.
+ /// See [/mob/living/proc/get_taste_sensitivity].
+ var/taste_sensitivity = 15
+ /// Whether this tongue modifies speech via signal
var/modifies_speech = FALSE
- var/static/list/languages_possible_base = typecacheof(list(
+
+/obj/item/organ/tongue/Initialize(mapload)
+ . = ..()
+ // Setup the possible languages list
+ // - get_possible_languages gives us a list of language paths
+ // - then we cache it via string list
+ // this results in tongues with identical possible languages sharing a cached list instance
+ languages_possible = string_list(get_possible_languages())
+
+/**
+ * Used in setting up the "languages possible" list.
+ *
+ * Override to have your tongue be only capable of speaking certain languages
+ * Extend to hvae a tongue capable of speaking additional languages to the base tongue
+ *
+ * While a user may be theoretically capable of speaking a language, they cannot physically speak it
+ * UNLESS they have a tongue with that language possible, UNLESS UNLESS they have omnitongue enabled.
+ */
+/obj/item/organ/tongue/proc/get_possible_languages()
+ RETURN_TYPE(/list)
+ // This is the default list of languages most humans should be capable of speaking
+ return list(
/datum/language/aphasia,
/datum/language/apidite,
/datum/language/beachbum,
@@ -36,11 +68,8 @@
/datum/language/sylvan,
/datum/language/terrum,
/datum/language/uncommon,
- /datum/language/sonus))
-
-/obj/item/organ/tongue/Initialize(mapload)
- . = ..()
- languages_possible = languages_possible_base
+ /datum/language/sonus,
+ )
/obj/item/organ/tongue/proc/handle_speech(datum/source, list/speech_args)
SIGNAL_HANDLER
@@ -56,8 +85,8 @@
M.RegisterSignal(M, COMSIG_MOB_SAY, TYPE_PROC_REF(/mob/living/carbon, handle_tongueless_speech))
return ..()
-/obj/item/organ/tongue/could_speak_language(datum/language/dt)
- return is_type_in_typecache(dt, languages_possible)
+/obj/item/organ/tongue/could_speak_language(datum/language/language_path)
+ return (language_path in languages_possible)
/obj/item/organ/tongue/lizard
name = "forked tongue"
@@ -191,16 +220,17 @@
say_mod = "hisses"
taste_sensitivity = 10 // LIZARDS ARE ALIENS CONFIRMED
modifies_speech = TRUE // not really, they just hiss
- var/static/list/languages_possible_alien = typecacheof(list(
+
+// Aliens can only speak alien and a few other languages.
+/obj/item/organ/tongue/alien/get_possible_languages()
+ return list(
/datum/language/xenocommon,
/datum/language/common,
+ /datum/language/uncommon,
/datum/language/draconic,
/datum/language/ratvar,
- /datum/language/monkey))
-
-/obj/item/organ/tongue/alien/Initialize(mapload)
- . = ..()
- languages_possible = languages_possible_alien
+ /datum/language/monkey,
+ )
/obj/item/organ/tongue/alien/handle_speech(datum/source, list/speech_args)
playsound(owner, "hiss", 25, 1, 1)
@@ -264,9 +294,8 @@
modifies_speech = TRUE
taste_sensitivity = 25 // not as good as an organic tongue
-/obj/item/organ/tongue/robot/Initialize(mapload)
- . = ..()
- languages_possible = languages_possible_base += typecacheof(/datum/language/machine) + typecacheof(/datum/language/voltaic)
+/obj/item/organ/tongue/robot/get_possible_languages()
+ return ..() + /datum/language/machine + /datum/language/voltaic
/obj/item/organ/tongue/robot/emp_act(severity)
owner.emote("scream")
@@ -301,9 +330,8 @@
taste_sensitivity = 101 // Not a tongue, they can't taste shit
toxic_food = NONE
-/obj/item/organ/tongue/ethereal/Initialize(mapload)
- . = ..()
- languages_possible = languages_possible_base += typecacheof(/datum/language/voltaic)
+/obj/item/organ/tongue/ethereal/get_possible_languages()
+ return ..() + /datum/language/voltaic
/obj/item/organ/tongue/golem
name = "mineral tongue"
@@ -312,9 +340,8 @@
taste_sensitivity = 101 //They don't eat.
icon_state = "adamantine_cords"
-/obj/item/organ/tongue/golem/Initialize(mapload)
- . = ..()
- languages_possible = languages_possible_base += typecacheof(/datum/language/terrum)
+/obj/item/organ/tongue/golem/get_possible_languages()
+ return ..() + /datum/language/terrum
/obj/item/organ/tongue/golem/bananium
name = "bananium tongue"
@@ -344,9 +371,8 @@
toxic_food = NONE
disliked_food = NONE
-/obj/item/organ/tongue/slime/Initialize(mapload)
- . = ..()
- languages_possible = languages_possible_base += typecacheof(/datum/language/slime)
+/obj/item/organ/tongue/slime/get_possible_languages()
+ return ..() + /datum/language/slime
/obj/item/organ/tongue/moth
name = "mothic tongue"
diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm
index d6bae900fb7ba..006dbda02f1f0 100644
--- a/code/modules/surgery/tools.dm
+++ b/code/modules/surgery/tools.dm
@@ -157,7 +157,7 @@
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP_ACCURATE
+ sharpness = SHARP
bleed_force = BLEED_CUT
tool_behaviour = TOOL_SCALPEL
toolspeed = 1
@@ -182,7 +182,7 @@
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
toolspeed = 0.5
hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP_ACCURATE
+ sharpness = SHARP
bleed_force = BLEED_CUT
/obj/item/scalpel/suicide_act(mob/living/user)
@@ -207,7 +207,7 @@
custom_materials = list(/datum/material/iron=10000, /datum/material/glass=6000)
attack_verb_continuous = list("attacks", "slashes", "saws", "cuts")
attack_verb_simple = list("attack", "slash", "saw", "cut")
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_DEEP_WOUND
tool_behaviour = TOOL_SAW
toolspeed = 1
@@ -233,7 +233,7 @@
toolspeed = 0.5
attack_verb_continuous = list("attacks", "slashes", "saws", "cuts")
attack_verb_simple = list("attack", "slash", "saw", "cut")
- sharpness = IS_SHARP
+ sharpness = SHARP
bleed_force = BLEED_DEEP_WOUND
/obj/item/hacksaw
@@ -252,7 +252,7 @@
custom_materials = list(/datum/material/iron=12000)
attack_verb_continuous = list("attacks", "slashes", "saws", "cuts")
attack_verb_simple = list("attack", "slash", "saw", "cut")
- sharpness = IS_SHARP
+ sharpness = SHARP
tool_behaviour = TOOL_SAW
toolspeed = 2
@@ -358,7 +358,7 @@
light_system = MOVABLE_LIGHT
light_range = 1
light_color = LIGHT_COLOR_GREEN
- sharpness = IS_SHARP_ACCURATE
+ sharpness = SHARP
// It cauterises the wound it causes
bleed_force = 0
diff --git a/code/modules/tgui/states/language_menu.dm b/code/modules/tgui/states/language_menu.dm
index 6389b05cd5e80..72f1c230c93e7 100644
--- a/code/modules/tgui/states/language_menu.dm
+++ b/code/modules/tgui/states/language_menu.dm
@@ -12,6 +12,6 @@ GLOBAL_DATUM_INIT(language_menu_state, /datum/ui_state/language_menu, new)
if(check_rights_for(user.client, R_ADMIN))
. = UI_INTERACTIVE
else if(istype(src_object, /datum/language_menu))
- var/datum/language_menu/LM = src_object
- if(LM.language_holder.get_atom() == user)
+ var/datum/language_menu/my_languages = src_object
+ if(my_languages.language_holder.owner == user)
. = UI_INTERACTIVE
diff --git a/code/modules/tgui/tgui_window.dm b/code/modules/tgui/tgui_window.dm
index 5e27f9b1c9cd6..bc060eae51bce 100644
--- a/code/modules/tgui/tgui_window.dm
+++ b/code/modules/tgui/tgui_window.dm
@@ -83,6 +83,8 @@
var/html = SStgui.basehtml
html = replacetextEx(html, "\[tgui:windowId]", id)
html = replacetextEx(html, "\[tgui:strictMode]", strict_mode)
+ html = replacetextEx(html, "\[tgui:byondMajor\]", client.byond_version)
+ html = replacetextEx(html, "\[tgui:byondMinor\]", client.byond_build)
// Inject assets
var/inline_assets_str = ""
for(var/datum/asset/asset in assets)
diff --git a/code/modules/tgui_input/say_modal/typing.dm b/code/modules/tgui_input/say_modal/typing.dm
index 2325440d36ff3..248e419cc5fe5 100644
--- a/code/modules/tgui_input/say_modal/typing.dm
+++ b/code/modules/tgui_input/say_modal/typing.dm
@@ -1,8 +1,6 @@
-/// Thinking
-GLOBAL_DATUM_INIT(thinking_indicator, /mutable_appearance, mutable_appearance('icons/mob/talk.dmi', "default3", CALCULATE_MOB_OVERLAY_LAYER(TYPING_LAYER)))
+
/// Typing
GLOBAL_DATUM_INIT(blind_typing_indicator, /mutable_appearance, mutable_appearance('icons/mob/talk.dmi', "default0", (-TYPING_LAYER), BLIND_FEATURE_PLANE, appearance_flags = KEEP_TOGETHER))
-GLOBAL_DATUM_INIT(typing_indicator, /mutable_appearance, mutable_appearance('icons/mob/talk.dmi', "default0", CALCULATE_MOB_OVERLAY_LAYER(TYPING_LAYER)))
/** Creates a thinking indicator over the mob. */
/mob/proc/create_thinking_indicator()
@@ -33,21 +31,17 @@ GLOBAL_DATUM_INIT(typing_indicator, /mutable_appearance, mutable_appearance('ico
remove_all_indicators()
return ..()
-/** Sets the mob as "thinking" - with indicator and variable thinking_IC */
+/** Sets the mob as "thinking" - with indicator and the TRAIT_THINKING_IN_CHARACTER trait */
/datum/tgui_say/proc/start_thinking()
- if(!window_open || !istype(client.mob))
- return FALSE
- /// Special exemptions
- if(isabductor(client.mob))
+ if(!window_open)
return FALSE
- client.mob.thinking_IC = TRUE
- client.mob.create_thinking_indicator()
+ return client.start_thinking()
/** Removes typing/thinking indicators and flags the mob as not thinking */
/datum/tgui_say/proc/stop_thinking()
if(!istype(client.mob))
return FALSE
- client.mob.remove_all_indicators()
+ return client.stop_thinking()
/**
* Handles the user typing. After a brief period of inactivity,
@@ -56,53 +50,48 @@ GLOBAL_DATUM_INIT(typing_indicator, /mutable_appearance, mutable_appearance('ico
/datum/tgui_say/proc/start_typing()
if(!istype(client.mob))
return FALSE
- client.mob.remove_thinking_indicator()
- if(!window_open || !client.mob.thinking_IC)
+ if(!window_open)
return FALSE
- client.mob.create_typing_indicator()
- addtimer(CALLBACK(src, PROC_REF(stop_typing)), 5 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_STOPPABLE)
+ return client.start_typing()
/**
- * Callback to remove the typing indicator after a brief period of inactivity.
+ * Remove the typing indicator after a brief period of inactivity or during say events.
* If the user was typing IC, the thinking indicator is shown.
*/
/datum/tgui_say/proc/stop_typing()
- if(!client?.mob)
- return FALSE
- client.mob.remove_typing_indicator()
- if(!window_open || !client.mob.thinking_IC)
+ if(!window_open)
return FALSE
- client.mob.create_thinking_indicator()
+ client.stop_typing()
/// Overrides for overlay creation
/mob/living/create_thinking_indicator()
- if(thinking_indicator || typing_indicator || !thinking_IC || stat != CONSCIOUS )
+ if(active_thinking_indicator || active_typing_indicator || stat != CONSCIOUS || !HAS_TRAIT(src, TRAIT_THINKING_IN_CHARACTER))
return FALSE
- add_overlay(GLOB.thinking_indicator)
- thinking_indicator = TRUE
+ active_thinking_indicator = mutable_appearance('icons/mob/talk.dmi', "[bubble_icon]3", TYPING_LAYER)
+ add_overlay(active_thinking_indicator)
/mob/living/remove_thinking_indicator()
- if(!thinking_indicator)
+ if(!active_thinking_indicator)
return FALSE
- cut_overlay(GLOB.thinking_indicator)
- thinking_indicator = FALSE
+ cut_overlay(active_thinking_indicator)
+ active_thinking_indicator = null
/mob/living/create_typing_indicator(override = FALSE)
- if(typing_indicator || ((thinking_indicator || !thinking_IC) && !override) || stat != CONSCIOUS)
+ if(active_typing_indicator || ((active_thinking_indicator || !HAS_TRAIT(src, TRAIT_THINKING_IN_CHARACTER)) && !override) || stat != CONSCIOUS)
return FALSE
- add_overlay(GLOB.typing_indicator)
+ active_typing_indicator = mutable_appearance('icons/mob/talk.dmi', "[bubble_icon]0", TYPING_LAYER)
+ add_overlay(active_typing_indicator)
add_overlay(GLOB.blind_typing_indicator)
- typing_indicator = TRUE
/mob/living/remove_typing_indicator()
- if(!typing_indicator)
+ if(!active_typing_indicator)
return FALSE
- cut_overlay(GLOB.typing_indicator)
+ cut_overlay(active_typing_indicator)
cut_overlay(GLOB.blind_typing_indicator)
- typing_indicator = FALSE
+ active_typing_indicator = null
/mob/living/remove_all_indicators()
- thinking_IC = FALSE
+ REMOVE_TRAIT(src, TRAIT_THINKING_IN_CHARACTER, CURRENTLY_TYPING_TRAIT)
remove_thinking_indicator()
remove_typing_indicator()
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 6a8bf96075e3f..7788bda1ba68c 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -41,6 +41,7 @@
#include "heretic_rituals.dm"
#include "icon_smoothing_unit_test.dm"
#include "keybinding_init.dm"
+#include "language_transfer.dm"
#include "merge_type.dm"
#include "metabolizing.dm"
#include "missing_icons.dm"
diff --git a/code/modules/unit_tests/language_transfer.dm b/code/modules/unit_tests/language_transfer.dm
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index 947d4163f75b9..d8c49e9a55811 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -32,10 +32,8 @@
var/custom_price
///Does the item have a custom premium price override
var/custom_premium_price
- /** GAGs recolorability
///Whether the product can be recolored by the GAGS system
- var/colorable
- **/
+ //var/colorable
///List of items that have been returned to the vending machine.
var/list/returned_products
/// The category the product was in, if any.
@@ -348,9 +346,7 @@
R.max_amount = amount
R.custom_price = initial(temp.custom_price)
R.custom_premium_price = initial(temp.custom_premium_price)
- /* GAGS recolorability
//R.colorable = !!(initial(temp.greyscale_config) && initial(temp.greyscale_colors) && (initial(temp.flags_1) & IS_PLAYER_COLORABLE_1))
- */
R.category = product_to_category[typepath]
recordlist += R
@@ -907,8 +903,8 @@
switch(action)
if("vend")
. = vend(params)
- //if("select_colors")
- // . = select_colors(params)
+ if("select_colors")
+ . = select_colors(params)
/obj/machinery/vending/proc/can_vend(user, silent=FALSE)
. = FALSE
diff --git a/code/modules/vending/clothesmate.dm b/code/modules/vending/clothesmate.dm
index ac09f4ba2563c..aba6405de02bf 100644
--- a/code/modules/vending/clothesmate.dm
+++ b/code/modules/vending/clothesmate.dm
@@ -28,6 +28,9 @@
/obj/item/clothing/head/beanie/rasta = 3,
/obj/item/clothing/head/beret = 3,
/obj/item/clothing/head/beret/black = 3,
+ /obj/item/clothing/mask/bandana = 3,
+ /obj/item/clothing/mask/bandana/striped = 3,
+ /obj/item/clothing/mask/bandana/skull = 3,
/obj/item/clothing/neck/scarf/pink = 3,
/obj/item/clothing/neck/scarf/red = 3,
/obj/item/clothing/neck/scarf/green = 3,
diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm
index c1cfd17d79391..85812f4d13bf4 100644
--- a/code/modules/vending/wardrobes.dm
+++ b/code/modules/vending/wardrobes.dm
@@ -27,7 +27,7 @@
/obj/item/clothing/shoes/jackboots = 3,
/obj/item/clothing/head/beret/sec = 3,
/obj/item/clothing/head/soft/sec = 3,
- /obj/item/clothing/mask/bandana/red = 3,
+ /obj/item/clothing/mask/bandana/striped/security = 3,
/obj/item/clothing/mask/gas/sechailer = 6,
/obj/item/clothing/under/rank/security/officer/skirt = 3,
/obj/item/clothing/under/rank/security/officer/white = 3,
@@ -68,6 +68,7 @@
/obj/item/clothing/under/rank/medical/doctor/nurse = 4,
/obj/item/clothing/head/costume/nursehat = 4,
/obj/item/clothing/head/beret/med = 4,
+ /obj/item/clothing/mask/bandana/striped/medical = 4,
/obj/item/clothing/under/rank/medical/doctor/blue = 4,
/obj/item/clothing/under/rank/medical/doctor/green = 4,
/obj/item/clothing/under/rank/medical/doctor/purple = 4,
@@ -106,9 +107,11 @@
/obj/item/clothing/under/rank/engineering/engineer/skirt = 3,
/obj/item/clothing/suit/hazardvest = 3,
/obj/item/clothing/shoes/workboots = 3,
+ /obj/item/clothing/head/beret/eng = 3,
+ /obj/item/clothing/mask/bandana/striped/engineering = 3,
/obj/item/clothing/head/utility/hardhat = 3,
/obj/item/clothing/head/utility/hardhat/welding = 3,
- /obj/item/clothing/head/beret/eng = 3)
+ )
contraband = list(/obj/item/clothing/suit/hooded/wintercoat/engineering/old = 3)
refill_canister = /obj/item/vending_refill/wardrobe/engi_wardrobe
dept_req_for_free = ACCOUNT_ENG_BITFLAG
@@ -149,26 +152,33 @@
product_ads = "Upgraded Assistant Style! Pick yours today!;These shorts are comfy and easy to wear, get yours now!"
vend_reply = "Thank you for using the CargoDrobe!"
extra_price = 50
- products = list(/obj/item/clothing/suit/hooded/wintercoat/cargo = 3,
- /obj/item/clothing/under/rank/cargo/tech = 3,
- /obj/item/clothing/under/rank/cargo/tech/skirt = 3,
- /obj/item/clothing/under/plasmaman/cargo = 3,
- /obj/item/clothing/head/helmet/space/plasmaman/cargo = 3,
- /obj/item/clothing/shoes/sneakers/black = 3,
- /obj/item/clothing/gloves/fingerless = 3,
- /obj/item/clothing/head/soft/cargo = 3,
- /obj/item/clothing/head/beret/supply = 3,
- /obj/item/radio/headset/headset_cargo = 3)
- premium = list( /obj/item/clothing/under/rank/cargo/miner = 3,
- /obj/item/clothing/head/costume/mailman = 2,
- /obj/item/clothing/under/misc/mailman/skirt = 2,
- /obj/item/clothing/under/misc/mailman = 2,
- /obj/item/storage/backpack/satchel/mail = 2,
- /obj/item/clothing/under/plasmaman/mailman = 2,
- /obj/item/clothing/head/helmet/space/plasmaman/mailman = 2
+ products = list(
+ /obj/item/clothing/suit/hooded/wintercoat/cargo = 3,
+ /obj/item/clothing/under/rank/cargo/tech = 3,
+ /obj/item/clothing/under/rank/cargo/tech/skirt = 3,
+ /obj/item/clothing/under/plasmaman/cargo = 3,
+ /obj/item/clothing/head/helmet/space/plasmaman/cargo = 3,
+ /obj/item/clothing/shoes/sneakers/black = 3,
+ /obj/item/clothing/gloves/fingerless = 3,
+ /obj/item/clothing/mask/bandana/striped/cargo = 3,
+ /obj/item/clothing/head/soft/cargo = 3,
+ /obj/item/clothing/head/beret/supply = 3,
+ /obj/item/radio/headset/headset_cargo = 3
+ )
+
+ premium = list(
+ /obj/item/clothing/under/rank/cargo/miner = 3,
+ /obj/item/clothing/head/costume/mailman = 2,
+ /obj/item/clothing/under/misc/mailman/skirt = 2,
+ /obj/item/clothing/under/misc/mailman = 2,
+ /obj/item/storage/backpack/satchel/mail = 2,
+ /obj/item/clothing/under/plasmaman/mailman = 2,
+ /obj/item/clothing/head/helmet/space/plasmaman/mailman = 2
+ )
+ contraband = list(
+ /obj/item/radio/headset/headset_quartermaster = 1,
+ /obj/item/clothing/suit/hooded/wintercoat/cargo/old = 3
)
- contraband = list(/obj/item/radio/headset/headset_quartermaster = 1,
- /obj/item/clothing/suit/hooded/wintercoat/cargo/old = 3)
refill_canister = /obj/item/vending_refill/wardrobe/cargo_wardrobe
dept_req_for_free = ACCOUNT_CAR_BITFLAG
@@ -192,7 +202,7 @@
/obj/item/clothing/shoes/sneakers/black = 2,
/obj/item/clothing/gloves/fingerless = 2,
/obj/item/clothing/head/soft/black = 2,
- /obj/item/clothing/mask/bandana/skull = 2,
+ /obj/item/clothing/mask/bandana/skull/black = 2,
/obj/item/clothing/head/beret/sci = 2)
contraband = list(/obj/item/clothing/suit/hooded/techpriest = 2,
@@ -219,6 +229,7 @@
/obj/item/storage/backpack/satchel/tox = 3,
/obj/item/storage/backpack/duffelbag/science = 3,
/obj/item/clothing/suit/hooded/wintercoat/science = 3,
+ /obj/item/clothing/mask/bandana/striped/science = 3,
/obj/item/clothing/under/rank/rnd/scientist = 3,
/obj/item/clothing/under/rank/rnd/scientist/skirt = 3,
/obj/item/clothing/under/plasmaman/science = 3,
@@ -250,9 +261,9 @@
/obj/item/clothing/suit/apron/overalls = 3,
/obj/item/clothing/under/rank/civilian/hydroponics = 3,
/obj/item/clothing/under/rank/civilian/hydroponics/skirt = 3,
+ /obj/item/clothing/mask/bandana/striped/botany = 3,
/obj/item/clothing/under/plasmaman/botany = 3,
/obj/item/clothing/head/helmet/space/plasmaman/botany = 3,
- /obj/item/clothing/mask/bandana/botany = 3,
/obj/item/clothing/accessory/armband/hydro = 3,
/obj/item/clothing/head/cowboy = 3)
contraband = list(/obj/item/clothing/suit/hooded/wintercoat/hydro/old = 3)
@@ -370,6 +381,7 @@
/obj/item/computer_hardware/hard_drive/role/janitor = 2,
/obj/item/clothing/gloves/color/black = 2,
/obj/item/clothing/head/soft/purple = 2,
+ /obj/item/clothing/mask/bandana/purple = 2,
/obj/item/pushbroom = 2,
/obj/item/paint/paint_remover = 2,
/obj/item/melee/flyswatter = 2,
diff --git a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm
index 8bfb1997822da..7a32176d15f95 100644
--- a/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm
+++ b/code/modules/xenoarchaeology/traits/xenoartifact_minors.dm
@@ -80,7 +80,7 @@
return TRUE
/datum/xenoartifact_trait/minor/sharp/on_init(obj/item/xenoartifact/X)
- X.sharpness = IS_SHARP_ACCURATE
+ X.sharpness = SHARP
X.bleed_force = BLEED_CUT
X.force = X.charge_req*0.12
X.attack_verb_continuous = list("cleaves", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
diff --git a/html/changelog.html b/html/changelog.html
index 664a876c0d137..b3678e7091064 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -56,6 +56,68 @@
-->
+
24 December 2024
+
BarteG44 updated:
+
+
Removes the second wire from the c4 charge
+
+
DeltaFire15 updated:
+
+
keycard authentication devices require an ID to use.
+
IDs used to initiate / confirm keycard auth events must not be the same ID.
+
+
PowerfulBacon updated:
+
+
Clients running 515 will now be able to see icons in the alt-click stat panel menu.
+
+
Rukofamicom updated:
+
+
Completely overhauled dismemberment and weapon sharpness. Weapons now have four levels of sharpness: Blunt, Sharp, Dismember and Easy Dismember.
+
Blunt weapons are now only able to cause dismemberment on species that have the easy dismemberment trait, all of which can easily regenerate or otherwise slap their dismembered limb back into place.
+
Species with the easy dismemberment trait (Skeletons, IPCs, Oozelings) have a chance equal to the damage received to lose a targetted limb as long as the limb was already damaged before being struck.
+
Ordinary sharp weapons such as knives and surgical tools can only dismember a dead target.
+
Large weapons such as swords, axes and chainsaws are only able to dismember a limb which has already been fully disabled. This means that it will take at least three hits with a fire axe to take someone's leg off.
+
Bladed energy weapons have a chance equal to their damage to dismember a targetted limb regardless of how much damage it has taken, in addition to being guaranteed to dismember a limb that has been fully disabled by damage. The nightmare's light eater is the only non-energy weapon in this group right now.
+
Mobs cannot be decapitated through the simplified mechanics while they are fully conscious - they must at minimum be asleep or in crit in order for dismemberment to apply when targetting someone's head for balance reasons.
+
+
Therealdoooc213 updated:
+
+
the blackbox is now a large item
+
+
XeonMations updated:
+
+
Fixed AIs being unable to select a crew member as a hologram.
Split personalities can no longer throw out their original body's mind out of their own body permenantly.
+
+
XeonMations, timothymtorres updated:
+
+
Fixed a lot of oversights related to gravity, such as lavaland not having gravity or simplemobs floating roundstart.
+
You can no longer defy gravity through the power of love
+
+
rkz, Ebb-Real, AnturK updated:
+
+
GAGSifies bandanas
+
refactors large mob icons to no longer need snowflaked .dmi sizes. Just edit a variable
+
+
rkz, MrMelbert, Cyberboss updated:
+
+
refactored language holders, species changes will no longer delete all known languages
+
refactored tongue language lists to kill typecache nonsense and handle subtypes sanely
+
unit tests language transfers
+
fix config errors before /world/New()
+
+
rkz, haukeschaumann, JohnFulpWillard, jlsnow301, Crumpaloo, San7890, MrStonedOne, Lilah Novi updated:
+
+
fix custom typing indicators. Cyborgs, xenos, and lawyers (among others) now have unique typing indicators again after 3 years.
+
new *Animated* typing indicator sprites
+
typing in the command bar will now activate typing indicator.
+
traitified a typing indicator var
+
+
22 December 2024
BarteG44 updated:
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index cc31079781f8b..be9c1b3cd1326 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -45235,3 +45235,64 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
- bugfix: you can no longer get to centcom with warped runes
Tsar-Salat:
- bugfix: fixes segmented turf textures applying to nonsegmented turfs... very ugly...
+2024-12-24:
+ BarteG44:
+ - tweak: Removes the second wire from the c4 charge
+ DeltaFire15:
+ - balance: keycard authentication devices require an ID to use.
+ - balance: IDs used to initiate / confirm keycard auth events must not be the same
+ ID.
+ PowerfulBacon:
+ - rscadd: Clients running 515 will now be able to see icons in the alt-click stat
+ panel menu.
+ Rukofamicom:
+ - balance: 'Completely overhauled dismemberment and weapon sharpness. Weapons now
+ have four levels of sharpness: Blunt, Sharp, Dismember and Easy Dismember.'
+ - balance: Blunt weapons are now only able to cause dismemberment on species that
+ have the easy dismemberment trait, all of which can easily regenerate or otherwise
+ slap their dismembered limb back into place.
+ - balance: Species with the easy dismemberment trait (Skeletons, IPCs, Oozelings)
+ have a chance equal to the damage received to lose a targetted limb as long
+ as the limb was already damaged before being struck.
+ - balance: Ordinary sharp weapons such as knives and surgical tools can only dismember
+ a dead target.
+ - balance: Large weapons such as swords, axes and chainsaws are only able to dismember
+ a limb which has already been fully disabled. This means that it will take at
+ least three hits with a fire axe to take someone's leg off.
+ - balance: Bladed energy weapons have a chance equal to their damage to dismember
+ a targetted limb regardless of how much damage it has taken, in addition to
+ being guaranteed to dismember a limb that has been fully disabled by damage.
+ The nightmare's light eater is the only non-energy weapon in this group right
+ now.
+ - balance: Mobs cannot be decapitated through the simplified mechanics while they
+ are fully conscious - they must at minimum be asleep or in crit in order for
+ dismemberment to apply when targetting someone's head for balance reasons.
+ Therealdoooc213:
+ - tweak: the blackbox is now a large item
+ XeonMations:
+ - bugfix: Fixed AIs being unable to select a crew member as a hologram.
+ - refactor: TGUI-ified the AI hologram selection menu.
+ XeonMations, ManiacalAnxiety, therealdoooc213:
+ - bugfix: Split personalities can no longer throw out their original body's mind
+ out of their own body permenantly.
+ XeonMations, timothymtorres:
+ - bugfix: Fixed a lot of oversights related to gravity, such as lavaland not having
+ gravity or simplemobs floating roundstart.
+ - bugfix: You can no longer defy gravity through the power of love
+ rkz, Ebb-Real, AnturK:
+ - rscadd: GAGSifies bandanas
+ - refactor: refactors large mob icons to no longer need snowflaked .dmi sizes. Just
+ edit a variable
+ rkz, MrMelbert, Cyberboss:
+ - refactor: refactored language holders, species changes will no longer delete all
+ known languages
+ - refactor: refactored tongue language lists to kill typecache nonsense and handle
+ subtypes sanely
+ - code_imp: unit tests language transfers
+ - code_imp: fix config errors before /world/New()
+ rkz, haukeschaumann, JohnFulpWillard, jlsnow301, Crumpaloo, San7890, MrStonedOne, Lilah Novi:
+ - bugfix: fix custom typing indicators. Cyborgs, xenos, and lawyers (among others)
+ now have unique typing indicators again after 3 years.
+ - rscadd: new *Animated* typing indicator sprites
+ - rscadd: typing in the command bar will now activate typing indicator.
+ - code_imp: traitified a typing indicator var
diff --git a/html/typing_indicator.html b/html/typing_indicator.html
new file mode 100644
index 0000000000000..2988edff55fa0
--- /dev/null
+++ b/html/typing_indicator.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
diff --git a/icons/mob/clothing/head/costume.dmi b/icons/mob/clothing/head/costume.dmi
index 5c7146529650f..13a743de03452 100644
Binary files a/icons/mob/clothing/head/costume.dmi and b/icons/mob/clothing/head/costume.dmi differ
diff --git a/icons/mob/clothing/head/helmet.dmi b/icons/mob/clothing/head/helmet.dmi
index 130204b8bce9e..e5f8a40a057a4 100644
Binary files a/icons/mob/clothing/head/helmet.dmi and b/icons/mob/clothing/head/helmet.dmi differ
diff --git a/icons/mob/clothing/mask.dmi b/icons/mob/clothing/mask.dmi
index b5514e6cadfdc..25e03333e6587 100644
Binary files a/icons/mob/clothing/mask.dmi and b/icons/mob/clothing/mask.dmi differ
diff --git a/icons/mob/large-worn-icons/64x64/head.dmi b/icons/mob/large-worn-icons/64x64/head.dmi
deleted file mode 100644
index 6486f3a4a1bea..0000000000000
Binary files a/icons/mob/large-worn-icons/64x64/head.dmi and /dev/null differ
diff --git a/icons/mob/talk.dmi b/icons/mob/talk.dmi
index 429e54980382a..1beb0adf98636 100644
Binary files a/icons/mob/talk.dmi and b/icons/mob/talk.dmi differ
diff --git a/icons/obj/clothing/masks.dmi b/icons/obj/clothing/masks.dmi
index 13f977d2e0a16..23475b642f0a0 100644
Binary files a/icons/obj/clothing/masks.dmi and b/icons/obj/clothing/masks.dmi differ
diff --git a/interface/skin.dmf b/interface/skin.dmf
index e8643569a7a5a..335f2d218e238 100644
--- a/interface/skin.dmf
+++ b/interface/skin.dmf
@@ -119,6 +119,15 @@ window "mainwindow"
anchor2 = none
is-visible = false
saved-params = ""
+ elem "commandbar_spy"
+ type = BROWSER
+ is-default = false
+ pos = 0,0
+ size = 200x200
+ anchor1 = -1,-1
+ anchor2 = -1,-1
+ is-visible = false
+ saved-params = ""
window "mapwindow"
elem "mapwindow"
diff --git a/tgui/global.d.ts b/tgui/global.d.ts
index 6e28fc9be14a1..af2db9ac7fd59 100644
--- a/tgui/global.d.ts
+++ b/tgui/global.d.ts
@@ -34,6 +34,16 @@ type ByondType = {
*/
windowId: string;
+ /**
+ * The major version of byond.
+ */
+ BYOND_MAJOR: string;
+
+ /**
+ * The minor (build) version of byond.
+ */
+ BYOND_MINOR: string;
+
/**
* True if javascript is running in BYOND.
*/
diff --git a/tgui/packages/tgui-panel/stat/StatText.js b/tgui/packages/tgui-panel/stat/StatText.js
index aa844f2a21642..f2c97846ad9c9 100644
--- a/tgui/packages/tgui-panel/stat/StatText.js
+++ b/tgui/packages/tgui-panel/stat/StatText.js
@@ -18,7 +18,7 @@ export const StatText = (props, context) => {
}
return (