diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index 3332e46ee20..dee779f0b58 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -314,6 +314,11 @@ GLOBAL_LIST_INIT(human_invader_antagonists, list(
#define UPLINK_SHARED_STOCK_KITS "uplink_shared_stock_kits"
#define UPLINK_SHARED_STOCK_SURPLUS "uplink_shared_stock_surplus"
+/// Does this item provide illegal tech?
+#define SYNDIE_ILLEGAL_TECH (1 << 0)
+/// Does this item go off when scanned by a contraband scanner?
+#define SYNDIE_TRIPS_CONTRABAND (1 << 1)
+
// Used for traitor objectives
/// If the objective hasn't been taken yet
#define OBJECTIVE_STATE_INACTIVE 1
diff --git a/code/__DEFINES/blackmarket.dm b/code/__DEFINES/blackmarket.dm
index c3b8ad0bf46..f0a19ad056d 100644
--- a/code/__DEFINES/blackmarket.dm
+++ b/code/__DEFINES/blackmarket.dm
@@ -9,3 +9,4 @@
#define SHIPPING_METHOD_LAUNCH "Launch"
// Sends a supply pod to the buyer's location, showy.
#define SHIPPING_METHOD_SUPPLYPOD "Supply Pod"
+
diff --git a/code/__DEFINES/devices.dm b/code/__DEFINES/devices.dm
index afd41570b5b..7368d8d0457 100644
--- a/code/__DEFINES/devices.dm
+++ b/code/__DEFINES/devices.dm
@@ -10,6 +10,7 @@
#define CLOWN_INSPECTOR_PRINT_SOUND_MODE_LAST 4
#define INSPECTOR_ENERGY_USAGE_HONK (0.015 * STANDARD_CELL_CHARGE)
#define INSPECTOR_ENERGY_USAGE_NORMAL (0.005 * STANDARD_CELL_CHARGE)
+#define INSPECTOR_ENERGY_USAGE_LOW (0.001 * STANDARD_CELL_CHARGE)
#define INSPECTOR_TIME_MODE_SLOW 1
#define INSPECTOR_TIME_MODE_FAST 2
#define INSPECTOR_TIME_MODE_HONK 3
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index 0b3321ea450..03c8022208e 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -321,6 +321,15 @@
#define PLANE_CRITICAL_FUCKO_PARALLAX (PLANE_CRITICAL_DISPLAY|PLANE_CRITICAL_NO_RELAY|PLANE_CRITICAL_CUT_RENDER)
+//---------- Plane Master offsetting_flags -------------
+// Describes how different plane masters behave regarding being offset
+/// This plane master will not be offset itself, existing only once with an offset of 0
+/// Mostly used for planes that really don't need to be duplicated, like the hud planes
+#define BLOCKS_PLANE_OFFSETTING (1<<0)
+/// This plane master will have its relays offset to match the highest rendering plane that matches the target
+/// Required for making things like the blind fullscreen not render over runechat
+#define OFFSET_RELAYS_MATCH_HIGHEST (1<<1)
+
/// A value of /datum/preference/numeric/multiz_performance that disables the option
#define MULTIZ_PERFORMANCE_DISABLE -1
/// We expect at most 3 layers of multiz
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index fe9e3d4282e..6c2fc4be7a0 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -770,6 +770,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_DANGEROUS_OBJECT "dangerous_object"
/// determines whether or not objects are haunted and teleport/attack randomly
#define TRAIT_HAUNTED "haunted"
+/// An item that, if it has contents, will ignore it's contents when scanning for contraband.
+#define TRAIT_CONTRABAND_BLOCKER "contraband_blocker"
//quirk traits
#define TRAIT_ALCOHOL_TOLERANCE "alcohol_tolerance"
@@ -1164,7 +1166,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Trait which means whatever has this is dancing by a dance machine
#define TRAIT_DISCO_DANCER "disco_dancer"
-/// That which allows mobs to instantly break down boulders.
+/// Trait which allows mobs to instantly break down boulders.
#define TRAIT_INSTANTLY_PROCESSES_BOULDERS "instantly_processes_boulders"
/// Trait applied to objects and mobs that can attack a boulder and break it down. (See /obj/item/boulder/manual_process())
@@ -1183,6 +1185,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Does this item bypass ranged armor checks?
#define TRAIT_BYPASS_RANGED_ARMOR "bypass_ranged_armor"
+/// Trait which means that this item is considered illegal contraband, and valid for the contraband bounty or when scanned by an nspect scanner.
+#define TRAIT_CONTRABAND "illegal_contraband"
+
/// Traits given by settler, each with their own specific effects for cases where someone would have that trait, but not the other settler effects
#define TRAIT_EXPERT_FISHER "expert_fisher" // fishing is easier
diff --git a/code/__HELPERS/atoms.dm b/code/__HELPERS/atoms.dm
index 406ea75143c..7106ec81be1 100644
--- a/code/__HELPERS/atoms.dm
+++ b/code/__HELPERS/atoms.dm
@@ -33,6 +33,18 @@
processing += checked_atom.contents
. += checked_atom
+///Returns the src and all recursive contents, but skipping going any deeper if an atom has a specific trait.
+/atom/proc/get_all_contents_skipping_traits(skipped_trait)
+ . = list(src)
+ if(!skipped_trait)
+ CRASH("get_all_contents_skipping_traits called without a skipped_trait")
+ var/i = 0
+ while(i < length(.))
+ var/atom/checked_atom = .[++i]
+ if(HAS_TRAIT(checked_atom, skipped_trait))
+ continue
+ . += checked_atom.contents
+
///Returns a list of all locations (except the area) the movable is within.
/proc/get_nested_locs(atom/movable/atom_on_location, include_turf = FALSE)
. = list()
diff --git a/code/__HELPERS/reagents.dm b/code/__HELPERS/reagents.dm
index 012a1fd5cc0..cb87e21cefa 100644
--- a/code/__HELPERS/reagents.dm
+++ b/code/__HELPERS/reagents.dm
@@ -190,6 +190,16 @@
var/picked_reagent = pick(random_reagents)
return picked_reagent
+///Returns a random reagent consumable ethanol object minus blacklisted reagents
+/proc/get_random_drink_id()
+ var/static/list/random_drinks = list()
+ if(!random_drinks.len)
+ for(var/datum/reagent/drink_path as anything in subtypesof(/datum/reagent/consumable/ethanol))
+ if(initial(drink_path.chemical_flags) & REAGENT_CAN_BE_SYNTHESIZED)
+ random_drinks += drink_path
+ var/picked_drink = pick(random_drinks)
+ return picked_drink
+
///Returns reagent datum from reagent name string
/proc/get_chem_id(chem_name)
for(var/X in GLOB.chemical_reagents_list)
diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm
index c1c9187f0a9..596e688a256 100644
--- a/code/__HELPERS/text.dm
+++ b/code/__HELPERS/text.dm
@@ -463,6 +463,9 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
/proc/random_color()
return random_string(6, GLOB.hex_characters)
+/proc/ready_random_color()
+ return "#" + random_string(6, GLOB.hex_characters)
+
//merges non-null characters (3rd argument) from "from" into "into". Returns result
//e.g. into = "Hello World"
// from = "Seeya______"
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 2d1085d8bbd..08bd959b3b0 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -118,6 +118,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
// SKYRAT EDIT ADDITON END - Synthetic wounds
/obj = list(
"TRAIT_WALLMOUNTED" = TRAIT_WALLMOUNTED,
+ "TRAIT_CONTRABAND" = TRAIT_CONTRABAND,
),
/mob = list(
"TRAIT_ABDUCTOR_SCIENTIST_TRAINING" = TRAIT_ABDUCTOR_SCIENTIST_TRAINING,
@@ -356,7 +357,6 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NO_EXTINGUISH" = TRAIT_NO_EXTINGUISH,
"TRAIT_NO_FLOATING_ANIM" = TRAIT_NO_FLOATING_ANIM,
"TRAIT_NO_GLIDE" = TRAIT_NO_GLIDE,
- "TRAIT_NO_PLASMA_TRANSFORM" = TRAIT_NO_PLASMA_TRANSFORM,
"TRAIT_NO_GUN_AKIMBO" = TRAIT_NO_GUN_AKIMBO,
"TRAIT_NO_IMMOBILIZE" = TRAIT_NO_IMMOBILIZE,
"TRAIT_NO_JUMPSUIT" = TRAIT_NO_JUMPSUIT,
@@ -435,7 +435,6 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_ROD_SUPLEX" = TRAIT_ROD_SUPLEX,
"TRAIT_ROUGHRIDER" = TRAIT_ROUGHRIDER,
"TRAIT_SABRAGE_PRO" = TRAIT_SABRAGE_PRO,
- "TRAIT_SACRIFICED" = TRAIT_SACRIFICED,
"TRAIT_SECURITY_HUD" = TRAIT_SECURITY_HUD,
"TRAIT_SEE_WORN_COLOURS" = TRAIT_SEE_WORN_COLOURS,
"TRAIT_SELF_AWARE" = TRAIT_SELF_AWARE,
@@ -534,9 +533,11 @@ GLOBAL_LIST_INIT(traits_by_type, list(
),
/obj/item = list(
"TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING,
+ "TRAIT_BAKEABLE" = TRAIT_BAKEABLE,
"TRAIT_BASIC_QUALITY_BAIT" = TRAIT_BASIC_QUALITY_BAIT,
"TRAIT_BLIND_TOOL" = TRAIT_BLIND_TOOL,
"TRAIT_BYPASS_RANGED_ARMOR" = TRAIT_BYPASS_RANGED_ARMOR,
+ "TRAIT_CONTRABAND_BLOCKER" = TRAIT_CONTRABAND_BLOCKER,
"TRAIT_CUSTOM_TAP_SOUND" = TRAIT_CUSTOM_TAP_SOUND,
"TRAIT_DANGEROUS_OBJECT" = TRAIT_DANGEROUS_OBJECT,
"TRAIT_FISHING_BAIT" = TRAIT_FISHING_BAIT,
@@ -546,6 +547,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_HAUNTED" = TRAIT_HAUNTED,
"TRAIT_HONKSPAMMING" = TRAIT_HONKSPAMMING,
"TRAIT_INNATELY_FANTASTICAL_ITEM" = TRAIT_INNATELY_FANTASTICAL_ITEM,
+ "TRAIT_INSTANTLY_PROCESSES_BOULDERS" = TRAIT_INSTANTLY_PROCESSES_BOULDERS,
"TRAIT_ITEM_OBJECTIVE_BLOCKED" = TRAIT_ITEM_OBJECTIVE_BLOCKED,
"TRAIT_NEEDS_TWO_HANDS" = TRAIT_NEEDS_TWO_HANDS,
"TRAIT_NO_BARCODES" = TRAIT_NO_BARCODES,
@@ -559,8 +561,6 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_TRANSFORM_ACTIVE" = TRAIT_TRANSFORM_ACTIVE,
"TRAIT_UNCATCHABLE" = TRAIT_UNCATCHABLE,
"TRAIT_WIELDED" = TRAIT_WIELDED,
- "TRAIT_BAKEABLE" = TRAIT_BAKEABLE,
- "TRAIT_INSTANTLY_PROCESSES_BOULDERS" = TRAIT_INSTANTLY_PROCESSES_BOULDERS,
),
/obj/item/ammo_casing = list(
"TRAIT_DART_HAS_INSERT" = TRAIT_DART_HAS_INSERT,
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 2a2b28d7ebe..cbfd80d97df 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -210,7 +210,7 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
for(var/group_key as anything in master_groups)
var/datum/plane_master_group/group = master_groups[group_key]
- group.transform_lower_turfs(src, current_plane_offset)
+ group.build_planes_offset(src, current_plane_offset)
/datum/hud/proc/should_use_scale()
return should_sight_scale(mymob.sight)
@@ -229,10 +229,9 @@ GLOBAL_LIST_INIT(available_erp_ui_styles, list(
current_plane_offset = new_offset
SEND_SIGNAL(src, COMSIG_HUD_OFFSET_CHANGED, old_offset, new_offset)
- if(should_use_scale())
- for(var/group_key as anything in master_groups)
- var/datum/plane_master_group/group = master_groups[group_key]
- group.transform_lower_turfs(src, new_offset)
+ for(var/group_key as anything in master_groups)
+ var/datum/plane_master_group/group = master_groups[group_key]
+ group.build_planes_offset(src, new_offset)
/datum/hud/Destroy()
if(mymob.hud_used == src)
diff --git a/code/_onclick/hud/rendering/plane_master_group.dm b/code/_onclick/hud/rendering/plane_master_group.dm
index 894190984e6..23096cc0e9c 100644
--- a/code/_onclick/hud/rendering/plane_master_group.dm
+++ b/code/_onclick/hud/rendering/plane_master_group.dm
@@ -45,7 +45,7 @@
our_hud = viewing_hud
our_hud.master_groups[key] = src
show_hud()
- transform_lower_turfs(our_hud, active_offset)
+ build_planes_offset(our_hud, active_offset)
/// Hide the plane master from its current hud, fully clear it out
/datum/plane_master_group/proc/orphan_hud()
@@ -64,7 +64,7 @@
hide_hud()
rebuild_plane_masters()
show_hud()
- transform_lower_turfs(our_hud, active_offset)
+ build_planes_offset(our_hud, active_offset)
/// Regenerate our plane masters, this is useful if we don't have a mob but still want to rebuild. Such in the case of changing the screen_loc of relays
/datum/plane_master_group/proc/rebuild_plane_masters()
@@ -97,7 +97,7 @@
/datum/plane_master_group/proc/build_plane_masters(starting_offset, ending_offset)
for(var/atom/movable/screen/plane_master/mytype as anything in get_plane_types())
for(var/plane_offset in starting_offset to ending_offset)
- if(plane_offset != 0 && !initial(mytype.allows_offsetting))
+ if(plane_offset != 0 && (initial(mytype.offsetting_flags) & BLOCKS_PLANE_OFFSETTING))
continue
var/atom/movable/screen/plane_master/instance = new mytype(null, null, src, plane_offset)
plane_masters["[instance.plane]"] = instance
@@ -110,7 +110,7 @@
// It would be nice to setup parallaxing for stairs and things when doing this
// So they look nicer. if you can't it's all good, if you think you can sanely look at monster's work
// It's hard, and potentially expensive. be careful
-/datum/plane_master_group/proc/transform_lower_turfs(datum/hud/source, new_offset, use_scale = TRUE)
+/datum/plane_master_group/proc/build_planes_offset(datum/hud/source, new_offset, use_scale = TRUE)
// Check if this feature is disabled for the client, in which case don't use scale.
var/mob/our_mob = our_hud?.mymob
if(!our_mob?.client?.prefs?.read_preference(/datum/preference/toggle/multiz_parallax))
@@ -158,7 +158,11 @@
for(var/plane_key in plane_masters)
var/atom/movable/screen/plane_master/plane = plane_masters[plane_key]
- if(!plane.allows_offsetting)
+ if(plane.offsetting_flags & BLOCKS_PLANE_OFFSETTING)
+ if(plane.offsetting_flags & OFFSET_RELAYS_MATCH_HIGHEST)
+ // Don't offset the plane, do offset where the relays point
+ // Required for making things like the blind fullscreen not render over runechat
+ plane.offset_relays_in_place(new_offset)
continue
var/visual_offset = plane.offset - new_offset
@@ -199,13 +203,13 @@
#undef MAX_CLIENT_BUILD_WITH_WORKING_SECONDARY_MAPS
-/datum/plane_master_group/popup/transform_lower_turfs(datum/hud/source, new_offset, use_scale = TRUE)
+/datum/plane_master_group/popup/build_planes_offset(datum/hud/source, new_offset, use_scale = TRUE)
return ..(source, new_offset, FALSE)
/// Holds the main plane master
/datum/plane_master_group/main
-/datum/plane_master_group/main/transform_lower_turfs(datum/hud/source, new_offset, use_scale = TRUE)
+/datum/plane_master_group/main/build_planes_offset(datum/hud/source, new_offset, use_scale = TRUE)
if(use_scale)
return ..(source, new_offset, source.should_use_scale())
return ..()
diff --git a/code/_onclick/hud/rendering/plane_masters/_plane_master.dm b/code/_onclick/hud/rendering/plane_masters/_plane_master.dm
index 13f94fa9f5a..bebf872f8eb 100644
--- a/code/_onclick/hud/rendering/plane_masters/_plane_master.dm
+++ b/code/_onclick/hud/rendering/plane_masters/_plane_master.dm
@@ -20,9 +20,9 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/plane_master)
/// The plane master group we're a member of, our "home"
var/datum/plane_master_group/home
- /// If our plane master allows for offsetting
- /// Mostly used for planes that really don't need to be duplicated, like the hud planes
- var/allows_offsetting = TRUE
+ /// If our plane master has different offsetting logic
+ /// Possible flags are defined in [_DEFINES/layers.dm]
+ var/offsetting_flags = NONE
/// Our offset from our "true" plane, see below
var/offset
/// When rendering multiz, lower levels get their own set of plane masters
diff --git a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
index fa438af4b70..c96361348f0 100644
--- a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
+++ b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
@@ -7,7 +7,7 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
render_relay_planes = list()
// We do NOT allow offsetting, because there's no case where you would want to block only one layer, at least currently
- allows_offsetting = FALSE
+ offsetting_flags = BLOCKS_PLANE_OFFSETTING
// We mark as multiz_scaled FALSE so transforms don't effect us, and we draw to the planes below us as if they were us.
// This is safe because we will ALWAYS be on the top z layer, so it DON'T MATTER
multiz_scaled = FALSE
@@ -368,7 +368,7 @@
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
render_relay_planes = list(RENDER_PLANE_NON_GAME)
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- allows_offsetting = FALSE
+ offsetting_flags = BLOCKS_PLANE_OFFSETTING|OFFSET_RELAYS_MATCH_HIGHEST
/atom/movable/screen/plane_master/runechat
name = "Runechat"
@@ -397,7 +397,7 @@
plane = HUD_PLANE
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
render_relay_planes = list(RENDER_PLANE_NON_GAME)
- allows_offsetting = FALSE
+ offsetting_flags = BLOCKS_PLANE_OFFSETTING|OFFSET_RELAYS_MATCH_HIGHEST
/atom/movable/screen/plane_master/above_hud
name = "Above HUD"
@@ -405,7 +405,7 @@
plane = ABOVE_HUD_PLANE
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
render_relay_planes = list(RENDER_PLANE_NON_GAME)
- allows_offsetting = FALSE
+ offsetting_flags = BLOCKS_PLANE_OFFSETTING|OFFSET_RELAYS_MATCH_HIGHEST
/atom/movable/screen/plane_master/splashscreen
name = "Splashscreen"
@@ -413,7 +413,7 @@
plane = SPLASHSCREEN_PLANE
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
render_relay_planes = list(RENDER_PLANE_NON_GAME)
- allows_offsetting = FALSE
+ offsetting_flags = BLOCKS_PLANE_OFFSETTING|OFFSET_RELAYS_MATCH_HIGHEST
/atom/movable/screen/plane_master/escape_menu
name = "Escape Menu"
@@ -421,4 +421,4 @@
plane = ESCAPE_MENU_PLANE
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
render_relay_planes = list(RENDER_PLANE_MASTER)
- allows_offsetting = FALSE
+ offsetting_flags = BLOCKS_PLANE_OFFSETTING|OFFSET_RELAYS_MATCH_HIGHEST
diff --git a/code/_onclick/hud/rendering/render_plate.dm b/code/_onclick/hud/rendering/render_plate.dm
index aa7f9fe30c5..e8466352982 100644
--- a/code/_onclick/hud/rendering/render_plate.dm
+++ b/code/_onclick/hud/rendering/render_plate.dm
@@ -457,6 +457,8 @@
// That's what this is for
if(show_to)
show_to.screen += relay
+ if(offsetting_flags & OFFSET_RELAYS_MATCH_HIGHEST && home.our_hud)
+ offset_relay(relay, home.our_hud.current_plane_offset)
return relay
/// Breaks a connection between this plane master, and the passed in place
@@ -479,3 +481,40 @@
return relay
return null
+
+/**
+ * Offsets our relays in place using the given parameter by adjusting their plane and
+ * layer values, avoiding changing the layer for relays with custom-set layers.
+ *
+ * Used in [proc/build_planes_offset] to make the relays for non-offsetting planes
+ * match the highest rendering plane that matches the target, to avoid them rendering
+ * on the highest level above things that should be visible.
+ *
+ * Parameters:
+ * - new_offset: the offset we will adjust our relays to
+ */
+/atom/movable/screen/plane_master/proc/offset_relays_in_place(new_offset)
+ for(var/atom/movable/render_plane_relay/rpr in relays)
+ offset_relay(rpr, new_offset)
+
+/**
+ * Offsets a given render relay using the given parameter by adjusting its plane and
+ * layer values, avoiding changing the layer if it has a custom-set layer.
+ *
+ * Parameters:
+ * - rpr: the render plane relay we will offset
+ * - new_offset: the offset we will adjust it by
+ */
+/atom/movable/screen/plane_master/proc/offset_relay(atom/movable/render_plane_relay/rpr, new_offset)
+ var/base_relay_plane = PLANE_TO_TRUE(rpr.plane)
+ var/old_offset = PLANE_TO_OFFSET(rpr.plane)
+ rpr.plane = GET_NEW_PLANE(base_relay_plane, new_offset)
+
+ var/old_offset_plane = real_plane - (PLANE_RANGE * old_offset)
+ var/old_layer = (old_offset_plane + abs(LOWEST_EVER_PLANE * 30))
+ if(rpr.layer != old_layer) // Avoid overriding custom-set layers
+ return
+
+ var/offset_plane = real_plane - (PLANE_RANGE * new_offset)
+ var/new_layer = (offset_plane + abs(LOWEST_EVER_PLANE * 30))
+ rpr.layer = new_layer
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 8297085dbcf..30fdfe389ff 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -471,5 +471,6 @@
* If higher than 1, it'll lean toward common spawns even more.
*/
/datum/config_entry/number/random_loot_weight_modifier
+ integer = FALSE
default = 1
min_val = 0.05
diff --git a/code/controllers/subsystem/blackmarket.dm b/code/controllers/subsystem/blackmarket.dm
index f6a4aa25566..5c88177583b 100644
--- a/code/controllers/subsystem/blackmarket.dm
+++ b/code/controllers/subsystem/blackmarket.dm
@@ -79,7 +79,7 @@ SUBSYSTEM_DEF(blackmarket)
to_chat(buyer, span_notice("[purchase.uplink] flashes a message noting that the order is being teleported to [get_area(targetturf)] in 60 seconds."))
// do_teleport does not want to teleport items from nullspace, so it just forceMoves and does sparks.
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/controller/subsystem/blackmarket,fake_teleport), purchase, targetturf), 60 SECONDS)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/controller/subsystem/blackmarket, fake_teleport), purchase, targetturf), 60 SECONDS)
// Get the current location of the uplink if it exists, then throws the item from space at the station from a random direction.
if(SHIPPING_METHOD_LAUNCH)
@@ -88,6 +88,7 @@ SUBSYSTEM_DEF(blackmarket)
var/pickedloc = spaceDebrisStartLoc(startSide, T.z)
var/atom/movable/item = purchase.entry.spawn_item(pickedloc, purchase)
+ purchase.post_purchase_effects(item)
item.throw_at(purchase.uplink, 3, 3, spin = FALSE)
to_chat(buyer, span_notice("[purchase.uplink] flashes a message noting the order is being launched at the station from [dir2text(startSide)]."))
@@ -110,6 +111,7 @@ SUBSYSTEM_DEF(blackmarket)
if(QDELETED(purchase))
return
var/atom/movable/thing = purchase.entry.spawn_item(target, purchase)
+ purchase.post_purchase_effects(thing)
var/datum/effect_system/spark_spread/sparks = new
sparks.set_up(5, 1, target)
sparks.attach(thing)
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 3aa9fba84e6..eac5ba56e09 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -898,7 +898,7 @@ ADMIN_VERB(load_away_mission, R_FUN, "Load Away Mission", "Load a specific away
var/offset_plane = GET_NEW_PLANE(plane_to_use, plane_offset)
var/string_plane = "[offset_plane]"
- if(!initial(master_type.allows_offsetting))
+ if(initial(master_type.offsetting_flags) & BLOCKS_PLANE_OFFSETTING)
plane_offset_blacklist[string_plane] = TRUE
var/render_target = initial(master_type.render_target)
if(!render_target)
diff --git a/code/datums/sprite_accessories.dm b/code/datums/sprite_accessories.dm
index e85c42e5226..dedb624078a 100644
--- a/code/datums/sprite_accessories.dm
+++ b/code/datums/sprite_accessories.dm
@@ -661,6 +661,10 @@
name = "Short Bangs"
icon_state = "hair_shortbangs"
+/datum/sprite_accessory/hair/shortbangs2
+ name = "Short Bangs 2"
+ icon_state = "hair_shortbangs2"
+
/datum/sprite_accessory/hair/short
name = "Short Hair"
icon_state = "hair_a"
diff --git a/code/game/machinery/scan_gate.dm b/code/game/machinery/scanner_gate.dm
similarity index 84%
rename from code/game/machinery/scan_gate.dm
rename to code/game/machinery/scanner_gate.dm
index 4ff8161de1b..ccee8fd3881 100644
--- a/code/game/machinery/scan_gate.dm
+++ b/code/game/machinery/scanner_gate.dm
@@ -5,6 +5,7 @@
#define SCANGATE_WANTED "Wanted"
#define SCANGATE_SPECIES "Species"
#define SCANGATE_NUTRITION "Nutrition"
+#define SCANGATE_CONTRABAND "Contraband"
#define SCANGATE_HUMAN "human"
#define SCANGATE_LIZARD "lizard"
@@ -45,7 +46,7 @@
var/next_beep = 0
///Bool to check if the scanner's controls are locked by an ID.
var/locked = FALSE
- ///Which setting is the scanner checking for? See defines in scan_gate.dm for the list.
+ ///Which setting is the scanner checking for? See defines in scanner_gate.dm for the list.
var/scangate_mode = SCANGATE_NONE
///Is searching for a disease, what severity is enough to trigger the gate?
var/disease_threshold = DISEASE_SEVERITY_MINOR
@@ -62,6 +63,8 @@
///Does the scanner ignore light_pass and light_fail for sending signals?
var/ignore_signals = FALSE
var/detect_gender = "male" //SKYRAT EDIT - MORE SCANNER GATE OPTIONS
+ ///Is an n-spect scanner attached to the gate? Enables contraband scanning.
+ var/obj/item/inspector/n_spect = null
/obj/machinery/scanner_gate/Initialize(mapload)
@@ -72,18 +75,37 @@
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
+ register_context()
/obj/machinery/scanner_gate/Destroy()
qdel(wires)
set_wires(null)
. = ..()
+/obj/machinery/scanner_gate/atom_deconstruct(disassembled)
+ . = ..()
+ if(n_spect)
+ n_spect.forceMove(drop_location())
+ n_spect = null
+
/obj/machinery/scanner_gate/examine(mob/user)
. = ..()
if(locked)
. += span_notice("The control panel is ID-locked. Swipe a valid ID to unlock it.")
else
. += span_notice("The control panel is unlocked. Swipe an ID to lock it.")
+ if(n_spect)
+ . += span_notice("The scanner is equipped with an N-Spect scanner. Use a [span_boldnotice("crowbar")] to uninstall.")
+
+/obj/machinery/scanner_gate/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = ..()
+ if(n_spect && held_item?.tool_behaviour == TOOL_CROWBAR)
+ context[SCREENTIP_CONTEXT_LMB] = "Remove N-Spect scanner"
+ return CONTEXTUAL_SCREENTIP_SET
+ if(!n_spect && istype(held_item, /obj/item/inspector))
+ context[SCREENTIP_CONTEXT_LMB] = "Install N-Spect scanner"
+ return CONTEXTUAL_SCREENTIP_SET
+
/obj/machinery/scanner_gate/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER
@@ -100,6 +122,19 @@
if(duration)
scanline_timer = addtimer(CALLBACK(src, PROC_REF(set_scanline), "passive"), duration, TIMER_STOPPABLE)
+/obj/machinery/scanner_gate/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, /obj/item/inspector))
+ if(n_spect)
+ to_chat(user, span_warning("The scanner is already equipped with an N-Spect scanner."))
+ return ITEM_INTERACT_BLOCKING
+ else
+ to_chat(user, span_notice("You install an N-Spect scanner on [src]."))
+ n_spect = tool
+ if(!user.transferItemToLoc(tool, src))
+ return ITEM_INTERACT_BLOCKING
+ return ITEM_INTERACT_SUCCESS
+ return NONE
+
/obj/machinery/scanner_gate/attackby(obj/item/W, mob/user, params)
var/obj/item/card/id/card = W.GetID()
if(card)
@@ -122,6 +157,20 @@
wires.interact(user)
return ..()
+/obj/machinery/scanner_gate/crowbar_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(n_spect)
+ to_chat(user, span_notice("You uninstall [n_spect] from [src]."))
+ n_spect.forceMove(drop_location())
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/scanner_gate/Exited(atom/gone)
+ . = ..()
+ if(gone == n_spect)
+ n_spect = null
+ if(scangate_mode == SCANGATE_CONTRABAND)
+ scangate_mode = SCANGATE_NONE
+
/obj/machinery/scanner_gate/emag_act(mob/user, obj/item/card/emag/emag_card)
if(obj_flags & EMAGGED)
return FALSE
@@ -226,6 +275,13 @@
if(scanned_human.gender == detect_gender)
beep = TRUE
//SKYRAT EDIT END - MORE SCANNER GATE OPTIONS
+ if(SCANGATE_CONTRABAND)
+ for(var/obj/item/content in M.get_all_contents_skipping_traits(TRAIT_CONTRABAND_BLOCKER))
+ if(content.is_contraband())
+ beep = TRUE
+ break
+ if(!n_spect.scans_correctly)
+ beep = !beep //We do a little trolling
if(reverse)
beep = !beep
@@ -274,6 +330,7 @@
data["target_species"] = detect_species
data["target_nutrition"] = detect_nutrition
data["target_gender"] = detect_gender //SKYRAT EDIT - MORE SCANNER GATE OPTIONS
+ data["contraband_enabled"] = !!n_spect
return data
/obj/machinery/scanner_gate/ui_act(action, params)
@@ -338,6 +395,7 @@
#undef SCANGATE_WANTED
#undef SCANGATE_SPECIES
#undef SCANGATE_NUTRITION
+#undef SCANGATE_CONTRABAND
#undef SCANGATE_HUMAN
#undef SCANGATE_LIZARD
diff --git a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm
index c57a629d85c..2e2039dfaec 100644
--- a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm
+++ b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm
@@ -88,11 +88,15 @@
pixel_y = -16
duration = 0.5 SECONDS
color = COLOR_LIME
+ var/max_alpha = 255
/obj/effect/temp_visual/bioscrambler_wave/Initialize(mapload)
transform = matrix().Scale(0.1)
animate(src, transform = matrix().Scale(2), time = duration, flags = ANIMATION_PARALLEL)
- animate(src, alpha = 255, time = duration * 0.6, flags = ANIMATION_PARALLEL)
+ animate(src, alpha = max_alpha, time = duration * 0.6, flags = ANIMATION_PARALLEL)
animate(alpha = 0, time = duration * 0.4)
apply_wibbly_filters(src)
return ..()
+
+/obj/effect/temp_visual/bioscrambler_wave/light
+ max_alpha = 128
diff --git a/code/game/objects/effects/posters/contraband.dm b/code/game/objects/effects/posters/contraband.dm
index 52528c251b6..04bc790daea 100644
--- a/code/game/objects/effects/posters/contraband.dm
+++ b/code/game/objects/effects/posters/contraband.dm
@@ -4,6 +4,10 @@
poster_type = /obj/structure/sign/poster/contraband/random
icon_state = "rolled_poster"
+/obj/item/poster/random_contraband/Initialize(mapload, obj/structure/sign/poster/new_poster_structure)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
+
/obj/structure/sign/poster/contraband
poster_item_name = "contraband poster"
poster_item_desc = "This poster comes with its own automatic adhesive mechanism, for easy pinning to any vertical surface. Its vulgar themes have marked it as contraband aboard Nanotrasen space facilities."
diff --git a/code/game/objects/effects/spawners/random/contraband.dm b/code/game/objects/effects/spawners/random/contraband.dm
index f17656c6119..c32d46125c0 100644
--- a/code/game/objects/effects/spawners/random/contraband.dm
+++ b/code/game/objects/effects/spawners/random/contraband.dm
@@ -25,6 +25,12 @@
/obj/item/reagent_containers/pill/maintenance = 5,
)
+
+/obj/effect/spawner/random/contraband/make_item(spawn_loc, type_path_to_make)
+ var/obj/item/made = ..()
+ ADD_TRAIT(made, TRAIT_CONTRABAND, INNATE_TRAIT)
+ return made
+
/obj/effect/spawner/random/contraband/plus
name = "contraband loot spawner plus"
desc = "Where'd ya find this?"
diff --git a/code/game/objects/effects/spawners/random/random.dm b/code/game/objects/effects/spawners/random/random.dm
index fae8ff14cda..9614c4a1781 100644
--- a/code/game/objects/effects/spawners/random/random.dm
+++ b/code/game/objects/effects/spawners/random/random.dm
@@ -103,7 +103,7 @@
var/loot_weight = loot_list[loot_type]
if(loot_weight <= 1)
if(exponent < 1)
- loot_list[loot_type] *= precision
+ loot_list[loot_type] = precision
continue
loot_list[loot_type] = round(loot_weight ** exponent * precision, 1)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 5105c5556a2..3a3f3371d1d 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -1829,6 +1829,20 @@
SEND_SIGNAL(loc, COMSIG_ATOM_CONTENTS_WEIGHT_CLASS_CHANGED, src, old_w_class, new_w_class)
return TRUE
+/**
+ * Used to determine if an item should be considered contraband by N-spect scanners or scanner gates.
+ * Returns true when an item has the contraband trait, or is included in the traitor uplink.
+ */
+/obj/item/proc/is_contraband()
+ if(HAS_TRAIT(src, TRAIT_CONTRABAND))
+ return TRUE
+ for(var/datum/uplink_item/traitor_item as anything in SStraitor.uplink_items)
+ if(istype(src, traitor_item.item))
+ if(!(traitor_item.uplink_item_flags & SYNDIE_TRIPS_CONTRABAND))
+ return FALSE
+ return TRUE
+ return FALSE
+
/// Fetches embedding data
/obj/item/proc/get_embed()
RETURN_TYPE(/datum/embed_data)
diff --git a/code/game/objects/items/drug_items.dm b/code/game/objects/items/drug_items.dm
index f313dad5f74..d25c9571455 100644
--- a/code/game/objects/items/drug_items.dm
+++ b/code/game/objects/items/drug_items.dm
@@ -18,6 +18,10 @@
icon_state = "saturnx_glob" //tell kryson to sprite two more variants in the future.
food_reagents = list(/datum/reagent/drug/saturnx = 10)
+/obj/item/food/drug/saturnx/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
+
/obj/item/food/drug/moon_rock
name = "moon rock"
desc = "A small hard lump of kronkaine freebase.\nIt is said the average kronkaine addict causes as much criminal damage as four cat burglars, two arsonists and one rabid pit bull terrier combined."
@@ -28,6 +32,7 @@
. = ..()
icon_state = pick("moon_rock1", "moon_rock2", "moon_rock3")
AddElement(/datum/element/swabable, CELL_LINE_TABLE_MOONICORN, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
/obj/item/reagent_containers/cup/blastoff_ampoule
name = "bLaSToFF ampoule" //stylized name
@@ -70,3 +75,7 @@
SplashReagents(hit_atom, TRUE)
qdel(src)
hit_atom.Bumped(ampoule_shard)
+
+/obj/item/reagent_containers/cup/blastoff_ampoule/Initialize(mapload, vol)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
diff --git a/code/game/objects/items/implants/implant_storage.dm b/code/game/objects/items/implants/implant_storage.dm
index 216d8481afa..e513967bc46 100644
--- a/code/game/objects/items/implants/implant_storage.dm
+++ b/code/game/objects/items/implants/implant_storage.dm
@@ -36,7 +36,7 @@
return TRUE
return FALSE
create_storage(storage_type = /datum/storage/implant)
-
+ ADD_TRAIT(src, TRAIT_CONTRABAND_BLOCKER, INNATE_TRAIT)
return ..()
/obj/item/implanter/storage
diff --git a/code/game/objects/items/inspector.dm b/code/game/objects/items/inspector.dm
index 82a36336c42..b73917bfba8 100644
--- a/code/game/objects/items/inspector.dm
+++ b/code/game/objects/items/inspector.dm
@@ -8,7 +8,7 @@
*/
/obj/item/inspector
name = "\improper N-spect scanner"
- desc = "Central Command-issued inspection device. Performs inspections according to Nanotrasen protocols when activated, then prints an encrypted report regarding the maintenance of the station. Definitely not giving you cancer."
+ desc = "Central Command standard issue inspection device. Can perform either wide area scans that central command can use to verify the security of the station, or detailed scans to determine if an item is contraband."
icon = 'icons/obj/devices/scanner.dmi'
icon_state = "inspector"
worn_icon_state = "salestagger"
@@ -32,11 +32,15 @@
var/cell_cover_open = FALSE
///Energy used per print.
var/energy_per_print = INSPECTOR_ENERGY_USAGE_NORMAL
+ ///Does this item scan for contraband correctly? If not, will provide a flipped response.
+ var/scans_correctly = TRUE
/obj/item/inspector/Initialize(mapload)
. = ..()
if(ispath(cell))
cell = new cell(src)
+ register_context()
+ register_item_context()
// Clean up the cell on destroy
/obj/item/inspector/Exited(atom/movable/gone, direction)
@@ -93,6 +97,54 @@
else
. += "\The [cell] is firmly in place. [span_info("Ctrl-click with an empty hand to remove it.")]"
+/obj/item/inspector/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!user.Adjacent(interacting_with))
+ return ITEM_INTERACT_BLOCKING
+ if(cell_cover_open)
+ balloon_alert(user, "close cover first!")
+ return ITEM_INTERACT_BLOCKING
+ if(!cell || !cell.use(INSPECTOR_ENERGY_USAGE_LOW))
+ balloon_alert(user, "check cell!")
+ return ITEM_INTERACT_BLOCKING
+ if(!isitem(interacting_with))
+ return ITEM_INTERACT_BLOCKING
+ var/obj/item/contraband_item = interacting_with
+ var/contraband_status = contraband_item.is_contraband()
+ if((!contraband_status && scans_correctly) || (contraband_status && !scans_correctly))
+ playsound(src, 'sound/machines/ping.ogg', 20)
+ balloon_alert(user, "clear")
+ return ITEM_INTERACT_SUCCESS
+
+ playsound(src, 'sound/machines/uplinkerror.ogg', 40)
+ balloon_alert(user, "contraband detected!")
+ return ITEM_INTERACT_SUCCESS
+
+/obj/item/inspector/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ var/update_context = FALSE
+ if(cell_cover_open && cell)
+ context[SCREENTIP_CONTEXT_CTRL_LMB] = "Remove cell"
+ update_context = TRUE
+
+ if(cell_cover_open && !cell && istype(held_item, /obj/item/stock_parts/power_store/cell))
+ context[SCREENTIP_CONTEXT_LMB] = "Install cell"
+ update_context = TRUE
+
+ if(held_item?.tool_behaviour == TOOL_CROWBAR)
+ context[SCREENTIP_CONTEXT_LMB] = "[cell_cover_open ? "close" : "open"] battery panel"
+ update_context = TRUE
+
+ if(update_context)
+ return CONTEXTUAL_SCREENTIP_SET
+ return NONE
+
+/obj/item/inspector/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
+ if(cell_cover_open || !cell)
+ return NONE
+ if(isitem(target))
+ context[SCREENTIP_CONTEXT_LMB] = "Contraband Scan"
+ return CONTEXTUAL_SCREENTIP_SET
+ return NONE
+
/**
* Create our report
*
@@ -178,6 +230,7 @@
* Can be crafted into a bananium HONK-spect scanner
*/
/obj/item/inspector/clown
+ scans_correctly = FALSE
///will only cycle through modes with numbers lower than this
var/max_mode = CLOWN_INSPECTOR_PRINT_SOUND_MODE_LAST
///names of modes, ordered first to last
diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm
index 5660de8d604..f6357b229ef 100644
--- a/code/game/objects/items/robot/ai_upgrades.dm
+++ b/code/game/objects/items/robot/ai_upgrades.dm
@@ -8,6 +8,9 @@
icon = 'icons/obj/devices/circuitry_n_data.dmi'
icon_state = "datadisk3"
+/obj/item/malf_upgrade/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
/obj/item/malf_upgrade/pre_attack(atom/A, mob/living/user, proximity)
if(!proximity)
@@ -37,6 +40,10 @@
icon = 'icons/obj/devices/circuitry_n_data.dmi'
icon_state = "datadisk3"
+/obj/item/surveillance_upgrade/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
+
/obj/item/surveillance_upgrade/pre_attack(atom/A, mob/living/user, proximity)
if(!proximity)
return ..()
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index a8ed3b75725..ba072d79b74 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -213,6 +213,10 @@
icon_state = "cyborg_upgrade3"
require_model = TRUE
+/obj/item/borg/upgrade/syndicate/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
+
/obj/item/borg/upgrade/syndicate/action(mob/living/silicon/robot/borg, mob/living/user = usr)
. = ..()
if(!.)
diff --git a/code/game/objects/items/stickers.dm b/code/game/objects/items/stickers.dm
index a02cfd9515e..19ac58f6f40 100644
--- a/code/game/objects/items/stickers.dm
+++ b/code/game/objects/items/stickers.dm
@@ -174,6 +174,10 @@
icon_state = "synd"
contraband = TRUE
+/obj/item/sticker/syndicate/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
+
/obj/item/sticker/syndicate/c4
name = "C-4 sticker"
icon_state = "c4"
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index 7763025d767..9ac35368c1a 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -387,6 +387,7 @@
AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE, INVISIBILITY_MAXIMUM, use_anchor = TRUE) // SKYRAT EDIT - Ghosts can't see smuggler's satchels
atom_storage.max_total_storage = 15
atom_storage.set_holdable(cant_hold_list = /obj/item/storage/backpack/satchel/flat) //muh recursive backpacks
+ ADD_TRAIT(src, TRAIT_CONTRABAND_BLOCKER, INNATE_TRAIT)
/obj/item/storage/backpack/satchel/flat/PopulateContents()
for(var/items in 1 to 4)
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 595d95c168e..0f0a47d162f 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -85,6 +85,7 @@ Striking a noncultist, however, will tear their flesh."}
speed = 4 SECONDS, \
effectiveness = 100, \
)
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
/obj/item/melee/cultblade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
if(IS_CULTIST(owner) && prob(final_block_chance))
@@ -658,6 +659,10 @@ Striking a noncultist, however, will tear their flesh."}
righthand_file = 'icons/mob/inhands/items/drinks_righthand.dmi'
list_reagents = list(/datum/reagent/fuel/unholywater = 50)
+/obj/item/reagent_containers/cup/beaker/unholywater/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
+
///how many times can the shuttle be cursed?
#define MAX_SHUTTLE_CURSES 3
///if the max number of shuttle curses are used within this duration, the entire cult gets an achievement
diff --git a/code/modules/antagonists/cult/cult_structures.dm b/code/modules/antagonists/cult/cult_structures.dm
index 5067dcf9799..2cdb2c2e6f4 100644
--- a/code/modules/antagonists/cult/cult_structures.dm
+++ b/code/modules/antagonists/cult/cult_structures.dm
@@ -131,6 +131,7 @@
COOLDOWN_START(src, use_cooldown, use_cooldown_duration)
for(var/item_to_make in spawned_items)
var/obj/item/made_item = new item_to_make(get_turf(src))
+ ADD_TRAIT(made_item, TRAIT_CONTRABAND, INNATE_TRAIT)
succcess_message(user, made_item)
diff --git a/code/modules/antagonists/cult/datums/cultist.dm b/code/modules/antagonists/cult/datums/cultist.dm
index b0fbea4421a..f56d79a8f4b 100644
--- a/code/modules/antagonists/cult/datums/cultist.dm
+++ b/code/modules/antagonists/cult/datums/cultist.dm
@@ -151,7 +151,8 @@
///Attempts to make a new item and put it in a potential inventory slot in the provided mob.
/datum/antagonist/cult/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob)
- var/item = new item_path(mob)
+ var/obj/item = new item_path(mob)
+ ADD_TRAIT(item, TRAIT_CONTRABAND, INNATE_TRAIT)
var/where = mob.equip_conspicuous_item(item)
if(!where)
to_chat(mob, span_userdanger("Unfortunately, you weren't able to get [item]. This is very bad and you should adminhelp immediately (press F1)."))
diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm
index 6cfa8db0560..5b78d1b7efb 100644
--- a/code/modules/antagonists/heretic/heretic_knowledge.dm
+++ b/code/modules/antagonists/heretic/heretic_knowledge.dm
@@ -155,7 +155,9 @@
return FALSE
for(var/result in result_atoms)
- new result(loc)
+ var/atom/result_item = new result(loc)
+ if(isitem(result_item))
+ ADD_TRAIT(result_item, TRAIT_CONTRABAND, INNATE_TRAIT)
return TRUE
/**
diff --git a/code/modules/antagonists/heretic/items/heretic_armor.dm b/code/modules/antagonists/heretic/items/heretic_armor.dm
index 45ddea163fa..0c64e4a227e 100644
--- a/code/modules/antagonists/heretic/items/heretic_armor.dm
+++ b/code/modules/antagonists/heretic/items/heretic_armor.dm
@@ -97,6 +97,7 @@
. = ..()
create_storage(storage_type = /datum/storage/pockets/void_cloak)
make_visible()
+ ADD_TRAIT(src, TRAIT_CONTRABAND_BLOCKER, INNATE_TRAIT)
/obj/item/clothing/suit/hooded/cultrobes/void/equipped(mob/user, slot)
. = ..()
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm
index 462f10f03c3..18e2dae715c 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm
@@ -215,6 +215,11 @@
log_spellbook("[key_name(user)] bought [src] for [cost] points")
SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
log_purchase(user.key)
+
+ ADD_TRAIT(spawned_path, TRAIT_CONTRABAND, INNATE_TRAIT)
+ for(var/obj/contained as anything in spawned_path.contents)
+ ADD_TRAIT(contained, TRAIT_CONTRABAND, INNATE_TRAIT)
+
try_equip_item(user, spawned_path)
return spawned_path
diff --git a/code/modules/cargo/bounties/security.dm b/code/modules/cargo/bounties/security.dm
index cc83aa228a8..8281408c179 100644
--- a/code/modules/cargo/bounties/security.dm
+++ b/code/modules/cargo/bounties/security.dm
@@ -1,8 +1,8 @@
/datum/bounty/item/security/recharger
- name = "Rechargers"
- description = "Nanotrasen military academy is conducting marksmanship exercises. They request that rechargers be shipped."
+ name = "Weapon Recharger"
+ description = "Nanotrasen military academy is conducting marksmanship exercises. They request that a recharger be shipped."
reward = CARGO_CRATE_VALUE * 4
- required_count = 3
+ required_count = 1
wanted_types = list(/obj/machinery/recharger = TRUE)
/datum/bounty/item/security/pepperspray
@@ -81,3 +81,13 @@
if(istype(slip.scanned_area, demanded_area))
return TRUE
return FALSE
+
+/datum/bounty/item/security/contraband
+ name = "Confiscated Contraband"
+ description = "The syndicate are constantly acting to subvert crewmates of Nanotrasen afilliated stations. Ship us your latest batch of confiscated contraband."
+ reward = CARGO_CRATE_VALUE * 4
+ required_count = 5
+ wanted_types = list(/obj/item = TRUE)
+
+/datum/bounty/item/security/contraband/applies_to(obj/O)
+ return HAS_TRAIT(O, TRAIT_CONTRABAND)
diff --git a/code/modules/cargo/markets/_market.dm b/code/modules/cargo/markets/_market.dm
index 78585ed842f..7bef341842f 100644
--- a/code/modules/cargo/markets/_market.dm
+++ b/code/modules/cargo/markets/_market.dm
@@ -10,6 +10,8 @@
var/list/available_items = list()
/// Item categories available from this market, only items which are in these categories can be gotten from this market. Automatically assigned, so don't manually adjust.
var/list/categories = list()
+ /// Are the items from this market legal or illegal? If illegal, apply a contrband trait to the bought object.
+ var/legal_status = TRUE
/// Adds item to the available items and add it's category if it is not in categories yet.
/datum/market/proc/add_item(datum/market_item/item)
@@ -30,7 +32,15 @@
if(!length(available_items[item.category]))
available_items -= item.category
-/// Handles buying the item, this is mainly for future use and moving the code away from the uplink.
+/**
+ * Handles buying the item for a market.
+ *
+ * @param identifier The identifier of the item to buy.
+ * @param category The category of the item to buy.
+ * @param method The shipping method to use to get the item on the station.
+ * @param uplink The uplink object that is buying the item.
+ * @param user The mob that is buying the item.
+ */
/datum/market/proc/purchase(identifier, category, method, obj/item/market_uplink/uplink, user)
var/datum/market_item/item = available_items[category][identifier]
if(isnull(item))
@@ -54,7 +64,7 @@
to_chat(user, span_warning("You don't have enough credits in [uplink] for [item] with [method] shipping."))
return FALSE
- if(item.buy(uplink, user, method))
+ if(item.buy(uplink, user, method, legal_status))
uplink.current_user.adjust_money(-price, "Other: Third Party Transaction")
if(ismob(user))
var/mob/m_user = user
@@ -70,3 +80,4 @@
SHIPPING_METHOD_LAUNCH = 10,
SHIPPING_METHOD_TELEPORT= 75,
)
+ legal_status = FALSE
diff --git a/code/modules/cargo/markets/market_item.dm b/code/modules/cargo/markets/market_item.dm
index 21ff3d01deb..5e3ce4efb6c 100644
--- a/code/modules/cargo/markets/market_item.dm
+++ b/code/modules/cargo/markets/market_item.dm
@@ -81,8 +81,15 @@
return new item(loc)
CRASH("Invalid item type for market item [item || "null"]")
-/// Buys the item and makes SSblackmarket handle it.
-/datum/market_item/proc/buy(obj/item/market_uplink/uplink, mob/buyer, shipping_method)
+/**
+ * Buys the item and makes SSblackmarket handle it.
+ *
+ * @param uplink The uplink that is buying the item.
+ * @param buyer The mob that is buying the item.
+ * @param shipping_method The shipping method used to get the market item onto the station.
+ * @param legal_status The legal status of the market. Determines if the item to be spawned is contraband.
+ */
+/datum/market_item/proc/buy(obj/item/market_uplink/uplink, mob/buyer, shipping_method, legal_status)
SHOULD_CALL_PARENT(TRUE)
// Sanity
if(!istype(uplink) || !istype(buyer))
@@ -93,7 +100,7 @@
return FALSE
// Alright, the item has been purchased.
- var/datum/market_purchase/purchase = new(src, uplink, shipping_method)
+ var/datum/market_purchase/purchase = new(src, uplink, shipping_method, legal_status)
// SSblackmarket takes care of the shipping.
if(SSblackmarket.queue_item(purchase))
@@ -102,6 +109,7 @@
return TRUE
return FALSE
+
// This exists because it is easier to keep track of all the vars this way.
/datum/market_purchase
/// The entry being purchased.
@@ -112,13 +120,16 @@
var/obj/item/market_uplink/uplink
/// Shipping method used to buy this item.
var/method
+ /// Is this item considered contraband? If illegal, applies the contraband trait to the item when spawned.
+ var/legallity
-/datum/market_purchase/New(datum/market_item/entry, obj/item/market_uplink/uplink, method)
+/datum/market_purchase/New(datum/market_item/entry, obj/item/market_uplink/uplink, method, legal_status)
if(!uplink || !entry || !method)
CRASH("[type] created with a false value arg: (entry: [entry] - uplink: [uplink] - method: [method])")
src.entry = entry
src.uplink = uplink
src.method = method
+ src.legallity = legal_status
RegisterSignal(entry, COMSIG_QDELETING, PROC_REF(on_instance_del))
RegisterSignal(uplink, COMSIG_QDELETING, PROC_REF(on_instance_del))
if(ismovable(entry.item))
@@ -137,3 +148,13 @@
return
// Uh oh, uplink or item is gone. We will just keep the money and you will not get your order.
qdel(src)
+
+/**
+ * Proc that applies secondary effects to objects that are spawned via a market.
+ *
+ * @param spawned_item - Reference to the atom being spawned.
+ * @param legal_status - Is this item considered legal? If illegal, will apply the contraband trait to the spawned item.
+ */
+/datum/market_purchase/proc/post_purchase_effects(atom/spawned_item)
+ if(!legallity && isobj(spawned_item))
+ ADD_TRAIT(spawned_item, TRAIT_CONTRABAND, INNATE_TRAIT)
diff --git a/code/modules/cargo/markets/market_telepad.dm b/code/modules/cargo/markets/market_telepad.dm
index 4545b07e487..7c5b509a942 100644
--- a/code/modules/cargo/markets/market_telepad.dm
+++ b/code/modules/cargo/markets/market_telepad.dm
@@ -91,6 +91,7 @@
if(receiving)
receiving.item = receiving.entry.spawn_item(turf, receiving)
+ receiving.post_purchase_effects(receiving.item)
use_energy(energy_usage_per_teleport / power_efficiency)
var/datum/effect_system/spark_spread/sparks = new
diff --git a/code/modules/cargo/markets/market_uplink.dm b/code/modules/cargo/markets/market_uplink.dm
index df8c8eb36a5..d13f59937b6 100644
--- a/code/modules/cargo/markets/market_uplink.dm
+++ b/code/modules/cargo/markets/market_uplink.dm
@@ -157,6 +157,9 @@
accessible_markets = list(/datum/market/blackmarket)
custom_premium_price = PAYCHECK_CREW * 2.5
+/obj/item/market_uplink/blackmarket/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
/datum/crafting_recipe/blackmarket_uplink
name = "Black Market Uplink"
diff --git a/code/modules/cargo/order.dm b/code/modules/cargo/order.dm
index 3f8ceb5ca02..c675352d024 100644
--- a/code/modules/cargo/order.dm
+++ b/code/modules/cargo/order.dm
@@ -185,6 +185,9 @@
else
account_holder = "Cargo"
var/obj/structure/closet/crate/crate = pack.generate(A, paying_account)
+ if(pack.contraband)
+ for(var/atom/movable/item_within as anything in crate.get_all_contents())
+ ADD_TRAIT(item_within, TRAIT_CONTRABAND, INNATE_TRAIT)
if(department_destination)
crate.AddElement(/datum/element/deliver_first, department_destination, pack.cost)
generateManifest(crate, account_holder, pack, pack.cost)
diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm
index a48e9db6bc6..6e5643e383e 100644
--- a/code/modules/cargo/orderconsole.dm
+++ b/code/modules/cargo/orderconsole.dm
@@ -178,6 +178,7 @@
"desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name.
"goody" = P.goody,
"access" = P.access,
+ "contraband" = P.contraband,
))
return data
diff --git a/code/modules/cargo/packs/_packs.dm b/code/modules/cargo/packs/_packs.dm
index 662938320cb..b6c533050f6 100644
--- a/code/modules/cargo/packs/_packs.dm
+++ b/code/modules/cargo/packs/_packs.dm
@@ -77,6 +77,7 @@
continue
A.flags_1 |= ADMIN_SPAWNED_1
+
/// For generating supply packs at runtime. Returns a list of supply packs to use instead of this one.
/datum/supply_pack/proc/generate_supply_packs()
return
diff --git a/code/modules/client/preferences/multiz_parallax.dm b/code/modules/client/preferences/multiz_parallax.dm
index d4f77e206b2..dab945e4bb3 100644
--- a/code/modules/client/preferences/multiz_parallax.dm
+++ b/code/modules/client/preferences/multiz_parallax.dm
@@ -13,4 +13,4 @@
for(var/group_key as anything in my_hud.master_groups)
var/datum/plane_master_group/group = my_hud.master_groups[group_key]
- group.transform_lower_turfs(my_hud, my_hud.current_plane_offset)
+ group.build_planes_offset(my_hud, my_hud.current_plane_offset)
diff --git a/code/modules/client/preferences/multiz_performance.dm b/code/modules/client/preferences/multiz_performance.dm
index 7591401f2d8..f4575357e34 100644
--- a/code/modules/client/preferences/multiz_performance.dm
+++ b/code/modules/client/preferences/multiz_performance.dm
@@ -18,4 +18,4 @@
for(var/group_key as anything in my_hud.master_groups)
var/datum/plane_master_group/group = my_hud.master_groups[group_key]
- group.transform_lower_turfs(my_hud, my_hud.current_plane_offset)
+ group.build_planes_offset(my_hud, my_hud.current_plane_offset)
diff --git a/code/modules/clothing/chameleon/generic_chameleon_clothing.dm b/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
index 63bede9ec88..47b6f4db185 100644
--- a/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
+++ b/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
@@ -252,6 +252,10 @@ do { \
name = "backpack"
actions_types = list(/datum/action/item_action/chameleon/change/backpack)
+/obj/item/storage/backpack/chameleon/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND_BLOCKER, INNATE_TRAIT)
+
/obj/item/storage/backpack/chameleon/broken
/obj/item/storage/backpack/chameleon/broken/Initialize(mapload)
diff --git a/code/modules/clothing/suits/ghostsheet.dm b/code/modules/clothing/suits/ghostsheet.dm
index 9bd8753f50b..65213fd176d 100644
--- a/code/modules/clothing/suits/ghostsheet.dm
+++ b/code/modules/clothing/suits/ghostsheet.dm
@@ -22,8 +22,18 @@
if(!isinhands && check_holidays(HALLOWEEN))
. += emissive_appearance('icons/mob/simple/mob.dmi', "ghost", offset_spokesman = src, alpha = src.alpha)
-/obj/item/clothing/suit/costume/ghost_sheet/spooky
+/obj/item/clothing/suit/spooky_ghost_sheet
name = "spooky ghost"
desc = "This is obviously just a bedsheet, but maybe try it on?"
+ icon = 'icons/obj/clothing/suits/costume.dmi'
+ worn_icon = 'icons/mob/clothing/suits/costume.dmi'
user_vars_to_edit = list("name" = "Spooky Ghost", "real_name" = "Spooky Ghost" , "incorporeal_move" = INCORPOREAL_MOVE_BASIC, "appearance_flags" = KEEP_TOGETHER|TILE_BOUND, "alpha" = 150)
+ inhand_icon_state = null
+ throwforce = 0
+ throw_speed = 1
+ throw_range = 2
+ w_class = WEIGHT_CLASS_TINY
+ flags_inv = HIDEGLOVES|HIDEEARS|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
+ species_exception = list(/datum/species/golem)
+ supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
alternate_worn_layer = ABOVE_BODY_FRONT_LAYER //so the bedsheet goes over everything but fire
diff --git a/code/modules/hydroponics/grown/kronkus.dm b/code/modules/hydroponics/grown/kronkus.dm
index e0b6e6b66aa..90d264230e7 100644
--- a/code/modules/hydroponics/grown/kronkus.dm
+++ b/code/modules/hydroponics/grown/kronkus.dm
@@ -16,6 +16,10 @@
growing_icon = 'icons/obj/service/hydroponics/growing.dmi'
reagents_add = list(/datum/reagent/consumable/nutriment = 0.05)
+/obj/item/seeds/kronkus/Initialize(mapload, nogenes)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
+
/obj/item/food/grown/kronkus
seed = /obj/item/seeds/kronkus
name = "kronkus vine segment"
@@ -24,3 +28,7 @@
filling_color = "#37946e"
foodtypes = VEGETABLES | TOXIC
distill_reagent = /datum/reagent/kronkus_extract
+
+/obj/item/food/grown/kronkus/Initialize(mapload, nogenes)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 706d22783b5..6f28cd9d068 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -294,8 +294,8 @@
else
Knockdown(stun_duration)
-/mob/living/carbon/proc/help_shake_act(mob/living/carbon/helper)
- var/nosound = FALSE //SKYRAT EDIT ADDITION - EMOTES
+/mob/living/carbon/proc/help_shake_act(mob/living/carbon/helper, force_friendly)
+ var/nosound = FALSE //SKYRATEDIT ADDITION - EMOTES
if(on_fire)
to_chat(helper, span_warning("You can't put [p_them()] out with just your bare hands!"))
return
@@ -419,7 +419,7 @@
else if(bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT)
to_chat(helper, span_warning("It feels like [src] is freezing as you hug [p_them()]."))
- if(HAS_TRAIT(helper, TRAIT_FRIENDLY))
+ if(HAS_TRAIT(helper, TRAIT_FRIENDLY) || force_friendly)
if (helper.mob_mood.sanity >= SANITY_GREAT)
new /obj/effect/temp_visual/heart(loc)
add_mood_event("friendly_hug", /datum/mood_event/besthug, helper)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index acec80f5652..41c129dc4ab 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -553,7 +553,7 @@
. = rand(-1000, 1000)
..() //Called afterwards because getting the mind after getting gibbed is sketchy
-/mob/living/carbon/human/help_shake_act(mob/living/carbon/helper)
+/mob/living/carbon/human/help_shake_act(mob/living/carbon/helper, force_friendly)
if(!istype(helper))
return
diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm
index d58e71ca8ff..e9b018bfcde 100644
--- a/code/modules/mod/mod_types.dm
+++ b/code/modules/mod/mod_types.dm
@@ -354,6 +354,9 @@
/obj/item/mod/module/quick_cuff,
)
+/obj/item/mod/control/pre_equipped/infiltrator/Initialize(mapload, new_theme, new_skin, new_core)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND_BLOCKER, INNATE_TRAIT)
/obj/item/mod/control/pre_equipped/interdyne
theme = /datum/mod_theme/interdyne
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index 9fffd5ccc7c..06537e591c9 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -316,6 +316,10 @@
name = "unlabeled syringe"
desc = "A syringe containing some sort of unknown chemical cocktail."
+/obj/item/reagent_containers/syringe/contraband/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
+
/obj/item/reagent_containers/syringe/contraband/space_drugs
list_reagents = list(/datum/reagent/drug/space_drugs = 15)
diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm
index b8b11e7b44f..7572a735292 100644
--- a/code/modules/research/designs/machine_designs.dm
+++ b/code/modules/research/designs/machine_designs.dm
@@ -660,7 +660,7 @@
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_MEDICAL
)
- departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_SCIENCE
+ departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING | DEPARTMENT_BITFLAG_SCIENCE | DEPARTMENT_BITFLAG_SECURITY | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_MEDICAL
/datum/design/board/holopad
name = "AI Holopad Board"
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index e7c02c199aa..ec562dd4d26 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -616,6 +616,12 @@
PROC_REF(uncontrolled_teleport),
PROC_REF(heat_and_explode),
PROC_REF(rapid_self_dupe),
+ PROC_REF(drink_dispenser),
+ PROC_REF(tummy_ache),
+ PROC_REF(charger),
+ PROC_REF(hugger),
+ PROC_REF(dimensional_shift),
+ PROC_REF(disguiser),
)
/obj/item/relic/attack_self(mob/user)
@@ -721,6 +727,164 @@
throw_smoke(get_turf(user))
warn_admins(user, "Teleport", 0)
+// Creates a glass and fills it up with a drink.
+/obj/item/relic/proc/drink_dispenser(mob/user)
+ var/obj/item/reagent_containers/cup/glass/drinkingglass/freebie = new(get_step_rand(user))
+ playsound(freebie, 'sound/effects/phasein.ogg', rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ addtimer(CALLBACK(src, PROC_REF(dispense_drink), freebie), 0.5 SECONDS)
+
+/obj/item/relic/proc/dispense_drink(obj/item/reagent_containers/cup/glass/glasser)
+ glasser.reagents.add_reagent(get_random_drink_id(), rand(glasser.volume * 0.3, glasser.volume))
+ playsound(glasser, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ throw_smoke(get_turf(glasser))
+
+// Scrambles your organs. 33% chance to delete after use.
+/obj/item/relic/proc/tummy_ache(mob/user)
+ new /obj/effect/temp_visual/bioscrambler_wave/light(get_turf(src))
+ to_chat(user, span_notice("Your stomach starts growling..."))
+ addtimer(CALLBACK(src, PROC_REF(scrambliticus), user), rand(1 SECONDS, 3 SECONDS)) // throw it away!
+
+/obj/item/relic/proc/scrambliticus(mob/user)
+ new /obj/effect/temp_visual/bioscrambler_wave(get_turf(src))
+ playsound(src, 'sound/magic/cosmic_energy.ogg', vol = 50, vary = TRUE)
+ for(var/mob/living/carbon/nearby in hearers(2, src))
+ nearby.bioscramble(name)
+ playsound(nearby, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ throw_smoke(get_turf(nearby))
+ to_chat(nearby, span_notice("You feel weird."))
+ if(prob(33))
+ qdel(src)
+
+// Charges an item or two in your inventory. Also yourself.
+/obj/item/relic/proc/charger(mob/living/user)
+ to_chat(user, span_danger("You're recharged!"))
+ var/stunner = 1.25 SECONDS
+ if(iscarbon(user))
+ var/mob/living/carbon/carboner = user
+ carboner.electrocute_act(15, src, flags = SHOCK_NOGLOVES, stun_duration = stunner)
+ else
+ user.electrocute_act(15, src, flags = SHOCK_NOGLOVES)
+ playsound(user, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+
+ var/list/chargeable_batteries = list()
+ for(var/obj/item/stock_parts/power_store/C in user.get_all_contents())
+ if(C.charge < (C.maxcharge * 0.95)) // otherwise the PDA always gets recharged
+ chargeable_batteries |= C
+
+ lightning_fx(user, stunner)
+ var/recharges = rand(1, 2)
+ if(!length(chargeable_batteries))
+ to_chat(user, span_notice("You have a strange feeling for a moment, but then it passes."))
+ return
+ for(var/obj/item/stock_parts/power_store/to_charge as anything in recharges)
+ to_charge = pick(chargeable_batteries)
+ to_charge.charge = to_charge.maxcharge
+ // The device powered by the cell is assumed to be its location.
+ var/obj/device = to_charge.loc
+ // If it's not an object, or the loc's assigned power_store isn't the cell, undo.
+ if(!istype(device) || (device.get_cell() != to_charge))
+ device = to_charge
+ device.update_appearance(UPDATE_ICON|UPDATE_OVERLAYS)
+ to_chat(user, span_notice("[device] feels energized!"))
+ lightning_fx(device, 0.8 SECONDS)
+
+/obj/item/relic/proc/lightning_fx(atom/shocker, time)
+ var/lightning = mutable_appearance('icons/effects/effects.dmi', "electricity3", layer = ABOVE_MOB_LAYER)
+ shocker.add_overlay(lightning)
+ addtimer(CALLBACK(src, PROC_REF(cut_the_overlay), shocker, lightning), time)
+
+/obj/item/relic/proc/cut_the_overlay(atom/shocker, lightning)
+ shocker.cut_overlay(lightning)
+
+// Hugs/shakes everyone in range!
+/obj/item/relic/proc/hugger(mob/user)
+ var/list/mob/living/carbon/huggeds = oviewers(3, user)
+ for(var/mob/living/carbon/victim in huggeds)
+ victim.help_shake_act(user, force_friendly = TRUE)
+ new /obj/effect/temp_visual/heart(victim.loc)
+ if(length(huggeds))
+ to_chat(user, span_nicegreen("You feel friendly!"))
+ else
+ to_chat(user, pick(span_notice("You hug yourself, for some reason."), span_notice("You have a strange feeling for a moment, but then it passes.")))
+
+// Converts a 3x3 area into a random dimensional theme.
+/obj/item/relic/proc/dimensional_shift(mob/user)
+ var/new_theme_path = pick(subtypesof(/datum/dimension_theme))
+ var/datum/dimension_theme/shifter = SSmaterials.dimensional_themes[new_theme_path]
+ for(var/turf/shiftee in range(1, user))
+ shifter.apply_theme(shiftee, show_effect = TRUE)
+ qdel(shifter)
+ // prevent *total* spam conversion
+ min_cooldown += 2 SECONDS
+ max_cooldown += 2 SECONDS
+
+// Replaces your clothing with a random costume, and your ID with a cardboard one.
+// TODO: make them part of the same kit (lobster hat, lobster suit)
+/obj/item/relic/proc/disguiser(mob/user)
+ if(!iscarbon(user))
+ to_chat(user, span_notice("You have a strange feeling for a moment, but then it passes."))
+ return
+
+ if(prob(80)) // >:)
+ ADD_TRAIT(user, TRAIT_NO_JUMPSUIT, REF(src)) // prevent dropping pockets & belt
+
+ // magic trick!
+ playsound(user, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ throw_smoke(user)
+
+ // carbons always get a hat at least
+ var/mob/living/carbon/carbonius = user
+ //hat
+ var/obj/item/clothing/head/costume/disguise_hat = roll_costume(/obj/item/clothing/head/costume, HIDEMASK)
+ carbonius.dropItemToGround(carbonius.head)
+ carbonius.equip_to_slot_or_del(disguise_hat, ITEM_SLOT_HEAD)
+ if(!ishuman(carbonius))
+ to_chat(user, span_notice("You have a peculiar feeling for a moment, but then it passes."))
+ return
+
+ var/mob/living/carbon/human/humerus = carbonius
+ // uniform
+ var/obj/item/clothing/under/costume/disguise_uniform = roll_costume(/obj/item/clothing/under/costume)
+ humerus.dropItemToGround(humerus.w_uniform)
+ humerus.equip_to_slot_or_del(disguise_uniform, ITEM_SLOT_ICLOTHING)
+ // suit
+ var/obj/item/clothing/suit/costume/disguise_suit = roll_costume(/obj/item/clothing/suit/costume)
+ humerus.dropItemToGround(humerus.wear_suit)
+ humerus.equip_to_slot_or_del(disguise_suit, ITEM_SLOT_OCLOTHING)
+ // id
+ var/obj/item/card/cardboard/card_id = new()
+ humerus.dropItemToGround(humerus.wear_id)
+ humerus.equip_to_slot_or_del(card_id, ITEM_SLOT_ID)
+
+ // edit the card to a random job & name
+ if(!card_id)
+ return
+ card_id.scribbled_name = "[pick(GLOB.first_names)] [pick(GLOB.last_names)]"
+ card_id.update_name()
+ card_id.details_colors = list(ready_random_color(), ready_random_color(), ready_random_color())
+ card_id.item_flags |= DROPDEL
+
+ var/datum/id_trim/random_trim = pick(subtypesof(/datum/id_trim)) // this can pick silly things
+ random_trim = new random_trim()
+ if(random_trim.trim_state && random_trim.assignment)
+ card_id.scribbled_trim = replacetext(random_trim.trim_state, "trim_", "cardboard_")
+ card_id.scribbled_assignment = random_trim.assignment
+ card_id.update_overlays()
+ REMOVE_TRAIT(user, TRAIT_NO_JUMPSUIT, REF(src))
+
+/obj/item/relic/proc/roll_costume(type, flagcheck)
+ var/list/candidates = list()
+ for(var/obj/item/thingy as anything in subtypesof(type))
+ if(flagcheck && !(initial(thingy.flags_inv) & flagcheck))
+ continue
+ if(isnull(initial(thingy.icon_state)))
+ continue
+ candidates |= thingy
+ var/obj/item/new_costume = pick(candidates)
+ new_costume = new new_costume()
+ new_costume.item_flags |= DROPDEL
+ return new_costume
+
//Admin Warning proc for relics
/obj/item/relic/proc/warn_admins(mob/user, relic_type, priority = 1)
var/turf/location = get_turf(src)
diff --git a/code/modules/research/techweb/nodes/mech_nodes.dm b/code/modules/research/techweb/nodes/mech_nodes.dm
index 6b7a9803ae4..d6b7171d55b 100644
--- a/code/modules/research/techweb/nodes/mech_nodes.dm
+++ b/code/modules/research/techweb/nodes/mech_nodes.dm
@@ -1,8 +1,8 @@
/datum/techweb_node/mech_assembly
id = TECHWEB_NODE_MECH_ASSEMBLY
starting_node = TRUE
- display_name = "Mech Assembly"
- description = "Development of mech designed to contend with artificial gravity while transporting cargo."
+ display_name = "Exosuit Assembly"
+ description = "Development of mechanical exosuits designed to contend with artificial gravity while transporting cargo."
prereq_ids = list(TECHWEB_NODE_ROBOTICS)
design_ids = list(
"mechapower",
@@ -21,7 +21,7 @@
/datum/techweb_node/mech_equipment
id = TECHWEB_NODE_MECH_EQUIPMENT
display_name = "Expedition Equipment"
- description = "Specialized mech gear tailored for navigating space and celestial bodies, ensuring durability and functionality in the harshest conditions."
+ description = "Specialized exosuit gear tailored for navigating space and celestial bodies, ensuring durability and functionality in the harshest conditions."
prereq_ids = list(TECHWEB_NODE_MECH_ASSEMBLY)
design_ids = list(
"mechacontrol",
@@ -63,7 +63,7 @@
/datum/techweb_node/mech_medical
id = TECHWEB_NODE_MECH_MEDICAL
- display_name = "Medical Mech"
+ display_name = "Medical Exosuit"
description = "Advanced robotic unit equipped with syringe guns and healing beams, revolutionizing medical assistance in hazardous environments."
prereq_ids = list(TECHWEB_NODE_MECH_ASSEMBLY, TECHWEB_NODE_CHEM_SYNTHESIS)
design_ids = list(
@@ -83,8 +83,8 @@
/datum/techweb_node/mech_mining
id = TECHWEB_NODE_MECH_MINING
- display_name = "Mining Mech"
- description = "Robust mech engineered to withstand lava and storms for continuous off-station mining operations."
+ display_name = "Mining Exosuit"
+ description = "Robust exosuit engineered to withstand lava and storms for continuous off-station mining operations."
prereq_ids = list(TECHWEB_NODE_MECH_EQUIPMENT, TECHWEB_NODE_MINING)
design_ids = list(
"clarke_chassis",
@@ -99,8 +99,8 @@
/datum/techweb_node/mech_combat
id = TECHWEB_NODE_MECH_COMBAT
- display_name = "Combat Mechs"
- description = "Modular armor upgrades and specialized equipment for security mechs."
+ display_name = "Combat Exosuits"
+ description = "Modular armor upgrades and specialized equipment for security exosuits."
prereq_ids = list(TECHWEB_NODE_MECH_EQUIPMENT)
design_ids = list(
"mech_ccw_armor",
@@ -115,8 +115,8 @@
/datum/techweb_node/mech_assault
id = TECHWEB_NODE_MECH_ASSAULT
- display_name = "Assault Mech"
- description = "Heavy battle mech boasting robust armor but sacrificing speed for enhanced durability."
+ display_name = "Assault Exosuits"
+ description = "Heavy battle exosuits boasting robust armor but sacrificing speed for enhanced durability."
prereq_ids = list(TECHWEB_NODE_MECH_COMBAT)
design_ids = list(
"durand_armor",
@@ -135,8 +135,8 @@
/datum/techweb_node/mech_light
id = TECHWEB_NODE_MECH_LIGHT
- display_name = "Light Combat Mech"
- description = "Agile combat mech equipped with overclocking capabilities for temporary speed boosts, prioritizing speed over durability on the battlefield."
+ display_name = "Light Combat Exosuits"
+ description = "Agile combat exosuits equipped with overclocking capabilities for temporary speed boosts, prioritizing speed over durability on the battlefield."
prereq_ids = list(TECHWEB_NODE_MECH_COMBAT)
design_ids = list(
"gygax_armor",
@@ -155,7 +155,7 @@
/datum/techweb_node/mech_heavy
id = TECHWEB_NODE_MECH_HEAVY
- display_name = "Heavy Mech"
+ display_name = "Heavy Exosuits"
description = "Advanced heavy mechanized unit with dual pilot capability, designed for robust battlefield performance and increased tactical versatility."
prereq_ids = list(TECHWEB_NODE_MECH_ASSAULT)
design_ids = list(
@@ -175,8 +175,8 @@
/datum/techweb_node/mech_infiltrator
id = TECHWEB_NODE_MECH_INFILTRATOR
- display_name = "Infiltration Mech"
- description = "Advanced mech with phasing capabilities, allowing it to move through walls and obstacles, ideal for covert and special operations."
+ display_name = "Infiltration Exosuits"
+ description = "Advanced exosuit with phasing capabilities, allowing it to move through walls and obstacles, ideal for covert and special operations."
prereq_ids = list(TECHWEB_NODE_MECH_LIGHT, TECHWEB_NODE_ANOMALY_RESEARCH)
design_ids = list(
"phazon_armor",
@@ -211,8 +211,8 @@
/datum/techweb_node/mech_energy_guns
id = TECHWEB_NODE_MECH_ENERGY_GUNS
- display_name = "Mech Energy Guns"
- description = "Scaled-up versions of electric weapons optimized for mech deployment."
+ display_name = "Exosuit Energy Guns"
+ description = "Scaled-up versions of electric weapons optimized for exosuit deployment."
prereq_ids = list(TECHWEB_NODE_MECH_COMBAT, TECHWEB_NODE_ELECTRIC_WEAPONS)
design_ids = list(
"mech_laser",
@@ -225,7 +225,7 @@
/datum/techweb_node/mech_firearms
id = TECHWEB_NODE_MECH_FIREARMS
- display_name = "Mech Firearms"
+ display_name = "Exosuit Firearms"
description = "Mounted ballistic weaponry, enhancing combat capabilities for mechanized units."
prereq_ids = list(TECHWEB_NODE_MECH_ENERGY_GUNS, TECHWEB_NODE_EXOTIC_AMMO)
design_ids = list(
@@ -240,7 +240,7 @@
/datum/techweb_node/mech_heavy_arms
id = TECHWEB_NODE_MECH_HEAVY_ARMS
- display_name = "Heavy Mech Firearms"
+ display_name = "Heavy Exosuit Firearms"
description = "High-impact weaponry integrated into mechs, optimized for maximum firepower."
prereq_ids = list(TECHWEB_NODE_MECH_HEAVY, TECHWEB_NODE_EXOTIC_AMMO)
design_ids = list(
@@ -255,7 +255,7 @@
/datum/techweb_node/mech_equip_bluespace
id = TECHWEB_NODE_BLUESPACE
- display_name = "Bluespace Mech Equipment"
+ display_name = "Bluespace Exosuit Equipment"
description = "An array of equipment empowered by bluespace, providing unmatched mobility and utility."
prereq_ids = list(TECHWEB_NODE_MECH_INFILTRATOR, TECHWEB_NODE_BLUESPACE_TRAVEL)
design_ids = list(
diff --git a/code/modules/research/techweb/nodes/modsuit_nodes.dm b/code/modules/research/techweb/nodes/modsuit_nodes.dm
index d4e30e9cd5d..56addbad127 100644
--- a/code/modules/research/techweb/nodes/modsuit_nodes.dm
+++ b/code/modules/research/techweb/nodes/modsuit_nodes.dm
@@ -1,7 +1,7 @@
/datum/techweb_node/mod_suit
id = TECHWEB_NODE_MOD_SUIT
starting_node = TRUE
- display_name = "Modular Exosuit"
+ display_name = "Modular Suit"
description = "Specialized back mounted power suits with various different modules."
prereq_ids = list(TECHWEB_NODE_ROBOTICS)
design_ids = list(
@@ -52,7 +52,7 @@
/datum/techweb_node/mod_medical
id = TECHWEB_NODE_MOD_MEDICAL
display_name = "Medical Modular Suit"
- description = "Medical exosuits for quick rescue purposes."
+ description = "Medical MODsuits for quick rescue purposes."
prereq_ids = list(TECHWEB_NODE_MOD_SUIT, TECHWEB_NODE_CHEM_SYNTHESIS)
design_ids = list(
"mod_plating_medical",
@@ -98,7 +98,7 @@
/datum/techweb_node/mod_medical_adv
id = TECHWEB_NODE_MOD_MEDICAL_ADV
display_name = "Field Surgery Modules"
- description = "Medical exosuit equipment designed for conducting surgical operations in field conditions."
+ description = "Medical MODsuit equipment designed for conducting surgical operations in field conditions."
prereq_ids = list(TECHWEB_NODE_MOD_MEDICAL, TECHWEB_NODE_SURGERY_ADV)
design_ids = list(
"mod_defib",
@@ -130,7 +130,7 @@
/datum/techweb_node/mod_anomaly
id = TECHWEB_NODE_MOD_ANOMALY
display_name = "Anomalock Modular Suit"
- description = "Modules for exosuits that require anomaly cores to function."
+ description = "Modules for MODsuits that require anomaly cores to function."
prereq_ids = list(TECHWEB_NODE_MOD_ENGI_ADV, TECHWEB_NODE_ANOMALY_RESEARCH)
design_ids = list(
"mod_antigrav",
diff --git a/code/modules/research/techweb/nodes/syndicate_nodes.dm b/code/modules/research/techweb/nodes/syndicate_nodes.dm
index 377ac392f93..7743b9442f8 100644
--- a/code/modules/research/techweb/nodes/syndicate_nodes.dm
+++ b/code/modules/research/techweb/nodes/syndicate_nodes.dm
@@ -33,7 +33,7 @@
required_items_to_unlock = list()
for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
var/datum/uplink_item/item = SStraitor.uplink_items_by_type[item_path]
- if(!item.item || !item.illegal_tech)
+ if(!item.item || !(item.uplink_item_flags & SYNDIE_ILLEGAL_TECH))
continue
required_items_to_unlock |= item.item //allows deconning to unlock.
diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm
index 6e3d9a0ee7c..0bb8afc6ced 100644
--- a/code/modules/surgery/organs/autosurgeon.dm
+++ b/code/modules/surgery/organs/autosurgeon.dm
@@ -149,6 +149,10 @@
surgery_speed = 0.75
loaded_overlay = "autosurgeon_syndicate_loaded_overlay"
+/obj/item/autosurgeon/syndicate/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
+
/obj/item/autosurgeon/syndicate/laser_arm
desc = "A single use autosurgeon that contains a combat arms-up laser augment. A screwdriver can be used to remove it, but implants can't be placed back in."
uses = 1
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index e17488bc96a..bf963587ae1 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -78,8 +78,8 @@
var/purchase_log_vis = TRUE // Visible in the purchase log?
/// Whether this purchase is restricted or not (VR/Events related)
var/restricted = FALSE
- /// Can this item be deconstructed to unlock certain techweb research nodes?
- var/illegal_tech = TRUE
+ /// Flags related to if an item will provide illegal tech, or trips contraband detectors once spawned in as an item.
+ var/uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
/// String to be shown instead of the price, e.g for the Random item.
var/cost_override_string = ""
/// Whether this item locks all other items from being purchased. Used by syndicate balloon and a few other purchases.
@@ -142,6 +142,12 @@
spawned_item = spawn_path
if(refundable)
spawned_item.AddElement(/datum/element/uplink_reimburse, (refund_amount ? refund_amount : cost))
+
+
+ if(uplink_item_flags & SYNDIE_TRIPS_CONTRABAND) // Ignore things that shouldn't be detectable as contraband on the station.
+ ADD_TRAIT(spawned_item, TRAIT_CONTRABAND, INNATE_TRAIT)
+ for(var/obj/contained as anything in spawned_item.get_all_contents())
+ ADD_TRAIT(contained, TRAIT_CONTRABAND, INNATE_TRAIT)
var/mob/living/carbon/human/human_user = user
if(istype(human_user) && isitem(spawned_item) && human_user.put_in_hands(spawned_item))
to_chat(human_user, span_boldnotice("[spawned_item] materializes into your hands!"))
@@ -154,8 +160,10 @@
/// Can be used to "de-restrict" some items, such as Nukie guns spawning with Syndicate pins
/datum/uplink_item/proc/spawn_item_for_generic_use(mob/user)
var/atom/movable/created = new item(user.loc)
-
- if(isgun(created))
+ if(uplink_item_flags & SYNDIE_TRIPS_CONTRABAND) // Things that shouldn't be detectable as contraband on the station.
+ ADD_TRAIT(created, TRAIT_CONTRABAND, INNATE_TRAIT)
+ for(var/obj/contained as anything in created.get_all_contents())
+ ADD_TRAIT(contained, TRAIT_CONTRABAND, INNATE_TRAIT)
replace_pin(created)
else if(istype(created, /obj/item/storage/toolbox/guncase))
for(var/obj/item/gun/gun in created)
@@ -205,7 +213,7 @@
name = "Objective-Specific Equipment"
desc = "Equipment necessary for accomplishing specific objectives. If you are seeing this, something has gone wrong."
limited_stock = 1
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
purchasable_from = parent_type::purchasable_from & ~UPLINK_SPY // Ditto
/datum/uplink_item/special_equipment/purchase(mob/user, datum/component/uplink/U)
diff --git a/code/modules/uplink/uplink_items/ammunition.dm b/code/modules/uplink/uplink_items/ammunition.dm
index 705204f98a1..2276485a2b7 100644
--- a/code/modules/uplink/uplink_items/ammunition.dm
+++ b/code/modules/uplink/uplink_items/ammunition.dm
@@ -12,7 +12,7 @@
item = /obj/item/ammo_box/foambox/riot
cost = 2
surplus = 0
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
purchasable_from = ~UPLINK_SERIOUS_OPS
/datum/uplink_item/ammo/pistol
@@ -21,7 +21,7 @@
item = /obj/item/ammo_box/magazine/m9mm
cost = 1
purchasable_from = ~UPLINK_ALL_SYNDIE_OPS
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/ammo/pistolap
name = "9mm Armour Piercing Magazine"
@@ -54,4 +54,4 @@
item = /obj/item/ammo_box/a357
cost = 4
purchasable_from = ~(UPLINK_ALL_SYNDIE_OPS | UPLINK_SPY) //nukies get their own version
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
diff --git a/code/modules/uplink/uplink_items/badass.dm b/code/modules/uplink/uplink_items/badass.dm
index da7212ee8fc..08cf3affe07 100644
--- a/code/modules/uplink/uplink_items/badass.dm
+++ b/code/modules/uplink/uplink_items/badass.dm
@@ -14,7 +14,7 @@
cost = 20
lock_other_purchases = TRUE
cant_discount = TRUE
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/badass/balloon/spawn_item(spawn_path, mob/user, datum/uplink_handler/uplink_handler, atom/movable/source)
. = ..()
@@ -36,14 +36,14 @@
item = /obj/item/toy/cards/deck/syndicate
cost = 1
surplus = 40
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/badass/syndiecigs
name = "Syndicate Smokes"
desc = "Strong flavor, dense smoke, infused with omnizine."
item = /obj/item/storage/fancy/cigarettes/cigpack_syndicate
cost = 2
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/badass/syndiecash
name = "Syndicate Briefcase Full of Cash"
@@ -53,7 +53,7 @@
item = /obj/item/storage/briefcase/secure/syndie
cost = 3
restricted = TRUE
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/badass/costumes/clown
name = "Clown Costume"
diff --git a/code/modules/uplink/uplink_items/clownops.dm b/code/modules/uplink/uplink_items/clownops.dm
index 8d607cb0d5c..a5b4b0ec1df 100644
--- a/code/modules/uplink/uplink_items/clownops.dm
+++ b/code/modules/uplink/uplink_items/clownops.dm
@@ -35,7 +35,7 @@
cost = 1 //much cheaper for clown ops than for clowns
item = /obj/item/firing_pin/clown/ultra
purchasable_from = UPLINK_CLOWN_OPS
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/weapon_kits/clownopsuperpin
name = "Super Ultra Hilarious Firing Pin"
@@ -43,7 +43,7 @@
cost = 4 //much cheaper for clown ops than for clowns
item = /obj/item/firing_pin/clown/ultra/selfdestruct
purchasable_from = UPLINK_CLOWN_OPS
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/weapon_kits/foamsmg
name = "Toy Submachine Gun"
@@ -159,4 +159,4 @@
item = /obj/item/dnainjector/clumsymut
cost = 1
purchasable_from = UPLINK_CLOWN_OPS
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
diff --git a/code/modules/uplink/uplink_items/dangerous.dm b/code/modules/uplink/uplink_items/dangerous.dm
index 11cca6c4426..691f4f2c8f3 100644
--- a/code/modules/uplink/uplink_items/dangerous.dm
+++ b/code/modules/uplink/uplink_items/dangerous.dm
@@ -29,7 +29,7 @@
throwing weapons. The bolas can knock a target down and the shurikens will embed into limbs."
item = /obj/item/storage/box/syndie_kit/throwing_weapons
cost = 3
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/dangerous/sword
name = "Energy Sword"
diff --git a/code/modules/uplink/uplink_items/device_tools.dm b/code/modules/uplink/uplink_items/device_tools.dm
index 05252f8a1af..567d359e82e 100644
--- a/code/modules/uplink/uplink_items/device_tools.dm
+++ b/code/modules/uplink/uplink_items/device_tools.dm
@@ -12,7 +12,7 @@
item = /obj/item/soap/syndie
cost = 1
surplus = 50
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/device_tools/surgerybag
name = "Syndicate Surgery Duffel Bag"
@@ -113,7 +113,7 @@
item = /obj/item/disk/nuclear/fake
cost = 1
surplus = 1
- illegal_tech = FALSE
+ uplink_item_flags = NONE
/datum/uplink_item/device_tools/frame
name = "F.R.A.M.E. disk"
@@ -165,7 +165,7 @@
multitool and combat gloves that are resistant to shocks and heat."
item = /obj/item/storage/toolbox/syndicate
cost = 1
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/device_tools/rad_laser
name = "Radioactive Microlaser"
diff --git a/code/modules/uplink/uplink_items/job.dm b/code/modules/uplink/uplink_items/job.dm
index 626e5ca385b..aac6be5cdd1 100644
--- a/code/modules/uplink/uplink_items/job.dm
+++ b/code/modules/uplink/uplink_items/job.dm
@@ -21,7 +21,7 @@
desc = "A box of five (5) counterfeit devices. Each single-use device can hold one normal sized object, and impersonate an ordinary postal envelope addressed to whoever you choose. Optionally, can be rigged to activate held items - great for if you want to surprise someone with a primed grenade!"
item = /obj/item/storage/box/syndie_kit/mail_counterfeit
cost = 2
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
restricted_roles = list(JOB_CARGO_TECHNICIAN, JOB_QUARTERMASTER)
surplus = 5
@@ -45,7 +45,7 @@
item = /obj/item/dnainjector/clumsymut
cost = 1
restricted_roles = list(JOB_CLOWN)
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
surplus = 25
/datum/uplink_item/role_restricted/ancient_jumpsuit
@@ -70,7 +70,7 @@
cost = 4
item = /obj/item/firing_pin/clown/ultra
restricted_roles = list(JOB_CLOWN)
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
surplus = 25
/datum/uplink_item/role_restricted/clownsuperpin
@@ -79,7 +79,7 @@
cost = 7
item = /obj/item/firing_pin/clown/ultra/selfdestruct
restricted_roles = list(JOB_CLOWN)
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
surplus = 25
/datum/uplink_item/role_restricted/syndimmi
diff --git a/code/modules/uplink/uplink_items/nukeops.dm b/code/modules/uplink/uplink_items/nukeops.dm
index 94a5e3b3de4..de53589de2d 100644
--- a/code/modules/uplink/uplink_items/nukeops.dm
+++ b/code/modules/uplink/uplink_items/nukeops.dm
@@ -385,7 +385,7 @@
item = /obj/item/ammo_box/magazine/plastikov9mm
cost = 1
purchasable_from = UPLINK_SERIOUS_OPS
- illegal_tech = FALSE
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
// Explosives and Grenades
// ~~ Grenades ~~
diff --git a/code/modules/uplink/uplink_items/spy_unique.dm b/code/modules/uplink/uplink_items/spy_unique.dm
index b53cf60cefd..7d2f5fb34cb 100644
--- a/code/modules/uplink/uplink_items/spy_unique.dm
+++ b/code/modules/uplink/uplink_items/spy_unique.dm
@@ -10,12 +10,14 @@
// Cost doesn't really matter since it's free, but it determines which loot pool it falls into.
// By default, these fall into easy-medium spy bounty loot pool
cost = SPY_LOWER_COST_THRESHOLD
+ uplink_item_flags = NONE
/datum/uplink_item/spy_unique/syndie_bowman
name = "Syndicate Bowman"
desc = "A bowman headset for members of the Syndicate. Not very conspicuous."
item = /obj/item/radio/headset/syndicate/alt
cost = 1
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH
/datum/uplink_item/spy_unique/megaphone
name = "Megaphone"
@@ -43,6 +45,7 @@
name = "Kudzu"
desc = "A packet of Kudzu - plant and forget, a great distraction."
item = /obj/item/seeds/kudzu
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/spy_unique/big_knife
name = "Combat Knife"
@@ -53,6 +56,7 @@
name = "Switchblade"
desc = "A switchblade. Switches between not sharp and sharp."
item = /obj/item/switchblade
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/spy_unique/sechud_implant
name = "SecHUD Implant"
@@ -64,30 +68,35 @@
desc = "A bolt-action rifle, with a scope. Won't jam, either."
item = /obj/item/gun/ballistic/rifle/boltaction/prime
cost = SPY_UPPER_COST_THRESHOLD
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/spy_unique/cycler_shotgun
name = "Cycler Shotgun"
desc = "A cycler shotgun. It's a shotgun that cycles between two barrels."
item = /obj/item/gun/ballistic/shotgun/automatic/dual_tube/deadly
cost = SPY_UPPER_COST_THRESHOLD
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/spy_unique/bulldog_shotgun
name = "Bulldog Shotgun"
desc = "A bulldog shotgun. It's a shotgun that shoots bulldogs."
item = /obj/item/gun/ballistic/shotgun/bulldog/unrestricted
cost = SPY_UPPER_COST_THRESHOLD
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/spy_unique/ansem_pistol
name = "Ansem Pistol"
desc = "A pistol that's really good at making people sleep."
item = /obj/item/gun/ballistic/automatic/pistol/clandestine
cost = SPY_UPPER_COST_THRESHOLD
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/spy_unique/rocket_launcher
name = "Rocket Launcher"
desc = "A rocket launcher. I would recommend against jumping with it."
item = /obj/item/gun/ballistic/rocketlauncher
cost = SPY_UPPER_COST_THRESHOLD - 1 // It's a meme item
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/spy_unique/shotgun_ammo
name = "Box of Buckshot"
@@ -110,17 +119,20 @@
desc = "A stealth belt that lets you sneak behind enemy lines."
item = /obj/item/shadowcloak/weaker
cost = SPY_UPPER_COST_THRESHOLD
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH
/datum/uplink_item/spy_unique/katana
name = "Katana"
desc = "A really sharp Katana. Did I mention it's sharp?"
item = /obj/item/katana
cost = /datum/uplink_item/dangerous/doublesword::cost // Puts it in the same pool as Desword
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/spy_unique/medkit_lite
name = "Syndicate First Medic Kit"
desc = "A syndicate tactical combat medkit, but only stocked enough to do basic first aid."
item = /obj/item/storage/medkit/tactical_lite
+ uplink_item_flags = SYNDIE_TRIPS_CONTRABAND
/datum/uplink_item/spy_unique/antistun
name = /datum/uplink_item/implants/nuclear/antistun::name
diff --git a/code/modules/uplink/uplink_items/stealthy.dm b/code/modules/uplink/uplink_items/stealthy.dm
index 898b82d1b6a..e4c4de412fb 100644
--- a/code/modules/uplink/uplink_items/stealthy.dm
+++ b/code/modules/uplink/uplink_items/stealthy.dm
@@ -4,6 +4,7 @@
/datum/uplink_item/stealthy_weapons
category = /datum/uplink_category/stealthy
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH
/datum/uplink_item/stealthy_weapons/dart_pistol
diff --git a/code/modules/uplink/uplink_items/stealthy_tools.dm b/code/modules/uplink/uplink_items/stealthy_tools.dm
index a225d04d667..48c25e638de 100644
--- a/code/modules/uplink/uplink_items/stealthy_tools.dm
+++ b/code/modules/uplink/uplink_items/stealthy_tools.dm
@@ -4,6 +4,7 @@
/datum/uplink_item/stealthy_tools
category = /datum/uplink_category/stealthy_tools
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH
/datum/uplink_item/stealthy_tools/agent_card
@@ -90,7 +91,7 @@
item = /obj/item/storage/backpack/satchel/flat/with_tools
cost = 1
surplus = 30
- illegal_tech = FALSE
+ uplink_item_flags = NONE
/datum/uplink_item/stealthy_tools/mail_counterfeit
name = "GLA Brand Mail Counterfeit Device"
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index 30b9ed53715..21f4831af36 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -1445,6 +1445,8 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
var/obj/item/vended_item
if(!LAZYLEN(item_record.returned_products)) //always give out free returned stuff first, e.g. to avoid walling a traitor objective in a bag behind paid items
vended_item = new item_record.product_path(get_turf(src))
+ if(vended_item.type in contraband)
+ ADD_TRAIT(vended_item, TRAIT_CONTRABAND, INNATE_TRAIT)
on_dispense(vended_item)
else
vended_item = LAZYACCESS(item_record.returned_products, LAZYLEN(item_record.returned_products)) //first in, last out
diff --git a/html/changelogs/AutoChangeLog-pr-84003.yml b/html/changelogs/AutoChangeLog-pr-84003.yml
new file mode 100644
index 00000000000..e180b38a1be
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84003.yml
@@ -0,0 +1,10 @@
+author: "ArcaneMusic"
+delete-after: True
+changes:
+ - rscadd: "Items spawned via traitor uplinks or are known illegal contraband on the station can now be scanned and identified as such by the N-spect scanners in security. These only applies to overt traitor or antagonist items, and \"stealth\" items will not be seen as such."
+ - rscadd: "Scanner gates can now be upgraded by using an N-spect scanner on it to unlock \"contraband scanning\" mode."
+ - rscadd: "Security officers can now be offered a bounty to turn in pieces of contraband."
+ - rscadd: "Some stealthy storage items like storage implants, smuggler's satchels, void cloaks, the infiltrator modsuit, and the chameleon backpack will block the presence of contraband on your person when placed inside."
+ - qol: "N-spect scanner contextual screentips."
+ - balance: "Recharger security bounties ask for a quantity of 1, down from 3."
+ - qol: "security, cargo, and medbay have access to scanner gate boards."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84184.yml b/html/changelogs/AutoChangeLog-pr-84184.yml
new file mode 100644
index 00000000000..3798eba1216
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84184.yml
@@ -0,0 +1,4 @@
+author: "00-Steven"
+delete-after: True
+changes:
+ - bugfix: "You can see runechat above fullscreen overlays on lower multi-z levels again. Rejoice, blind players. Please report any weird rendering layering issues."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84775.yml b/html/changelogs/AutoChangeLog-pr-84775.yml
new file mode 100644
index 00000000000..b9d24b0c408
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84775.yml
@@ -0,0 +1,5 @@
+author: "carlarctg"
+delete-after: True
+changes:
+ - rscadd: "Adds six new strange object powers! Drink dispenser, bioscrambler, recharger, hugger, dimensional, disguiser."
+ - code_imp: "Removed some unused code and items, and added a proc that returns a random #colorstring."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84800.yml b/html/changelogs/AutoChangeLog-pr-84800.yml
new file mode 100644
index 00000000000..7b3d21cd581
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84800.yml
@@ -0,0 +1,4 @@
+author: "GPeckman"
+delete-after: True
+changes:
+ - spellcheck: "The techweb no longer erroneously refers to MODsuits as exosuits."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84804.yml b/html/changelogs/AutoChangeLog-pr-84804.yml
new file mode 100644
index 00000000000..189f718e576
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84804.yml
@@ -0,0 +1,4 @@
+author: "Hardly3D"
+delete-after: True
+changes:
+ - rscadd: "Added new hairstyle: Short Bangs 2"
\ No newline at end of file
diff --git a/icons/mob/human/human_face.dmi b/icons/mob/human/human_face.dmi
index d96800c3a8b..886094ec495 100644
Binary files a/icons/mob/human/human_face.dmi and b/icons/mob/human/human_face.dmi differ
diff --git a/modular_skyrat/modules/company_imports/code/objects/nakamura_modsuits/plate_compression_tech.dm b/modular_skyrat/modules/company_imports/code/objects/nakamura_modsuits/plate_compression_tech.dm
index ff9ab5becd5..7a6b808374e 100644
--- a/modular_skyrat/modules/company_imports/code/objects/nakamura_modsuits/plate_compression_tech.dm
+++ b/modular_skyrat/modules/company_imports/code/objects/nakamura_modsuits/plate_compression_tech.dm
@@ -1,2 +1,2 @@
/datum/uplink_item/suits/plate_compression
- illegal_tech = FALSE
+ uplink_item_flags = NONE
diff --git a/modular_skyrat/modules/skyrat-uplinks/code/categories/device_tools.dm b/modular_skyrat/modules/skyrat-uplinks/code/categories/device_tools.dm
index c33882e6c72..977492ea7de 100644
--- a/modular_skyrat/modules/skyrat-uplinks/code/categories/device_tools.dm
+++ b/modular_skyrat/modules/skyrat-uplinks/code/categories/device_tools.dm
@@ -21,14 +21,14 @@
desc = "A pair of highly robust combat gripper gloves that excels at performing takedowns at close range, with an added lining of insulation. Careful not to hit a wall!"
item = /obj/item/clothing/gloves/tackler/combat/insulated
cost = 2
- illegal_tech = FALSE
+ uplink_item_flags = NONE
/datum/uplink_item/device_tools/ammo_pouch
name = "Ammo Pouch"
desc = "A small yet large enough pouch that can fit in your pocket, and has room for three magazines."
item = /obj/item/storage/pouch/ammo
cost = 1
- illegal_tech = FALSE
+ uplink_item_flags = NONE
/datum/uplink_item/device_tools/syndie_glue
name = "Glue"
diff --git a/modular_skyrat/modules/skyrat-uplinks/code/categories/stealthy_weapons.dm b/modular_skyrat/modules/skyrat-uplinks/code/categories/stealthy_weapons.dm
index 2c42f87d34d..1ba86193d05 100644
--- a/modular_skyrat/modules/skyrat-uplinks/code/categories/stealthy_weapons.dm
+++ b/modular_skyrat/modules/skyrat-uplinks/code/categories/stealthy_weapons.dm
@@ -5,4 +5,4 @@
cost = 2
surplus = 0
progression_minimum = 10 MINUTES
- illegal_tech = FALSE
+ uplink_item_flags = NONE
diff --git a/modular_skyrat/modules/skyrat-uplinks/code/categories/suits.dm b/modular_skyrat/modules/skyrat-uplinks/code/categories/suits.dm
index e03cf398757..f6cc2c94f6d 100644
--- a/modular_skyrat/modules/skyrat-uplinks/code/categories/suits.dm
+++ b/modular_skyrat/modules/skyrat-uplinks/code/categories/suits.dm
@@ -3,7 +3,7 @@
desc = "A slim Type I armored vest that provides decent protection against most types of damage."
item = /obj/item/clothing/suit/armor/vest
cost = 1
- illegal_tech = FALSE
+ uplink_item_flags = NONE
progression_minimum = 10 MINUTES
/datum/uplink_item/suits/bulletproof_armor
@@ -11,7 +11,7 @@
desc = "A Type III heavy bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent."
item = /obj/item/clothing/suit/armor/bulletproof
cost = 3
- illegal_tech = FALSE
+ uplink_item_flags = NONE
progression_minimum = 15 MINUTES
/datum/uplink_item/suits/swathelmet_traitor
@@ -19,5 +19,5 @@
desc = "An extremely robust, space-worthy helmet in a nefarious red and black stripe pattern."
item = /obj/item/clothing/head/helmet/swat
cost = 2
- illegal_tech = FALSE
+ uplink_item_flags = NONE
progression_minimum = 10 MINUTES
diff --git a/tgstation.dme b/tgstation.dme
index 26411a13c64..ae7e01aeebf 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2138,7 +2138,7 @@
#include "code\game\machinery\recycler.dm"
#include "code\game\machinery\requests_console.dm"
#include "code\game\machinery\roulette_machine.dm"
-#include "code\game\machinery\scan_gate.dm"
+#include "code\game\machinery\scanner_gate.dm"
#include "code\game\machinery\sheetifier.dm"
#include "code\game\machinery\shieldgen.dm"
#include "code\game\machinery\sleepers.dm"
diff --git a/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx b/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx
index baccf90c4bd..aea852ce5e7 100644
--- a/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx
+++ b/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx
@@ -206,6 +206,11 @@ function CatalogList(props: CatalogListProps) {