diff --git a/code/datums/elements/cleaning.dm b/code/datums/elements/cleaning.dm
index fd48f582dd17..21ea954f2122 100644
--- a/code/datums/elements/cleaning.dm
+++ b/code/datums/elements/cleaning.dm
@@ -1,66 +1,73 @@
-/datum/element/cleaning/Attach(datum/target)
+/datum/element/cleaning
+ element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
+ id_arg_index = 2
+ /// Range of cleaning on moving
+ var/range = 0
+
+/datum/element/cleaning/Attach(atom/movable/cleaner, cleaning_range)
. = ..()
- if(!ismovable(target))
+ if(!istype(cleaner))
return ELEMENT_INCOMPATIBLE
- RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(Clean))
+ if(cleaning_range)
+ range = cleaning_range
+ RegisterSignal(cleaner, COMSIG_MOVABLE_MOVED, PROC_REF(Clean))
-/datum/element/cleaning/Detach(datum/target)
+/datum/element/cleaning/Detach(atom/movable/cleaner)
. = ..()
- UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(cleaner, COMSIG_MOVABLE_MOVED)
-/datum/element/cleaning/proc/Clean(datum/source)
- var/atom/movable/AM = source
- var/turf/T = AM.loc
- SEND_SIGNAL(T, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
- for(var/A in T)
- if(is_cleanable(A))
- qdel(A)
- else if(isitem(A))
- var/obj/item/cleaned_item = A
- SEND_SIGNAL(cleaned_item, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
- cleaned_item.clean_blood()
- if(ismob(cleaned_item.loc))
- var/mob/M = cleaned_item.loc
- M.regenerate_icons()
- else if(ishuman(A))
- var/mob/living/carbon/human/cleaned_human = A
- if(cleaned_human.lying)
- if(cleaned_human.head)
- SEND_SIGNAL(cleaned_human.head, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
- cleaned_human.head.clean_blood()
- cleaned_human.update_inv_head()
- if(cleaned_human.wear_suit)
- SEND_SIGNAL(cleaned_human.wear_suit, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
- cleaned_human.wear_suit.clean_blood()
- cleaned_human.update_inv_wear_suit()
- else if(cleaned_human.w_uniform)
- SEND_SIGNAL(cleaned_human.w_uniform, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
- cleaned_human.w_uniform.clean_blood()
- cleaned_human.update_inv_w_uniform()
- //skyrat edit
- else if(cleaned_human.w_underwear)
- SEND_SIGNAL(cleaned_human.w_underwear, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
- cleaned_human.w_underwear.clean_blood()
- cleaned_human.update_inv_w_underwear()
- else if(cleaned_human.w_socks)
- SEND_SIGNAL(cleaned_human.w_socks, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
- cleaned_human.w_socks.clean_blood()
- cleaned_human.update_inv_w_socks()
- else if(cleaned_human.w_shirt)
- SEND_SIGNAL(cleaned_human.w_shirt, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
- cleaned_human.w_shirt.clean_blood()
- cleaned_human.update_inv_w_shirt()
- else if(cleaned_human.wrists)
- SEND_SIGNAL(cleaned_human.wrists, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
- cleaned_human.w_shirt.clean_blood()
- cleaned_human.update_inv_wrists()
- //
- if(cleaned_human.shoes)
- SEND_SIGNAL(cleaned_human.shoes, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
- cleaned_human.shoes.clean_blood()
- cleaned_human.update_inv_shoes()
- SEND_SIGNAL(cleaned_human, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
- cleaned_human.clean_blood()
- cleaned_human.wash_cream()
- cleaned_human.regenerate_icons()
- to_chat(cleaned_human, "[AM] cleans your face!")
+/datum/element/cleaning/proc/Clean(atom/movable/cleaner)
+ for (var/turf/turf in RANGE_TURFS(range, cleaner.loc))
+ SEND_SIGNAL(turf, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
+ for(var/atom/atom as anything in turf)
+ if(is_cleanable(atom))
+ qdel(atom)
+ else if(isitem(atom))
+ var/obj/item/cleaned_item = atom
+ SEND_SIGNAL(cleaned_item, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
+ cleaned_item.clean_blood()
+ if(ismob(cleaned_item.loc))
+ var/mob/mob = cleaned_item.loc
+ mob.regenerate_icons()
+ else if(ishuman(atom))
+ var/mob/living/carbon/human/cleaned_human = atom
+ if(cleaned_human.lying)
+ if(cleaned_human.head)
+ SEND_SIGNAL(cleaned_human.head, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
+ cleaned_human.head.clean_blood()
+ cleaned_human.update_inv_head()
+ if(cleaned_human.wear_suit)
+ SEND_SIGNAL(cleaned_human.wear_suit, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
+ cleaned_human.wear_suit.clean_blood()
+ cleaned_human.update_inv_wear_suit()
+ else if(cleaned_human.w_uniform)
+ SEND_SIGNAL(cleaned_human.w_uniform, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
+ cleaned_human.w_uniform.clean_blood()
+ cleaned_human.update_inv_w_uniform()
+ //skyrat edit
+ else if(cleaned_human.w_underwear)
+ SEND_SIGNAL(cleaned_human.w_underwear, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
+ cleaned_human.w_underwear.clean_blood()
+ cleaned_human.update_inv_w_underwear()
+ else if(cleaned_human.w_socks)
+ SEND_SIGNAL(cleaned_human.w_socks, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
+ cleaned_human.w_socks.clean_blood()
+ cleaned_human.update_inv_w_socks()
+ else if(cleaned_human.w_shirt)
+ SEND_SIGNAL(cleaned_human.w_shirt, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
+ cleaned_human.w_shirt.clean_blood()
+ cleaned_human.update_inv_w_shirt()
+ else if(cleaned_human.wrists)
+ SEND_SIGNAL(cleaned_human.wrists, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
+ cleaned_human.w_shirt.clean_blood()
+ cleaned_human.update_inv_wrists()
+ //
+ if(cleaned_human.shoes)
+ SEND_SIGNAL(cleaned_human.shoes, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
+ cleaned_human.shoes.clean_blood()
+ cleaned_human.update_inv_shoes()
+ SEND_SIGNAL(cleaned_human, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
+ cleaned_human.clean_blood()
+ cleaned_human.wash_cream()
+ cleaned_human.regenerate_icons()
+ to_chat(cleaned_human, span_danger("[cleaner] cleans your face!"))
diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm
index a52bbb75f35f..d2fb3172de9a 100644
--- a/code/game/objects/items/cigs_lighters.dm
+++ b/code/game/objects/items/cigs_lighters.dm
@@ -560,6 +560,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
. += lighter_overlay
/obj/item/lighter/ignition_effect(atom/A, mob/user)
+ playsound(src, 'modular_sand/sound/items/lighter/light.ogg', 50, 0)
if(get_temperature())
. = "With a single flick of [user.p_their()] wrist, [user] smoothly lights [A] with [src]. Damn [user.p_theyre()] cool."
@@ -583,6 +584,9 @@ CIGARETTE PACKETS ARE IN FANCY.DM
/obj/item/lighter/attack_self(mob/living/user)
if(user.is_holding(src))
if(!lit)
+ // SANDSTORM EDIT
+ playsound(src, 'modular_sand/sound/items/lighter/open.ogg', 50, 0)
+ // End of edit
set_lit(TRUE)
if(fancy)
user.visible_message("Without even breaking stride, [user] flips open and lights [src] in one smooth movement.", "Without even breaking stride, you flip open and light [src] in one smooth movement.")
@@ -606,6 +610,9 @@ CIGARETTE PACKETS ARE IN FANCY.DM
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "burnt_thumb", /datum/mood_event/burnt_thumb)
else
+ // SANDSTORM EDIT
+ playsound(src, 'modular_sand/sound/items/lighter/close.ogg', 50, 0)
+ // Edit end
set_lit(FALSE)
if(fancy)
user.visible_message("You hear a quiet click, as [user] shuts off [src] without even looking at what [user.p_theyre()] doing. Wow.", "You quietly shut off [src] without even looking at what you're doing. Wow.")
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index d6a35116d4f2..c2505565ac71 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1650,7 +1650,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.Jitter(5)
hunger_rate = 3 * HUNGER_FACTOR
hunger_rate *= H.physiology.hunger_mod
- H.adjust_nutrition(-hunger_rate)
+
+ // SANDSTORM EDIT
+ if (H.client)
+ H.adjust_nutrition(-hunger_rate)
+ else
+ // Do not allow SSD players to get too hungry.
+ if (H.nutrition >= NUTRITION_LEVEL_FED)
+ H.adjust_nutrition(-hunger_rate)
+ // End of sandstorm edit
if (H.nutrition > NUTRITION_LEVEL_FULL)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 4eff3fab6779..e4b86ff584b0 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -926,7 +926,7 @@
status_flags &= ~CANPUSH
if(module.clean_on_move)
- AddElement(/datum/element/cleaning)
+ AddElement(/datum/element/cleaning, cleaning_range = 1)
else
RemoveElement(/datum/element/cleaning)
@@ -1196,8 +1196,8 @@
var/mob/unbuckle_me_now = i
unbuckle_mob(unbuckle_me_now, FALSE)
-/mob/living/silicon/robot/proc/TryConnectToAI()
- set_connected_ai(select_active_ai_with_fewest_borgs(z))
+/mob/living/silicon/robot/proc/TryConnectToAI(mob/living/silicon/ai/connect_to)
+ set_connected_ai(connect_to || select_active_ai_with_fewest_borgs(z))
if(connected_ai)
lawsync()
lawupdate = TRUE
diff --git a/config/config.txt b/config/config.txt
index f9a588e63ad0..e491231d0730 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -40,9 +40,9 @@ $include entries/urls.txt
$include entries/vote.txt
$include plushies/defines.txt
-# Special Sandstorm configs
-$include sandstorm/config.txt
-$include sandstorm/balance.txt
+
+# Sandstorm configs
+$include sandstorm/includes.txt
# Special SPLURT configs
$include splurt/fetish_content.txt
diff --git a/config/sandstorm/includes.txt b/config/sandstorm/includes.txt
new file mode 100644
index 000000000000..adf290da66dc
--- /dev/null
+++ b/config/sandstorm/includes.txt
@@ -0,0 +1,4 @@
+# This file is mostly so main config file relays to here, and this one relays to the rest
+$include sandstorm/balance.txt
+$include sandstorm/config.txt
+$include sandstorm/qol.txt
diff --git a/config/sandstorm/qol.txt b/config/sandstorm/qol.txt
new file mode 100644
index 000000000000..4f2b082bd25c
--- /dev/null
+++ b/config/sandstorm/qol.txt
@@ -0,0 +1,7 @@
+###
+## AI
+###
+
+## How many shells would an AI gain at roundstart
+## Comment to disable
+ROUNDSTART_AI_SHELLS 1
diff --git a/html/changelogs/archive/2024-11.yml b/html/changelogs/archive/2024-11.yml
index a0ebfb0ce902..98c160e85c7e 100644
--- a/html/changelogs/archive/2024-11.yml
+++ b/html/changelogs/archive/2024-11.yml
@@ -21,3 +21,9 @@
2024-11-27:
Mosley:
- bugfix: Fixes the rest of the announcement systems from the update
+2024-11-28:
+ xTheLifex:
+ - rscadd: AIs will now spawn with a AI Shell
+ - tweak: SSD Players will no longer decrease hunger below normal stats.
+ - soundadd: Added sounds to lighters
+ - code_imp: Cleaning element now supports a range variable.
diff --git a/modular_sand/code/controllers/configuration/entries/sandstorm.dm b/modular_sand/code/controllers/configuration/entries/sandstorm.dm
index 155cfb32c52b..b8461801e454 100644
--- a/modular_sand/code/controllers/configuration/entries/sandstorm.dm
+++ b/modular_sand/code/controllers/configuration/entries/sandstorm.dm
@@ -28,3 +28,6 @@
value_mode = VALUE_MODE_TYPE
splitter = " | "
lowercase_key = FALSE
+
+/datum/config_entry/number/roundstart_ai_shells // Per AI, if you ever do get a triumvirate!
+ min_val = 0
diff --git a/modular_sand/code/modules/jobs/job_types/ai.dm b/modular_sand/code/modules/jobs/job_types/ai.dm
index 5f80699ea7fe..df0f3ff8429a 100644
--- a/modular_sand/code/modules/jobs/job_types/ai.dm
+++ b/modular_sand/code/modules/jobs/job_types/ai.dm
@@ -1,6 +1,21 @@
/datum/job/ai/after_spawn(mob/living/silicon/ai/AI, client/player_client, latejoin)
. = ..()
- if(!istype(AI))
+ setup_silicon_law_prefs(AI, player_client)
+
+ if(latejoin)
return
- setup_silicon_law_prefs(AI, player_client)
+ var/free_shells = CONFIG_GET(number/roundstart_ai_shells)
+ if(!free_shells)
+ return
+
+ var/turf/open/turf = locate() in RANGE_TURFS(1, AI)
+
+ // Why the fuck is the station AI completely blocked
+ var/turf/where = turf || get_turf(AI)
+
+ // Creating AI shells for the AI.
+ for(var/iteration in 1 to free_shells)
+ var/mob/living/silicon/robot/free_borg = new(where)
+ free_borg.shell = TRUE
+ free_borg.TryConnectToAI(AI)
diff --git a/modular_sand/code/modules/mob/living/carbon/human/species.dm b/modular_sand/code/modules/mob/living/carbon/human/species.dm
index e40880f1285f..6f3ba0c8a5c8 100644
--- a/modular_sand/code/modules/mob/living/carbon/human/species.dm
+++ b/modular_sand/code/modules/mob/living/carbon/human/species.dm
@@ -3,7 +3,12 @@
return
//Put more things here if you plan on adding more things. I know this proc is a bit empty at the moment
- H.adjust_thirst(-THIRST_FACTOR)
+ if (H.client)
+ H.adjust_thirst(-THIRST_FACTOR)
+ else
+ // Do not allow SSD players to too thirsty.
+ if (H.thirst >= THIRST_LEVEL_QUENCHED)
+ H.adjust_thirst(-THIRST_FACTOR)
/* switch(get_thirst(src))
if(THIRST_LEVEL_THIRSTY to INFINITY)
diff --git a/modular_sand/sound/items/lighter/close.ogg b/modular_sand/sound/items/lighter/close.ogg
new file mode 100644
index 000000000000..2435711fa75f
Binary files /dev/null and b/modular_sand/sound/items/lighter/close.ogg differ
diff --git a/modular_sand/sound/items/lighter/light.ogg b/modular_sand/sound/items/lighter/light.ogg
new file mode 100644
index 000000000000..9ca5270bc794
Binary files /dev/null and b/modular_sand/sound/items/lighter/light.ogg differ
diff --git a/modular_sand/sound/items/lighter/open.ogg b/modular_sand/sound/items/lighter/open.ogg
new file mode 100644
index 000000000000..79b2c00a6150
Binary files /dev/null and b/modular_sand/sound/items/lighter/open.ogg differ