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 ee6ca15f83d..c1764ddf1c6 100644
--- a/_maps/map_files/cyberiad/z2.dmm
+++ b/_maps/map_files/cyberiad/z2.dmm
@@ -8543,7 +8543,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
@@ -8729,7 +8729,7 @@
/area/admin)
"uY" = (
/obj/structure/rack,
-/obj/item/clothing/under/chameleon/all{
+/obj/item/clothing/mask/chameleon{
pixel_x = -3;
pixel_y = 3
},
@@ -9036,7 +9036,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
},
@@ -12275,7 +12275,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 dff9426c641..479a936489e 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -401,4 +401,11 @@
#define GHOST_ORBIT_TRIANGLE "triangle"
#define GHOST_ORBIT_HEXAGON "hexagon"
#define GHOST_ORBIT_SQUARE "square"
-#define GHOST_ORBIT_PENTAGON "pentagon"
\ No newline at end of file
+#define GHOST_ORBIT_PENTAGON "pentagon"
+
+//suit sensors: sensor_mode defines
+
+#define SENSOR_OFF 0
+#define SENSOR_LIVING 1
+#define SENSOR_VITALS 2
+#define SENSOR_COORDS 3
\ No newline at end of file
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 d316bd29b41..5972579834b 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -107,6 +107,7 @@
/datum/action/item_action
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING|AB_CHECK_CONSCIOUS
var/use_itemicon = TRUE
+
/datum/action/item_action/New(Target, custom_icon, custom_icon_state)
..()
var/obj/item/I = target
@@ -406,6 +407,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 a714e17d236..aaeffa08b02 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -1092,20 +1092,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 +1145,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."
+ reference = "CHAM"
+ item = /obj/item/storage/box/syndie_kit/chameleon
+ cost = 2
/datum/uplink_item/stealthy_tools/chameleon_proj
name = "Chameleon-Projector"
@@ -1210,7 +1210,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 9d199a404d0..6d91b4c8778 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..04206e1f5b2 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 = COMIC
+ ..()
+
+/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 = COMIC
..()
\ 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/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 9613ce173af..a0e3b169c7e 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -574,8 +574,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
@@ -587,7 +588,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 3e9ff0b7ba6..e3b9b516909 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -196,6 +196,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/ninja/suit/mask.dm b/code/modules/ninja/suit/mask.dm
index 910ccbc953c..bfc87e0104c 100644
--- a/code/modules/ninja/suit/mask.dm
+++ b/code/modules/ninja/suit/mask.dm
@@ -6,7 +6,7 @@ Contents:
*/
-/obj/item/clothing/mask/gas/voice/space_ninja
+/obj/item/clothing/mask/gas/space_ninja
name = "ninja mask"
desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement."
icon_state = "s-ninja(norm)"
@@ -19,3 +19,13 @@ Contents:
"Tajaran" = 'icons/mob/species/tajaran/mask.dmi',
"Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi'
)
+
+ var/obj/item/voice_changer/voice_changer
+
+/obj/item/clothing/mask/gas/space_ninja/Initialize(mapload)
+ . = ..()
+ voice_changer = new(src)
+
+/obj/item/clothing/mask/gas/space_ninja/Destroy()
+ QDEL_NULL(voice_changer)
+ return ..()
\ No newline at end of file
diff --git a/code/modules/ninja/suit/suit.dm b/code/modules/ninja/suit/suit.dm
index f03a08d237a..dfd430a5754 100644
--- a/code/modules/ninja/suit/suit.dm
+++ b/code/modules/ninja/suit/suit.dm
@@ -24,7 +24,7 @@ Contents:
var/obj/item/clothing/head/helmet/space/space_ninja/suitHood
var/obj/item/clothing/gloves/space_ninja/suitGloves
var/obj/item/clothing/shoes/space_ninja/suitShoes
- var/obj/item/clothing/mask/gas/voice/space_ninja/suitMask
+ var/obj/item/clothing/mask/gas/space_ninja/suitMask
var/mob/living/carbon/human/suitOccupant
/obj/item/clothing/suit/space/space_ninja/proc/toggle_suit_lock(mob/living/carbon/human/user)
@@ -41,7 +41,7 @@ Contents:
if(!istype(user.shoes, /obj/item/clothing/shoes/space_ninja))
to_chat(user, "ERROR: Unable to locate foot gear.\nABORTING...")
return 0
- if(!istype(user.wear_mask, /obj/item/clothing/mask/gas/voice/space_ninja))
+ if(!istype(user.wear_mask, /obj/item/clothing/mask/gas/space_ninja))
to_chat(user, "ERROR: Unable to locate mask.\nABORTING...")
return 0
diff --git a/code/modules/paperwork/stamps.dm b/code/modules/paperwork/stamps.dm
index 8e78f0ce230..8c24cc55c9d 100644
--- a/code/modules/paperwork/stamps.dm
+++ b/code/modules/paperwork/stamps.dm
@@ -92,27 +92,3 @@
icon_state = "stamp-syndicate"
item_color = "syndicate"
-// Syndicate stamp to forge documents.
-
-/obj/item/stamp/chameleon/attack_self(mob/user as mob)
-
- var/list/stamp_types = typesof(/obj/item/stamp) - src.type // Get all stamp types except our own
- var/list/stamps = list()
-
- // Generate them into a list
- for(var/stamp_type in stamp_types)
- var/obj/item/stamp/S = new stamp_type
- stamps[capitalize(S.name)] = S
-
- var/list/show_stamps = list("EXIT" = null) + sortList(stamps) // the list that will be shown to the user to pick from
-
- var/input_stamp = input(user, "Choose a stamp to disguise as.", "Choose a stamp.") in show_stamps
-
- if(user && src in user.contents)
-
- var/obj/item/stamp/chosen_stamp = stamps[capitalize(input_stamp)]
-
- if(chosen_stamp)
- name = chosen_stamp.name
- icon_state = chosen_stamp.icon_state
- item_color = chosen_stamp.item_color
diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi
index b4d8a34a6e0..7bc556c628c 100644
Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index 02de48a6f41..469934ca932 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 1b7a691c820..f96d9d6da59 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -853,6 +853,7 @@
#include "code\game\objects\items\devices\traitordevices.dm"
#include "code\game\objects\items\devices\transfer_valve.dm"
#include "code\game\objects\items\devices\uplinks.dm"
+#include "code\game\objects\items\devices\voice.dm"
#include "code\game\objects\items\devices\whistle.dm"
#include "code\game\objects\items\devices\radio\beacon.dm"
#include "code\game\objects\items\devices\radio\electropack.dm"
@@ -1319,7 +1320,6 @@
#include "code\modules\clothing\masks\breath.dm"
#include "code\modules\clothing\masks\gasmask.dm"
#include "code\modules\clothing\masks\miscellaneous.dm"
-#include "code\modules\clothing\masks\voice.dm"
#include "code\modules\clothing\patreon\glasses.dm"
#include "code\modules\clothing\patreon\hats.dm"
#include "code\modules\clothing\shoes\colour.dm"