diff --git a/_maps/map_files/RandomZLevels/stationCollision.dmm b/_maps/map_files/RandomZLevels/stationCollision.dmm
index a8e3a533033..cc1767248cf 100644
--- a/_maps/map_files/RandomZLevels/stationCollision.dmm
+++ b/_maps/map_files/RandomZLevels/stationCollision.dmm
@@ -4123,7 +4123,7 @@
/turf/simulated/floor/plasteel,
/area/awaymission/arrivalblock)
"kG" = (
-/obj/item/clothing/shoes/syndigaloshes,
+/obj/item/clothing/shoes/chameleon/noslip,
/obj/item/clothing/under/syndicate,
/obj/effect/decal/remains/human{
desc = "This guy seemed to have died in terrible way! Half his remains are dust.";
diff --git a/_maps/map_files/RandomZLevels/terrorspiders.dmm b/_maps/map_files/RandomZLevels/terrorspiders.dmm
index 1a64a0d7f0a..ed0bb019b83 100644
--- a/_maps/map_files/RandomZLevels/terrorspiders.dmm
+++ b/_maps/map_files/RandomZLevels/terrorspiders.dmm
@@ -13398,7 +13398,7 @@
/area/awaymission/UO71/loot)
"zt" = (
/obj/structure/chair/wood/normal,
-/obj/item/clothing/shoes/syndigaloshes,
+/obj/item/clothing/shoes/chameleon/noslip,
/turf/simulated/floor/carpet,
/area/awaymission/UO71/loot)
"zu" = (
diff --git a/_maps/map_files/cyberiad/z2.dmm b/_maps/map_files/cyberiad/z2.dmm
index 190f9ef72ee..dda71eb7e09 100644
--- a/_maps/map_files/cyberiad/z2.dmm
+++ b/_maps/map_files/cyberiad/z2.dmm
@@ -8544,7 +8544,7 @@
pixel_y = 3
},
/obj/item/clothing/mask/gas/cyborg,
-/obj/item/clothing/mask/gas/voice,
+/obj/item/clothing/mask/chameleon,
/obj/item/clothing/mask/balaclava{
pixel_x = 3;
pixel_y = -3
@@ -8730,7 +8730,7 @@
/area/admin)
"uY" = (
/obj/structure/rack,
-/obj/item/clothing/under/chameleon/all{
+/obj/item/clothing/mask/chameleon{
pixel_x = -3;
pixel_y = 3
},
@@ -9037,7 +9037,7 @@
pixel_y = 3
},
/obj/item/clothing/shoes/centcom,
-/obj/item/clothing/shoes/syndigaloshes{
+/obj/item/clothing/shoes/chameleon/noslip{
pixel_x = 3;
pixel_y = -3
},
@@ -12276,7 +12276,7 @@
"Hv" = (
/obj/structure/rack,
/obj/item/katana/energy,
-/obj/item/clothing/mask/gas/voice/space_ninja,
+/obj/item/clothing/mask/gas/space_ninja,
/turf/unsimulated/floor{
icon_state = "engine"
},
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index a5fcd9e219a..2afb121a0a3 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -382,4 +382,10 @@
//Fullscreen overlay resolution in tiles.
#define FULLSCREEN_OVERLAY_RESOLUTION_X 15
-#define FULLSCREEN_OVERLAY_RESOLUTION_Y 15
\ No newline at end of file
+#define FULLSCREEN_OVERLAY_RESOLUTION_Y 15
+
+//suit sensors: sensor_mode defines
+#define SENSOR_OFF 0
+#define SENSOR_LIVING 1
+#define SENSOR_VITALS 2
+#define SENSOR_COORDS 3
diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm
index f1e6f38a2d4..6b5807deaef 100644
--- a/code/__HELPERS/lists.dm
+++ b/code/__HELPERS/lists.dm
@@ -124,9 +124,13 @@
. += A
//Like typesof() or subtypesof(), but returns a typecache instead of a list
-/proc/typecacheof(path, ignore_root_path)
+/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE)
if(ispath(path))
- var/list/types = ignore_root_path ? subtypesof(path) : typesof(path)
+ var/list/types = list()
+ if(only_root_path)
+ types = list(path)
+ else
+ types = ignore_root_path ? subtypesof(path) : typesof(path)
var/list/L = list()
for(var/T in types)
L[T] = TRUE
@@ -140,8 +144,11 @@
L[T] = TRUE
else
for(var/P in pathlist)
- for(var/T in typesof(P))
- L[T] = TRUE
+ if(only_root_path)
+ L[P] = TRUE
+ else
+ for(var/T in typesof(P))
+ L[T] = TRUE
return L
//Removes any null entries from the list
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 08c92a37bad..3b98a0fef1a 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -408,6 +408,19 @@
name = "Use [target.name]"
button.name = name
+/datum/action/item_action/voice_changer/toggle
+ name = "Toggle Voice Changer"
+
+/datum/action/item_action/voice_changer/voice
+ name = "Set Voice"
+
+/datum/action/item_action/voice_changer/voice/Trigger()
+ if(!IsAvailable())
+ return FALSE
+
+ var/obj/item/voice_changer/V = target
+ V.set_voice(usr)
+
// for clothing accessories like holsters
/datum/action/item_action/accessory
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING|AB_CHECK_CONSCIOUS
diff --git a/code/datums/outfits/outfit.dm b/code/datums/outfits/outfit.dm
index 8c172f3592f..184f1155da2 100644
--- a/code/datums/outfits/outfit.dm
+++ b/code/datums/outfits/outfit.dm
@@ -25,6 +25,7 @@
var/list/implants = null
var/list/cybernetic_implants = null
+ var/list/chameleon_extras //extra types for chameleon outfit changes, mostly guns
/datum/outfit/proc/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
//to be overriden for customization depending on client prefs,species etc
@@ -151,4 +152,10 @@
H.r_store.add_fingerprint(H, 1)
if(H.wear_pda)
H.wear_pda.add_fingerprint(H, 1)
- return 1
\ No newline at end of file
+ return 1
+
+/datum/outfit/proc/get_chameleon_disguise_info()
+ var/list/types = list(uniform, suit, back, belt, gloves, shoes, head, mask, l_ear, r_ear, glasses, id, l_pocket, r_pocket, suit_store, r_hand, l_hand, pda)
+ types += chameleon_extras
+ listclearnulls(types)
+ return types
diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm
index 3ac473edfeb..09282ec77b1 100644
--- a/code/datums/outfits/outfit_admin.dm
+++ b/code/datums/outfits/outfit_admin.dm
@@ -123,7 +123,7 @@
/datum/outfit/admin/syndicate/spy
name = "Syndicate Spy"
uniform = /obj/item/clothing/under/suit_jacket/really_black
- shoes = /obj/item/clothing/shoes/syndigaloshes/black
+ shoes = /obj/item/clothing/shoes/chameleon/noslip
uplink_uses = 40
id_access = "Syndicate Agent"
@@ -228,7 +228,7 @@
back = /obj/item/storage/backpack
belt = /obj/item/storage/belt/utility/full/multitool
gloves = /obj/item/clothing/gloves/combat
- shoes = /obj/item/clothing/shoes/syndigaloshes/black
+ shoes = /obj/item/clothing/shoes/chameleon/noslip
l_ear = /obj/item/radio/headset/centcom
id = /obj/item/card/id
pda = /obj/item/pda
@@ -990,7 +990,7 @@
suit = /obj/item/clothing/suit/hooded/chaplain_hoodie
back = /obj/item/storage/backpack
gloves = /obj/item/clothing/gloves/combat
- shoes = /obj/item/clothing/shoes/syndigaloshes/black
+ shoes = /obj/item/clothing/shoes/chameleon/noslip
l_ear = /obj/item/radio/headset/syndicate
id = /obj/item/card/id/syndicate
l_hand = /obj/item/twohanded/dualsaber/red
@@ -1021,7 +1021,7 @@
suit = /obj/item/clothing/suit/draculacoat
back = /obj/item/storage/backpack
gloves = /obj/item/clothing/gloves/combat
- shoes = /obj/item/clothing/shoes/syndigaloshes/black
+ shoes = /obj/item/clothing/shoes/chameleon/noslip
l_ear = /obj/item/radio/headset/syndicate
id = /obj/item/card/id
backpack_contents = list(
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index d1377fef558..3a78dfc9b28 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -1067,13 +1067,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/stealthy_tools
category = "Stealth and Camouflage Items"
-/datum/uplink_item/stealthy_tools/chameleon_jumpsuit
- name = "Chameleon Jumpsuit"
- desc = "A jumpsuit used to imitate the uniforms of Nanotrasen crewmembers."
- reference = "CJ"
- item = /obj/item/clothing/under/chameleon
- cost = 2
-
/datum/uplink_item/stealthy_tools/chameleon_stamp
name = "Chameleon Stamp"
desc = "A stamp that can be activated to imitate an official Nanotrasen Stamp. The disguised stamp will work exactly like the real stamp and will allow you to forge false documents to gain access or equipment; \
@@ -1092,20 +1085,19 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
surplus = 35
/datum/uplink_item/stealthy_tools/syndigaloshes
- name = "No-Slip Syndicate Shoes"
- desc = "These allow you to run on wet floors. They do not work on lubricated surfaces."
+ name = "No-Slip Chameleon Shoes"
+ desc = "These shoes will allow the wearer to run on wet floors and slippery objects without falling down. \
+ They do not work on heavily lubricated surfaces."
reference = "NSSS"
- item = /obj/item/clothing/shoes/syndigaloshes
+ item = /obj/item/clothing/shoes/chameleon/noslip
cost = 2
excludefrom = list(/datum/game_mode/nuclear)
/datum/uplink_item/stealthy_tools/syndigaloshes/nuke
- name = "Tactical No-Slip Brown Shoes"
- desc = "These allow you to run on wet floors. They do not work on lubricated surfaces, and the maker swears they're better than normal ones, somehow."
- reference = "NNSSS"
+ reference = "TNSSS"
cost = 4 //but they aren't
- gamemodes = list(/datum/game_mode/nuclear)
excludefrom = list()
+ gamemodes = list(/datum/game_mode/nuclear)
/datum/uplink_item/stealthy_tools/chamsechud
name = "Chameleon Security HUD"
@@ -1146,12 +1138,13 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/card/id/syndicate
cost = 2
-/datum/uplink_item/stealthy_tools/voice_changer
- name = "Voice Changer"
- desc = "A conspicuous gas mask that mimics the voice named on your identification card. When no identification is worn, the mask will render your voice unrecognisable."
- reference = "VC"
- item = /obj/item/clothing/mask/gas/voice
- cost = 3
+/datum/uplink_item/stealthy_tools/chameleon
+ name = "Chameleon Kit"
+ desc = "A set of items that contain chameleon technology allowing you to disguise as pretty much anything on the station, and more! \
+ Due to budget cuts, the shoes don't provide protection against slipping. The set comes with a complementary chameleon stamp."
+ reference = "CHAM"
+ item = /obj/item/storage/box/syndie_kit/chameleon
+ cost = 2
/datum/uplink_item/stealthy_tools/chameleon_proj
name = "Chameleon-Projector"
@@ -1210,7 +1203,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/stealthy_tools/clownkit
name = "Honk Brand Infiltration Kit"
- desc = "All the tools you need to play the best prank Nanotrasen has ever seen. Includes a voice changer clown mask, magnetic clown shoes, and standard clown outfit, tools, and backpack."
+ desc = "All the tools you need to play the best prank Nanotrasen has ever seen. Includes a voice changer mask, magnetic clown shoes, and standard clown outfit, tools, and backpack."
reference = "HBIK"
item = /obj/item/storage/backpack/clown/syndie
cost = 6
diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm
index 057c52a2a4c..b7311519f7a 100644
--- a/code/game/objects/effects/spawners/lootdrop.dm
+++ b/code/game/objects/effects/spawners/lootdrop.dm
@@ -140,8 +140,8 @@
/obj/item/suppressor = 4,
/obj/item/clothing/under/chameleon = 2,
/obj/item/stamp/chameleon = 2,
- /obj/item/clothing/shoes/syndigaloshes = 5,
- /obj/item/clothing/mask/gas/voice = 2,
+ /obj/item/clothing/shoes/chameleon/noslip = 5,
+ /obj/item/clothing/mask/chameleon = 2,
/obj/item/dnascrambler = 1,
/obj/item/storage/backpack/satchel_flat = 2,
/obj/item/storage/toolbox/syndicate = 2,
diff --git a/code/game/objects/effects/spawners/random_spawners.dm b/code/game/objects/effects/spawners/random_spawners.dm
index 510e5b90380..03e49c56a50 100644
--- a/code/game/objects/effects/spawners/random_spawners.dm
+++ b/code/game/objects/effects/spawners/random_spawners.dm
@@ -214,7 +214,7 @@
/obj/item/storage/pill_bottle/zoom = 1,
/obj/item/storage/pill_bottle/random_drug_bottle = 2,
/obj/item/storage/backpack/duffel/syndie/surgery = 1,
- /obj/item/clothing/shoes/syndigaloshes = 1,
+ /obj/item/clothing/shoes/chameleon/noslip = 1,
/obj/item/storage/belt/military = 1,
/obj/item/clothing/under/chameleon = 1,
/obj/item/storage/backpack/satchel_flat = 1,
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 089317524e9..23509a06f63 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -600,3 +600,9 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
/obj/item/MouseExited()
deltimer(tip_timer) //delete any in-progress timer if the mouse is moved off the item before it finishes
closeToolTip(usr)
+
+/obj/item/proc/update_slot_icon()
+ if(!ismob(loc))
+ return
+ var/mob/owner = loc
+ owner.regenerate_icons()
diff --git a/code/game/objects/items/devices/megaphone.dm b/code/game/objects/items/devices/megaphone.dm
index 59a7a650383..941e90a31ef 100644
--- a/code/game/objects/items/devices/megaphone.dm
+++ b/code/game/objects/items/devices/megaphone.dm
@@ -35,7 +35,7 @@
return
if(H.mind)
span = H.mind.speech_span
- if((COMIC in H.mutations) || H.get_int_organ(/obj/item/organ/internal/cyberimp/brain/clown_voice) || istype(H.get_item_by_slot(slot_wear_mask), /obj/item/clothing/mask/gas/voice/clown))
+ if((COMIC in H.mutations) || H.get_int_organ(/obj/item/organ/internal/cyberimp/brain/clown_voice))
span = "sans"
if(spamcheck)
to_chat(user, "\The [src] needs to recharge!")
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 83a8c9304a1..00efc83668b 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -397,7 +397,6 @@ var/global/list/default_medbay_channels = list(
var/mob/living/carbon/human/H = M
displayname = H.voice
if(H.voice != real_name)
- jobname = "Unknown"
voicemask = TRUE
if(syndiekey && syndiekey.change_voice && connection.frequency == SYND_FREQ)
diff --git a/code/game/objects/items/devices/voice.dm b/code/game/objects/items/devices/voice.dm
new file mode 100644
index 00000000000..339271cdcca
--- /dev/null
+++ b/code/game/objects/items/devices/voice.dm
@@ -0,0 +1,44 @@
+/obj/item/voice_changer
+ name = "voice changer"
+ desc = "A voice scrambling module."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "voice_changer_off"
+
+ actions_types = list(/datum/action/item_action/voice_changer/toggle, /datum/action/item_action/voice_changer/voice)
+
+ var/obj/item/parent
+
+ var/voice
+ var/active
+
+/obj/item/voice_changer/New()
+ . = ..()
+
+ if(isitem(loc))
+ parent = loc
+ parent.actions |= actions
+
+/obj/item/voice_changer/Destroy()
+ if(isitem(parent))
+ parent.actions -= actions
+
+ return ..()
+
+/obj/item/voice_changer/attack_self(mob/user)
+ active = !active
+ icon_state = "voice_changer_[active ? "on" : "off"]"
+ to_chat(user, "You toggle [src] [active ? "on" : "off"].")
+
+ for(var/X in actions)
+ var/datum/action/A = X
+ A.UpdateButtonIcon()
+
+/obj/item/voice_changer/proc/set_voice(mob/user)
+ var/chosen_voice = input(user, "What voice would you like to mimic? Leave this empty to use the voice on your ID card.", "Set Voice Changer", voice) as text
+ if(!chosen_voice)
+ voice = null
+ to_chat(user, "You are now mimicking the voice on your ID card.")
+ return
+
+ voice = sanitize(copytext(chosen_voice, 1, MAX_MESSAGE_LEN))
+ to_chat(user, "You are now mimicking [voice].")
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm
index d2f2ed2c54d..c7627688b5e 100644
--- a/code/game/objects/items/weapons/dna_injector.dm
+++ b/code/game/objects/items/weapons/dna_injector.dm
@@ -668,4 +668,27 @@
/obj/item/dnainjector/m2h/Initialize()
block = MONKEYBLOCK
+ ..()
+
+
+/obj/item/dnainjector/comic
+ name = "DNA-Injector (Comic)"
+ desc = "Honk!"
+ datatype = DNA2_BUF_SE
+ value = 0xFFF
+ forcedmutation = TRUE
+
+/obj/item/dnainjector/comic/Initialize()
+ block = COMICBLOCK
+ ..()
+
+/obj/item/dnainjector/anticomic
+ name = "DNA-Injector (Ant-Comic)"
+ desc = "Honk...?"
+ datatype = DNA2_BUF_SE
+ value = 0x001
+ forcedmutation = TRUE
+
+/obj/item/dnainjector/anticomic/Initialize()
+ block = COMICBLOCK
..()
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index e5ee3562228..56ae2864071 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -112,7 +112,7 @@
..()
new /obj/item/clothing/under/rank/clown(src)
new /obj/item/clothing/shoes/magboots/clown(src)
- new /obj/item/clothing/mask/gas/voice/clown(src)
+ new /obj/item/clothing/mask/chameleon(src)
new /obj/item/radio/headset/headset_service(src)
new /obj/item/pda/clown(src)
new /obj/item/storage/box/survival(src)
@@ -124,6 +124,7 @@
new /obj/item/reagent_containers/food/drinks/bottle/bottleofbanana(src)
new /obj/item/instrument/bikehorn(src)
new /obj/item/bikehorn(src)
+ new /obj/item/dnainjector/comic(src)
/obj/item/storage/backpack/mime
name = "Parcel Parceaux"
diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm
index ecf1acd701e..56db11ad058 100644
--- a/code/game/objects/items/weapons/storage/uplink_kits.dm
+++ b/code/game/objects/items/weapons/storage/uplink_kits.dm
@@ -5,7 +5,7 @@
if("bloodyspai") // 28TC
new /obj/item/twohanded/garrote(src)
new /obj/item/pinpointer/advpinpointer(src)
- new /obj/item/clothing/mask/gas/voice(src)
+ new /obj/item/clothing/mask/chameleon(src)
new /obj/item/clothing/under/chameleon(src)
new /obj/item/card/id/syndicate(src)
new /obj/item/flashlight/emp(src)
@@ -19,7 +19,7 @@
new /obj/item/chameleon(src)
new /obj/item/clothing/gloves/color/black/thief(src)
new /obj/item/card/id/syndicate(src)
- new /obj/item/clothing/shoes/syndigaloshes(src)
+ new /obj/item/clothing/shoes/chameleon/noslip(src)
new /obj/item/storage/box/syndie_kit/safecracking(src)
return
@@ -83,14 +83,14 @@
new /obj/item/dnainjector/telemut/darkbundle(src)
new /obj/item/clothing/suit/hooded/chaplain_hoodie(src)
new /obj/item/card/id/syndicate(src)
- new /obj/item/clothing/shoes/syndigaloshes(src)
- new /obj/item/clothing/mask/gas/voice(src)
+ new /obj/item/clothing/shoes/chameleon/noslip(src)
+ new /obj/item/clothing/mask/chameleon(src)
return
if("gadgets") // 30TC
new /obj/item/clothing/gloves/color/yellow/power(src)
new /obj/item/pen/sleepy(src)
- new /obj/item/clothing/shoes/syndigaloshes(src)
+ new /obj/item/clothing/shoes/chameleon/noslip(src)
new /obj/item/clothing/glasses/thermal/syndi(src)
new /obj/item/flashlight/emp(src)
new /obj/item/stamp/chameleon(src)
@@ -340,4 +340,21 @@ To apply, hold the injector a short distance away from the outer thigh before ap
new /obj/item/clothing/gloves/color/latex/nitrile(src)
new /obj/item/clothing/mask/balaclava(src)
new /obj/item/clothing/accessory/stethoscope(src)
- new /obj/item/book/manual/engineering_hacking(src)
\ No newline at end of file
+ new /obj/item/book/manual/engineering_hacking(src)
+
+/obj/item/storage/box/syndie_kit/chameleon
+ name = "chameleon kit"
+
+/obj/item/storage/box/syndie_kit/chameleon/New()
+ ..()
+ new /obj/item/clothing/under/chameleon(src)
+ new /obj/item/clothing/suit/chameleon(src)
+ new /obj/item/clothing/gloves/chameleon(src)
+ new /obj/item/clothing/shoes/chameleon(src)
+ new /obj/item/clothing/glasses/chameleon(src)
+ new /obj/item/clothing/head/chameleon(src)
+ new /obj/item/clothing/mask/chameleon(src)
+ new /obj/item/storage/backpack/chameleon(src)
+ new /obj/item/radio/headset/chameleon(src)
+ new /obj/item/stamp/chameleon(src)
+ new /obj/item/pda/chameleon(src)
\ No newline at end of file
diff --git a/code/modules/admin/verbs/infiltratorteam_syndicate.dm b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
index 551076be62a..4597a838733 100644
--- a/code/modules/admin/verbs/infiltratorteam_syndicate.dm
+++ b/code/modules/admin/verbs/infiltratorteam_syndicate.dm
@@ -170,7 +170,7 @@ var/global/sent_syndicate_infiltration_team = 0
equip_or_collect(new /obj/item/pda(src), slot_in_backpack)
// Other gear
- equip_to_slot_or_del(new /obj/item/clothing/shoes/syndigaloshes(src), slot_shoes)
+ equip_to_slot_or_del(new /obj/item/clothing/shoes/chameleon/noslip(src), slot_shoes)
var/obj/item/card/id/syndicate/W = new(src)
if (flag_mgmt)
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 08958b0d005..2cc570ded70 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -600,8 +600,9 @@ BLIND // can't see anything
"Drask" = 'icons/mob/species/drask/uniform.dmi',
"Grey" = 'icons/mob/species/grey/uniform.dmi'
)
- var/has_sensor = 1//For the crew computer 2 = unable to change mode
- var/sensor_mode = 0
+ var/has_sensor = TRUE//For the crew computer 2 = unable to change mode
+ var/sensor_mode = SENSOR_OFF
+ var/random_sensor = TRUE
/*
1 = Report living/dead
2 = Report detailed damages
@@ -613,7 +614,8 @@ BLIND // can't see anything
var/basecolor
/obj/item/clothing/under/rank/New()
- sensor_mode = pick(0,1,2,3)
+ if(random_sensor)
+ sensor_mode = pick(SENSOR_OFF, SENSOR_LIVING, SENSOR_VITALS, SENSOR_COORDS)
..()
/obj/item/clothing/under/Destroy()
diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm
index 0c4dba9ae49..1bd53217770 100644
--- a/code/modules/clothing/masks/gasmask.dm
+++ b/code/modules/clothing/masks/gasmask.dm
@@ -162,11 +162,6 @@
item_state = "sexymime"
burn_state = FLAMMABLE
-/obj/item/clothing/mask/gas/death_commando
- name = "Death Commando Mask"
- icon_state = "death_commando_mask"
- item_state = "death_commando_mask"
-
/obj/item/clothing/mask/gas/cyborg
name = "cyborg visor"
desc = "Beep boop"
diff --git a/code/modules/clothing/masks/voice.dm b/code/modules/clothing/masks/voice.dm
deleted file mode 100644
index b5b6298dcb2..00000000000
--- a/code/modules/clothing/masks/voice.dm
+++ /dev/null
@@ -1,38 +0,0 @@
-/obj/item/voice_changer
- name = "voice changer"
- desc = "A voice scrambling module. If you can see this, report it as a bug on the tracker."
- var/voice //If set and item is present in mask/suit, this name will be used for the wearer's speech.
- var/active
-
-/obj/item/clothing/mask/gas/voice
- name = "gas mask"
-// desc = "A face-covering mask that can be connected to an air supply. It seems to house some odd electronics."
- var/obj/item/voice_changer/changer
- burn_state = FIRE_PROOF
-
-/obj/item/clothing/mask/gas/voice/verb/Toggle_Voice_Changer()
- set category = "Object"
- set src in usr
-
- changer.active = !changer.active
- to_chat(usr, "You [changer.active ? "enable" : "disable"] the voice-changing module in \the [src].")
-
-/obj/item/clothing/mask/gas/voice/verb/Set_Voice(name as text)
- set category = "Object"
- set src in usr
-
- var/voice = sanitize(copytext(name,1,MAX_MESSAGE_LEN))
- if(!voice || !length(voice)) return
- changer.voice = voice
- to_chat(usr, "You are now mimicking [changer.voice].")
-
-/obj/item/clothing/mask/gas/voice/New()
- ..()
- changer = new(src)
-
-/obj/item/clothing/mask/gas/voice/clown
- name = "clown wig and mask"
- desc = "A true prankster's facial attire. A clown is incomplete without his wig and mask."
- icon_state = "clown"
- item_state = "clown_hat"
- flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR
\ No newline at end of file
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index f3a2efed260..d4411fbdbe6 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -1,21 +1,3 @@
-/obj/item/clothing/shoes/syndigaloshes
- desc = "A pair of brown shoes. They seem to have extra grip."
- name = "brown shoes"
- icon_state = "brown"
- item_state = "brown"
- permeability_coefficient = 0.05
- flags = NOSLIP
- origin_tech = "syndicate=2"
- burn_state = FIRE_PROOF
- var/list/clothing_choices = list()
- silence_steps = 1
-
-/obj/item/clothing/shoes/syndigaloshes/black
- name = "black shoes"
- icon_state = "black"
- item_color = "black"
- desc = "A pair of black shoes. They seem to have extra grip."
-
/obj/item/clothing/shoes/mime
name = "mime shoes"
icon_state = "mime"
diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm
index aa62763a6d3..bfb326eae1d 100644
--- a/code/modules/clothing/spacesuits/rig/modules/utility.dm
+++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm
@@ -269,14 +269,13 @@
/obj/item/rig_module/voice/New()
..()
voice_holder = new(src)
- voice_holder.active = 0
+ voice_holder.active = FALSE
/obj/item/rig_module/voice/installed()
..()
holder.speech = src
/obj/item/rig_module/voice/engage()
-
if(!..())
return 0
@@ -287,17 +286,17 @@
switch(choice)
if("Enable")
- active = 1
- voice_holder.active = 1
+ active = TRUE
+ voice_holder.active = TRUE
to_chat(usr, "You enable the speech synthesiser.")
if("Disable")
- active = 0
- voice_holder.active = 0
+ active = FALSE
+ voice_holder.active = FALSE
to_chat(usr, "You disable the speech synthesiser.")
if("Set Name")
var/raw_choice = sanitize(input(usr, "Please enter a new name.") as text|null, MAX_NAME_LEN)
if(!raw_choice)
- return 0
+ return FALSE
voice_holder.voice = raw_choice
to_chat(usr, "You are now mimicking [voice_holder.voice].")
return 1
diff --git a/code/modules/clothing/under/chameleon.dm b/code/modules/clothing/under/chameleon.dm
index b320237996e..b08a1ce72c9 100644
--- a/code/modules/clothing/under/chameleon.dm
+++ b/code/modules/clothing/under/chameleon.dm
@@ -1,86 +1,478 @@
+#define EMP_RANDOMISE_TIME 300
+
+/datum/action/chameleon_outfit
+ name = "Select Chameleon Outfit"
+ button_icon_state = "chameleon_outfit"
+ var/list/outfit_options //By default, this list is shared between all instances. It is not static because if it were, subtypes would not be able to have their own. If you ever want to edit it, copy it first.
+
+/datum/action/chameleon_outfit/New()
+ ..()
+ initialize_outfits()
+
+/datum/action/chameleon_outfit/proc/initialize_outfits()
+ var/static/list/standard_outfit_options
+ if(!standard_outfit_options)
+ standard_outfit_options = list()
+ for(var/path in subtypesof(/datum/outfit/job))
+ var/datum/outfit/O = path
+ standard_outfit_options[initial(O.name)] = path
+ sortTim(standard_outfit_options, /proc/cmp_text_asc)
+ outfit_options = standard_outfit_options
+
+/datum/action/chameleon_outfit/Trigger()
+ return select_outfit(owner)
+
+/datum/action/chameleon_outfit/proc/select_outfit(mob/user)
+ if(!user || !IsAvailable())
+ return FALSE
+ var/selected = input("Select outfit to change into", "Chameleon Outfit") as null|anything in outfit_options
+ if(!IsAvailable() || QDELETED(src) || QDELETED(user))
+ return FALSE
+ var/outfit_type = outfit_options[selected]
+ if(!outfit_type)
+ return FALSE
+ var/datum/outfit/O = new outfit_type()
+ var/list/outfit_types = O.get_chameleon_disguise_info()
+
+ for(var/V in user.chameleon_item_actions)
+ var/datum/action/item_action/chameleon/change/A = V
+ var/done = FALSE
+ for(var/T in outfit_types)
+ for(var/name in A.chameleon_list)
+ if(A.chameleon_list[name] == T)
+ A.update_look(user, T)
+ outfit_types -= T
+ done = TRUE
+ break
+ if(done)
+ break
+ //hardsuit helmets/suit hoods
+ if(ispath(O.suit, /obj/item/clothing/suit/hooded) && ishuman(user))
+ var/mob/living/carbon/human/H = user
+ //make sure they are actually wearing the suit, not just holding it, and that they have a chameleon hat
+ if(istype(H.wear_suit, /obj/item/clothing/suit/chameleon) && istype(H.head, /obj/item/clothing/head/chameleon))
+ var/helmet_type
+ var/obj/item/clothing/suit/hooded/hooded = O.suit
+ helmet_type = initial(hooded.hoodtype)
+
+ if(helmet_type)
+ var/obj/item/clothing/head/chameleon/hat = H.head
+ hat.chameleon_action.update_look(user, helmet_type)
+ qdel(O)
+ return TRUE
+
+
+/datum/action/item_action/chameleon/change
+ name = "Chameleon Change"
+ var/list/chameleon_blacklist = list() //This is a typecache
+ var/list/chameleon_list = list()
+ var/chameleon_type = null
+ var/chameleon_name = "Item"
+
+ var/emp_timer
+
+/datum/action/item_action/chameleon/change/Grant(mob/M)
+ if(M && (owner != M))
+ if(!M.chameleon_item_actions)
+ M.chameleon_item_actions = list(src)
+ var/datum/action/chameleon_outfit/O = new /datum/action/chameleon_outfit()
+ O.Grant(M)
+ else
+ M.chameleon_item_actions |= src
+ ..()
+
+/datum/action/item_action/chameleon/change/Remove(mob/M)
+ if(M && (M == owner))
+ LAZYREMOVE(M.chameleon_item_actions, src)
+ if(!LAZYLEN(M.chameleon_item_actions))
+ var/datum/action/chameleon_outfit/O = locate(/datum/action/chameleon_outfit) in M.actions
+ qdel(O)
+ ..()
+
+/datum/action/item_action/chameleon/change/proc/initialize_disguises()
+ if(button)
+ button.name = "Change [chameleon_name] Appearance"
+
+ chameleon_blacklist |= typecacheof(target.type)
+ for(var/V in typesof(chameleon_type))
+ if(ispath(V) && ispath(V, /obj/item))
+ var/obj/item/I = V
+ if(chameleon_blacklist[V] || (initial(I.flags) & ABSTRACT) || !initial(I.icon_state))
+ continue
+ var/chameleon_item_name = "[initial(I.name)] ([initial(I.icon_state)])"
+ chameleon_list[chameleon_item_name] = I
+
+/datum/action/item_action/chameleon/change/proc/select_look(mob/user)
+ var/obj/item/picked_item
+ var/picked_name
+ picked_name = input("Select [chameleon_name] to change into", "Chameleon [chameleon_name]", picked_name) as null|anything in chameleon_list
+ if(!picked_name)
+ return
+ picked_item = chameleon_list[picked_name]
+ if(!picked_item)
+ return
+ update_look(user, picked_item)
+
+/datum/action/item_action/chameleon/change/proc/random_look(mob/user)
+ var/picked_name = pick(chameleon_list)
+ // If a user is provided, then this item is in use, and we
+ // need to update our icons and stuff
+
+ if(user)
+ update_look(user, chameleon_list[picked_name])
+
+ // Otherwise, it's likely a random initialisation, so we
+ // don't have to worry
+
+ else
+ update_item(chameleon_list[picked_name])
+
+/datum/action/item_action/chameleon/change/proc/update_look(mob/user, obj/item/picked_item)
+ if(isliving(user))
+ var/mob/living/C = user
+ if(C.stat != CONSCIOUS)
+ return
+
+ update_item(picked_item)
+ var/obj/item/thing = target
+ thing.update_slot_icon()
+ UpdateButtonIcon()
+
+/datum/action/item_action/chameleon/change/proc/update_item(obj/item/picked_item)
+ target.name = initial(picked_item.name)
+ target.desc = initial(picked_item.desc)
+ target.icon_state = initial(picked_item.icon_state)
+ if(isitem(target))
+ var/obj/item/I = target
+ I.item_state = initial(picked_item.item_state)
+ I.item_color = initial(picked_item.item_color)
+ if(istype(I, /obj/item/clothing) && istype(initial(picked_item), /obj/item/clothing))
+ var/obj/item/clothing/CL = I
+ var/obj/item/clothing/PCL = picked_item
+ CL.flags_cover = initial(PCL.flags_cover)
+ target.icon = initial(picked_item.icon)
+
+/datum/action/item_action/chameleon/change/Trigger()
+ if(!IsAvailable())
+ return
+
+ select_look(owner)
+ return 1
+
+/datum/action/item_action/chameleon/change/proc/emp_randomise(var/amount = EMP_RANDOMISE_TIME)
+ START_PROCESSING(SSprocessing, src)
+ random_look(owner)
+
+ var/new_value = world.time + amount
+ if(new_value > emp_timer)
+ emp_timer = new_value
+
+/datum/action/item_action/chameleon/change/process()
+ if(world.time > emp_timer)
+ STOP_PROCESSING(SSprocessing, src)
+ return
+ random_look(owner)
+
/obj/item/clothing/under/chameleon
//starts off as black
name = "black jumpsuit"
icon_state = "black"
item_state = "bl_suit"
item_color = "black"
- desc = "It's a plain jumpsuit. It seems to have a small dial on the wrist."
- origin_tech = "syndicate=2"
- var/list/clothing_choices = list()
- burn_state = FIRE_PROOF
- armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0)
+ desc = "It's a plain jumpsuit. It has a small dial on the wrist."
+ sensor_mode = SENSOR_OFF //Hey who's this guy on the Syndicate Shuttle??
+ random_sensor = FALSE
+ resistance_flags = NONE
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
- New()
- ..()
- var/blocked = list(/obj/item/clothing/under/color/random, /obj/item/clothing/under/rank/centcom) // Stops random coloured jumpsuit and undefined centcomm suit appearing in the list.
- for(var/U in subtypesof(/obj/item/clothing/under/color) - blocked)
- var/obj/item/clothing/under/V = new U
- src.clothing_choices += V
+ var/datum/action/item_action/chameleon/change/chameleon_action
- for(var/U in subtypesof(/obj/item/clothing/under/rank) - blocked)
- var/obj/item/clothing/under/V = new U
- src.clothing_choices += V
- return
+/obj/item/clothing/under/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/under
+ chameleon_action.chameleon_name = "Jumpsuit"
+ chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/clothing/under, /obj/item/clothing/under/color, /obj/item/clothing/under/rank), only_root_path = TRUE)
+ chameleon_action.initialize_disguises()
+/obj/item/clothing/under/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
- attackby(obj/item/clothing/under/U as obj, mob/user as mob, params)
- ..()
- if(istype(U, /obj/item/clothing/under/chameleon))
- to_chat(user, "Nothing happens.")
- return
- if(istype(U, /obj/item/clothing/under))
- if(src.clothing_choices.Find(U))
- to_chat(user, "Pattern is already recognised by the suit.")
- return
- src.clothing_choices += U
- to_chat(user, "Pattern absorbed by the suit.")
+/obj/item/clothing/under/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+/obj/item/clothing/suit/chameleon
+ name = "armor"
+ desc = "A slim armored vest that protects against most types of damage."
+ icon_state = "armor"
+ item_state = "armor"
+ blood_overlay_type = "armor"
+ resistance_flags = NONE
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
- emp_act(severity)
- name = "psychedelic"
- desc = "Groovy!"
- icon_state = "psyche"
- item_color = "psyche"
- usr.update_inv_w_uniform()
- spawn(200)
- name = initial(name)
- icon_state = initial(icon_state)
- item_color = initial(item_color)
- desc = initial(desc)
- usr.update_inv_w_uniform()
- ..()
+ var/datum/action/item_action/chameleon/change/chameleon_action
+/obj/item/clothing/suit/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/suit
+ chameleon_action.chameleon_name = "Suit"
+ chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/clothing/suit/armor/abductor), only_root_path = TRUE)
+ chameleon_action.initialize_disguises()
- verb/change()
- set name = "Change Color"
- set category = "Object"
- set src in usr
+/obj/item/clothing/suit/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
- if(icon_state == "psyche")
- to_chat(usr, "Your suit is malfunctioning")
- return
+/obj/item/clothing/suit/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
- var/obj/item/clothing/under/A
- A = input("Select Colour to change it to", "BOOYEA", A) in clothing_choices
- if(!A)
- return
+/obj/item/clothing/glasses/chameleon
+ name = "Optical Meson Scanner"
+ desc = "Used by engineering and mining staff to see basic structural and terrain layouts through walls, regardless of lighting condition."
+ icon_state = "meson"
+ item_state = "meson"
+ resistance_flags = NONE
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
- desc = null
- permeability_coefficient = 0.90
+ var/datum/action/item_action/chameleon/change/chameleon_action
- desc = A.desc
- name = A.name
- icon_state = A.icon_state
- item_state = A.item_state
- item_color = A.item_color
- usr.update_inv_w_uniform() //so our overlays update.
+/obj/item/clothing/glasses/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/glasses
+ chameleon_action.chameleon_name = "Glasses"
+ chameleon_action.chameleon_blacklist = list()
+ chameleon_action.initialize_disguises()
+/obj/item/clothing/glasses/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+/obj/item/clothing/glasses/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
-/obj/item/clothing/under/chameleon/all/New()
- ..()
- var/blocked = list(/obj/item/clothing/under/chameleon, /obj/item/clothing/under/chameleon/all)
- //to prevent an infinite loop
- for(var/U in typesof(/obj/item/clothing/under)-blocked)
- var/obj/item/clothing/under/V = new U
- src.clothing_choices += V
+/obj/item/clothing/gloves/chameleon
+ desc = "These gloves will protect the wearer from electric shock."
+ name = "insulated gloves"
+ icon_state = "yellow"
+ item_state = "ygloves"
+
+ resistance_flags = NONE
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/clothing/gloves/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/gloves
+ chameleon_action.chameleon_name = "Gloves"
+ chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/clothing/gloves, /obj/item/clothing/gloves/color), only_root_path = TRUE)
+ chameleon_action.initialize_disguises()
+
+/obj/item/clothing/gloves/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/clothing/gloves/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/clothing/head/chameleon
+ name = "grey cap"
+ desc = "It's a baseball hat in a tasteful grey colour."
+ icon_state = "greysoft"
+ item_color = "grey"
+
+ resistance_flags = NONE
+ armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/clothing/head/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/head
+ chameleon_action.chameleon_name = "Hat"
+ chameleon_action.chameleon_blacklist = list()
+ chameleon_action.initialize_disguises()
+
+/obj/item/clothing/head/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/clothing/head/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/clothing/mask/chameleon
+ name = "gas mask"
+ desc = "A face-covering mask that can be connected to an air supply. While good for concealing your identity, it isn't good for blocking gas flow." //More accurate
+ icon_state = "gas_alt"
+ item_state = "gas_alt"
+ resistance_flags = NONE
+ armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+ flags = AIRTIGHT | BLOCK_GAS_SMOKE_EFFECT
+ flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE
+ gas_transfer_coefficient = 0.01
+ permeability_coefficient = 0.01
+ flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH
+
+ var/obj/item/voice_changer/voice_changer
+
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/clothing/mask/chameleon/Initialize()
+ . = ..()
+
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/mask
+ chameleon_action.chameleon_name = "Mask"
+ chameleon_action.chameleon_blacklist = list()
+ chameleon_action.initialize_disguises()
+
+ voice_changer = new(src)
+
+/obj/item/clothing/mask/chameleon/Destroy()
+ QDEL_NULL(voice_changer)
+ return ..()
+
+/obj/item/clothing/mask/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/clothing/mask/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/clothing/shoes/chameleon
+ name = "black shoes"
+ icon_state = "black"
+ item_color = "black"
+ desc = "A pair of black shoes."
+ permeability_coefficient = 0.05
+ resistance_flags = NONE
+ armor = list("melee" = 10, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
+
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/clothing/shoes/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/clothing/shoes
+ chameleon_action.chameleon_name = "Shoes"
+ chameleon_action.chameleon_blacklist = list()
+ chameleon_action.initialize_disguises()
+
+/obj/item/clothing/shoes/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/clothing/shoes/chameleon/noslip
+ name = "black shoes"
+ icon_state = "black"
+ item_color = "black"
+ desc = "A pair of black shoes."
+ flags = NOSLIP
+
+/obj/item/clothing/shoes/chameleon/noslip/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/storage/backpack/chameleon
+ name = "backpack"
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/storage/backpack/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/storage/backpack
+ chameleon_action.chameleon_name = "Backpack"
+ chameleon_action.initialize_disguises()
+
+/obj/item/storage/backpack/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/storage/backpack/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/storage/belt/chameleon
+ name = "toolbelt"
+ desc = "Holds tools."
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/storage/belt/chameleon/Initialize()
+ . = ..()
+
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/storage/belt
+ chameleon_action.chameleon_name = "Belt"
+ chameleon_action.initialize_disguises()
+
+/obj/item/storage/belt/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/storage/belt/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/radio/headset/chameleon
+ name = "radio headset"
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/radio/headset/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/radio/headset
+ chameleon_action.chameleon_name = "Headset"
+ chameleon_action.initialize_disguises()
+
+/obj/item/radio/headset/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/radio/headset/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/pda/chameleon
+ name = "PDA"
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/pda/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/pda
+ chameleon_action.chameleon_name = "PDA"
+ chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/pda/heads), only_root_path = TRUE)
+ chameleon_action.initialize_disguises()
+
+/obj/item/pda/chameleon/emp_act(severity)
+ . = ..()
+ chameleon_action.emp_randomise()
+
+/obj/item/pda/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
+
+/obj/item/stamp/chameleon
+ var/datum/action/item_action/chameleon/change/chameleon_action
+
+/obj/item/stamp/chameleon/Initialize()
+ . = ..()
+ chameleon_action = new(src)
+ chameleon_action.chameleon_type = /obj/item/stamp
+ chameleon_action.chameleon_name = "Stamp"
+ chameleon_action.initialize_disguises()
+
+/obj/item/stamp/chameleon/broken/Initialize()
+ . = ..()
+ chameleon_action.emp_randomise(INFINITY)
\ No newline at end of file
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index e937ec25ef9..6be1d3bcf70 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -671,8 +671,8 @@ var/list/non_fakeattack_weapons = list(/obj/item/gun/projectile, /obj/item/ammo_
/obj/item/gun/energy/kinetic_accelerator/crossbow,\
/obj/item/storage/box/syndicate, /obj/item/storage/box/emps,\
/obj/item/cartridge/syndicate, /obj/item/clothing/under/chameleon,\
- /obj/item/clothing/shoes/syndigaloshes, /obj/item/card/id/syndicate,\
- /obj/item/clothing/mask/gas/voice, /obj/item/clothing/glasses/thermal,\
+ /obj/item/clothing/shoes/chameleon/noslip, /obj/item/card/id/syndicate,\
+ /obj/item/clothing/mask/chameleon, /obj/item/clothing/glasses/thermal,\
/obj/item/chameleon, /obj/item/card/emag,\
/obj/item/storage/toolbox/syndicate, /obj/item/aiModule,\
/obj/item/radio/headset/syndicate, /obj/item/grenade/plastic/c4,\
diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm
index 1be6fc60a25..2f181d77077 100644
--- a/code/modules/mob/living/carbon/human/say.dm
+++ b/code/modules/mob/living/carbon/human/say.dm
@@ -62,27 +62,38 @@
return ..()
/mob/living/carbon/human/proc/HasVoiceChanger()
- if(istype(back,/obj/item/rig))
+ if(istype(back, /obj/item/rig))
var/obj/item/rig/rig = back
if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice)
return rig.speech.voice_holder.voice
- for(var/obj/item/gear in list(wear_mask,wear_suit,head))
+ for(var/obj/item/gear in list(wear_mask, wear_suit, head))
if(!gear)
continue
+
var/obj/item/voice_changer/changer = locate() in gear
- if(changer && changer.active && changer.voice)
- return changer.voice
- return 0
+ if(changer && changer.active)
+ if(changer.voice)
+ return changer.voice
+ else if(wear_id)
+ var/obj/item/card/id/idcard = wear_id.GetID()
+ if(istype(idcard))
+ return idcard.registered_name
+
+ return FALSE
/mob/living/carbon/human/GetVoice()
var/has_changer = HasVoiceChanger()
+
if(has_changer)
return has_changer
+
if(mind && mind.changeling && mind.changeling.mimicing)
return mind.changeling.mimicing
+
if(GetSpecialVoice())
return GetSpecialVoice()
+
return real_name
/mob/living/carbon/human/IsVocal()
@@ -124,7 +135,6 @@
span = mind.speech_span
if((COMIC in mutations) \
|| (locate(/obj/item/organ/internal/cyberimp/brain/clown_voice) in internal_organs) \
- || istype(get_item_by_slot(slot_wear_mask), /obj/item/clothing/mask/gas/voice/clown) \
|| GetComponent(/datum/component/jestosterone))
span = "sans"
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 35851ba30d0..60a3637166a 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -195,6 +195,7 @@
var/list/permanent_huds = list()
var/list/actions = list()
+ var/list/datum/action/chameleon_item_actions
var/list/progressbars = null //for stacking do_after bars
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index bbc3d6d7abe..427bcfcb659 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -464,8 +464,7 @@
dat += " [a.title].
"
- dat += "Choose from the following open positions:
"
-
+ var/num_jobs_available = 0
var/list/activePlayers = list()
var/list/categorizedJobs = list(
"Command" = list(jobs = list(), titles = command_positions, color = "#aac1ee"),
@@ -480,6 +479,7 @@
)
for(var/datum/job/job in SSjobs.occupations)
if(job && IsJobAvailable(job.title) && !job.barred_by_disability(client))
+ num_jobs_available++
activePlayers[job] = 0
var/categorized = 0
// Only players with the job assigned and AFK for less than 10 minutes count as active
@@ -502,23 +502,27 @@
if(!categorized)
categorizedJobs["Miscellaneous"]["jobs"] += job
- dat += "
| " - for(var/jobcat in categorizedJobs) - if(categorizedJobs[jobcat]["colBreak"]) - dat += " | "
- if(length(categorizedJobs[jobcat]["jobs"]) < 1)
- continue
- var/color = categorizedJobs[jobcat]["color"]
- dat += " " + if(num_jobs_available) + dat += "Choose from the following open positions: " + dat += "
|