diff --git a/Delete empty folders.bat b/Delete empty folders.bat
new file mode 100644
index 0000000000..44a796bc2a
--- /dev/null
+++ b/Delete empty folders.bat
@@ -0,0 +1,3 @@
+REM Instructions: Drop in root folder, all directories and
+REM subdirectories will be purged of empty folders.
+for /f "delims=" %%i in ('dir /s /b /ad ^| sort /r') do rd "%%i" 2>NUL
diff --git a/code/__DEFINES/screentips.dm b/code/__DEFINES/screentips.dm
index a3b1018352..4918db85ce 100644
--- a/code/__DEFINES/screentips.dm
+++ b/code/__DEFINES/screentips.dm
@@ -32,4 +32,4 @@
#define SCREENTIP_PREFERENCE_CONTEXT_ONLY "Only with tips"
/// Regardless of intent
-#define INTENT_ANY "intent_any"
+#define INTENT_ANY "any"
diff --git a/code/__HELPERS/screentips.dm b/code/__HELPERS/screentips.dm
index 967e1c3bb6..dfd3390ab4 100644
--- a/code/__HELPERS/screentips.dm
+++ b/code/__HELPERS/screentips.dm
@@ -1,31 +1,30 @@
+#define HINT_ICON_FILE 'icons/UI_Icons/screentips/cursor_hints.dmi'
+
// Generate intent icons
-/// Help intent icon for screentip context
-GLOBAL_DATUM_INIT(icon_intent_help, /image, image('icons/emoji.dmi', icon_state = INTENT_HELP))
-/// Disarm intent icon for screentip context
-GLOBAL_DATUM_INIT(icon_intent_disarm, /image, image('icons/emoji.dmi', icon_state = INTENT_DISARM))
-/// Grab intent icon for screentip context
-GLOBAL_DATUM_INIT(icon_intent_grab, /image, image('icons/emoji.dmi', icon_state = INTENT_GRAB))
-/// Harm intent icon for screentip context
-GLOBAL_DATUM_INIT(icon_intent_harm, /image, image('icons/emoji.dmi', icon_state = INTENT_HARM))
+GLOBAL_LIST_INIT_TYPED(screentip_context_icons, /image, prepare_screentip_context_icons())
+
+/proc/prepare_screentip_context_icons()
+ . = list()
+ for(var/state in icon_states(HINT_ICON_FILE))
+ .[state] = image(HINT_ICON_FILE, icon_state = state)
/*
* # Builds context with each intent for this key
* Args:
* - context = list (REQUIRED)
* - key = string (REQUIRED)
+ * - allow_image = boolean (not required)
*/
-/proc/build_context(list/context, key)
+/proc/build_context(list/context, key, allow_image)
var/list/to_add
for(var/intent in context[key])
- switch(intent)
- if(INTENT_HELP)
- LAZYADD(to_add, "\icon[GLOB.icon_intent_help] [key]: [context[key][INTENT_HELP]]")
- if(INTENT_DISARM)
- LAZYADD(to_add, "\icon[GLOB.icon_intent_disarm] [key]: [context[key][INTENT_DISARM]]")
- if(INTENT_GRAB)
- LAZYADD(to_add, "\icon[GLOB.icon_intent_grab] [key]: [context[key][INTENT_GRAB]]")
- if(INTENT_HARM)
- LAZYADD(to_add, "\icon[GLOB.icon_intent_harm] [key]: [context[key][INTENT_HARM]]")
- else // If you're adding intent-less YOU BETTER ADD IT FIRST IN THE LIST
- LAZYADD(to_add, "[key]: [context[key][intent]]")
- return english_list(to_add, "", " ", " ")
+ var/key_help = "[length(key) > 3 ? copytext(key, 1, -3) : ""]"
+ var/icon = "[copytext(key, -3)]-[intent]"
+ if(allow_image)
+ icon = "\icon[GLOB.screentip_context_icons[icon]]"
+ LAZYADD(to_add, "[key_help][icon]: [context[key][intent]]")
+
+ var/separator = "[allow_image ? " " : " | "]"
+ return english_list(to_add, "", separator, separator)
+
+#undef HINT_ICON_FILE
diff --git a/code/__SANDCODE/DEFINES/dcs/signals.dm b/code/__SANDCODE/DEFINES/dcs/signals.dm
new file mode 100644
index 0000000000..a5381c42a8
--- /dev/null
+++ b/code/__SANDCODE/DEFINES/dcs/signals.dm
@@ -0,0 +1,13 @@
+/*
+ * # COMSIG_MOB_ORGAN_ADD
+ * From modular_sand\code\modules\surgery\organs
+ * Used by signals for determining when an organ was inserted
+*/
+#define COMSIG_MOB_ORGAN_ADD "mob_organ_added"
+
+/*
+ * # COMSIG_MOB_ORGAN_REMOVE
+ * From modular_sand\code\modules\surgery\organs
+ * Used by signals for determining when an organ was removed
+*/
+#define COMSIG_MOB_ORGAN_REMOVE "mob_organ_removed"
diff --git a/code/__SANDCODE/DEFINES/keybindings.dm b/code/__SANDCODE/DEFINES/keybindings.dm
new file mode 100644
index 0000000000..ac65f5acdb
--- /dev/null
+++ b/code/__SANDCODE/DEFINES/keybindings.dm
@@ -0,0 +1 @@
+#define COMSIG_KB_MOB_PIXELSHIFT "keybinding_mob_pixelshift"
diff --git a/code/__SPLURTCODE/DEFINES/loadout.dm b/code/__SPLURTCODE/DEFINES/loadout.dm
new file mode 100644
index 0000000000..881080f664
--- /dev/null
+++ b/code/__SPLURTCODE/DEFINES/loadout.dm
@@ -0,0 +1,8 @@
+//the names of the customization tabs
+//#define SETTINGS_TAB 0 //Already exists
+#define GAME_PREFERENCES_TAB 1
+#define APPEARANCE_TAB 2
+#define SPEECH_TAB 3
+#define LOADOUT_TAB 4
+#define CONTENT_PREFERENCES_TAB 5
+#define KEYBINDINGS_TAB_OLD 6
diff --git a/code/__SPLURTCODE/DEFINES/quirks.dm b/code/__SPLURTCODE/DEFINES/quirks.dm
index 9353f87dc7..041d473117 100644
--- a/code/__SPLURTCODE/DEFINES/quirks.dm
+++ b/code/__SPLURTCODE/DEFINES/quirks.dm
@@ -1,3 +1,9 @@
//Dominant/Well-trained quirks
#define DOMINANT_DETECT_RANGE 5
#define DOMINANT_SNAP_COOLDOWN 10 SECONDS
+
+// Quirk mood types
+#define QMOOD_NUDIST "mood_nudist"
+#define QMOOD_MASKED_MOOK "mood_masked_mook"
+#define QMOOD_HIDE_BAG "mood_storage_concealment"
+#define QMOOD_WELL_TRAINED "mood_dom_trained"
diff --git a/code/__SPLURTCODE/DEFINES/species.dm b/code/__SPLURTCODE/DEFINES/species.dm
index d9d5642ce8..3248d840c8 100644
--- a/code/__SPLURTCODE/DEFINES/species.dm
+++ b/code/__SPLURTCODE/DEFINES/species.dm
@@ -1,2 +1,4 @@
-#define SPECIES_SPECBOT "spectre_bot"
-#define SPECIES_SHADEKIN "shadekin"
+#define SPECIES_SPECBOT "spectre_bot"
+#define SPECIES_SHADEKIN "shadekin"
+#define SPECIES_ASHWALKER_WEST "ashlizard_west"
+#define SPECIES_ASHWALKER_EAST "ashlizard_east"
diff --git a/code/__SPLURTCODE/DEFINES/traits.dm b/code/__SPLURTCODE/DEFINES/traits.dm
index 4dcb8e375b..ee983a25a1 100644
--- a/code/__SPLURTCODE/DEFINES/traits.dm
+++ b/code/__SPLURTCODE/DEFINES/traits.dm
@@ -1,29 +1,33 @@
-#define SLAVER_TRAIT "slaver"
-#define PREGNANCY_TRAIT "pregnancy"
+// General traits
+#define TRAIT_PREGNANT "pregnant"
+#define TRAIT_FLOORED "floored"
+#define IGNORE_FAKE_Z_AXIS "ignore_fake_z_axis"
+#define TRAIT_TONGUELESS_SPEECH "tongueless_speech"
-#define TRAIT_HYPNOTIC_GAZE "Hypnotic Gaze"
-#define TRAIT_PREGNANT "pregnant"
+// Trait types
+#define SLAVER_TRAIT "slaver"
+#define PREGNANCY_TRAIT "pregnancy"
-#define TRAIT_FLOORED "floored"
-#define IGNORE_FAKE_Z_AXIS "ignore_fake_z_axis"
-
-#define TRAIT_GFLUID_DETECT "genital_fluid_detect"
-
-#define TRAIT_ASHRESISTANCE "TRAIT_ASHRESISTANCE"
-
-#define TRAIT_TONGUELESS_SPEECH "tongueless-speech" //for dephelm stuff
-
-// Hyperstation traits
-#define TRAIT_PHARMA "hepatic_pharmacokinesis"
+// Quirk traits
+#define TRAIT_HYPNOTIC_GAZE "hypnotic_gaze"
+#define TRAIT_GFLUID_DETECT "genital_fluid_detect"
+#define TRAIT_ASHRESISTANCE "ash_resistance"
+#define TRAIT_PHARMA "hepatic_pharmacokinesis"
#define TRAIT_CHOKE_SLUT "choke_slut"
-
-#define TRAIT_BLOODFLEDGE "BloodFledge"
-
-#define TRAIT_INCUBUS "Incubus"
-#define TRAIT_SUCCUBUS "Succubus"
-
-#define TRAIT_ARACHNID "Arachnid"
+#define TRAIT_BLOODFLEDGE "bloodfledge"
+#define TRAIT_INCUBUS "incubus"
+#define TRAIT_SUCCUBUS "succubus"
+#define TRAIT_ARACHNID "arachnid"
#define TRAIT_FLUTTER "flutter"
-#define TRAIT_NUDIST "Nudist"
+#define TRAIT_NUDIST "nudist"
#define TRAIT_CLOTH_EATER "cloth_eater"
-#define TRAIT_WEREWOLF "Werewolf"
+#define TRAIT_WEREWOLF "werewolf"
+#define TRAIT_PRIMITIVE "primitive"
+#define TRAIT_STEEL_ASS "steel_ass"
+#define TRAIT_CURSED_BLOOD "cursed_blood"
+#define TRAIT_HEADPAT_SLUT "headpat_slut"
+#define TRAIT_DISTANT "headpat_hater"
+#define TRAIT_ILLITERATE "illiterate"
+#define TRAIT_HIDE_BACKPACK "hide_backpack"
+#define TRAIT_DUMB4CUM "dumb4cum"
+#define TRAIT_RAD_FIEND "RadFiend"
diff --git a/code/__SPLURTCODE/DEFINES/zeros/species.dm b/code/__SPLURTCODE/DEFINES/zeros/species.dm
deleted file mode 100644
index e24d77ca0f..0000000000
--- a/code/__SPLURTCODE/DEFINES/zeros/species.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-#define SPECIES_ASHWALKER_WEST "ashlizard_west"
-#define SPECIES_ASHWALKER_EAST "ashlizard_east"
diff --git a/code/__SPLURTCODE/DEFINES/zeros/traits.dm b/code/__SPLURTCODE/DEFINES/zeros/traits.dm
deleted file mode 100644
index 199962dbfd..0000000000
--- a/code/__SPLURTCODE/DEFINES/zeros/traits.dm
+++ /dev/null
@@ -1,9 +0,0 @@
-#define TRAIT_PRIMITIVE "primitive"
-#define TRAIT_STEEL_ASS "steel_ass"
-#define TRAIT_CURSED_BLOOD "cursed_blood" //Yo dawg I heard you like bloodborne references so I put a
-#define TRAIT_HEADPAT_SLUT "headpat_slut"
-#define TRAIT_DISTANT "headpat_hater"
-#define TRAIT_ILLITERATE "illiterate"
-#define TRAIT_HIDE_BACKPACK "hide_backpack"
-
-#define TRAIT_DUMB4CUM "dumb4cum"
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 08d8796455..82600f25b9 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -106,6 +106,11 @@
/// A luminescence-shifted value of the last color calculated for chatmessage overlays
var/chat_color_darkened
+ // Use SET_BASE_PIXEL(x, y) to set these in typepath definitions, it'll handle pixel_x and y for you
+ ///Default pixel x shifting for the atom's icon.
+ var/base_pixel_x = 0
+ ///Default pixel y shifting for the atom's icon.
+ var/base_pixel_y = 0
///Used for changing icon states for different base sprites.
var/base_icon_state
@@ -1487,6 +1492,7 @@
if (isliving(user) || isovermind(user) || isaicamera(user))
var/obj/item/held_item = user.get_active_held_item()
+ var/allow_images = user.client.prefs.screentip_allow_images
if (flags_1 & HAS_CONTEXTUAL_SCREENTIPS_1 || held_item?.item_flags & ITEM_HAS_CONTEXTUAL_SCREENTIPS)
var/list/context = list()
@@ -1499,15 +1505,15 @@
// LMB and RMB on one line...
var/lmb_text = ""
if((SCREENTIP_CONTEXT_LMB in context) && (length(context[SCREENTIP_CONTEXT_LMB]) > 0))
- lmb_text = build_context(context, SCREENTIP_CONTEXT_LMB)
+ lmb_text = build_context(context, SCREENTIP_CONTEXT_LMB, allow_images)
var/rmb_text = ""
if((SCREENTIP_CONTEXT_RMB in context) && (length(context[SCREENTIP_CONTEXT_RMB]) > 0))
- rmb_text = build_context(context, SCREENTIP_CONTEXT_RMB)
+ rmb_text = build_context(context, SCREENTIP_CONTEXT_RMB, allow_images)
if (lmb_text)
lmb_rmb_line = lmb_text
if (rmb_text)
- lmb_rmb_line += " | [rmb_text]"
+ lmb_rmb_line += " | [allow_images ? " " : ""][rmb_text]"
else if (rmb_text)
lmb_rmb_line = rmb_text
@@ -1516,37 +1522,37 @@
lmb_rmb_line += "
"
extra_lines++
if((SCREENTIP_CONTEXT_CTRL_LMB in context) && (length(context[SCREENTIP_CONTEXT_CTRL_LMB]) > 0))
- ctrl_lmb_ctrl_rmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_LMB)
+ ctrl_lmb_ctrl_rmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_LMB, allow_images)
if((SCREENTIP_CONTEXT_CTRL_RMB in context) && (length(context[SCREENTIP_CONTEXT_CTRL_RMB]) > 0))
if (ctrl_lmb_ctrl_rmb_line != "")
- ctrl_lmb_ctrl_rmb_line += " | "
+ ctrl_lmb_ctrl_rmb_line += " | [allow_images ? " " : ""]"
ctrl_lmb_ctrl_rmb_line += "[SCREENTIP_CONTEXT_CTRL_RMB]: [context[SCREENTIP_CONTEXT_CTRL_RMB]]"
- ctrl_lmb_ctrl_rmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_RMB)
+ ctrl_lmb_ctrl_rmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_RMB, allow_images)
// Alt-LMB, Alt-RMB on one line...
if (ctrl_lmb_ctrl_rmb_line != "")
ctrl_lmb_ctrl_rmb_line += "
"
extra_lines++
if((SCREENTIP_CONTEXT_ALT_LMB in context) && (length(context[SCREENTIP_CONTEXT_ALT_LMB]) > 0))
- alt_lmb_alt_rmb_line = build_context(context, SCREENTIP_CONTEXT_ALT_LMB)
+ alt_lmb_alt_rmb_line = build_context(context, SCREENTIP_CONTEXT_ALT_LMB, allow_images)
if((SCREENTIP_CONTEXT_ALT_RMB in context) && (length(context[SCREENTIP_CONTEXT_ALT_RMB]) > 0))
if (alt_lmb_alt_rmb_line != "")
- alt_lmb_alt_rmb_line += " | "
- alt_lmb_alt_rmb_line = build_context(context, SCREENTIP_CONTEXT_ALT_RMB)
+ alt_lmb_alt_rmb_line += " | [allow_images ? " " : ""]"
+ alt_lmb_alt_rmb_line = build_context(context, SCREENTIP_CONTEXT_ALT_RMB, allow_images)
// Shift-LMB, Ctrl-Shift-LMB on one line...
if (alt_lmb_alt_rmb_line != "")
alt_lmb_alt_rmb_line += "
"
extra_lines++
if((SCREENTIP_CONTEXT_SHIFT_LMB in context) && (length(context[SCREENTIP_CONTEXT_SHIFT_LMB]) > 0))
- shift_lmb_ctrl_shift_lmb_line = build_context(context, SCREENTIP_CONTEXT_SHIFT_LMB)
+ shift_lmb_ctrl_shift_lmb_line = build_context(context, SCREENTIP_CONTEXT_SHIFT_LMB, allow_images)
if((SCREENTIP_CONTEXT_CTRL_SHIFT_LMB in context) && (length(context[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB]) > 0))
if (shift_lmb_ctrl_shift_lmb_line != "")
- shift_lmb_ctrl_shift_lmb_line += " | "
+ shift_lmb_ctrl_shift_lmb_line += " | [allow_images ? " " : ""]"
shift_lmb_ctrl_shift_lmb_line += "[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB]: [context[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB]]"
- shift_lmb_ctrl_shift_lmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_SHIFT_LMB)
+ shift_lmb_ctrl_shift_lmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_SHIFT_LMB, allow_images)
if (shift_lmb_ctrl_shift_lmb_line != "")
extra_lines++
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 69d597d321..f0aeead3ef 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -814,7 +814,7 @@ GLOBAL_LIST_INIT(plastic_recipes, list(
new /datum/stack_recipe("water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/empty), \
new /datum/stack_recipe("large water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty,3), \
new /datum/stack_recipe("shower curtain", /obj/structure/curtain, 10, time = 10, one_per_turf = 1, on_floor = 1), \
- new /datum/stack_recipe("duct", /obj/item/stack/ducts,1), \
+ new /datum/stack_recipe("duct", /obj/item/stack/ducts, 1, 5, 50), \
new /datum/stack_recipe("laser pointer case", /obj/item/glasswork/glass_base/laserpointer_shell, 30), \
new /datum/stack_recipe("wet floor sign", /obj/item/clothing/suit/caution, 2),
new /datum/stack_recipe("micro bricks", /obj/item/stack/sheet/micro_bricks, 1, 5, 15,)))
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 266d8b7b86..b2cd0f481b 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -514,44 +514,3 @@
/obj/item/stack/microwave_act(obj/machinery/microwave/M)
if(istype(M) && M.dirty < 100)
M.dirty += amount
-
-/*
- * Recipe datum
- */
-/datum/stack_recipe
- var/title = "ERROR"
- var/result_type
- var/req_amount = 1
- var/res_amount = 1
- var/max_res_amount = 1
- var/time = 0
- var/one_per_turf = FALSE
- var/on_floor = FALSE
- var/placement_checks = FALSE
- var/applies_mats = FALSE
- var/trait_booster = null
- var/trait_modifier = 1
-
-/datum/stack_recipe/New(title, result_type, req_amount = 1, res_amount = 1, max_res_amount = 1,time = 0, one_per_turf = FALSE, on_floor = FALSE, window_checks = FALSE, placement_checks = FALSE, applies_mats = FALSE, trait_booster = null, trait_modifier = 1)
- src.title = title
- src.result_type = result_type
- src.req_amount = req_amount
- src.res_amount = res_amount
- src.max_res_amount = max_res_amount
- src.time = time
- src.one_per_turf = one_per_turf
- src.on_floor = on_floor
- src.placement_checks = placement_checks
- src.applies_mats = applies_mats
- src.trait_booster = trait_booster
- src.trait_modifier = trait_modifier
-/*
- * Recipe list datum
- */
-/datum/stack_recipe_list
- var/title = "ERROR"
- var/list/recipes
-
-/datum/stack_recipe_list/New(title, recipes)
- src.title = title
- src.recipes = recipes
diff --git a/code/game/objects/items/stacks/stack_recipe.dm b/code/game/objects/items/stacks/stack_recipe.dm
new file mode 100644
index 0000000000..b8cbfe3b61
--- /dev/null
+++ b/code/game/objects/items/stacks/stack_recipe.dm
@@ -0,0 +1,69 @@
+
+/*
+ * Recipe datum
+ */
+/datum/stack_recipe
+ /// The title of the recipe
+ var/title = "ERROR"
+ /// What atom the recipe makes, typepath
+ var/atom/result_type
+ /// Amount of stack required to make
+ var/req_amount = 1
+ /// Amount of resulting atoms made
+ var/res_amount = 1
+ /// Max amount of resulting atoms made
+ var/max_res_amount = 1
+ /// How long it takes to make
+ var/time = 0
+ /// If only one of the resulting atom is allowed per turf
+ var/one_per_turf = FALSE
+ /// If the atom requires a floor below
+ var/on_floor = FALSE
+ /// Bitflag of additional placement checks required to place. (STACK_CHECK_CARDINALS|STACK_CHECK_ADJACENT)
+ var/placement_checks = NONE
+ /// If TRUE, the created atom will gain custom mat datums
+ var/applies_mats = FALSE
+ /// What trait, if any, boosts the construction speed of this item
+ var/trait_booster
+ /// How much the trait above, if supplied, boosts the construct speed of this item
+ var/trait_modifier = 1
+
+/datum/stack_recipe/New(
+ title,
+ result_type,
+ req_amount = 1,
+ res_amount = 1,
+ max_res_amount = 1,
+ time = 0,
+ one_per_turf = FALSE,
+ on_floor = FALSE,
+ window_checks = FALSE,
+ placement_checks = NONE,
+ applies_mats = FALSE,
+ trait_booster,
+ trait_modifier = 1,
+)
+
+ src.title = title
+ src.result_type = result_type
+ src.req_amount = req_amount
+ src.res_amount = res_amount
+ src.max_res_amount = max_res_amount
+ src.time = time
+ src.one_per_turf = one_per_turf
+ src.on_floor = on_floor
+ src.placement_checks = placement_checks
+ src.applies_mats = applies_mats
+ src.trait_booster = trait_booster
+ src.trait_modifier = trait_modifier
+
+/*
+ * Recipe list datum
+ */
+/datum/stack_recipe_list
+ var/title = "ERROR"
+ var/list/recipes
+
+/datum/stack_recipe_list/New(title, recipes)
+ src.title = title
+ src.recipes = recipes
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 7a5f4a0e3e..aa6795af52 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -64,6 +64,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/outline_color = COLOR_THEME_MIDNIGHT
var/screentip_pref = SCREENTIP_PREFERENCE_ENABLED
var/screentip_color = "#ffd391"
+ var/screentip_allow_images = FALSE
var/buttons_locked = FALSE
var/hotkeys = FALSE
@@ -1264,6 +1265,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "Outline Color: [outline_color ? "" : "Theme-based (null)"][outline_color] Change
"
dat += "Screentip: [screentip_pref]
"
dat += "Screentip Color: [screentip_color] Change
"
+ dat += "\
+ Screentip context with images: [screentip_allow_images ? "Allowed" : "Disallowed"]
"
dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"]
"
dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
"
dat += "Show Runechat Chat Bubbles: [chat_on_map ? "Enabled" : "Disabled"]
"
@@ -1402,6 +1406,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "Be Antagonist Victim: [be_victim ? be_victim : BEVICTIM_ASK]
"
dat += "Disable combat mode cursor: [disable_combat_cursor?"Yes":"No"]
"
dat += "Splashscreen Player Panel Style: [(toggles & TG_PLAYER_PANEL)?"TG":"Old"]
"
+ dat += "Character Creation Menu Style: [new_character_creator ? "New" : "Old"]
"
//SPLURT Edit end
dat += "
"
@@ -3561,6 +3566,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/pickedScreentipColor = input(user, "Choose your screentip color.", "General Preference", screentip_color) as color|null
if(pickedScreentipColor)
screentip_color = pickedScreentipColor
+ if("screentip_allow_images")
+ screentip_allow_images = !screentip_allow_images
if("tgui_lock")
tgui_lock = !tgui_lock
if("winflash")
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index c265604f81..6bfb3684a9 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -5,7 +5,7 @@
// You do not need to raise this if you are adding new values that have sane defaults.
// Only raise this value when changing the meaning/format/name/layout of an existing value
// where you would want the updater procs below to run
-#define SAVEFILE_VERSION_MAX 57
+#define SAVEFILE_VERSION_MAX 57.01
/*
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
@@ -423,6 +423,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["outline_enabled"] >> outline_enabled
S["screentip_pref"] >> screentip_pref
S["screentip_color"] >> screentip_color
+ S["screentip_allow_images"] >> screentip_allow_images
S["hotkeys"] >> hotkeys
S["chat_on_map"] >> chat_on_map
S["max_chat_length"] >> max_chat_length
@@ -490,6 +491,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["disable_combat_cursor"] >> disable_combat_cursor
S["use_new_playerpanel"] >> use_new_playerpanel
S["gfluid_blacklist"] >> gfluid_blacklist
+ S["new_character_creator"] >> new_character_creator
+
//favorite outfits
S["favorite_outfits"] >> favorite_outfits
@@ -555,6 +558,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
key_bindings = sanitize_islist(key_bindings, list())
modless_key_bindings = sanitize_islist(modless_key_bindings, list())
favorite_outfits = SANITIZE_LIST(favorite_outfits)
+ screentip_color = sanitize_hexcolor(screentip_color, 6, 1, initial(screentip_color))
//SKYRAT CHANGES BEGIN
see_chat_emotes = sanitize_integer(see_chat_emotes, 0, 1, initial(see_chat_emotes))
@@ -633,6 +637,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["outline_color"], outline_color)
WRITE_FILE(S["screentip_pref"], screentip_pref)
WRITE_FILE(S["screentip_color"], screentip_color)
+ WRITE_FILE(S["screentip_allow_images"], screentip_allow_images)
WRITE_FILE(S["hotkeys"], hotkeys)
WRITE_FILE(S["chat_on_map"], chat_on_map)
WRITE_FILE(S["max_chat_length"], max_chat_length)
@@ -692,6 +697,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["disable_combat_cursor"], disable_combat_cursor)
WRITE_FILE(S["use_new_playerpanel"], use_new_playerpanel)
WRITE_FILE(S["gfluid_blacklist"], gfluid_blacklist)
+ WRITE_FILE(S["new_character_creator"], new_character_creator)
var/mob/living/carbon/human/H = parent.mob
if(istype(H))
diff --git a/code/modules/keybindings/keybind/movement.dm b/code/modules/keybindings/keybind/movement.dm
index 296b127cb4..4e14b69ae9 100644
--- a/code/modules/keybindings/keybind/movement.dm
+++ b/code/modules/keybindings/keybind/movement.dm
@@ -74,7 +74,7 @@
var/mob/M = user.mob
M.westface()
return TRUE
-
+/*
/datum/keybinding/mob/shift_north
hotkey_keys = list("CtrlShiftW", "CtrlShiftNorth")
name = "pixel_shift_north"
@@ -122,7 +122,7 @@
var/mob/M = user.mob
M.westshift()
return TRUE
-
+*/
/datum/keybinding/living/hold_sprint
hotkey_keys = list()
classic_keys = list()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 94aa69473d..4703712f31 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -678,7 +678,7 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
setDir(SOUTH)
client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY
return TRUE
-
+/*
/mob/verb/eastshift()
set hidden = TRUE
if(!canface())
@@ -710,7 +710,7 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
if(pixel_y >= -32)
pixel_y--
is_shifted = TRUE
-
+*/
/mob/proc/IsAdvancedToolUser()//This might need a rename but it should replace the can this mob use things check
return FALSE
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index a0d003b5af..7b5461ceb7 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -55,6 +55,8 @@
var/resting = 0 //Carbon
var/lying = 0
var/lying_prev = 0
+
+ /// Whether the mob is pixel shifted or not
var/is_shifted = FALSE
/// List of movement speed modifiers applying to this mob
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 1da355a419..83f7bbbd82 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -45,6 +45,14 @@
if(mob.force_moving)
return FALSE
+ // Sandstorm Edit
+ if(mob.shifting)
+ mob.pixel_shift(direction)
+ return FALSE
+ else if(mob.is_shifted)
+ mob.unpixel_shift()
+ //
+
var/mob/living/L = mob //Already checked for isliving earlier
if(L.incorporeal_move) //Move though walls
Process_Incorpmove(direction)
diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm
index 8a27f2669c..59816544ab 100644
--- a/code/modules/plumbing/ducts.dm
+++ b/code/modules/plumbing/ducts.dm
@@ -388,7 +388,7 @@ All the important duct code:
singular_name = "duct"
icon = 'icons/obj/plumbing/fluid_ducts.dmi'
icon_state = "ducts"
- custom_materials = list(/datum/material/iron=500)
+ custom_materials = list(/datum/material/plastic = 400)
w_class = WEIGHT_CLASS_TINY
novariants = FALSE
max_amount = 50
diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm
index 7862f24715..30d0c70566 100644
--- a/code/modules/research/machinery/_production.dm
+++ b/code/modules/research/machinery/_production.dm
@@ -120,7 +120,7 @@
return round(A / max(1, all_materials[mat] * ef))
/obj/machinery/rnd/production/proc/efficient_with(path)
- return !ispath(path, /obj/item/stack/sheet) && !ispath(path, /obj/item/stack/ore/bluespace_crystal)
+ return !ispath(path, /obj/item/stack/sheet) && !ispath(path, /obj/item/stack/ore/bluespace_crystal) && !ispath(path, /obj/item/stack/ducts)
/obj/machinery/rnd/production/proc/user_try_print_id(id, amount)
if((!istype(linked_console) && requires_console) || !id)
diff --git a/config/sandstorm/balance.txt b/config/sandstorm/balance.txt
index bc10b7a471..9df8fcebd6 100644
--- a/config/sandstorm/balance.txt
+++ b/config/sandstorm/balance.txt
@@ -90,3 +90,11 @@ TETRIS_SCORE_MAX_SCI 10000
## This applies to both prizes and research points
## Time is measured in ticks
TETRIS_TIME_COOLDOWN 600
+
+##
+## PROTOLATHE RESTRICTIONS
+###
+
+## Should production machinery be restricted for skeleton crews?
+## Uncomment this to enable
+#PROTOLOCK_DURING_LOWPOP
diff --git a/html/changelogs/archive/2023-02.yml b/html/changelogs/archive/2023-02.yml
index f758362529..ec1398fab4 100644
--- a/html/changelogs/archive/2023-02.yml
+++ b/html/changelogs/archive/2023-02.yml
@@ -108,3 +108,48 @@
bourbon, resulting in 10 units.
LeDrascol:
- rscdel: Removed techweb entry for Infective Exo-Locomotion nanites
+2023-02-19:
+ LeDrascol:
+ - bugfix: Fixed techweb exploit with 357 speed loader
+ - rscadd: Added more feedback messages to Cryptominer account changing
+ - tweak: Standard cryptominers will report account changes on Cargo radio
+ - balance: Setting a cryptominer's account now takes five seconds
+ - admin: Added game logging for Cryptominer account changes
+ - balance: Production machines now check ID cards for access
+ - balance: R&D console starts locked by default
+ - config: Added new configuration options for production machine access
+ - tweak: In Estrous examine text is now based on the holder's reproductive organs
+ - tweak: In Estrous gain text no longer implies the holder intends to be an organ
+ recipient
+ - code_imp: Added signals for organ insertion and removal
+ - tweak: The Gear Harness is now non-covering
+ - rscadd: Added Synthetic species flavor text for In Estrous
+ - tweak: In Estrous no longer requires both female organs
+ - code_imp: In Estrous examine text uses English lists
+ SandPoot:
+ - imageadd: Changed screentips icons to something a lot fancier unified with the
+ LMB / RMB text.
+2023-02-20:
+ LeDrascol:
+ - refactor: Refactored trait defines
+2023-02-21:
+ SandPoot:
+ - tweak: Tweaked pixelshifting. (Default keybinding is B, check your keybinds!)
+ - bugfix: Duct pipes will always have the same cost no matter where you make it.
+ - bugfix: Hand-made duct pipes will properly stack with machine made ones.
+2023-02-22:
+ BongaTheProto:
+ - tweak: The pixelfishing keybind is now B, although you can use use the previous
+ ctrl+shift keybinds. Please make sure you check your keybinds!
+2023-02-23:
+ Comicao1:
+ - tweak: Wendigos may now work in a proper way, and are also able to enslave people
+ through sex while grabbing them.
+ LeDrascol:
+ - rscadd: 'Added positive quirk: Rad Fiend'
+ WoolyAypa:
+ - rscadd: Added a tajaran snout option
+ - rscadd: Added an alt version of the dragon wings
+ - rscadd: Added a stripper version of the explorer jumpsuit
+ - rscadd: Added the security stripper outfit in the loadout
+ - tweak: Now the security stripper outfit can be used with digitigrade
diff --git a/icons/UI_Icons/screentips/cursor_hints.dmi b/icons/UI_Icons/screentips/cursor_hints.dmi
new file mode 100644
index 0000000000..9533d240a5
Binary files /dev/null and b/icons/UI_Icons/screentips/cursor_hints.dmi differ
diff --git a/icons/UI_Icons/screentips/full_mouse.dmi b/icons/UI_Icons/screentips/full_mouse.dmi
new file mode 100644
index 0000000000..32e0b8f67c
Binary files /dev/null and b/icons/UI_Icons/screentips/full_mouse.dmi differ
diff --git a/modular_sand/code/controllers/configuration/entries/sandstorm_balance.dm b/modular_sand/code/controllers/configuration/entries/sandstorm_balance.dm
index 8d738c425a..f181b9bffe 100644
--- a/modular_sand/code/controllers/configuration/entries/sandstorm_balance.dm
+++ b/modular_sand/code/controllers/configuration/entries/sandstorm_balance.dm
@@ -92,3 +92,7 @@
// Minimum time between giving rewards
/datum/config_entry/number/tetris_time_cooldown
config_entry_value = 600
+
+/// R&D Machinery ///
+// Should production machinery be restricted for skeleton crews?
+/datum/config_entry/flag/protolock_during_lowpop
diff --git a/modular_sand/code/datums/traits/neutral.dm b/modular_sand/code/datums/traits/neutral.dm
index 363f6f7e25..ea574d896b 100644
--- a/modular_sand/code/datums/traits/neutral.dm
+++ b/modular_sand/code/datums/traits/neutral.dm
@@ -36,16 +36,50 @@
desc = "Your system burns with the desire to be bred. Satisfying your lust will make you happy, while ignoring it may cause you to become sad and needy."
value = 0
mob_trait = TRAIT_ESTROUS_ACTIVE
- gain_text = span_love("You body burns with the desire to be bred.")
+ gain_text = span_love("You body burns with the desire to engage in breeding.")
lose_text = span_notice("You feel more in control of your body and thoughts.")
+ // Default heat message for examine text
+ var/heat_type = "influenced by the estrous cycle"
+
+ // Type of interaction to be performed
+ // Intended for use with downstream quirks
+ var/positional_orientation = "engage in breeding"
+
/datum/quirk/estrous_active/add()
// Add examine hook
RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, .proc/quirk_examine_estrous_active)
+ // Add organ change hooks
+ RegisterSignal(quirk_holder, COMSIG_MOB_ORGAN_ADD, .proc/update_heat_type)
+ RegisterSignal(quirk_holder, COMSIG_MOB_ORGAN_REMOVE, .proc/update_heat_type)
+
/datum/quirk/estrous_active/remove()
- // Remove examine hook
- UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+ // Remove signals
+ UnregisterSignal(quirk_holder, list(COMSIG_MOB_ORGAN_ADD, COMSIG_MOB_ORGAN_REMOVE, COMSIG_PARENT_EXAMINE))
+
+/datum/quirk/estrous_active/post_add()
+ // Update text used by message
+ update_heat_type()
+
+/datum/quirk/estrous_active/proc/update_heat_type()
+ // Define temporary list of heat phrases
+ var/list/heat_phrases = list()
+
+ // Check for male hormonal organ
+ if(quirk_holder.has_balls())
+ heat_phrases += "in rut"
+
+ // Check for female hormonal organ
+ if(quirk_holder.getorganslot(ORGAN_SLOT_WOMB))
+ heat_phrases += "in estrous"
+
+ // Check for synthetic
+ if(isrobotic(quirk_holder))
+ heat_phrases += "simulating hormones"
+
+ // Build English list
+ heat_type = english_list(heat_phrases, nothing_text = "experiencing high hormonal levels")
/datum/quirk/estrous_active/proc/quirk_examine_estrous_active(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list)
SIGNAL_HANDLER
@@ -59,4 +93,4 @@
return
// Add quirk message
- examine_list += span_love("[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] currently influenced by the estrous cycle, and long for breeding.")
+ examine_list += span_love("[quirk_holder.p_they(TRUE)] [quirk_holder.p_are()] currently [heat_type], with a longing to [positional_orientation].")
diff --git a/modular_sand/code/game/machinery/cryptominers.dm b/modular_sand/code/game/machinery/cryptominers.dm
index 6738511b4f..20f5bd69b4 100644
--- a/modular_sand/code/game/machinery/cryptominers.dm
+++ b/modular_sand/code/game/machinery/cryptominers.dm
@@ -19,7 +19,7 @@
/obj/machinery/cryptominer
name = "cryptocurrency miner"
- desc = "This handy-dandy machine will produce credits for your enjoyment."
+ desc = "This handy-dandy machine will produce credits for Cargo's enjoyment."
icon = 'modular_sand/icons/obj/machines/cryptominer.dmi'
icon_state = "off"
density = TRUE
@@ -32,9 +32,23 @@
var/miningtime = 3000
var/miningpoints = 50
var/datum/bank_account/pay_me = null
+ var/obj/item/radio/cargo_radio
+ // Should this machine send messages on cargo radio?
+ var/radio_snitch = TRUE
/obj/machinery/cryptominer/Initialize(mapload)
. = ..()
+
+ // Create new radio
+ cargo_radio = new /obj/item/radio(src)
+
+ // Prevent radio listening
+ cargo_radio.listening = 0
+
+ // Set radio frequency
+ cargo_radio.set_frequency(FREQ_SUPPLY)
+
+ // Set default account
pay_me = SSeconomy.get_dep_account(ACCOUNT_CAR)
/obj/machinery/cryptominer/update_icon()
@@ -48,6 +62,7 @@
/obj/machinery/cryptominer/Destroy()
STOP_PROCESSING(SSmachines,src)
+ QDEL_NULL(cargo_radio)
return ..()
/obj/machinery/cryptominer/deconstruct()
@@ -61,35 +76,107 @@
return
if(default_unfasten_wrench(user, W))
return
+
+ // Check if disabled
+ // Check if panel is open
+ // Check if user is in HELP intent
if(!mining && panel_open && user.a_intent == INTENT_HELP)
+ // Attempt to locate user's ID
var/id_card = W.GetID()
- if(id_card)
- var/obj/item/card/id/CARD = id_card
- if(CARD.bank_support != ID_FREE_BANK_ACCOUNT)
- to_chat(user, span_warning("This ID has no banking support whatsover, must be an older model..."))
- return
- if(!CARD.registered_account)
- to_chat(user, span_warning("ERROR: No bank account found."))
- return
- to_chat(user, span_notice("You link \the [CARD] to \the [src]."))
- pay_me = CARD.registered_account
- say("Now using [pay_me.account_holder ? "[pay_me.account_holder]s" : span_boldwarning("ERROR")] account.")
+
+ // Check if ID exists
+ if(!id_card)
return
+ // Define ID card
+ var/obj/item/card/id/CARD = id_card
+
+ // Check if ID card has banking support
+ if(CARD.bank_support != ID_FREE_BANK_ACCOUNT)
+ // Warn in local chat and return
+ say("ERROR: No banking support found on provided ID card.")
+ return
+
+ // Check if ID card has a linked account
+ if(!CARD.registered_account)
+ // Warn in local chat and return
+ say("ERROR: No bank account found on provided ID card.")
+ return
+
+ // Display message in local chat
+ user.visible_message(span_notice("[user] begins setting \the [src] to use \the [CARD]."), span_notice("[user] begins setting \the [src] to use \the [CARD]."))
+
+ // Display alert balloon
+ balloon_alert(user, "Configuring new account...")
+
+ // Perform interaction timer
+ if(!do_after(user, 5 SECONDS, target = src))
+ // Warn in local chat
+ user.visible_message(span_warning("[user] fails to link \the [src] to a new account!"), span_warning("You fail to link \the [src] to a new account!"))
+
+ // Display alert balloon
+ balloon_alert(user, "Configuration failed!")
+
+ // Return with no further effects
+ return
+
+ // Alert user of linking success
+ to_chat(user, span_notice("You link \the [CARD] to \the [src]."))
+
+ // Define previous account
+ var/old_account = pay_me
+
+ // Set payment account to ID card's account
+ pay_me = CARD.registered_account
+
+ // Say in local chat
+ say("Now using [pay_me.account_holder ? "[pay_me.account_holder]s" : span_boldwarning("ERROR")] account.")
+
+ // Log interaction
+ log_game("[user] set \the [src] in [get_area(src)] to pay into their personal account. Previous account was [old_account].")
+
+ // Check if old account was cargo
+ // Check if this machine should report over radio
+ if(radio_snitch && (old_account == SSeconomy.get_dep_account(ACCOUNT_CAR)))
+ // Send radio notice
+ cargo_radio.talk_into(src, "CRYPTO ALERT: Crew member [user] has set \the [src] in [get_area(src)] to use [pay_me.account_holder]\'s account, instead of Cargo!", FREQ_SUPPLY)
+
/obj/machinery/cryptominer/AltClick(mob/user)
- user.visible_message(span_warning("[user] begins resetting \the [src]."),
- span_warning("You begin resetting \the [src]."))
- balloon_alert(user, "resetting")
- if(do_after(user, 5 SECONDS, target = src))
- pay_me = SSeconomy.get_dep_account(ACCOUNT_CAR)
- say("Now using [pay_me.account_holder]s account.")
+ // Alert user in chat
+ user.visible_message(span_warning("[user] begins resetting \the [src]."), span_warning("You begin resetting \the [src]."))
+
+ // Display alert balloon
+ balloon_alert(user, "Resetting account...")
+
+ // Perform interaction timer
+ if(!do_after(user, 5 SECONDS, target = src))
+ // Warn in local chat
+ user.visible_message(span_warning("[user] fails to reset \the [src]."), span_warning("You fail to reset \the [src]."))
+
+ // Display alert balloon
+ balloon_alert(user, "Reset failed!")
+
+ // Return with no further effects
+ return
+
+ // Define previous account
+ var/old_account = pay_me
+
+ // Reset account to Cargo
+ pay_me = SSeconomy.get_dep_account(ACCOUNT_CAR)
+
+ // Say in local chat
+ say("Now using [pay_me.account_holder]s account.")
+
+ // Log interaction
+ log_game("[user] reset the \the [src] in [get_area(src)] to pay Cargo's departmental account. Previous account was [old_account]")
/obj/machinery/cryptominer/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
. += span_notice("A little screen on the machine reads: Currently the linked bank account is [pay_me.account_holder ? "[pay_me.account_holder]s" : span_boldwarning("ERROR")].")
- . += "Modify the destination of the credits using your id on it while it is inactive and has its panel open."
- . += "Alt-Click to reset to the Cargo budget."
+ . += "Modify the destination of the credits using your id on it while it is inactive and has its panel open."
+ . += "Alt-Click to reset to the Cargo budget."
/obj/machinery/cryptominer/process()
// Get turf
@@ -190,7 +277,7 @@
/obj/machinery/cryptominer/syndie
name = "syndicate cryptocurrency miner"
- desc = "This handy-dandy machine will produce credits for your enjoyment. It lasts a little longer."
+ desc = "This handy-dandy machine will produce credits for your own personal enjoyment. This one won't snitch on you to Cargo."
icon_state = "off_syndie"
density = TRUE
use_power = IDLE_POWER_USE
@@ -199,6 +286,7 @@
circuit = /obj/item/circuitboard/machine/cryptominer/syndie
miningtime = 6000
miningpoints = 100
+ radio_snitch = FALSE // Illegal tech!
/obj/machinery/cryptominer/syndie/update_icon()
. = ..()
@@ -219,6 +307,7 @@
active_power_usage = 1
miningtime = 600000
miningpoints = 1000
+ radio_snitch = FALSE // None of cargo's business!
/obj/machinery/cryptominer/nanotrasen/update_icon()
. = ..()
diff --git a/modular_sand/code/modules/clothing/under/miscellaneous.dm b/modular_sand/code/modules/clothing/under/miscellaneous.dm
new file mode 100644
index 0000000000..dd965b8111
--- /dev/null
+++ b/modular_sand/code/modules/clothing/under/miscellaneous.dm
@@ -0,0 +1,8 @@
+/obj/item/clothing/under/misc/gear_harness
+ can_adjust = FALSE
+ body_parts_covered = NONE
+
+/obj/item/clothing/under/misc/gear_harness/toggle_jumpsuit_adjust()
+ // Alert user and return
+ to_chat(usr, span_notice("[src] cannot be adjusted!"))
+ return FALSE
diff --git a/modular_sand/code/modules/mob/living/living_movement.dm b/modular_sand/code/modules/mob/living/living_movement.dm
index bf9c5e2912..4a0464ef6b 100644
--- a/modular_sand/code/modules/mob/living/living_movement.dm
+++ b/modular_sand/code/modules/mob/living/living_movement.dm
@@ -15,6 +15,7 @@
to_chat(src, span_warning("You cannot increase your layer priority any further."))
return
+ layer = clamp(layer, MOB_LAYER_SHIFT_MIN, MOB_LAYER_SHIFT_MAX)
layer += MOB_LAYER_SHIFT_INCREMENT
var/layer_priority = FLOOR((layer - MOB_LAYER) * 100, 1) // Just for text feedback
to_chat(src, span_notice("Your layer priority is now [layer_priority]."))
@@ -31,6 +32,7 @@
to_chat(src, span_warning("You cannot decrease your layer priority any further."))
return
+ layer = clamp(layer, MOB_LAYER_SHIFT_MIN, MOB_LAYER_SHIFT_MAX)
layer -= MOB_LAYER_SHIFT_INCREMENT
var/layer_priority = FLOOR((layer - MOB_LAYER) * 100, 1) // Just for text feedback
to_chat(src, span_notice("Your layer priority is now [layer_priority]."))
diff --git a/modular_sand/code/modules/mob/mob.dm b/modular_sand/code/modules/mob/mob.dm
index 7957645e54..ec6412f591 100644
--- a/modular_sand/code/modules/mob/mob.dm
+++ b/modular_sand/code/modules/mob/mob.dm
@@ -1,3 +1,7 @@
+// Configuration defines
+#define JOB_MINIMAL_ACCESS CONFIG_GET(flag/jobs_have_minimal_access)
+#define PROTOLOCK_DURING_LOWPOP CONFIG_GET(flag/protolock_during_lowpop)
+
// Only Clients should have a panel for them, okay?
/mob/Login()
. = ..()
@@ -23,3 +27,44 @@
/mob/proc/set_thirst(change)
thirst = max(0, change)
+
+/mob/proc/can_use_production(obj/machinery/rnd/production/machine_target)
+ // Check if server is NOT using minimal access
+ // This is intended for low populations
+ if((!PROTOLOCK_DURING_LOWPOP) && (!JOB_MINIMAL_ACCESS))
+ // Allow unrestricted use
+ return TRUE
+
+ // Define machine user
+ var/mob/living/carbon/human/machine_user = src
+
+ // Check if user exists
+ if(!istype(machine_user))
+ return TRUE
+
+ // Define user ID card
+ var/obj/item/card/id/user_id = machine_user.get_idcard()
+
+ // Check if ID card was found
+ if(!istype(user_id))
+ // Warn in local chat, then return
+ machine_target.say("Access denied: Unable to scan user ID card.")
+ return FALSE
+
+ // Check for Captain
+ if(ACCESS_CAPTAIN in user_id.access)
+ // Allow usage
+ return TRUE
+
+ // Check if access requirements are met
+ if(machine_target.check_access(user_id))
+ // Allow use
+ return TRUE
+
+ // User does not have access
+ // Warn in local chat, then return
+ machine_target.say("Access denied: No valid departmental credentials detected.")
+ return FALSE
+
+#undef JOB_MINIMAL_ACCESS
+#undef PROTOLOCK_DURING_LOWPOP
diff --git a/modular_sand/code/modules/pixel_shift/pixel_shift.dm b/modular_sand/code/modules/pixel_shift/pixel_shift.dm
new file mode 100644
index 0000000000..253ab050f3
--- /dev/null
+++ b/modular_sand/code/modules/pixel_shift/pixel_shift.dm
@@ -0,0 +1,101 @@
+#define MAXIMUM_PIXEL_SHIFT 16
+#define PASSABLE_SHIFT_THRESHOLD 8
+
+/mob
+ /// If we are in the shifting setting.
+ var/shifting = FALSE
+
+ /// Takes the four cardinal direction defines. Any atoms moving into this atom's tile will be allowed to from the added directions.
+ var/passthroughable = NONE
+
+/datum/keybinding/mob/pixel_shift
+ hotkey_keys = list("B")
+ name = "pixel_shift"
+ full_name = "Pixel Shift"
+ description = "Shift your characters offset."
+ category = CATEGORY_MOVEMENT
+ keybind_signal = COMSIG_KB_MOB_PIXELSHIFT
+
+/datum/keybinding/mob/pixel_shift/down(client/user)
+ . = ..()
+ if(.)
+ return
+ var/mob/M = user.mob
+ M.shifting = TRUE
+ return TRUE
+
+/datum/keybinding/mob/pixel_shift/up(client/user)
+ . = ..()
+ if(.)
+ return
+ var/mob/M = user.mob
+ M.shifting = FALSE
+ return TRUE
+
+/mob/proc/unpixel_shift()
+ return
+
+/mob/living/unpixel_shift()
+ . = ..()
+ passthroughable = NONE
+ if(is_shifted)
+ is_shifted = FALSE
+ pixel_x = get_standard_pixel_x_offset() + base_pixel_x
+ client?.pixel_x = 0
+ pixel_y = get_standard_pixel_y_offset() + base_pixel_y
+ client?.pixel_y = 0
+
+/mob/proc/pixel_shift(direction)
+ return
+
+/mob/living/set_pull_offsets(mob/living/pull_target, grab_state)
+ pull_target.unpixel_shift()
+ return ..()
+
+/mob/living/reset_pull_offsets(mob/living/pull_target, override)
+ pull_target.unpixel_shift()
+ return ..()
+
+/mob/living/pixel_shift(direction)
+ passthroughable = NONE
+ // switch(direction) // diagonal pixel-shifting, rejoice
+ if(CHECK_BITFIELD(direction, NORTH))
+ if(pixel_y <= MAXIMUM_PIXEL_SHIFT + base_pixel_y)
+ pixel_y++
+ client?.pixel_y++
+ is_shifted = TRUE
+ if(CHECK_BITFIELD(direction, EAST))
+ if(pixel_x <= MAXIMUM_PIXEL_SHIFT + base_pixel_x)
+ pixel_x++
+ client?.pixel_x++
+ is_shifted = TRUE
+ if(CHECK_BITFIELD(direction, SOUTH))
+ if(pixel_y >= -MAXIMUM_PIXEL_SHIFT + base_pixel_y)
+ pixel_y--
+ client?.pixel_y--
+ is_shifted = TRUE
+ if(CHECK_BITFIELD(direction, WEST))
+ if(pixel_x >= -MAXIMUM_PIXEL_SHIFT + base_pixel_x)
+ pixel_x--
+ client?.pixel_x--
+ is_shifted = TRUE
+
+ // Yes, I know this sets it to true for everything if more than one is matched.
+ // Movement doesn't check diagonals, and instead just checks EAST or WEST, depending on where you are for those.
+ if(pixel_y > PASSABLE_SHIFT_THRESHOLD)
+ passthroughable |= EAST | SOUTH | WEST
+ if(pixel_x > PASSABLE_SHIFT_THRESHOLD)
+ passthroughable |= NORTH | SOUTH | WEST
+ if(pixel_y < -PASSABLE_SHIFT_THRESHOLD)
+ passthroughable |= NORTH | EAST | WEST
+ if(pixel_x < -PASSABLE_SHIFT_THRESHOLD)
+ passthroughable |= NORTH | EAST | SOUTH
+
+/mob/living/CanAllowThrough(atom/movable/mover, turf/target)
+ // Make sure to not allow projectiles of any kind past where they normally wouldn't.
+ if(!istype(mover, /obj/item/projectile) && !mover.throwing && passthroughable & mover.dir)
+ return TRUE
+ return ..()
+
+#undef MAXIMUM_PIXEL_SHIFT
+#undef PASSABLE_SHIFT_THRESHOLD
diff --git a/modular_sand/code/modules/research/machinery/_production.dm b/modular_sand/code/modules/research/machinery/_production.dm
new file mode 100644
index 0000000000..936840ad4a
--- /dev/null
+++ b/modular_sand/code/modules/research/machinery/_production.dm
@@ -0,0 +1,29 @@
+/obj/machinery/rnd/production/Initialize(mapload)
+ . = ..()
+
+ // Generate access lists
+ gen_access()
+
+/obj/machinery/rnd/production/ui_interact(mob/user)
+ // Check if access is required
+ if(!req_access)
+ return ..()
+
+ // Check if user can use machine
+ if(!user.can_use_production(src))
+ return
+
+ // Return normally
+ . = ..()
+
+/obj/machinery/rnd/production/Topic(raw, ls)
+ // Check if access is required
+ if(!req_access)
+ return ..()
+
+ // Check if user can use machine
+ if(!usr.can_use_production(src))
+ return
+
+ // Return normally
+ . = ..()
diff --git a/modular_sand/code/modules/research/machinery/departmental_circuit_imprinter.dm b/modular_sand/code/modules/research/machinery/departmental_circuit_imprinter.dm
new file mode 100644
index 0000000000..712ea33cda
--- /dev/null
+++ b/modular_sand/code/modules/research/machinery/departmental_circuit_imprinter.dm
@@ -0,0 +1,2 @@
+/obj/machinery/rnd/production/circuit_imprinter/department/science
+ req_access = list(ACCESS_RESEARCH)
diff --git a/modular_sand/code/modules/research/machinery/departmental_protolathe.dm b/modular_sand/code/modules/research/machinery/departmental_protolathe.dm
new file mode 100644
index 0000000000..4904828374
--- /dev/null
+++ b/modular_sand/code/modules/research/machinery/departmental_protolathe.dm
@@ -0,0 +1,17 @@
+/obj/machinery/rnd/production/protolathe/department/engineering
+ req_access = list(ACCESS_ENGINE)
+
+/obj/machinery/rnd/production/protolathe/department/service
+ req_one_access = list(ACCESS_BAR, ACCESS_KITCHEN, ACCESS_HYDROPONICS, ACCESS_JANITOR)
+
+/obj/machinery/rnd/production/protolathe/department/medical
+ req_access = list(ACCESS_MEDICAL)
+
+/obj/machinery/rnd/production/protolathe/department/cargo
+ req_access = list(ACCESS_CARGO)
+
+/obj/machinery/rnd/production/protolathe/department/science
+ req_access = list(ACCESS_RESEARCH)
+
+/obj/machinery/rnd/production/protolathe/department/security
+ req_access = list(ACCESS_SECURITY)
diff --git a/modular_sand/code/modules/research/machinery/departmental_techfab.dm b/modular_sand/code/modules/research/machinery/departmental_techfab.dm
new file mode 100644
index 0000000000..7f9601ab3a
--- /dev/null
+++ b/modular_sand/code/modules/research/machinery/departmental_techfab.dm
@@ -0,0 +1,17 @@
+/obj/machinery/rnd/production/techfab/department/engineering
+ req_access = list(ACCESS_ENGINE)
+
+/obj/machinery/rnd/production/techfab/department/service
+ req_one_access = list(ACCESS_BAR, ACCESS_KITCHEN, ACCESS_HYDROPONICS, ACCESS_JANITOR)
+
+/obj/machinery/rnd/production/techfab/department/medical
+ req_access = list(ACCESS_MEDICAL)
+
+/obj/machinery/rnd/production/techfab/department/cargo
+ req_access = list(ACCESS_CARGO)
+
+/obj/machinery/rnd/production/techfab/department/science
+ req_access = list(ACCESS_RESEARCH)
+
+/obj/machinery/rnd/production/techfab/department/security
+ req_access = list(ACCESS_SECURITY)
diff --git a/modular_sand/code/modules/research/rdconsole.dm b/modular_sand/code/modules/research/rdconsole.dm
new file mode 100644
index 0000000000..0248ae315b
--- /dev/null
+++ b/modular_sand/code/modules/research/rdconsole.dm
@@ -0,0 +1,2 @@
+/obj/machinery/computer/rdconsole
+ locked = TRUE
diff --git a/modular_sand/code/modules/surgery/organs/organ_internal.dm b/modular_sand/code/modules/surgery/organs/organ_internal.dm
new file mode 100644
index 0000000000..1492bce86b
--- /dev/null
+++ b/modular_sand/code/modules/surgery/organs/organ_internal.dm
@@ -0,0 +1,22 @@
+/obj/item/organ/Insert(mob/living/carbon/organ_mob, special, drop_if_replaced)
+ . = ..()
+
+ // Check parent return
+ if(!.)
+ return
+
+ // Send signal to mob
+ SEND_SIGNAL(organ_mob, COMSIG_MOB_ORGAN_ADD, src, special, drop_if_replaced)
+
+/obj/item/organ/Remove(special)
+ . = ..()
+
+ // Define organ mob
+ var/mob/living/carbon/organ_mob = . || null
+
+ // Check if organ mob exists
+ if(!istype(organ_mob))
+ return
+
+ // Send signal to mob
+ SEND_SIGNAL(organ_mob, COMSIG_MOB_ORGAN_REMOVE, src, special)
diff --git a/modular_sand/code/modules/uplink/uplink_items.dm b/modular_sand/code/modules/uplink/uplink_items.dm
index be6d85b8f5..fca6b3cf20 100644
--- a/modular_sand/code/modules/uplink/uplink_items.dm
+++ b/modular_sand/code/modules/uplink/uplink_items.dm
@@ -11,3 +11,7 @@
desc = "An additional arm harvested from slaves captured by the Syndicate. Comes with an implanter."
item = /obj/item/extra_arm
cost = 8
+
+// Made craftable, and no longer illegal
+/datum/uplink_item/ammo/revolver
+ illegal_tech = FALSE
diff --git a/modular_splurt/code/datums/interactions/lewd/_lewd.dm b/modular_splurt/code/datums/interactions/lewd/_lewd.dm
index a5579511b5..2cd1a55817 100644
--- a/modular_splurt/code/datums/interactions/lewd/_lewd.dm
+++ b/modular_splurt/code/datums/interactions/lewd/_lewd.dm
@@ -127,6 +127,7 @@
ass = new
ass.Insert(partner)
ass.climax_modify_size(src, getorganslot(ORGAN_SLOT_PENIS))
+
else
switch(last_genital.type)
if(/obj/item/organ/genital/penis)
@@ -193,6 +194,10 @@
ass = new
ass.Insert(partner)
ass.climax_modify_size(src, last_genital)
+ if(iswendigo(partner) && partner.pulling == src)
+ var/mob/living/carbon/wendigo/W = partner
+ W.slaves |= src
+ to_chat(src, " You are now [W]'s slave! Serve your master properly! ")
if(!message)
return ..()
if(gender == MALE)
diff --git a/modular_splurt/code/datums/traits/good.dm b/modular_splurt/code/datums/traits/good.dm
index 70a84f4d82..89e8ec6f9a 100644
--- a/modular_splurt/code/datums/traits/good.dm
+++ b/modular_splurt/code/datums/traits/good.dm
@@ -48,6 +48,52 @@
quirk_holder.weather_immunities -= "ash"
*/
+/datum/quirk/rad_fiend
+ name = "Rad Fiend"
+ desc = "You've been blessed by Cherenkov's warming light, causing you to emit a subtle glow at all times. Only intense radiation is capable of penetrating your protective barrier."
+ value = 2
+ mob_trait = TRAIT_RAD_FIEND
+ gain_text = span_notice("You feel empowered by Cherenkov's glow.")
+ lose_text = span_notice("You realize that rads aren't so rad.")
+
+ // Variable for the radiation immunity check
+ var/can_gain = TRUE
+
+/datum/quirk/rad_fiend/add()
+ // Define quirk holder mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Check for any radiation immunity
+ if(HAS_TRAIT(quirk_mob, TRAIT_RADIMMUNE))
+ // Set gain status
+ can_gain = FALSE
+
+ // Return without doing anything
+ return
+
+ // Add glow control action
+ var/datum/action/rad_fiend/update_glow/quirk_action = new
+ quirk_action.Grant(quirk_mob)
+
+/datum/quirk/rad_fiend/post_add()
+ // Check if quirk effect was gained
+ if(can_gain)
+ return
+
+ // Alert quirk holder of gain status
+ to_chat(quirk_holder, span_warning("As you are immune to radiation, you were unable to gain Cherenkov's blessing. Please discuss alternatives with a medical professional."))
+
+/datum/quirk/rad_fiend/remove()
+ // Define quirk holder mob
+ var/mob/living/carbon/human/quirk_mob = quirk_holder
+
+ // Remove glow control action
+ var/datum/action/rad_fiend/update_glow/quirk_action = locate() in quirk_mob.actions
+ quirk_action.Remove(quirk_mob)
+
+ // Remove glow effect
+ quirk_mob.remove_filter("rad_fiend_glow")
+
/datum/quirk/dominant_aura
name = "Dominant Aura"
desc = "Your mere presence is assertive enough to appear as powerful to other people, so much in fact that the weaker kind can't help but throw themselves at your feet at the snap of a finger."
diff --git a/modular_splurt/code/datums/traits/neutral.dm b/modular_splurt/code/datums/traits/neutral.dm
index 5fdca5e6f5..9c7c6b8f67 100644
--- a/modular_splurt/code/datums/traits/neutral.dm
+++ b/modular_splurt/code/datums/traits/neutral.dm
@@ -152,7 +152,6 @@
gain_text = span_notice("You feel like being someone's pet...")
lose_text = span_notice("You no longer feel like being a pet...")
processing_quirk = TRUE
- var/mood_category = "dom_trained"
var/notice_delay = 0
var/mob/living/carbon/human/last_dom
@@ -202,10 +201,10 @@
//Handle the mood
var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
- if(istype(mood.mood_events[mood_category], /datum/mood_event/dominant/good_boy))
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, mood_category, /datum/mood_event/dominant/good_boy)
+ if(istype(mood.mood_events[QMOOD_WELL_TRAINED], /datum/mood_event/dominant/good_boy))
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_WELL_TRAINED, /datum/mood_event/dominant/good_boy)
else
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, mood_category, /datum/mood_event/dominant/need)
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_WELL_TRAINED, /datum/mood_event/dominant/need)
//Don't do anything if a previous dom was found
if(last_dom)
@@ -279,7 +278,6 @@
// The shame is unbearable
mood_quirk = FALSE
processing_quirk = FALSE
- var/mood_category = "backpack_implant_mood"
/datum/quirk/storage_concealment/on_spawn()
. = ..()
@@ -295,10 +293,10 @@
// Check the quirk holder for the trait
if(HAS_TRAIT(quirk_holder, TRAIT_HIDE_BACKPACK))
// When found: Mood bonus
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, mood_category, /datum/mood_event/dorsualiphobic_mood_positive)
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_HIDE_BAG, /datum/mood_event/dorsualiphobic_mood_positive)
else
// When not found: Mood penalty
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, mood_category, /datum/mood_event/dorsualiphobic_mood_negative)
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_HIDE_BAG, /datum/mood_event/dorsualiphobic_mood_negative)
//succubus and incubus below
/datum/quirk/incubus
@@ -635,15 +633,14 @@
value = 0
mood_quirk = TRUE
processing_quirk = TRUE
- var/mood_category = "nudist_mood"
/datum/quirk/nudist/on_process()
var/mob/living/carbon/human/H = quirk_holder
// Checking torso exposure appears to be a robust method.
if( ( H.is_chest_exposed() && H.is_groin_exposed() ) )
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, mood_category, /datum/mood_event/nudist_positive)
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_NUDIST, /datum/mood_event/nudist_positive)
else
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, mood_category, /datum/mood_event/nudist_negative)
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_NUDIST, /datum/mood_event/nudist_negative)
/datum/quirk/nudist/on_spawn()
. = ..()
@@ -662,15 +659,14 @@
mood_quirk = TRUE
medical_record_text = "Patient feels more secure when wearing a gas mask."
processing_quirk = TRUE
- var/mood_category = "masked_mook"
/datum/quirk/masked_mook/on_process()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/clothing/mask/gas/gasmask = H.get_item_by_slot(ITEM_SLOT_MASK)
if(istype(gasmask))
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, mood_category, /datum/mood_event/masked_mook)
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_MASKED_MOOK, /datum/mood_event/masked_mook)
else
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, mood_category, /datum/mood_event/masked_mook_incomplete)
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, QMOOD_MASKED_MOOK, /datum/mood_event/masked_mook_incomplete)
/datum/quirk/masked_mook/on_spawn()
. = ..()
diff --git a/modular_splurt/code/datums/traits/trait_actions.dm b/modular_splurt/code/datums/traits/trait_actions.dm
index da19988868..216d4cdea2 100644
--- a/modular_splurt/code/datums/traits/trait_actions.dm
+++ b/modular_splurt/code/datums/traits/trait_actions.dm
@@ -957,3 +957,67 @@
return
else
to_chat(H, span_warning("You are already conserving your energy!"))
+
+//
+// Quirk: Rad Fiend
+//
+
+/datum/action/rad_fiend
+ name = "Broken Rad Action"
+ desc = "Report this to a coder."
+ icon_icon = 'icons/effects/effects.dmi'
+ button_icon_state = "static"
+
+/datum/action/rad_fiend/update_glow
+ name = "Modify Glow"
+ desc = "Change your radioactive glow color."
+ button_icon_state = "blank"
+
+ // Glow color to use
+ var/glow_color = "#39ff14" // Neon green
+
+ // Thickness of glow outline
+ var/glow_range = 2
+
+/datum/action/rad_fiend/update_glow/Grant()
+ . = ..()
+
+ // Define user mob
+ var/mob/living/carbon/human/action_mob = owner
+
+ // Add outline effect
+ action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color+"30", "size" = glow_range))
+
+/datum/action/rad_fiend/update_glow/Remove()
+ . = ..()
+
+ // Define user mob
+ var/mob/living/carbon/human/action_mob = owner
+
+ // Remove glow
+ action_mob.remove_filter("rad_fiend_glow")
+
+/datum/action/rad_fiend/update_glow/Trigger()
+ . = ..()
+
+ // Define user mob
+ var/mob/living/carbon/human/action_mob = owner
+
+ // Ask user for color input
+ var/input_color = input(action_mob, "Select a color to use for your glow outline.", "Select Glow Color", glow_color) as color|null
+
+ // Check if color input was given
+ // Reset to stored color when not given input
+ glow_color = (input_color ? input_color : glow_color)
+
+ // Ask user for range input
+ var/input_range = input(action_mob, "How much do you glow? Value may range between 1 to 2.", "Select Glow Range", glow_range) as num|null
+
+ // Check if range input was given
+ // Reset to stored color when not given input
+ // Input is clamped in the 1-2 range
+ glow_range = (input_range ? clamp(input_range, 1, 2) : glow_range)
+
+ // Update outline effect
+ action_mob.remove_filter("rad_fiend_glow")
+ action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color+"30", "size" = glow_range))
diff --git a/modular_splurt/code/modules/antagonists/wendigo/mob/defines_init.dm b/modular_splurt/code/modules/antagonists/wendigo/mob/defines_init.dm
index 3b2fb09aa3..9cc5b9de67 100644
--- a/modular_splurt/code/modules/antagonists/wendigo/mob/defines_init.dm
+++ b/modular_splurt/code/modules/antagonists/wendigo/mob/defines_init.dm
@@ -37,6 +37,8 @@
var/obj/item/ears_extra = null
var/obj/item/wrists = null
+ var/list/slaves = list() //people enslaved
+
/mob/living/carbon/wendigo/Initialize()
. = ..()
/* //TODO: Uncomment when objectives + forest get finished
@@ -79,6 +81,7 @@
/mob/living/carbon/wendigo/Destroy()
QDEL_NULL(physiology)
+ slaves.Cut()
return ..()
/mob/living/carbon/wendigo/update_body_parts()
@@ -107,8 +110,10 @@
return TRUE
/mob/living/carbon/wendigo/IsAdvancedToolUser()
return TRUE
+/* So they can be pulled.
/mob/living/carbon/wendigo/can_be_pulled()
return FALSE
+*/
/mob/living/carbon/wendigo/is_literate()
return TRUE
/mob/living/carbon/wendigo/canBeHandcuffed()
@@ -122,7 +127,7 @@
//ORGANS
//
/mob/living/carbon/wendigo/create_internal_organs()
- internal_organs += new /obj/item/organ/eyes/wendigo
+ internal_organs += new /obj/item/organ/eyes/night_vision/wendigo
internal_organs += new /obj/item/organ/liver/wendigo
internal_organs += new /obj/item/organ/tongue
@@ -131,3 +136,12 @@
internal_organs += new /obj/item/organ/stomach
internal_organs += new /obj/item/organ/ears
..()
+
+/mob/living/carbon/wendigo/movement_delay()
+ . = ..()
+ var/adding = 0
+ var/turf/T = get_turf(src)
+ if(T.get_lumcount() < 0.2)
+ adding += 1
+ . += adding
+
diff --git a/modular_splurt/code/modules/antagonists/wendigo/mob/organs_bodyparts.dm b/modular_splurt/code/modules/antagonists/wendigo/mob/organs_bodyparts.dm
index 11c74a7573..5fba7e8215 100644
--- a/modular_splurt/code/modules/antagonists/wendigo/mob/organs_bodyparts.dm
+++ b/modular_splurt/code/modules/antagonists/wendigo/mob/organs_bodyparts.dm
@@ -20,7 +20,7 @@
var/mob/living/carbon/wendigo/A = C
A.metabolize_hunger()
-/obj/item/organ/eyes/wendigo
+/obj/item/organ/eyes/night_vision/wendigo
sight_flags = SEE_MOBS
/obj/item/bodypart/head/wendigo
diff --git a/modular_splurt/code/modules/antagonists/wendigo/mob/wendigo_defense.dm b/modular_splurt/code/modules/antagonists/wendigo/mob/wendigo_defense.dm
new file mode 100644
index 0000000000..d6a9a2314e
--- /dev/null
+++ b/modular_splurt/code/modules/antagonists/wendigo/mob/wendigo_defense.dm
@@ -0,0 +1,63 @@
+/mob/living/carbon/wendigo/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
+ if(user.a_intent == INTENT_HARM)
+ . = ..(user, TRUE)
+ if(.)
+ return
+ adjustBruteLoss(15)
+ var/hitverb = "punched"
+ if(mob_size < MOB_SIZE_LARGE)
+ step_away(src,user,15)
+ sleep(1)
+ step_away(src,user,15)
+ hitverb = "slammed"
+ playsound(loc, "punch", 25, 1, -1)
+ visible_message("[user] has [hitverb] [src]!", \
+ "[user] has [hitverb] [src]!", null, COMBAT_MESSAGE_RANGE)
+ return 1
+
+/mob/living/carbon/wendigo/on_attack_hand(mob/living/carbon/human/M)
+ . = ..()
+ if(.) //To allow surgery to return properly.
+ return
+ switch(M.a_intent)
+ if (INTENT_HARM)
+ if(M != pulling && !src.slaves.Find(M))
+ var/damage = rand(1, 9)
+ if (prob(90))
+ playsound(loc, "punch", 25, 1, -1)
+ visible_message("[M] has punched [src]!", \
+ "[M] has punched [src]!", null, COMBAT_MESSAGE_RANGE)
+ var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
+ apply_damage(damage, BRUTE, affecting)
+ log_combat(M, src, "attacked")
+ M.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
+ else
+ playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
+ visible_message("[M] has attempted to punch [src]!", \
+ "[M] has attempted to punch [src]!", null, COMBAT_MESSAGE_RANGE)
+
+ if (INTENT_DISARM)
+ if (!lying && M != pulling && !src.slaves.Find(M))
+ M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
+ if (prob(5))
+ Unconscious(40)
+ playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
+ log_combat(M, src, "pushed")
+ visible_message("[M] has pushed down [src]!", \
+ "[M] has pushed down [src]!")
+ else
+ if (prob(50))
+ dropItemToGround(get_active_held_item())
+ playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
+ visible_message("[M] has disarmed [src]!", \
+ "[M] has disarmed [src]!", null, COMBAT_MESSAGE_RANGE)
+ else
+ playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
+ visible_message("[M] has attempted to disarm [src]!",\
+ "[M] has attempted to disarm [src]!", null, COMBAT_MESSAGE_RANGE)
+
+/mob/living/carbon/wendigo/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
+ if(!no_effect && !visual_effect_icon)
+ visual_effect_icon = ATTACK_EFFECT_CLAW
+ ..()
+
diff --git a/modular_splurt/code/modules/arousal/arousal.dm b/modular_splurt/code/modules/arousal/arousal.dm
index b60b8e95e1..2fb47ccef0 100644
--- a/modular_splurt/code/modules/arousal/arousal.dm
+++ b/modular_splurt/code/modules/arousal/arousal.dm
@@ -1,6 +1,11 @@
/mob/living/carbon/human/mob_climax_partner(obj/item/organ/genital/G, mob/living/L, spillage, mb_time, obj/item/organ/genital/Lgen, forced = FALSE)
. = ..()
L.receive_climax(src, Lgen, G, spillage, forced = forced)
+ if(iswendigo(L))
+ var/mob/living/carbon/wendigo/W = L
+ if(W.pulling == src)
+ W.slaves |= src
+ to_chat(src, " You are now [W]'s slave! Serve your master properly! ")
/mob/living/proc/receive_climax(mob/living/partner, obj/item/organ/genital/receiver, obj/item/organ/genital/source, spill, forced)
//gregnancy...
diff --git a/modular_splurt/code/modules/client/loadout/uniform.dm b/modular_splurt/code/modules/client/loadout/uniform.dm
index 1333a8c764..12c14d564d 100644
--- a/modular_splurt/code/modules/client/loadout/uniform.dm
+++ b/modular_splurt/code/modules/client/loadout/uniform.dm
@@ -158,3 +158,15 @@
path = /obj/item/clothing/under/performer/polychromic
loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
loadout_initial_colors = list("#ffffff")
+
+/datum/gear/uniform/stripper/security
+ name = "Security Stripper Outfit"
+ path = /obj/item/clothing/under/rank/security/stripper
+ subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS
+ restricted_roles = list("Security Officer", "Warden", "Head Of Security")
+
+/datum/gear/uniform/stripper/explorer
+ name = "Explorer Stripper Outfit"
+ path = /obj/item/clothing/under/rank/cargo/miner/lavaland/stripper
+ subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS
+ restricted_roles = list("Shaft Miner")
diff --git a/modular_splurt/code/modules/client/preferences.dm b/modular_splurt/code/modules/client/preferences.dm
index 11a849e23c..b7362d542a 100644
--- a/modular_splurt/code/modules/client/preferences.dm
+++ b/modular_splurt/code/modules/client/preferences.dm
@@ -1,11 +1,13 @@
+#define APPEARANCE_CATEGORY_COLUMN "
| "
+ if(jobban_isbanned(user, "appearance"))
+ dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game. " + dat += "Random Name " + dat += "Always Random Name:[be_random_name ? "Yes" : "No"] " + + dat += "[nameless ? "Default designation" : "Name"]:" + dat += "[real_name] " + dat += "Be nameless: [nameless ? "Yes" : "No"] " + + dat += "Gender: [gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))] " + dat += "Age: [age] " + + dat += "Special Names: " + var/old_group + for(var/custom_name_id in GLOB.preferences_custom_names) + var/namedata = GLOB.preferences_custom_names[custom_name_id] + if(!old_group) + old_group = namedata["group"] + else if(old_group != namedata["group"]) + old_group = namedata["group"] + dat += " " + dat += "[namedata["pref_name"]]: [custom_names[custom_name_id]] " + dat += " " + + dat += "Custom job preferences: " + dat += "Preferred AI Core Display: [preferred_ai_core_display] " + dat += "Preferred Security Department: [prefered_security_department] | "
+ dat += "
"
+ dat += "Flavor Text" + dat += "Set Examine Text" + if(length(features["flavor_text"]) <= MAX_FLAVOR_PREVIEW_LEN) + if(!length(features["flavor_text"])) + dat += "\[...\] " //skyrat - adds //come to brazil or brazil comes to you + else + dat += "[html_encode(features["flavor_text"])] " //skyrat - adds and uses html_encode + else + dat += "[TextPreview(html_encode(features["flavor_text"]))]... " //skyrat edit, uses html_encode + //SPLURT edit - naked flavor text + dat += " Naked Flavor Text" + dat += "Set Naked Examine Text" + if(length(features["naked_flavor_text"]) <= 40) + if(!length(features["naked_flavor_text"])) + dat += "\[...\] " + else + dat += "[html_encode(features["naked_flavor_text"])] " + else + dat += "[TextPreview(html_encode(features["naked_flavor_text"]))]... " + //SPLURT edit end + dat += " Silicon Flavor Text" + dat += "Set Silicon Examine Text" + if(length(features["silicon_flavor_text"]) <= MAX_FLAVOR_PREVIEW_LEN) + if(!length(features["silicon_flavor_text"])) + dat += "\[...\] " + else + dat += "[features["silicon_flavor_text"]] " + else + dat += "[TextPreview(features["silicon_flavor_text"])]... " + dat += " OOC notes" + dat += "Set OOC notes" + var/ooc_notes_len = length(features["ooc_notes"]) + if(ooc_notes_len <= MAX_FLAVOR_PREVIEW_LEN) + if(!ooc_notes_len) + dat += "\[...\] " + else + dat += "[features["ooc_notes"]] " + else + dat += "[TextPreview(features["ooc_notes"])]... " + //SPLURT EDIT + dat += " Headshot Image" + dat += "Set Headshot Image" + if(features["headshot_link"]) + dat += " " + //SPLURT EDIT END + //SKYRAT EDIT + dat += "ERP : [erppref] " + dat += "Non-Con : [nonconpref] " + dat += "Vore : [vorepref] " + //END OF SKYRAT EDIT + + dat += " Records" + dat += "Security Records " + if(length_char(security_records) <= 40) + if(!length(security_records)) + dat += "\[...\]" + else + dat += "[security_records]" + else + dat += "[TextPreview(security_records)]... " + + dat += " Medical Records " + if(length_char(medical_records) <= 40) + if(!length(medical_records)) + dat += "\[...\] " + else + dat += "[medical_records]" + else + dat += "[TextPreview(medical_records)]... " + + dat += APPEARANCE_CATEGORY_COLUMN //body moves right sandstorm change + + dat += " Body" + dat += "Gender:[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]" + if(gender != NEUTER && pref_species.sexes) + dat += "Body Model:[features["body_model"] == MALE ? "Masculine" : "Feminine"] " + dat += "Limb Modification: " + dat += "Modify Limbs " + for(var/modification in modified_limbs) + if(modified_limbs[modification][1] == LOADOUT_LIMB_PROSTHETIC) + dat += "[modification]: [modified_limbs[modification][2]] " + else + dat += "[modification]: [modified_limbs[modification][1]] " + dat += " " + dat += "Species:[pref_species.name] " + dat += "Custom Species Name:[custom_species ? custom_species : "None"] " + dat += "Random Body:Randomize! " + dat += "Always Random Body:[be_random_body ? "Yes" : "No"] " + dat += " Cycle background:[bgstate] " + + dat += " | " //body moves right sandstorm change
+
+ var/use_skintones = pref_species.use_skintones
+ if(use_skintones)
+ dat += APPEARANCE_CATEGORY_COLUMN
+
+ dat += "
| [marking_list[2]] - [actual_name] | ˄ ˅ X [color_marking_dat] |
"
+ dat += "Clothing & Equipment" + /* skyrat change + dat += "Underwear:[underwear]" + if(GLOB.underwear_list[underwear]?.has_color) + dat += "Underwear Color: #[undie_color] Change" + dat += "Undershirt:[undershirt]" + if(GLOB.undershirt_list[undershirt]?.has_color) + dat += "Undershirt Color: #[shirt_color] Change " + dat += "Socks:[socks]" + if(GLOB.socks_list[socks]?.has_color) + dat += "Socks Color: #[socks_color] Change " + */ + dat += "Backpack:[backbag]" + dat += "Jumpsuit: [jumpsuit_style] " + if((HAS_FLESH in pref_species.species_traits) || (HAS_BONE in pref_species.species_traits)) + dat += " Temporal Scarring: [(persistent_scars) ? "Enabled" : "Disabled"]" + dat += "Clear scar slots " + dat += "Uplink Location:[uplink_spawn_loc]" + dat += " | "
+
+ dat += ""
+ dat += "Lewd preferences" + dat += "Lust tolerance:[lust_tolerance]" + dat += "Sexual potency:[sexual_potency]" + dat += " | "
+
+ //SPLURT EDIT BEGIN - gregnancy preferences
+ dat += ""
+ dat += "Pregnancy preferences" + dat += "Chance of impregnation:[virility ? virility : "Disabled"]" + dat += "Chance of getting pregnant:[fertility ? fertility : "Disabled"]" + dat += "Lay inert eggs:[features["inert_eggs"] == TRUE ? "Enabled" : "Disabled"]" + if(fertility) + dat += "Pregnancy inflation:[pregnancy_inflation ? "Enabled" : "Disabled"]" + dat += "Pregnancy breast growth:[pregnancy_breast_growth ? "Enabled" : "Disabled"]" + if(fertility || features["inert_eggs"]) + dat += "Egg shell:[egg_shell]" + dat += " | "
+ //SPLURT EDIT END
+ dat += APPEARANCE_CATEGORY_COLUMN
+ if(NOGENITALS in pref_species.species_traits)
+ dat += "Your species ([pref_species.name]) does not support genitals!
"
+ dat += "Speech preferences" + dat += "Custom Speech Verb:" + dat += "[custom_speech_verb] " + dat += "Custom Tongue: " + dat += "[custom_tongue] " + //SANDSTORM EDIT - additional language + runechat color + dat += "Additional Language " + var/list/languages_sorted = sortList(language) + dat += "[language.len ? languages_sorted.Join(", ") : "None"] " + dat += "Custom runechat color: [enable_personal_chat_color ? "Enabled" : "Disabled"] [enable_personal_chat_color ? "[personal_chat_color] Change" : ""] " + dat += " | "
+ //END OF SANDSTORM EDIT
+ dat += ""
+ dat += "Vocal Bark preferences" + var/datum/bark/B = GLOB.bark_list[bark_id] + dat += "Vocal Bark Sound:" + dat += "[B ? initial(B.name) : "INVALID"] " + dat += "Vocal Bark Speed: [bark_speed] " + dat += "Vocal Bark Pitch: [bark_pitch] " + dat += "Vocal Bark Variance: [bark_variance] " + dat += " Preview Bark " + dat += " | "
+ dat += "
"
+ dat += "General Settings" + dat += "UI Style: [UI_style]" + dat += "Outline: [outline_enabled ? "Enabled" : "Disabled"] " + dat += "Outline Color: [outline_color ? "" : "Theme-based (null)"][outline_color] Change " + dat += "Screentip: [screentip_pref] " + dat += "Screentip Color: [screentip_color] Change " + dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"] " + dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"] " + dat += "Input Framework: [(tgui_input_mode) ? "tgui" : "BYOND"] " + dat += "tgui Button Size: [(tgui_large_buttons) ? "Large" : "Small"] " + dat += "tgui Buttons Swapped: [(tgui_swapped_buttons) ? "Yes" : "No"] " + dat += "Show Runechat Chat Bubbles: [chat_on_map ? "Enabled" : "Disabled"] " + dat += "Runechat message char limit: [max_chat_length] " + dat += "See Runechat for non-mobs: [see_chat_non_mob ? "Enabled" : "Disabled"] " + //SKYRAT CHANGES BEGIN + dat += "See Runechat for emotes: [see_chat_emotes ? "Enabled" : "Disabled"] " + //SKYRAT CHANGES END + dat += " " + dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"] " + dat += " " + dat += "PDA Color: [pda_color] Change " + dat += "PDA Style: [pda_style] " + dat += "PDA Reskin: [pda_skin] " + dat += " " + dat += "Ghost Ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"] " + dat += "Ghost Radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "All Messages":"No Messages"] " + dat += "Ghost Sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"] " + dat += "Ghost Whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "All Speech" : "Nearest Creatures"] " + dat += "Ghost PDA: [(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"] " + dat += "Window Flashing: [(windowflashing) ? "Enabled":"Disabled"] " + dat += " " + dat += "Play Admin MIDIs: [(toggles & SOUND_MIDI) ? "Enabled":"Disabled"] " + dat += "Play Lobby Music: [(toggles & SOUND_LOBBY) ? "Enabled":"Disabled"] " + dat += "See Pull Requests: [(chat_toggles & CHAT_PULLR) ? "Enabled":"Disabled"] " + dat += " " + if(user.client) + if(unlock_content) + dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"] " + if(unlock_content || check_rights_for(user.client, R_ADMIN)) + dat += "OOC Color: [ooccolor ? ooccolor : GLOB.normal_ooc_colour] Change " + dat += "Antag OOC Color: [aooccolor ? aooccolor : GLOB.normal_aooc_colour] Change " + + dat += " | "
+
+ dat += ""
+
+ dat += "Special Role Settings" + + if(jobban_isbanned(user, ROLE_SYNDICATE)) + dat += "You are banned from antagonist roles." + src.be_special = list() + + dat += "DISABLE ALL ANTAGONISM [(toggles & NO_ANTAG) ? "YES" : "NO"]" + + for (var/i in GLOB.special_roles) + if(jobban_isbanned(user, i)) + dat += "Be [capitalize(i)]: BANNED " + else + var/days_remaining = null + if(ispath(GLOB.special_roles[i]) && CONFIG_GET(flag/use_age_restriction_for_jobs)) //If it's a game mode antag, check if the player meets the minimum age + var/mode_path = GLOB.special_roles[i] + var/datum/game_mode/temp_mode = new mode_path + days_remaining = temp_mode.get_remaining_days(user.client) + + if(days_remaining) + dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS] " + else + var/enabled_text = "" + if(i in be_special) + if(be_special[i] >= 1) + enabled_text = "Enabled" + else + enabled_text = "Low" + else + enabled_text = "Disabled" + dat += "Be [capitalize(i)]: [enabled_text] " + dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Enabled" : "Disabled"] " + + dat += " " + + dat += " | "
+
+ if(user.client.holder) // sandstorm start - moves admin prefs to the right
+ dat += "Admin Settings" + dat += "Adminhelp Sounds: [(toggles & SOUND_ADMINHELP)?"Enabled":"Disabled"]" + dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"Enabled":"Disabled"] " + dat += " " + dat += "Combo HUD Lighting: [(toggles & COMBOHUD_LIGHTING)?"Full-bright":"No Change"] " + dat += "Use Modern Player Panel: [use_new_playerpanel ? "Yes" : "No"] " // sandstorm end - moves admins prefs to the right + + //deadmin + dat += " Deadmin While Playing" + if(CONFIG_GET(flag/auto_deadmin_players)) + dat += "Always Deadmin: FORCED" + else + dat += "Always Deadmin: [(deadmin & DEADMIN_ALWAYS)?"Enabled":"Disabled"] " + if(!(deadmin & DEADMIN_ALWAYS)) + dat += " " + if(!CONFIG_GET(flag/auto_deadmin_antagonists)) + dat += "As Antag: [(deadmin & DEADMIN_ANTAGONIST)?"Deadmin":"Keep Admin"] " + else + dat += "As Antag: FORCED " + + if(!CONFIG_GET(flag/auto_deadmin_heads)) + dat += "As Command: [(deadmin & DEADMIN_POSITION_HEAD)?"Deadmin":"Keep Admin"] " + else + dat += "As Command: FORCED " + + if(!CONFIG_GET(flag/auto_deadmin_security)) + dat += "As Security: [(deadmin & DEADMIN_POSITION_SECURITY)?"Deadmin":"Keep Admin"] " + else + dat += "As Security: FORCED " + + if(!CONFIG_GET(flag/auto_deadmin_silicons)) + dat += "As Silicon: [(deadmin & DEADMIN_POSITION_SILICON)?"Deadmin":"Keep Admin"] " + else + dat += "As Silicon: FORCED " + + dat += " Citadel Preferences" //Because fuck me if preferences can't be fucking modularized and expected to update in a reasonable timeframe. + dat += "Widescreen: [widescreenpref ? "Enabled ([CONFIG_GET(string/default_view)])" : "Disabled (15x15)"]" + dat += "Long strip menu: [long_strip_menu ? "Enabled" : "Disabled"] " + dat += "Auto stand: [autostand ? "Enabled" : "Disabled"] " + dat += "Auto OOC: [auto_ooc ? "Enabled" : "Disabled"] " + dat += "Force Slot Storage HUD: [no_tetris_storage ? "Enabled" : "Disabled"] " + dat += "Screen Shake: [(screenshake==100) ? "Full" : ((screenshake==0) ? "None" : "[screenshake]")] " + if (user && user.client && !user.client.prefs.screenshake==0) + dat += "Damage Screen Shake: [(damagescreenshake==1) ? "On" : ((damagescreenshake==0) ? "Off" : "Only when down")] " + dat += "Recoil Screen Push: [(recoil_screenshake==100) ? "Full" : ((recoil_screenshake==0) ? "None" : "[screenshake]")] " + var/p_chaos + if (!preferred_chaos) + p_chaos = "No preference" + else + p_chaos = preferred_chaos + dat += "Preferred Chaos Amount: [p_chaos] " + + dat += " S.P.L.U.R.T. Preferences" + dat += "Be Antagonist Victim: [be_victim ? be_victim : BEVICTIM_ASK]" + dat += "Disable combat mode cursor: [disable_combat_cursor?"Yes":"No"] " + dat += "Splashscreen Player Panel Style: [(toggles & TG_PLAYER_PANEL)?"TG":"Old"] " + dat += "Character Creation Menu Style: [new_character_creator ? "New" : "Old"] " + dat += " " + + dat += " | "
+ dat += "
| Name | " + dat += "Cost | " + dat += "Restrictions | " + dat += "Description |
| [name][extra_loadout_data] | " + dat += "[gear.cost] | " + if(islist(gear.restricted_roles)) + if(gear.restricted_roles.len) + if(gear.restricted_desc) + dat += "" + dat += gear.restricted_desc + dat += "" + else + dat += "" + dat += gear.restricted_roles.Join(";") + dat += "" + if(!istype(gear, /datum/gear/unlockable)) + // the below line essentially means "if the loadout item is picked by the user and has a custom description, give it the custom description, otherwise give it the default description" + dat += " | [loadout_item ? (loadout_item[LOADOUT_CUSTOM_DESCRIPTION] ? loadout_item[LOADOUT_CUSTOM_DESCRIPTION] : gear.description) : gear.description] | [loadout_item ? (loadout_item[LOADOUT_CUSTOM_DESCRIPTION] ? loadout_item[LOADOUT_CUSTOM_DESCRIPTION] : gear.description) : gear.description] Progress: [min(progress_made, unlockable.progress_required)]/[unlockable.progress_required] | " + + dat += "
"
+ dat += "Fetish content prefs" + dat += "Allow Lewd Verbs: [(toggles & VERB_CONSENT) ? "Yes":"No"]" // Skyrat - ERP Mechanic Addition + dat += "Mute Lewd Verb Sounds: [(toggles & LEWD_VERB_SOUNDS) ? "Yes":"No"] " // Skyrat - ERP Mechanic Addition + dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"] " + dat += "Genital examine text:[(cit_toggles & GENITAL_EXAMINE) ? "Enabled" : "Disabled"] " + dat += "Vore examine text:[(cit_toggles & VORE_EXAMINE) ? "Enabled" : "Disabled"] " + dat += "Voracious MediHound sleepers: [(cit_toggles & MEDIHOUND_SLEEPER) ? "Yes" : "No"] " + dat += "Hear Vore Sounds: [(cit_toggles & EATING_NOISES) ? "Yes" : "No"] " + dat += "Hear Vore Digestion Sounds: [(cit_toggles & DIGESTION_NOISES) ? "Yes" : "No"] " + dat += "Allow trash forcefeeding (requires Trashcan quirk) [(cit_toggles & TRASH_FORCEFEED) ? "Yes" : "No"] " + dat += "Forced Feminization: [(cit_toggles & FORCED_FEM) ? "Allowed" : "Disallowed"] " + dat += "Forced Masculinization: [(cit_toggles & FORCED_MASC) ? "Allowed" : "Disallowed"] " + dat += "Lewd Hypno: [(cit_toggles & HYPNO) ? "Allowed" : "Disallowed"] " + dat += "Bimbofication: [(cit_toggles & BIMBOFICATION) ? "Allowed" : "Disallowed"] " + dat += " | "
+ dat +=""
+ dat += "Other content prefs" + dat += "Breast Enlargement: [(cit_toggles & BREAST_ENLARGEMENT) ? "Allowed" : "Disallowed"]" + dat += "Penis Enlargement: [(cit_toggles & PENIS_ENLARGEMENT) ? "Allowed" : "Disallowed"] " + dat += "Butt Enlargement: [(cit_toggles & BUTT_ENLARGEMENT) ? "Allowed" : "Disallowed"] " + dat += "Belly Inflation: [(cit_toggles & BELLY_INFLATION) ? "Allowed" : "Disallowed"] " + dat += "Hypno: [(cit_toggles & NEVER_HYPNO) ? "Disallowed" : "Allowed"] " + dat += "Aphrodisiacs: [(cit_toggles & NO_APHRO) ? "Disallowed" : "Allowed"] " + dat += "Ass Slapping: [(cit_toggles & NO_ASS_SLAP) ? "Disallowed" : "Allowed"] " + //SPLURT EDIT + dat += "? " + dat += "Unholy ERP verbs : [unholypref] " //https://www.youtube.com/watch?v=OHKARc-GObU + //END OF SPLURT EDIT + //SKYRAT EDIT + dat += "? " + dat += "Extreme ERP verbs : [extremepref] " // https://youtu.be/0YrU9ASVw6w + if(extremepref != "No") + dat += "? " + dat += "Harmful ERP verbs : [extremeharm] " + //END OF SKYRAT EDIT + dat += "Automatic Wagging: [(cit_toggles & NO_AUTO_WAG) ? "Disabled" : "Enabled"] " + dat += "? " + dat += "Genital Fluid Blacklist " + if(gfluid_blacklist?.len) + dat += "? " + dat += "Genital Fluid Un-Blacklist " + dat += " |