diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm
new file mode 100644
index 0000000000..cc6e5bd49a
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm
@@ -0,0 +1,3 @@
+/// Called from /mob/living/PushAM -- Called when this mob is about to push a movable, but before it moves
+/// (aotm/movable/being_pushed)
+#define COMSIG_LIVING_PUSHING_MOVABLE "living_pushing_movable"
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
new file mode 100644
index 0000000000..5d2a471c0a
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
@@ -0,0 +1,2 @@
+///From base of mob/living/MobBump() (mob/living)
+#define COMSIG_LIVING_MOB_BUMP "living_mob_bump"
diff --git a/code/__DEFINES/movement.dm b/code/__DEFINES/movement.dm
index 5bf7de8647..bccbd425dd 100644
--- a/code/__DEFINES/movement.dm
+++ b/code/__DEFINES/movement.dm
@@ -13,7 +13,7 @@ GLOBAL_VAR_INIT(glide_size_multiplier, 1.0)
/// Then that's multiplied by the global glide size multiplier. 1.25 by default feels pretty close to spot on. This is just to try to get byond to behave.
/// The whole result is then clamped to within the range above.
/// Not very readable but it works
-#define DELAY_TO_GLIDE_SIZE(delay) (clamp(((32 / max((delay) / world.tick_lag, 1)) * GLOB.glide_size_multiplier), MIN_GLIDE_SIZE, MAX_GLIDE_SIZE))
+#define DELAY_TO_GLIDE_SIZE(delay) (clamp(((world.icon_size / max((delay) / world.tick_lag, 1)) * GLOB.glide_size_multiplier), MIN_GLIDE_SIZE, MAX_GLIDE_SIZE))
/// Enables smooth movement
// #define SMOOTH_MOVEMENT
diff --git a/code/__HELPERS/matrices/transform_matrix.dm b/code/__HELPERS/matrices/transform_matrix.dm
index 251ef8151f..8f8a67535e 100644
--- a/code/__HELPERS/matrices/transform_matrix.dm
+++ b/code/__HELPERS/matrices/transform_matrix.dm
@@ -19,7 +19,7 @@
speed /= segments
if(parallel)
- animate(src, transform = matrices[1], time = speed, loops , flags = ANIMATION_PARALLEL)
+ animate(src, transform = matrices[1], time = speed, loops, flags = ANIMATION_PARALLEL)
else
animate(src, transform = matrices[1], time = speed, loops)
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 2771a3270b..0a9e68a790 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -379,6 +379,8 @@
parts += "[FOURSPACES]Shift Duration: [DisplayTimeText(world.time - SSticker.round_start_time)]"
parts += "[FOURSPACES]Station Integrity: [mode.station_was_nuked ? "Destroyed" : "[popcount["station_integrity"]]%"]"
+ if(mode.station_was_nuked && SSevents.holidays && SSevents.holidays[PRIDE_MONTH])
+ parts += "[FOURSPACES]Gender revealed: [pick(500; "Male", 500; "Female", "Bigender", "Agender", "Demiboy", "Demigirl", "Genderfluid", "Pangender", "Xenogender", "Clown", 50; "What", 50; "Oh no.", 50; "Excuse me?")]"
var/total_players = GLOB.joined_player_list.len
if(total_players)
parts+= "[FOURSPACES]Total Population: [total_players]"
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index b415b4720f..428784e953 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1176,25 +1176,22 @@ GLOBAL_REAL_VAR(list/stack_trace_storage)
var/initialpixely = pixel_y
var/shiftx = rand(-pixelshiftx,pixelshiftx)
var/shifty = rand(-pixelshifty,pixelshifty)
- animate(src, pixel_x = pixel_x + shiftx, pixel_y = pixel_y + shifty, time = 0.2, loop = duration)
- pixel_x = initialpixelx
- pixel_y = initialpixely
+ animate(src, pixel_x = shiftx, pixel_y = shifty, time = 0.2, loop = duration, flags = ANIMATION_RELATIVE)
+ animate(pixel_x = initialpixelx, pixel_y = initialpixely, time = 0.2)
-/atom/proc/do_jiggle(targetangle = 45, timer = 20)
+/atom/proc/do_jiggle(targetangle = 25, timer = 20)
var/matrix/OM = matrix(transform)
var/matrix/M = matrix(transform)
- var/halftime = timer * 0.5
M.Turn(pick(-targetangle, targetangle))
- animate(src, transform = M, time = halftime, easing = ELASTIC_EASING)
- animate(src, transform = OM, time = halftime, easing = ELASTIC_EASING)
+ animate(src, transform = M, time = timer * 0.1, easing = BACK_EASING | EASE_IN)
+ animate(transform = OM, time = timer * 0.4, easing = ELASTIC_EASING)
/atom/proc/do_squish(squishx = 1.2, squishy = 0.6, timer = 20)
var/matrix/OM = matrix(transform)
var/matrix/M = matrix(transform)
- var/halftime = timer * 0.5
M.Scale(squishx, squishy)
- animate(src, transform = M, time = halftime, easing = BOUNCE_EASING)
- animate(src, transform = OM, time = halftime, easing = BOUNCE_EASING)
+ animate(src, transform = M, time = timer * 0.5, easing = ELASTIC_EASING)
+ animate(transform = OM, time = timer * 0.5, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL)
/proc/weightclass2text(var/w_class)
switch(w_class)
diff --git a/code/__SPLURTCODE/DEFINES/cooldowns.dm b/code/__SPLURTCODE/DEFINES/cooldowns.dm
new file mode 100644
index 0000000000..2979db3592
--- /dev/null
+++ b/code/__SPLURTCODE/DEFINES/cooldowns.dm
@@ -0,0 +1,10 @@
+/*
+
+ Read code\__DEFINES\cooldowns.dm
+
+*/
+
+//INDEXES
+#define COOLDOWN_DOMINANT_SNAP "dominant_snap"
+#define COOLDOWN_DOMINANT_EXAMINE "dominant_examine"
+#define COOLDOWN_EMOTE_FART "emote_fart"
diff --git a/code/__SPLURTCODE/DEFINES/dcs/signals.dm b/code/__SPLURTCODE/DEFINES/dcs/signals.dm
new file mode 100644
index 0000000000..4b4f351e72
--- /dev/null
+++ b/code/__SPLURTCODE/DEFINES/dcs/signals.dm
@@ -0,0 +1,6 @@
+#define COMSIG_MOB_EMOTE "mob_emote" // from /mob/emote(): (proc args list)
+ // used to access COMSIG_MOB_SAY argslist
+ #define EMOTE_ACT 1
+ #define EMOTE_MTYPE 2
+ #define EMOTE_MESSAGE 3
+ #define EMOTE_INTENTIONAL 4
diff --git a/code/__SPLURTCODE/DEFINES/quirks.dm b/code/__SPLURTCODE/DEFINES/quirks.dm
new file mode 100644
index 0000000000..9353f87dc7
--- /dev/null
+++ b/code/__SPLURTCODE/DEFINES/quirks.dm
@@ -0,0 +1,3 @@
+//Dominant/Well-trained quirks
+#define DOMINANT_DETECT_RANGE 5
+#define DOMINANT_SNAP_COOLDOWN 10 SECONDS
diff --git a/code/__SPLURTCODE/DEFINES/spans.dm b/code/__SPLURTCODE/DEFINES/spans.dm
index 9e004d3972..d3a5934d3b 100644
--- a/code/__SPLURTCODE/DEFINES/spans.dm
+++ b/code/__SPLURTCODE/DEFINES/spans.dm
@@ -1,2 +1,3 @@
#define span_userlove(str) ("" + str + "")
#define span_love(str) ("" + str + "")
+#define span_lewd(str) ("" + str + "")
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index 692f2317ef..d3d0fb32b6 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -60,7 +60,7 @@
if(client && hud_used)
hud_used.reorganize_alerts()
thealert.transform = matrix(32, 6, MATRIX_TRANSLATE)
- animate(thealert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
+ animate(thealert, transform = matrix(), time = 2.5, easing = BACK_EASING)
if(thealert.timeout)
addtimer(CALLBACK(src, .proc/alert_timeout, thealert, category), thealert.timeout)
diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm
index ce581685ed..8f237c4a71 100644
--- a/code/_onclick/hud/radial.dm
+++ b/code/_onclick/hud/radial.dm
@@ -178,7 +178,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
starting.Scale(0.1,0.1)
E.transform = starting
var/matrix/TM = matrix()
- animate(E,pixel_x = px,pixel_y = py, transform = TM, time = timing)
+ animate(E,pixel_x = px,pixel_y = py, transform = TM, time = timing, easing = SINE_EASING | EASE_OUT)
else
E.pixel_y = py
E.pixel_x = px
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 8f3a7ef02c..23486cac29 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -126,6 +126,8 @@
return DISCARD_LAST_ACTION
user.do_attack_animation(O)
O.attacked_by(src, user)
+ if(force >= 20)
+ shake_camera(user, ((force - 15) * 0.01 + 1), ((force - 15) * 0.01))
/atom/movable/proc/attacked_by()
return
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index 1a5d54a306..79b3010b85 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -320,11 +320,11 @@ SUBSYSTEM_DEF(vote)
admintext += "\nIt should be noted that this is not a raw tally of votes but rather the median score plus a tiebreaker!"
for(var/i=1,i<=choices.len,i++)
var/votes = choices[choices[i]]
- admintext += "\n[choices[i]]: [votes]"
+ admintext += "\n[choices[i]]: [votes ? votes : "0"]" //This is raw data, but the raw data is null by default. If ya don't compensate for it, then it'll look weird!
else
for(var/i=1,i<=scores.len,i++)
var/score = scores[scores[i]]
- admintext += "\n[scores[i]]: [score]"
+ admintext += "\n[scores[i]]: [score ? score : "0"]"
message_admins(admintext)
return .
diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm
index 5eb89f4c10..f4a979c510 100644
--- a/code/datums/chatmessage.dm
+++ b/code/datums/chatmessage.dm
@@ -202,6 +202,7 @@
message.maptext_height = mheight
message.maptext_x = (CHAT_MESSAGE_WIDTH - owner.bound_width) * -0.5
message.maptext = MAPTEXT(complete_text)
+ message.pixel_x = -owner.pixel_x //Dogborgs and other wide boys have a pixel offset. This accounts for that
// View the message
LAZYADDASSOC(owned_by.seen_messages, message_loc, src)
@@ -212,10 +213,6 @@
scheduled_destruction = world.time + (lifespan - CHAT_MESSAGE_EOL_FADE)
enter_subsystem()
- var/mob/living/silicon/robot/R = target
- if(iscyborg(R))
- if((R.module.dogborg == TRUE || R.dogborg == TRUE) && isturf(R.loc)) //I hate whoever that thought that putting two types of dogborg that don't even sync up properly was good
- message.pixel_x = 16
/**
* Applies final animations to overlay CHAT_MESSAGE_EOL_FADE deciseconds prior to message deletion
diff --git a/code/datums/components/bouncy.dm b/code/datums/components/bouncy.dm
index fed603410e..3c4e228b59 100644
--- a/code/datums/components/bouncy.dm
+++ b/code/datums/components/bouncy.dm
@@ -32,7 +32,7 @@
var/atom/movable/A = parent
switch(rand(1, 3))
if(1)
- A.do_jiggle(45 + rand(-10, 10) * bouncy_mod, 14)
+ A.do_jiggle(25 + rand(-5, 5) * bouncy_mod, 14)
if(2)
var/min_b = 0.6/bouncy_mod
var/max_b = 1.2 * bouncy_mod
diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm
index 98bbdbed05..40daac88cf 100644
--- a/code/datums/components/storage/storage.dm
+++ b/code/datums/components/storage/storage.dm
@@ -432,25 +432,26 @@
if(M.incapacitated() || !M.canUseStorage())
return
var/atom/A = parent
- A.add_fingerprint(M)
// this must come before the screen objects only block, dunno why it wasn't before
if(over_object == M)
user_show_to_mob(M, trigger_on_found = TRUE)
if(isrevenant(M))
INVOKE_ASYNC(GLOBAL_PROC, .proc/RevenantThrow, over_object, M, source)
return
+ if(check_locked(null, M) || !M.CanReach(A) || (!M.CanReach(over_object) && !istype(over_object, /atom/movable/screen)))
+ return
+ playsound(A, "rustle", 50, TRUE, -5)
+ A.do_jiggle()
+ A.add_fingerprint(M)
if(!istype(over_object, /atom/movable/screen))
INVOKE_ASYNC(src, .proc/dump_content_at, over_object, M)
return
if(A.loc != M)
return
- playsound(A, "rustle", 50, TRUE, -5)
- A.do_jiggle()
if(istype(over_object, /atom/movable/screen/inventory/hand))
var/atom/movable/screen/inventory/hand/H = over_object
M.putItemFromInventoryInHandIfPossible(A, H.held_index)
return
- A.add_fingerprint(M)
/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE, trigger_on_found = FALSE)
var/atom/A = parent
diff --git a/code/datums/components/virtual_reality.dm b/code/datums/components/virtual_reality.dm
index 56e06e3582..c0e6e9dba6 100644
--- a/code/datums/components/virtual_reality.dm
+++ b/code/datums/components/virtual_reality.dm
@@ -84,9 +84,9 @@
if(VR)
VR.level_below = src
level_above = VR
- M.transfer_ckey(vr_M, FALSE)
mastermind = M.mind
mastermind.current.audiovisual_redirect = parent
+ M.transfer_ckey(vr_M, FALSE)
RegisterSignal(mastermind, COMSIG_PRE_MIND_TRANSFER, .proc/switch_player)
RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
RegisterSignal(M, COMSIG_MOB_PRE_PLAYER_CHANGE, .proc/player_hijacked)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 1de7a1576e..4077f86e52 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1322,7 +1322,7 @@
filters += filter(arglist(arguments))
UNSETEMPTY(filter_data)
-/atom/proc/transition_filter(name, time, list/new_params, easing, loop)
+/atom/proc/transition_filter(name, time, list/new_params, easing, loop, parallel = TRUE)
var/filter = get_filter(name)
if(!filter)
return
@@ -1333,7 +1333,7 @@
for(var/thing in new_params)
params[thing] = new_params[thing]
- animate(filter, new_params, time = time, easing = easing, loop = loop)
+ animate(filter, new_params, time = time, easing = easing, loop = loop, flags = (parallel ? ANIMATION_PARALLEL : 0))
for(var/param in params)
filter_data[name][param] = params[param]
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 4b137d8b60..0323217341 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -605,11 +605,11 @@
if(throwing && !throw_override)
return
if(on && !(movement_type & FLOATING))
- animate(src, pixel_y = 2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
- animate(pixel_y = -2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
+ animate(src, pixel_z = 2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
+ animate(pixel_z = -4, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
setMovetype(movement_type | FLOATING)
else if (!on && (movement_type & FLOATING))
- animate(src, pixel_y = initial(pixel_y), time = 10)
+ animate(src, pixel_z = initial(pixel_y), time = 10)
setMovetype(movement_type & ~FLOATING)
floating_need_update = FALSE // assume it's done
@@ -775,4 +775,4 @@
M.Turn(pick(-30, 30))
animate(I, alpha = 175, pixel_x = to_x, pixel_y = to_y, time = 3, transform = M, easing = CUBIC_EASING)
sleep(1)
- animate(I, alpha = 0, transform = matrix(), time = 1)
+ animate(I, alpha = 0, transform = matrix(), time = 1, flags = ANIMATION_PARALLEL)
diff --git a/code/game/objects/items/pneumaticCannon.dm b/code/game/objects/items/pneumaticCannon.dm
index 595b24d214..2b0b8da670 100644
--- a/code/game/objects/items/pneumaticCannon.dm
+++ b/code/game/objects/items/pneumaticCannon.dm
@@ -205,6 +205,10 @@
if(!throw_item(target, I, user))
break
+ if(user)
+ shake_camera(user, (pressureSetting * 0.75 + 1), (pressureSetting * 0.75))
+
+
/obj/item/pneumatic_cannon/proc/throw_item(turf/target, obj/item/I, mob/user)
if(!istype(I))
return FALSE
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index d48ddf880a..c1f20605a0 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -67,7 +67,7 @@
/obj/structure/proc/do_climb(atom/movable/A)
if(climbable)
density = FALSE
- . = step(A,get_dir(A,src.loc))
+ . = step(A, (A.loc == loc ? dir : get_dir(A,src.loc)))
density = TRUE
/obj/structure/proc/climb_structure(mob/living/user)
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 38886e4908..02c2936f8d 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -110,7 +110,11 @@ distance_multiplier - Can be used to multiply the distance at which the sound is
*/
/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/S, max_distance,
- falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = SOUND_DEFAULT_DISTANCE_MULTIPLIER, envwet = -10000, envdry = 0)
+ falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = SOUND_DEFAULT_DISTANCE_MULTIPLIER, envwet = -10000, envdry = 0, virtual_hearer)
+ if(audiovisual_redirect)
+ virtual_hearer = get_turf(src)
+ audiovisual_redirect.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, max_distance, falloff_distance, distance_multiplier, max(0, envwet), -10000, virtual_hearer)
+ //No return here, as we want to deliberately support the possibility of shenanigans in which mobs with clients can have active AV redirects to completely different players
if(!client)
return
@@ -134,7 +138,7 @@ distance_multiplier - Can be used to multiply the distance at which the sound is
S.frequency = get_rand_frequency()
if(isturf(turf_source))
- var/turf/T = get_turf(src)
+ var/turf/T = virtual_hearer || get_turf(src)
//sound volume falloff with distance
var/distance = get_dist(T, turf_source)
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index a6a30ba96e..1a3997272a 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -66,9 +66,21 @@
icon_state = "nuclearbomb_base"
anchored = TRUE //stops it being moved
+/obj/machinery/nuclearbomb/selfdestruct/Initialize(mapload)
+ . = ..()
+ if(SSevents.holidays && SSevents.holidays[PRIDE_MONTH] && prob(10))
+ name = "station-wide gender-reveal terminal"
+ desc = "For when the whole sector deserves to know a gender. But of whom? Don't ask."
+
/obj/machinery/nuclearbomb/syndicate
//ui_style = "syndicate" // actually the nuke op bomb is a stole nt bomb
+/obj/machinery/nuclearbomb/syndicate/Initialize(mapload)
+ . = ..()
+ if(SSevents.holidays && SSevents.holidays[PRIDE_MONTH] && prob(50))
+ name = "tactical gender-reveal device"
+ desc = "\"But whose gender is it revealing?\" you ponder. Don't worry. That comes later."
+
/obj/machinery/nuclearbomb/syndicate/get_cinematic_type(off_station)
var/datum/game_mode/nuclear/NM = SSticker.mode
switch(off_station)
diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm
index 6fc0fabf31..d3ad2ca4c7 100644
--- a/code/modules/clothing/gloves/miscellaneous.dm
+++ b/code/modules/clothing/gloves/miscellaneous.dm
@@ -360,3 +360,12 @@
desc = "Thin, pretty gloves intended for use in sexy feminine attire. A tag on the hem claims they pair great with black stockings."
icon_state = "eveningblack"
item_state = "eveningblack"
+
+/obj/item/clothing/gloves/polymaid
+ name = "polychromic maid gloves"
+ desc = "Colourable maid gloves!"
+ icon_state = "maid_arms"
+
+/obj/item/clothing/gloves/polymaid/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/polychromic, list("#333333", "#FFFFFF"), 2)
diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm
index 4e7ccb7111..f3868f703f 100644
--- a/code/modules/clothing/head/misc.dm
+++ b/code/modules/clothing/head/misc.dm
@@ -477,6 +477,15 @@
item_state = "maid"
dynamic_hair_suffix = ""
+/obj/item/clothing/head/maid/polychromic
+ name = "polychromic maid headband"
+ icon_state = "polymaid"
+ item_state = "polymaid"
+
+/obj/item/clothing/head/maid/polychromic/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/polychromic, list("#333333", "#FFFFFF"), 2)
+
/obj/item/clothing/head/widered
name = "Wide red hat"
desc = "It is both wide, and red. Stylish!"
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index 7f4de22bb4..7ac2f65205 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -259,6 +259,15 @@
name = "Collar Key"
desc = "A key for a tiny lock on a collar or bag."
+/obj/item/clothing/neck/maid
+ name = "polychromic maid collar"
+ desc = "A collar that goes with the polychromic maid outfit."
+ icon_state = "maid_neck"
+
+/obj/item/clothing/neck/maid/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/polychromic, list("#333333", "#FFFFFF"), 2)
+
//////////////
//DOPE BLING//
//////////////
diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm
index 41bdb3d5ca..6806f73c65 100644
--- a/code/modules/clothing/suits/_suits.dm
+++ b/code/modules/clothing/suits/_suits.dm
@@ -26,15 +26,12 @@
var/mob/living/carbon/human/M = loc
if(ishuman(M) && M.w_uniform)
var/obj/item/clothing/under/U = M.w_uniform
- //SKYRAT EDIT
+ //SANDSTORM EDIT
if(istype(U) && length(U.attached_accessories))
- var/please_overlay = FALSE
for(var/obj/item/clothing/accessory/attached in U.attached_accessories)
if(attached.above_suit)
- please_overlay = TRUE
- if(please_overlay)
- . += U.accessory_overlay
- //SKYRAT EDIT END
+ . += U.accessory_overlays
+ //SANDSTORM EDIT END
/obj/item/clothing/suit/update_clothes_damaged_state()
..()
diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm
index d9a090a9a7..c87095e5ff 100644
--- a/code/modules/clothing/under/_under.dm
+++ b/code/modules/clothing/under/_under.dm
@@ -22,11 +22,11 @@
var/adjusted = NORMAL_STYLE
var/alt_covers_chest = FALSE // for adjusted/rolled-down jumpsuits, FALSE = exposes chest and arms, TRUE = exposes arms only
var/dummy_thick = FALSE // is able to hold accessories on its item
- //SKYRAT EDIT - Removed the old attached accessory system. We use a list of accessories instead.
- var/list/obj/item/clothing/accessory/attached_accessories = list()
+ //SANDSTORM EDIT - Removed the old attached accessory system. We use a list of accessories instead.
var/max_accessories = 3
- //SKYRAT EDIT END
- var/mutable_appearance/accessory_overlay
+ var/list/obj/item/clothing/accessory/attached_accessories = list()
+ var/list/mutable_appearance/accessory_overlays = list()
+ //SANDSTORM EDIT END
/obj/item/clothing/under/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
. = ..()
@@ -36,8 +36,8 @@
. += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform")
if(blood_DNA)
. += mutable_appearance('icons/effects/blood.dmi', "uniformblood", color = blood_DNA_to_color(), blend_mode = blood_DNA_to_blend())
- if(accessory_overlay)
- . += accessory_overlay
+ if(length(accessory_overlays))
+ . += accessory_overlays
/obj/item/clothing/under/attackby(obj/item/I, mob/user, params)
if((sensordamage || (has_sensor < HAS_SENSORS && has_sensor != NO_SENSORS)) && istype(I, /obj/item/stack/cable_coil))
@@ -164,14 +164,18 @@
if((flags_inv & HIDEACCESSORY) || (A.flags_inv & HIDEACCESSORY))
return TRUE
- //SKYRAT EDIT
- accessory_overlay = mutable_appearance('icons/mob/clothing/accessories.dmi', "blank")
+ //SANDSTORM EDIT
+ accessory_overlays = list(mutable_appearance('icons/mob/clothing/accessories.dmi', "blank"))
for(var/obj/item/clothing/accessory/attached_accessory in attached_accessories)
- var/mutable_appearance/Y = mutable_appearance(attached_accessory.mob_overlay_icon, attached_accessory.icon_state, ABOVE_HUD_LAYER)
- Y.alpha = attached_accessory.alpha
- Y.color = attached_accessory.color
- accessory_overlay.add_overlay(Y)
- //SKYRAT EDIT END
+ var/datum/element/polychromic/polychromic = LAZYACCESS(attached_accessory.comp_lookup, "item_worn_overlays")
+ if(!polychromic)
+ var/mutable_appearance/accessory_overlay = mutable_appearance(attached_accessory.mob_overlay_icon, attached_accessory.item_state || attached_accessory.icon_state, ABOVE_HUD_LAYER)
+ accessory_overlay.alpha = attached_accessory.alpha
+ accessory_overlay.color = attached_accessory.color
+ accessory_overlays += accessory_overlay
+ else
+ polychromic.apply_worn_overlays(attached_accessory, FALSE, attached_accessory.mob_overlay_icon, attached_accessory.item_state || attached_accessory.icon_state, NONE, accessory_overlays)
+ //SANDSTORM EDIT END
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm
index 63d09a89de..e39c48f80f 100644
--- a/code/modules/clothing/under/accessories.dm
+++ b/code/modules/clothing/under/accessories.dm
@@ -48,9 +48,9 @@
TakeComponent(detached_pockets)
U.armor = U.armor.detachArmor(armor)
- //SKYRAT EDIT
+ //SANDSTORM EDIT
current_uniform = null
- //SKYRAT EDIT END
+ //SANDSTORM EDIT END
if(isliving(user))
on_uniform_dropped(U, user)
@@ -61,18 +61,23 @@
pixel_y = 0
layer = initial(layer)
plane = initial(plane)
- U.accessory_overlay = null
U.cut_overlays()
U.attached_accessories -= src
+ U.accessory_overlays = list()
if(length(U.attached_accessories))
- U.accessory_overlay = mutable_appearance('icons/mob/clothing/accessories.dmi', "blank", WRISTS_LAYER, U.plane)
+ U.accessory_overlays = list(mutable_appearance('icons/mob/clothing/accessories.dmi', "blank"))
for(var/obj/item/clothing/accessory/attached_accessory in U.attached_accessories)
attached_accessory.force_unto(U)
- var/mutable_appearance/Y = mutable_appearance(attached_accessory.mob_overlay_icon, attached_accessory.icon_state, WRISTS_LAYER, U.plane)
- Y.alpha = attached_accessory.alpha
- Y.color = attached_accessory.color
- U.accessory_overlay.add_overlay(Y)
-//SKYRAT EDIT
+ var/datum/element/polychromic/polychromic = LAZYACCESS(attached_accessory.comp_lookup, "item_worn_overlays")
+ if(!polychromic)
+ var/mutable_appearance/accessory_overlay = mutable_appearance(attached_accessory.mob_overlay_icon, attached_accessory.item_state || attached_accessory.icon_state, ABOVE_HUD_LAYER)
+ accessory_overlay.alpha = attached_accessory.alpha
+ accessory_overlay.color = attached_accessory.color
+ U.accessory_overlays += accessory_overlay
+ else
+ polychromic.apply_worn_overlays(attached_accessory, FALSE, attached_accessory.mob_overlay_icon, attached_accessory.item_state || attached_accessory.icon_state, NONE, U.accessory_overlays)
+
+//SANDSTORM EDIT
/obj/item/clothing/accessory/proc/force_unto(obj/item/clothing/under/U)
layer = FLOAT_LAYER
plane = FLOAT_PLANE
@@ -98,7 +103,7 @@
pixel_x += rand(-16, 16)
pixel_y += rand(-16, 16)
U.add_overlay(src)
-//SKYRAT EDIT END
+//SANDSTORM EDIT END
/obj/item/clothing/accessory/proc/on_uniform_equip(obj/item/clothing/under/U, user)
return
@@ -296,6 +301,15 @@
item_state = "maidapron"
minimize_when_attached = FALSE
+/obj/item/clothing/accessory/maidapron/polychromic
+ name = "polychromic maid apron"
+ icon_state = "polymaidapron"
+ item_state = "polymaidapron"
+
+/obj/item/clothing/accessory/maidapron/polychromic/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/polychromic, list("#333333", "#FFFFFF"), 2)
+
/obj/item/clothing/accessory/sleevecrop
name = "one sleeved crop top"
desc = "Off the shoulder crop top, for those nights out partying."
diff --git a/code/modules/food_and_drinks/recipes/drinks_recipes.dm b/code/modules/food_and_drinks/recipes/drinks_recipes.dm
index 363e263ad4..efd9809e1d 100644
--- a/code/modules/food_and_drinks/recipes/drinks_recipes.dm
+++ b/code/modules/food_and_drinks/recipes/drinks_recipes.dm
@@ -1016,8 +1016,8 @@
/datum/chemical_reaction/pinktea
name = "Strawberry Tea"
- id = /datum/reagent/consumable/pinktea
- results = list(/datum/reagent/consumable/pinktea = 5)
+ id = /datum/reagent/consumable/tea/pink
+ results = list(/datum/reagent/consumable/tea/pink = 5)
required_reagents = list(/datum/reagent/consumable/strawberryjuice = 1, /datum/reagent/consumable/tea/arnold_palmer = 1, /datum/reagent/consumable/sugar = 1)
/datum/chemical_reaction/catnip_tea
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 53a4b0e997..2dcb8ef98f 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -121,7 +121,8 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
remove_verb(src, /mob/dead/observer/verb/boo)
remove_verb(src, /mob/dead/observer/verb/possess)
- animate(src, pixel_y = 2, time = 10, loop = -1)
+ animate(src, pixel_z = 2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
+ animate(pixel_z = -4, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
add_to_dead_mob_list()
@@ -539,8 +540,9 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
/mob/dead/observer/stop_orbit(datum/component/orbiter/orbits)
. = ..()
//restart our floating animation after orbit is done.
- pixel_y = 0
- animate(src, pixel_y = 2, time = 10, loop = -1)
+ pixel_z = 0
+ animate(src, pixel_z = 2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
+ animate(pixel_z = -4, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
/mob/dead/observer/verb/jumptomob() //Moves the ghost instead of just changing the ghosts's eye -Nodrak
set category = "Ghost"
diff --git a/code/modules/mob/living/carbon/alien/humanoid/update_icons.dm b/code/modules/mob/living/carbon/alien/humanoid/update_icons.dm
index 9563959ebc..ff4a7d2bba 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/update_icons.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/update_icons.dm
@@ -51,7 +51,7 @@
// update_icons() //Handled in update_transform(), leaving this here as a reminder
update_transform()
-/mob/living/carbon/alien/humanoid/update_transform() //The old method of updating lying/standing was update_icons(). Aliens still expect that.
+/mob/living/carbon/alien/humanoid/update_transform(do_animate) //The old method of updating lying/standing was update_icons(). Aliens still expect that.
if(lying > 0)
lying = 90 //Anything else looks silly
..()
diff --git a/code/modules/mob/living/carbon/alien/larva/update_icons.dm b/code/modules/mob/living/carbon/alien/larva/update_icons.dm
index e6e7e657e8..85416261c7 100644
--- a/code/modules/mob/living/carbon/alien/larva/update_icons.dm
+++ b/code/modules/mob/living/carbon/alien/larva/update_icons.dm
@@ -21,7 +21,7 @@
else
icon_state = "larva[state]"
-/mob/living/carbon/alien/larva/update_transform() //All this is handled in update_icons()
+/mob/living/carbon/alien/larva/update_transform(do_animate) //All this is handled in update_icons()
..()
return update_icons()
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index d4a701bc68..f44f4564d3 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -292,6 +292,14 @@
if(ishuman(src))
S = dna.species
+ //SPLURT EDIT - Headpat traits
+ if(M.has_quirk(/datum/quirk/dominant_aura) && H.has_quirk(/datum/quirk/well_trained))
+ if(H.has_quirk(/datum/quirk/headpat_hater))
+ H.remove_quirk(/datum/quirk/headpat_hater)
+ if(!H.has_quirk(/datum/quirk/headpat_slut))
+ H.add_quirk(/datum/quirk/headpat_slut)
+ SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "dom_trained", /datum/mood_event/dominant/good_boy)
+
if(HAS_TRAIT(H, TRAIT_DISTANT)) //No mood buff since you're not really liking it.
M.visible_message("[M] gives [H] a pat on the head to make [p_them()] feel better! They seem annoyed...", \
"You give [H] a pat on the head to make [p_them()] feel better! They seem annoyed as they're now glaring towards you...")
@@ -315,12 +323,13 @@
"You give [src] a pat on the head to make [p_them()] feel better! They seem to like it way too much..", target = src,
target_message = "[M] gives you a pat on the head to make you feel better!")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "lewd_headpat", /datum/mood_event/lewd_headpat)
- H.add_lust(5) //Headpats are hot af
+ H.handle_post_sex(5, null, null) //Headpats are hot af
else
M.visible_message("[M] gives [src] a pat on the head to make [p_them()] feel better!", \
"You give [src] a pat on the head to make [p_them()] feel better!", target = src,
target_message = "[M] gives you a pat on the head to make you feel better!")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "headpat", /datum/mood_event/headpat)
+ //SPLURT EDIT END
if(!(client?.prefs.cit_toggles & NO_AUTO_WAG) && friendly_check)
if(S?.can_wag_tail(src) && !dna.species.is_wagging_tail())
diff --git a/code/modules/mob/living/carbon/monkey/punpun.dm b/code/modules/mob/living/carbon/monkey/punpun.dm
index c8b8dbbc9d..8fd7b45b65 100644
--- a/code/modules/mob/living/carbon/monkey/punpun.dm
+++ b/code/modules/mob/living/carbon/monkey/punpun.dm
@@ -46,6 +46,7 @@
..()
/mob/living/carbon/monkey/punpun/proc/Read_Memory()
+ var/saved_color
if(fexists("data/npc_saves/Punpun.sav")) //legacy compatability to convert old format to new
var/savefile/S = new /savefile("data/npc_saves/Punpun.sav")
S["ancestor_name"] >> ancestor_name
@@ -62,6 +63,9 @@
ancestor_chain = json["ancestor_chain"]
relic_hat = json["relic_hat"]
relic_mask = json["relic_hat"]
+ saved_color = json["color"]
+ if(!isnull(saved_color))
+ add_atom_colour(json_decode(saved_color), FIXED_COLOUR_PRIORITY)
/mob/living/carbon/monkey/punpun/proc/Write_Memory(dead, gibbed)
var/json_file = file("data/npc_saves/Punpun.json")
@@ -71,10 +75,12 @@
file_data["ancestor_chain"] = null
file_data["relic_hat"] = null
file_data["relic_mask"] = null
+ file_data["color"] = null
else
file_data["ancestor_name"] = ancestor_name ? ancestor_name : name
file_data["ancestor_chain"] = dead ? ancestor_chain + 1 : ancestor_chain
file_data["relic_hat"] = head ? head.type : null
file_data["relic_mask"] = wear_mask ? wear_mask.type : null
+ file_data["color"] = color ? json_encode(color) : null
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 6efa32b48b..e9150f202d 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -81,6 +81,7 @@
//Called when we bump onto a mob
/mob/living/proc/MobBump(mob/M)
+ SEND_SIGNAL(src, COMSIG_LIVING_MOB_BUMP, M)
//Even if we don't push/swap places, we "touched" them, so spread fire
spreadFire(M)
@@ -88,6 +89,7 @@
return TRUE
var/they_can_move = TRUE
+
if(isliving(M))
var/mob/living/L = M
they_can_move = CHECK_MOBILITY(L, MOBILITY_MOVE)
@@ -105,16 +107,16 @@
//Should stop you pushing a restrained person out of the way
if(L.pulledby && L.pulledby != src && L.restrained())
if(!(world.time % 5))
- to_chat(src, "[L] is restrained, you cannot push past.")
- return 1
+ to_chat(src, span_warning("[L] is restrained, you cannot push past."))
+ return TRUE
if(L.pulling)
if(ismob(L.pulling))
var/mob/P = L.pulling
if(P.restrained())
if(!(world.time % 5))
- to_chat(src, "[L] is restraining [P], you cannot push past.")
- return 1
+ to_chat(src, span_warning("[L] is restraining [P], you cannot push past."))
+ return TRUE
//CIT CHANGES START HERE - makes it so resting stops you from moving through standing folks or over prone bodies without a short delay
if(!CHECK_MOBILITY(src, MOBILITY_STAND))
@@ -142,7 +144,7 @@
//END OF CIT CHANGES
if(moving_diagonally)//no mob swap during diagonal moves.
- return 1
+ return TRUE
//handle micro bumping on help intent
if(a_intent == INTENT_HELP)
@@ -156,7 +158,8 @@
if(!too_strong)
mob_swap = TRUE
else
- if(M.pulledby == src && a_intent == INTENT_GRAB)
+ //You can swap with the person you are dragging on grab intent, and restrained people in most cases
+ if(M.pulledby == src && !too_strong)
mob_swap = TRUE
//restrained people act if they were on 'help' intent to prevent a person being pulled from being separated from their puller
else if((M.restrained() || M.a_intent == INTENT_HELP) && (restrained() || a_intent == INTENT_HELP))
@@ -164,8 +167,8 @@
if(mob_swap)
//switch our position with M
if(loc && !loc.Adjacent(M.loc))
- return 1
- now_pushing = 1
+ return TRUE
+ now_pushing = TRUE
var/oldloc = loc
var/oldMloc = M.loc
@@ -185,29 +188,33 @@
if(!M_passmob)
M.pass_flags &= ~PASSMOB
- now_pushing = 0
+ now_pushing = FALSE
if(!move_failed)
- return 1
+ return TRUE
//okay, so we didn't switch. but should we push?
//not if he's not CANPUSH of course
if(!(M.status_flags & CANPUSH))
- return 1
+ return TRUE
if(handle_micro_bump_other(M))
return TRUE
if(isliving(M))
var/mob/living/L = M
if(HAS_TRAIT(L, TRAIT_PUSHIMMUNE))
- return 1
- //If they're a human, and they're not in help intent, block pushing
- if(ishuman(M) && (M.a_intent != INTENT_HELP))
- return TRUE
+ return TRUE
+ if(M.a_intent != INTENT_HELP)
+ //If they're a human, and they're not in help intent, block pushing
+ if(ishuman(M))
+ return TRUE
+ //if they are a cyborg, and they're alive and not in help intent, block pushing
+ if(iscyborg(M) && M.stat != DEAD)
+ return TRUE
//anti-riot equipment is also anti-push
for(var/obj/item/I in M.held_items)
if(!istype(M, /obj/item/clothing))
if(prob(I.block_chance*2))
- return 1
+ return TRUE
/mob/living/get_photo_description(obj/item/camera/camera)
var/list/mob_details = list()
@@ -238,21 +245,40 @@
if(!client && (mob_size < MOB_SIZE_SMALL))
return
now_pushing = TRUE
- var/t = get_dir(src, AM)
+ SEND_SIGNAL(src, COMSIG_LIVING_PUSHING_MOVABLE, AM)
+ var/dir_to_target = get_dir(src, AM)
+
+ // If there's no dir_to_target then the player is on the same turf as the atom they're trying to push.
+ // This can happen when a player is stood on the same turf as a directional window. All attempts to push
+ // the window will fail as get_dir will return 0 and the player will be unable to move the window when
+ // it should be pushable.
+ // In this scenario, we will use the facing direction of the /mob/living attempting to push the atom as
+ // a fallback.
+ if(!dir_to_target)
+ dir_to_target = dir
+
var/push_anchored = FALSE
if((AM.move_resist * MOVE_FORCE_CRUSH_RATIO) <= force)
- if(move_crush(AM, move_force, t))
+ if(move_crush(AM, move_force, dir_to_target))
push_anchored = TRUE
- if((AM.move_resist * MOVE_FORCE_FORCEPUSH_RATIO) <= force) //trigger move_crush and/or force_push regardless of if we can push it normally
- if(force_push(AM, move_force, t, push_anchored))
+ if((AM.move_resist * MOVE_FORCE_FORCEPUSH_RATIO) <= force) //trigger move_crush and/or force_push regardless of if we can push it normally
+ if(force_push(AM, move_force, dir_to_target, push_anchored))
push_anchored = TRUE
+ if(ismob(AM))
+ var/mob/mob_to_push = AM
+ var/atom/movable/mob_buckle = mob_to_push.buckled
+ // If we can't pull them because of what they're buckled to, make sure we can push the thing they're buckled to instead.
+ // If neither are true, we're not pushing anymore.
+ if(mob_buckle && (mob_buckle.buckle_prevents_pull || (force < (mob_buckle.move_resist * MOVE_FORCE_PUSH_RATIO))))
+ now_pushing = FALSE
+ return
if((AM.anchored && !push_anchored) || (force < (AM.move_resist * MOVE_FORCE_PUSH_RATIO)))
now_pushing = FALSE
return
- if (istype(AM, /obj/structure/window))
+ if(istype(AM, /obj/structure/window))
var/obj/structure/window/W = AM
if(W.fulltile)
- for(var/obj/structure/window/win in get_step(W,t))
+ for(var/obj/structure/window/win in get_step(W, dir_to_target))
now_pushing = FALSE
return
if(pulling == AM)
@@ -260,8 +286,9 @@
var/current_dir
if(isliving(AM))
current_dir = AM.dir
- if(step(AM, t) && Process_Spacemove(t))
- step(src, t)
+ if(AM.Move(get_step(AM.loc, dir_to_target), dir_to_target, glide_size))
+ AM.add_fingerprint(src)
+ Move(get_step(loc, dir_to_target), dir_to_target)
if(current_dir)
AM.setDir(current_dir)
now_pushing = FALSE
@@ -361,29 +388,33 @@
offset = GRAB_PIXEL_SHIFT_NECK
if(GRAB_KILL)
offset = GRAB_PIXEL_SHIFT_NECK
- M.setDir(get_dir(M, src))
- switch(M.dir)
- if(NORTH)
- animate(M, pixel_x = 0, pixel_y = offset, 3)
- if(SOUTH)
- animate(M, pixel_x = 0, pixel_y = -offset, 3)
- if(EAST)
- if(M.lying == 270) //update the dragged dude's direction if we've turned
- M.lying = 90
- M.update_transform() //force a transformation update, otherwise it'll take a few ticks for update_mobility() to do so
- M.lying_prev = M.lying
- animate(M, pixel_x = offset, pixel_y = 0, 3)
- if(WEST)
- if(M.lying == 90)
- M.lying = 270
- M.update_transform()
- M.lying_prev = M.lying
- animate(M, pixel_x = -offset, pixel_y = 0, 3)
+ var/target_dir = get_dir(M, src)
+ M.setDir(target_dir)
+ var/target_x
+ var/target_y
+ if(target_dir & NORTH)
+ target_y += offset
+ if(target_dir & SOUTH)
+ target_y -= offset
+ if(target_dir & EAST)
+ target_x += offset
+ if(target_dir & WEST)
+ target_x -= offset
+ if(target_x || target_y)
+ if(0 < target_x && M.lying == 270)
+ M.lying = 90
+ M.update_transform(FALSE) //force a transformation update, otherwise it'll take a few ticks for update_mobility() to do so
+ M.lying_prev = M.lying
+ if(0 > target_x && M.lying == 90)
+ M.lying = 270
+ M.update_transform(FALSE)
+ M.lying_prev = M.lying
+ animate(M, pixel_x = target_x, pixel_y = target_y, time = 3, flags = ANIMATION_PARALLEL)
/mob/living/proc/reset_pull_offsets(mob/living/M, override)
if(!override && M.buckled)
return
- animate(M, pixel_x = 0, pixel_y = 0, 1)
+ animate(M, pixel_x = 0, pixel_y = 0, time = 3, flags = ANIMATION_PARALLEL)
//mob verbs are a lot faster than object verbs
//for more info on why this is not atom/pull, see examinate() in mob.dm
@@ -901,12 +932,11 @@
if(anchored || (buckled && buckled.anchored))
fixed = 1
if(on && !(movement_type & FLOATING) && !fixed)
- animate(src, pixel_y = pixel_y + 2, time = 10, loop = -1)
- sleep(10)
- animate(src, pixel_y = pixel_y - 2, time = 10, loop = -1)
+ animate(src, pixel_z = 2, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
+ animate(pixel_z = -4, time = 10, loop = -1, flags = ANIMATION_RELATIVE)
setMovetype(movement_type | FLOATING)
else if(((!on || fixed) && (movement_type & FLOATING)))
- animate(src, pixel_y = get_standard_pixel_y_offset(lying), time = 10)
+ animate(src, pixel_z = get_standard_pixel_y_offset(lying), time = 10)
setMovetype(movement_type & ~FLOATING)
// The src mob is trying to strip an item from someone
@@ -1005,7 +1035,7 @@
var/pixel_y_diff = rand(-amplitude/3, amplitude/3)
var/final_pixel_x = get_standard_pixel_x_offset(lying)
var/final_pixel_y = get_standard_pixel_y_offset(lying)
- animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff , time = 2, loop = 6)
+ animate(src, pixel_x = pixel_x_diff, pixel_y = pixel_y_diff , time = 2, loop = 6, flags = ANIMATION_PARALLEL | ANIMATION_RELATIVE)
animate(pixel_x = final_pixel_x , pixel_y = final_pixel_y , time = 2)
floating_need_update = TRUE
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index 49a60c0411..f9fcb35377 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -249,7 +249,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
/mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
SEND_SIGNAL(src, COMSIG_MOVABLE_HEAR, args) //parent calls can't overwrite the current proc args.
- if(!client)
+ if(!client && !audiovisual_redirect)
return
var/deaf_message
var/deaf_type
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index 0d04d4b562..c1897c3c05 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -136,6 +136,7 @@
..()
/mob/living/simple_animal/pet/cat/Runtime/proc/Read_Memory()
+ var/saved_color
if(fexists("data/npc_saves/Runtime.sav")) //legacy compatability to convert old format to new
var/savefile/S = new /savefile("data/npc_saves/Runtime.sav")
S["family"] >> family
@@ -146,14 +147,21 @@
return
var/list/json = json_decode(file2text(json_file))
family = json["family"]
+ saved_color = json["color"]
if(isnull(family))
family = list()
+ if(!isnull(saved_color))
+ add_atom_colour(json_decode(saved_color), FIXED_COLOUR_PRIORITY)
/mob/living/simple_animal/pet/cat/Runtime/proc/Write_Memory(dead)
var/json_file = file("data/npc_saves/Runtime.json")
var/list/file_data = list()
family = list()
if(!dead)
+ if(color)
+ file_data["color"] = json_encode(color)
+ else
+ file_data["color"] = null
for(var/mob/living/simple_animal/pet/cat/kitten/C in children)
if(istype(C,type) || C.stat || !C.z || !C.butcher_results) //That last one is a work around for hologram cats
continue
@@ -161,6 +169,8 @@
family[C.type] += 1
else
family[C.type] = 1
+ else
+ file_data["color"] = null
file_data["family"] = family
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm
index bce46fe10d..748a182ba3 100644
--- a/code/modules/mob/living/simple_animal/friendly/dog.dm
+++ b/code/modules/mob/living/simple_animal/friendly/dog.dm
@@ -436,6 +436,7 @@ GLOBAL_LIST_INIT(strippable_corgi_items, create_strippable_list(list(
/mob/living/simple_animal/pet/dog/corgi/Ian/proc/Read_Memory()
set waitfor = FALSE
+ var/saved_color
if(fexists("data/npc_saves/Ian.sav")) //legacy compatability to convert old format to new
var/savefile/S = new /savefile("data/npc_saves/Ian.sav")
S["age"] >> age
@@ -450,12 +451,15 @@ GLOBAL_LIST_INIT(strippable_corgi_items, create_strippable_list(list(
age = json["age"]
record_age = json["record_age"]
saved_head = json["saved_head"]
+ saved_color = json["color"]
if(isnull(age))
age = 0
if(isnull(record_age))
record_age = 1
if(saved_head)
place_on_head(new saved_head)
+ if(!isnull(saved_color))
+ add_atom_colour(json_decode(saved_color), FIXED_COLOUR_PRIORITY)
/mob/living/simple_animal/pet/dog/corgi/Ian/proc/Write_Memory(dead)
var/json_file = file("data/npc_saves/Ian.json")
@@ -470,10 +474,15 @@ GLOBAL_LIST_INIT(strippable_corgi_items, create_strippable_list(list(
file_data["saved_head"] = inventory_head.type
else
file_data["saved_head"] = null
+ if(color)
+ file_data["color"] = json_encode(color)
+ else
+ file_data["color"] = null
else
file_data["age"] = 0
file_data["record_age"] = record_age
file_data["saved_head"] = null
+ file_data["color"] = null
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index 1f12195ad6..c05aba8227 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -910,11 +910,13 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
speak += pick("...[longest_survival].", "The things I've seen!", "I have lived many lives!", "What are you before me?")
desc += " Old as sin, and just as loud. Claimed to be [rounds_survived]."
speak_chance = 20 //His hubris has made him more annoying/easier to justify killing
- add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY)
+ if(!color)
+ add_atom_colour("#EEEE22", FIXED_COLOUR_PRIORITY)
else if(rounds_survived == longest_deathstreak)
speak += pick("What are you waiting for!", "Violence breeds violence!", "Blood! Blood!", "Strike me down if you dare!")
desc += " The squawks of [-rounds_survived] dead parrots ring out in your ears..."
- add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY)
+ if(!color)
+ add_atom_colour("#BB7777", FIXED_COLOUR_PRIORITY)
else if(rounds_survived > 0)
speak += pick("...again?", "No, It was over!", "Let me out!", "It never ends!")
desc += " Over [rounds_survived] shifts without a \"terrible\" \"accident\"!"
@@ -947,6 +949,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
..(gibbed)
/mob/living/simple_animal/parrot/Poly/proc/Read_Memory()
+ var/saved_color
if(fexists("data/npc_saves/Poly.sav")) //legacy compatability to convert old format to new
var/savefile/S = new /savefile("data/npc_saves/Poly.sav")
S["phrases"] >> speech_buffer
@@ -963,8 +966,11 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
rounds_survived = json["roundssurvived"]
longest_survival = json["longestsurvival"]
longest_deathstreak = json["longestdeathstreak"]
+ saved_color = json["color"]
if(!islist(speech_buffer))
speech_buffer = list()
+ if(!isnull(saved_color))
+ add_atom_colour(json_decode(saved_color), FIXED_COLOUR_PRIORITY)
/mob/living/simple_animal/parrot/Poly/proc/Write_Memory(dead)
var/json_file = file("data/npc_saves/Poly.json")
@@ -978,6 +984,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
file_data["longestdeathstreak"] = rounds_survived - 1
else
file_data["longestdeathstreak"] = longest_deathstreak
+ file_data["color"] = null
else
file_data["roundssurvived"] = rounds_survived + 1
if(rounds_survived + 1 > longest_survival)
@@ -985,6 +992,10 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
else
file_data["longestsurvival"] = longest_survival
file_data["longestdeathstreak"] = longest_deathstreak
+ if(color)
+ file_data["color"] = json_encode(color)
+ else
+ file_data["color"] = null
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 5b487f503e..1671c4bca9 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -482,7 +482,7 @@
update_action_buttons_icon()
return mobility_flags
-/mob/living/simple_animal/update_transform()
+/mob/living/simple_animal/update_transform(do_animate)
var/matrix/ntransform = matrix(transform) //aka transform.Copy()
var/changed = 0
diff --git a/code/modules/mob/living/update_icons.dm b/code/modules/mob/living/update_icons.dm
index d3264c93a2..4ad861bf2b 100644
--- a/code/modules/mob/living/update_icons.dm
+++ b/code/modules/mob/living/update_icons.dm
@@ -1,5 +1,5 @@
//IMPORTANT: Multiple animate() calls do not stack well, so try to do them all at once if you can.
-/mob/living/update_transform()
+/mob/living/update_transform(do_animate = TRUE)
var/matrix/ntransform = matrix(transform) //aka transform.Copy()
var/final_pixel_y = pixel_y
var/changed = 0
@@ -25,5 +25,9 @@
resize = RESIZE_DEFAULT_SIZE
if(changed)
- animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, easing = EASE_IN|EASE_OUT)
+ if(do_animate)
+ animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, easing = EASE_IN|EASE_OUT, flags = ANIMATION_PARALLEL)
+ else
+ transform = ntransform
+ pixel_y = final_pixel_y
floating_need_update = TRUE
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index f22c87913f..cfc2a6822a 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -141,7 +141,7 @@
return
hearers -= ignored_mobs
- if(target_message && target && istype(target) && target.client)
+ if(target_message && target && istype(target) && (target.client || target.audiovisual_redirect))
hearers -= target
if(omni)
target.show_message(target_message)
@@ -158,7 +158,7 @@
if(self_message)
hearers -= src
for(var/mob/M in hearers)
- if(!M.client)
+ if(!M.client && !M.audiovisual_redirect)
continue
if(omni)
M.show_message(message)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index a3bcdb0e55..f59ed15c88 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -229,17 +229,23 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
return copytext_char(sanitize(.),1,MAX_MESSAGE_LEN)
/proc/shake_camera(mob/M, duration, strength=1)
- if(!M || !M.client || duration < 1)
+ set waitfor = FALSE
+ if(!M || !M.client || duration <= 0)
return
var/client/C = M.client
+ if (C.prefs.screenshake==0)
+ return
var/oldx = C.pixel_x
var/oldy = C.pixel_y
- var/max = strength*world.icon_size
- var/min = -(strength*world.icon_size)
+ var/clientscreenshake = (C.prefs.screenshake * 0.01)
+ var/max = (strength*clientscreenshake) * world.icon_size
+ var/min = -((strength*clientscreenshake) * world.icon_size)
+ var/roundedduration = -round(-duration) // round() with only one arg will always round down. so uh. this is Something all right
- for(var/i in 0 to duration-1)
+ for(var/i in 0 to roundedduration-1)
+ duration--
if (i == 0)
- animate(C, pixel_x=rand(min,max), pixel_y=rand(min,max), time=1)
+ animate(C, pixel_x=(rand(min,max)*duration), pixel_y=(rand(min,max)*duration), time=1)
else
animate(pixel_x=rand(min,max), pixel_y=rand(min,max), time=1)
animate(pixel_x=oldx, pixel_y=oldy, time=1)
diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm
index 836ed3b804..3f2f4d3e65 100644
--- a/code/modules/projectiles/guns/ballistic.dm
+++ b/code/modules/projectiles/guns/ballistic.dm
@@ -3,6 +3,7 @@
name = "projectile gun"
icon_state = "pistol"
w_class = WEIGHT_CLASS_NORMAL
+ recoil = 0.25
var/spawnwithmagazine = TRUE
var/mag_type = /obj/item/ammo_box/magazine/m10mm //Removes the need for max_ammo and caliber info
var/obj/item/ammo_box/magazine/magazine
diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm
index afb30631e0..fe42fc2d99 100644
--- a/code/modules/projectiles/guns/ballistic/revolver.dm
+++ b/code/modules/projectiles/guns/ballistic/revolver.dm
@@ -5,6 +5,7 @@
mag_type = /obj/item/ammo_box/magazine/internal/cylinder
fire_sound = "sound/weapons/revolvershot.ogg"
casing_ejector = FALSE
+ recoil = 0.5
/obj/item/gun/ballistic/revolver/Initialize(mapload)
. = ..()
@@ -285,6 +286,7 @@
item_state = "shotgun"
w_class = WEIGHT_CLASS_BULKY
weapon_weight = WEAPON_MEDIUM
+ recoil = 1
force = 10
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BACK
diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm
index c6fd27b103..25e4b6b12a 100644
--- a/code/modules/projectiles/guns/ballistic/shotgun.dm
+++ b/code/modules/projectiles/guns/ballistic/shotgun.dm
@@ -5,6 +5,7 @@
item_state = "shotgun"
fire_sound = "sound/weapons/gunshotshotgunshot.ogg"
w_class = WEIGHT_CLASS_BULKY
+ recoil = 1
force = 10
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BACK
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index dd759f9e3c..03dfc46713 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -16,6 +16,7 @@
name = "energy gun"
desc = "A basic energy-based gun."
icon = 'icons/obj/guns/energy.dmi'
+ recoil = 0.1
var/obj/item/stock_parts/cell/cell //What type of power cell this uses
var/cell_type = /obj/item/stock_parts/cell
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index 3f41ac859f..9b10a843e8 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -8,6 +8,7 @@
item_flags = NONE
obj_flags = UNIQUE_RENAME
weapon_weight = WEAPON_LIGHT
+ recoil = 0.5
can_flashlight = 1
flight_x_offset = 15
flight_y_offset = 9
diff --git a/code/modules/projectiles/guns/misc/syringe_gun.dm b/code/modules/projectiles/guns/misc/syringe_gun.dm
index 07066023b3..8de7633a79 100644
--- a/code/modules/projectiles/guns/misc/syringe_gun.dm
+++ b/code/modules/projectiles/guns/misc/syringe_gun.dm
@@ -4,6 +4,7 @@
icon_state = "syringegun"
item_state = "syringegun"
w_class = WEIGHT_CLASS_NORMAL
+ recoil = 0.1
throw_speed = 3
throw_range = 7
force = 4
diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
index 3554fc7052..d91a336db0 100644
--- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
@@ -1045,13 +1045,13 @@
glass_desc = "Delicious flavored strawberry syrup mixed with milk."
value = REAGENT_VALUE_VERY_COMMON
-/datum/reagent/consumable/tea/pinkmilk/on_mob_life(mob/living/carbon/M)
+/datum/reagent/consumable/pinkmilk/on_mob_life(mob/living/carbon/M)
if(prob(15))
to_chat(M, "[pick("You cant help to smile.","You feel nostalgia all of sudden.","You remember to relax.")]")
..()
. = 1
-/datum/reagent/consumable/pinktea //Tiny Tim song
+/datum/reagent/consumable/tea/pink //Tiny Tim song
name = "Strawberry Tea"
description = "A timeless classic!"
color = "#f76aeb"//rgb(247, 106, 235)
@@ -1061,7 +1061,7 @@
glass_name = "mug of strawberry tea"
glass_desc = "Delicious traditional tea flavored with strawberries."
-/datum/reagent/consumable/tea/pinktea/on_mob_life(mob/living/carbon/M)
+/datum/reagent/consumable/tea/pink/on_mob_life(mob/living/carbon/M)
if(prob(10))
to_chat(M, "[pick("Diamond skies where white deer fly.","Sipping strawberry tea.","Silver raindrops drift through timeless, Neverending June.","Crystal ... pearls free, with love!","Beaming love into me.")]")
..()
diff --git a/config/entries/general.txt b/config/entries/general.txt
index 66bf7e8afa..0a4372a9cc 100644
--- a/config/entries/general.txt
+++ b/config/entries/general.txt
@@ -477,7 +477,7 @@ BOX_RANDOM_ENGINE Box TEG,3
BOX_RANDOM_ENGINE Box Empty,0
BOX_RANDOM_ENGINE Box Antimatter,1
BOX_RANDOM_ENGINE Box P.A.C.M.A.N,1
-BOX_RANDOM_ENGINE Box RBMK,1
+BOX_RANDOM_ENGINE Box RBMK,0
## Whether the suicide verb is allowed.
diff --git a/html/changelogs/archive/2022-06.yml b/html/changelogs/archive/2022-06.yml
index 10450d8943..2827914b57 100644
--- a/html/changelogs/archive/2022-06.yml
+++ b/html/changelogs/archive/2022-06.yml
@@ -120,7 +120,80 @@
BongaTheProto:
- tweak: Makes the new player panel a toggle so you can choose between the old one
and TG's current. Option in Game Preferences
+2022-06-06:
SandPoot:
- refactor: Completely replaces the new player panel *not the admin one* with TG's.
timothyteakettle:
- rscadd: adds paper skin and glass bones quirks
+2022-06-07:
+ Bhijn & Myr:
+ - rscadd: During pride month, station nukes are significantly more efficient at
+ revealing genders.
+ - rscadd: Station pets now retain their color between shifts!
+ - bugfix: Admin vote deobfuscation now properly displays votes as 0 if they were
+ simply never voted for.
+ - bugfix: Audiovisual redirection now works exactly as it used to. In laymen's terms,
+ this means folks in VR sleepers are aware of the world around them again!
+ - bugfix: The shake animation, jiggle animation, squish animation, jitter animation,
+ and float animation, have all been fixed.
+ - bugfix: This also means that storage animations are fully working again. It's
+ been a few years!
+ - bugfix: The pull animation no longer interrupts ongoing animations.
+ - tweak: The pull animation now supports diagonal directions (currently only applies
+ to the initial grab! pull code is spaghetti)
+ - bugfix: All screenshake sources now fully support the screenshake pref
+ - code_imp: cit_screenshake.dm has been fully merged into the base files.
+ - bugfix: You can no longer remotely apply fingerprints to storage objects by clicking
+ and dragging it around. Additionally, storage objects now rustle again when
+ clicking and dragging (only took like,, a year to fix)
+ - bugfix: The pickup animation now properly slides into your spaceman again, instead
+ of just kinda disappearing.
+ - tweak: When pulling a resting mob, they'll now skip the laying animation entirely,
+ fixing a visual oddity where they shrink and grow to flip.
+ - tweak: Alerts are now a little snappier and bouncier, as they've been swapped
+ from sine easing, to back easing.
+ - tweak: Radial menus now use sine easing, rather than linear easing. This just
+ looks a little prettier.
+ - tweak: Runechat now properly respects an object's X offset. Previously, it relied
+ on a hardcoded check for dogborgs.
+ - tweak: The float animation now uses pixel_z instead of pixel_y, fixing issues
+ where the Y offset gets wiped due to the float animation. This also makes floating
+ fully compatible with ctrl+WASD."
+ BongaTheProto:
+ - rscadd: New panting emote
+ - rscadd: Adds a recipe for the cum donut
+ - rscadd: Adds a panting emote
+ - rscadd: Adds Dominant Aura and Well-trained quirks
+ - rscadd: Snack alt title for assistants
+ - soundadd: Adds sounds for the kiss them deeply interaction
+ - soundadd: Adds moaning sounds for verb moans
+ - tweak: Changes bane syndrome's value to -2
+ - tweak: Makes xenocommon a selectable language
+ - tweak: Fucking people's throats gives an actually harmful amount of oxyloss again
+ - tweak: If you have the voracious trait, the fat moodlet will become a positive
+ one.
+ - tweak: headpat slut will now give you an modified wuv element and possibly make
+ you cum
+ - bugfix: Fixes the notify verb so it actually removes you from the list if you
+ want it to
+ - bugfix: makes the cheesed moodlet actually check if you're a felinid
+ - bugfix: Gives Bane Syndrome its effects back
+ - bugfix: You can now actually dispense tea from the Soda Dispenser, rejoice!
+ - rscdel: deletes the double railing declaration in the code
+ - code_imp: changes some things to use the cooldown system
+ - code_imp: puts signal handlers in a couple procs where they needed to be
+ - code_imp: Adds a signal for when mobs use emotes
+ SandPoot:
+ - refactor: Does some extremely minor touches on pulling/pushing code.
+ timothyteakettle:
+ - rscadd: updates existing poly maid outfit with tg sprite but polychromatic
+ - rscadd: adds polychromic maid apron, gloves, collar, headband
+ - rscadd: adds support for polychromic accessories
+2022-06-08:
+ SandPoot:
+ - tweak: The "MobInteraction" is now written on typescript.
+2022-06-09:
+ Anonymous:
+ - rscadd: Players, who somehow ended up controlling a simple mob, now can change
+ their genital set just like robots and pAIs.
+ - rscdel: My sanity.
diff --git a/icons/mob/clothing/accessories.dmi b/icons/mob/clothing/accessories.dmi
index 03849efa5a..f801c483f1 100644
Binary files a/icons/mob/clothing/accessories.dmi and b/icons/mob/clothing/accessories.dmi differ
diff --git a/icons/mob/clothing/hands.dmi b/icons/mob/clothing/hands.dmi
index ca8a7312fa..9bd206e04e 100644
Binary files a/icons/mob/clothing/hands.dmi and b/icons/mob/clothing/hands.dmi differ
diff --git a/icons/mob/clothing/neck.dmi b/icons/mob/clothing/neck.dmi
index 20b8576277..2b317737d7 100644
Binary files a/icons/mob/clothing/neck.dmi and b/icons/mob/clothing/neck.dmi differ
diff --git a/icons/mob/clothing/uniform.dmi b/icons/mob/clothing/uniform.dmi
index 04d9b20a75..ce25963360 100644
Binary files a/icons/mob/clothing/uniform.dmi and b/icons/mob/clothing/uniform.dmi differ
diff --git a/icons/obj/clothing/accessories.dmi b/icons/obj/clothing/accessories.dmi
index 79cb2bff77..f633f030c3 100644
Binary files a/icons/obj/clothing/accessories.dmi and b/icons/obj/clothing/accessories.dmi differ
diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi
index d4cd4b4f2f..17073805c9 100644
Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ
diff --git a/icons/obj/clothing/neck.dmi b/icons/obj/clothing/neck.dmi
index 8540b2f392..c9491af6b2 100644
Binary files a/icons/obj/clothing/neck.dmi and b/icons/obj/clothing/neck.dmi differ
diff --git a/icons/obj/clothing/uniforms.dmi b/icons/obj/clothing/uniforms.dmi
index c16b416cd8..de7dd234b1 100644
Binary files a/icons/obj/clothing/uniforms.dmi and b/icons/obj/clothing/uniforms.dmi differ
diff --git a/modular_citadel/code/game/objects/cit_screenshake.dm b/modular_citadel/code/game/objects/cit_screenshake.dm
deleted file mode 100644
index 222de37f82..0000000000
--- a/modular_citadel/code/game/objects/cit_screenshake.dm
+++ /dev/null
@@ -1,51 +0,0 @@
-//we vlambeer now
-
-/obj/proc/shake_camera(mob/M, duration, strength=1)//byond's wonky with this shit
- set waitfor = FALSE
- if(!M || !M.client || duration <= 0)
- return
- var/client/C = M.client
- if (C.prefs.screenshake==0)
- return
- var/oldx = C.pixel_x
- var/oldy = C.pixel_y
- var/clientscreenshake = (C.prefs.screenshake * 0.01)
- var/max = (strength*clientscreenshake) * world.icon_size
- var/min = -((strength*clientscreenshake) * world.icon_size)
-
- for(var/i in 0 to duration-1)
- if (i == 0)
- animate(C, pixel_x=rand(min,max), pixel_y=rand(min,max), time=1)
- else
- animate(pixel_x=rand(min,max), pixel_y=rand(min,max), time=1)
- animate(pixel_x=oldx, pixel_y=oldy, time=1)
-
-/obj/item/gun/energy
- recoil = 0.1
-
-/obj/item/gun/energy/kinetic_accelerator
- recoil = 0.5
-
-/obj/item/gun/ballistic
- recoil = 0.25
-
-/obj/item/gun/ballistic/shotgun
- recoil = 1
-
-/obj/item/gun/ballistic/revolver
- recoil = 0.5
-
-/obj/item/gun/ballistic/revolver/doublebarrel
- recoil = 1
-
-/obj/item/gun/syringe
- recoil = 0.1
-
-/obj/item/pneumatic_cannon/fire_items(turf/target, mob/user)
- . = ..()
- shake_camera(user, (pressureSetting * 0.75 + 1), (pressureSetting * 0.75))
-
-/obj/item/attack_obj(obj/O, mob/living/user)
- . = ..()
- if(force >= 20)
- shake_camera(user, ((force - 15) * 0.01 + 1), ((force - 15) * 0.01))
diff --git a/modular_citadel/code/modules/client/loadout/backpack.dm b/modular_citadel/code/modules/client/loadout/backpack.dm
index 3824993009..0573a1a22a 100644
--- a/modular_citadel/code/modules/client/loadout/backpack.dm
+++ b/modular_citadel/code/modules/client/loadout/backpack.dm
@@ -175,3 +175,9 @@
/datum/gear/backpack/necklace//this is here because loadout doesn't support proper accessories
name = "A renameable necklace"
path = /obj/item/clothing/accessory/necklace
+
+/datum/gear/backpack/polymaidapron //this is ALSO here because loadout doesn't support proper accessories
+ name = "Polychromic maid apron"
+ path = /obj/item/clothing/accessory/maidapron/polychromic
+ loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
+ loadout_initial_colors = list("#333333", "#FFFFFF")
diff --git a/modular_citadel/code/modules/client/loadout/gloves.dm b/modular_citadel/code/modules/client/loadout/gloves.dm
index 0b87ec65e6..766c6a7cac 100644
--- a/modular_citadel/code/modules/client/loadout/gloves.dm
+++ b/modular_citadel/code/modules/client/loadout/gloves.dm
@@ -13,3 +13,9 @@
/datum/gear/gloves/midnight
name = "Midnight gloves"
path = /obj/item/clothing/gloves/evening/black
+
+/datum/gear/gloves/maidpoly // WHAT DO YOU EVEN CALL THEM
+ name = "Polychromic maid gloves"
+ path = /obj/item/clothing/gloves/polymaid
+ loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
+ loadout_initial_colors = list("#333333", "#FFFFFF")
diff --git a/modular_citadel/code/modules/client/loadout/head.dm b/modular_citadel/code/modules/client/loadout/head.dm
index f2a7ead5be..d0563d2986 100644
--- a/modular_citadel/code/modules/client/loadout/head.dm
+++ b/modular_citadel/code/modules/client/loadout/head.dm
@@ -58,6 +58,12 @@
name = "Maid headband"
path= /obj/item/clothing/head/maid
+/datum/gear/head/maidband/poly
+ name = "Polychromic maid headband"
+ path= /obj/item/clothing/head/maid/polychromic
+ loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
+ loadout_initial_colors = list("#333333", "#FFFFFF")
+
/datum/gear/head/flakhelm
name = "Flak Helmet"
path = /obj/item/clothing/head/flakhelm
diff --git a/modular_citadel/code/modules/client/loadout/neck.dm b/modular_citadel/code/modules/client/loadout/neck.dm
index dc7735de2b..64d1fcdc1f 100644
--- a/modular_citadel/code/modules/client/loadout/neck.dm
+++ b/modular_citadel/code/modules/client/loadout/neck.dm
@@ -106,3 +106,9 @@
path = /obj/item/clothing/neck/cloak/cancloak/polychromic
loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
loadout_initial_colors = list("#585858", "#373737", "#BEBEBE")
+
+/datum/gear/neck/maid
+ name = "Polychromatic Maid Collar"
+ path = /obj/item/clothing/neck/maid
+ loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
+ loadout_initial_colors = list("#333333", "#FFFFFF")
diff --git a/modular_citadel/code/modules/client/loadout/uniform.dm b/modular_citadel/code/modules/client/loadout/uniform.dm
index 4404d70bcd..c0c7846112 100644
--- a/modular_citadel/code/modules/client/loadout/uniform.dm
+++ b/modular_citadel/code/modules/client/loadout/uniform.dm
@@ -52,7 +52,7 @@
name = "Polychromic maid costume"
path = /obj/item/clothing/under/rank/civilian/janitor/maid/polychromic
loadout_flags = LOADOUT_CAN_NAME | LOADOUT_CAN_DESCRIPTION | LOADOUT_CAN_COLOR_POLYCHROMIC
- loadout_initial_colors = list("#FFFFFF", "#000000")
+ loadout_initial_colors = list("#333333", "#FFFFFF")
/datum/gear/uniform/mailmanuniform
name = "Mailman's jumpsuit"
diff --git a/modular_sand/code/datums/interactions/interaction_datums/lewd/facefuck.dm b/modular_sand/code/datums/interactions/interaction_datums/lewd/facefuck.dm
index e66cce7f28..8591f2f8a8 100644
--- a/modular_sand/code/datums/interactions/interaction_datums/lewd/facefuck.dm
+++ b/modular_sand/code/datums/interactions/interaction_datums/lewd/facefuck.dm
@@ -131,8 +131,8 @@
"slams in and out of \the [partner]'s mouth, [u_His] balls slapping off [t_His] face.")]"
if(rand(3))
partner.emote("chokes on \the [user]")
- if(prob(1) && istype(partner))
- partner.adjustOxyLoss(5)
+ /*if(prob(1) && istype(partner)) //Handled on modular_splurt
+ partner.adjustOxyLoss(5)*/
if(partner.a_intent == INTENT_HARM)
// adjustBruteLoss(5)
retaliation_message = pick(
diff --git a/modular_sand/code/modules/clothing/gloves/_gloves.dm b/modular_sand/code/modules/clothing/gloves/_gloves.dm
index b1984e9770..65f7de4825 100644
--- a/modular_sand/code/modules/clothing/gloves/_gloves.dm
+++ b/modular_sand/code/modules/clothing/gloves/_gloves.dm
@@ -1,16 +1,14 @@
/obj/item/clothing/gloves
var/dummy_thick = FALSE // is able to hold accessories on its item
- // Sandstorm edit - Removed the old attached accessory system. We use a list of accessories instead.
- var/list/obj/item/clothing/accessory/ring/attached_accessories = list()
var/max_accessories = 1
- // Sandstorm edit END
- var/mutable_appearance/accessory_overlay
+ var/list/obj/item/clothing/accessory/ring/attached_accessories = list()
+ var/list/mutable_appearance/accessory_overlays = list()
/obj/item/clothing/gloves/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
. = ..()
if(!isinhands)
- if(accessory_overlay)
- . += accessory_overlay
+ if(length(accessory_overlays))
+ . += accessory_overlays
/obj/item/clothing/gloves/attackby(obj/item/I, mob/user, params)
if(!attach_accessory(I, user))
@@ -20,7 +18,7 @@
. = ..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
return
- if(length(attached_accessories)) // Sandstorm edit
+ if(length(attached_accessories))
remove_accessory(user)
/obj/item/clothing/gloves/equipped(mob/user, slot)
@@ -70,14 +68,16 @@
if((flags_inv & HIDEACCESSORY) || (A.flags_inv & HIDEACCESSORY))
return TRUE
- // Sandstorm edit
- accessory_overlay = mutable_appearance(I.mob_overlay_icon, I.item_state)
- for(var/obj/item/clothing/accessory/ring/attached_accessory in attached_accessories)
- var/mutable_appearance/Y = mutable_appearance(attached_accessory.mob_overlay_icon, attached_accessory.icon_state, ABOVE_HUD_LAYER)
- Y.alpha = attached_accessory.alpha
- Y.color = attached_accessory.color
- accessory_overlay.add_overlay(Y)
- // Sandstorm edit END
+ accessory_overlays = list(mutable_appearance('icons/mob/clothing/accessories.dmi', "blank"))
+ for(var/obj/item/clothing/accessory/attached_accessory in attached_accessories)
+ var/datum/element/polychromic/polychromic = LAZYACCESS(attached_accessory.comp_lookup, "item_worn_overlays")
+ if(!polychromic)
+ var/mutable_appearance/accessory_overlay = mutable_appearance(attached_accessory.mob_overlay_icon, attached_accessory.item_state || attached_accessory.icon_state, ABOVE_HUD_LAYER)
+ accessory_overlay.alpha = attached_accessory.alpha
+ accessory_overlay.color = attached_accessory.color
+ accessory_overlays += accessory_overlay
+ else
+ polychromic.apply_worn_overlays(attached_accessory, FALSE, attached_accessory.mob_overlay_icon, attached_accessory.item_state || attached_accessory.icon_state, NONE, accessory_overlays)
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
@@ -91,10 +91,8 @@
if(!can_use(user))
return
- // Sandstorm edit
if(length(attached_accessories))
var/obj/item/clothing/accessory/ring/A = attached_accessories[length(attached_accessories)]
- // Sandstorm edit END
A.detach(src, user)
if(user.put_in_hands(A))
to_chat(user, "You detach [A] from [src].")
diff --git a/modular_sand/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/modular_sand/code/modules/reagents/chemistry/reagents/drink_reagents.dm
index 5b80e1b91c..c0b7ce2ca5 100644
--- a/modular_sand/code/modules/reagents/chemistry/reagents/drink_reagents.dm
+++ b/modular_sand/code/modules/reagents/chemistry/reagents/drink_reagents.dm
@@ -145,7 +145,7 @@
/datum/reagent/consumable/pinkmilk
hydration = 4
-/datum/reagent/consumable/pinktea
+/datum/reagent/consumable/tea/pink
hydration = 4
/datum/reagent/consumable/monkey_energy
diff --git a/modular_splurt/README.md b/modular_splurt/README.md
index 1249b0be0f..c0c251cf64 100644
--- a/modular_splurt/README.md
+++ b/modular_splurt/README.md
@@ -1,5 +1,5 @@
# About modularizing defines
-Since #define clauses can't be properly modularized and need to be loaded first than the rest of the code for it to catch them, the modular_splurt/code/_DEFINES/ folder has been deleted and all of its contents have been moved to code/__DEFINES/splurtcode/
+Since #define clauses can't be properly modularized and need to be loaded first than the rest of the code for it to catch them, the modular_splurt/code/_DEFINES/ folder has been deleted and all of its contents have been moved to code\\__SPLURTCODE\\DEFINES
Please use this folder the same way you'd use the _DEFINES folder once found inside this directory
diff --git a/modular_splurt/code/datums/elements/smalltalk.dm b/modular_splurt/code/datums/elements/smalltalk.dm
index cc236e7036..9ffa626757 100644
--- a/modular_splurt/code/datums/elements/smalltalk.dm
+++ b/modular_splurt/code/datums/elements/smalltalk.dm
@@ -14,4 +14,6 @@
UnregisterSignal(source, COMSIG_MOB_SAY)
/datum/element/smalltalk/proc/handle_speech(datum/source, list/speech_args)
+ SIGNAL_HANDLER
+
speech_args[SPEECH_SPANS] |= SPAN_SMALL
diff --git a/modular_splurt/code/datums/elements/wuv.dm b/modular_splurt/code/datums/elements/wuv.dm
new file mode 100644
index 0000000000..de94b61992
--- /dev/null
+++ b/modular_splurt/code/datums/elements/wuv.dm
@@ -0,0 +1,13 @@
+/datum/element/wuv/headpat/pet_the_dog(mob/target, mob/user) //For instances where only the headpat applies
+ if(QDELETED(target) || QDELETED(user) || target.stat != CONSCIOUS)
+ return
+ if(!(check_zone(user.zone_selected) == BODY_ZONE_HEAD) || user == target)
+ return
+ new /obj/effect/temp_visual/heart(target.loc)
+ if(pet_emote)
+ target.emote("me", pet_type, pet_emote)
+ if(pet_moodlet && !(target.flags_1 & HOLOGRAM_1)) //prevents unlimited happiness petting park exploit.
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, target, pet_moodlet, target)
+
+/datum/element/wuv/headpat/kick_the_dog(mob/target, mob/user)
+ return //No kicking the dog >:(( (until I find an use for this that is)
diff --git a/modular_splurt/code/datums/interactions/lewd/_lewd.dm b/modular_splurt/code/datums/interactions/lewd/_lewd.dm
index 7f5ecffe14..85fabb32bf 100644
--- a/modular_splurt/code/datums/interactions/lewd/_lewd.dm
+++ b/modular_splurt/code/datums/interactions/lewd/_lewd.dm
@@ -1,6 +1,21 @@
/mob/living
var/has_belly = FALSE
+/mob/living/moan()
+ var/moaned = lastmoan
+ var/miming = mind ? mind?.miming : FALSE
+ . = ..()
+ if((moaned == lastmoan) || is_muzzled() || miming || !prob(50))
+ return
+ var/list/moans
+ if(isalien(src))
+ moans = list('sound/voice/hiss6.ogg')
+ else if(gender == FEMALE)
+ moans = list('modular_splurt/sound/voice/moan_f1.ogg', 'modular_splurt/sound/voice/moan_f2.ogg', 'modular_splurt/sound/voice/moan_f3.ogg', 'modular_splurt/sound/voice/moan_f4.ogg', 'modular_splurt/sound/voice/moan_f5.ogg', 'modular_splurt/sound/voice/moan_f6.ogg', 'modular_splurt/sound/voice/moan_f7.ogg')
+ else
+ moans = list('modular_splurt/sound/voice/moan_m1.ogg', 'modular_splurt/sound/voice/moan_m2.ogg', 'modular_splurt/sound/voice/moan_m3.ogg')
+ playlewdinteractionsound(src, pick(moans), 50, 1, 4, 1.2, ignored_mobs = get_unconsenting())
+
/mob/living/proc/get_refraction_dif() //Got snapped in upstream, may delete later when I figure something out
var/dif = (refractory_period - world.time)
if(dif < 0)
diff --git a/modular_splurt/code/datums/interactions/lewd/lewd_datums.dm b/modular_splurt/code/datums/interactions/lewd/lewd_datums.dm
index b284be8e1f..c11b335955 100644
--- a/modular_splurt/code/datums/interactions/lewd/lewd_datums.dm
+++ b/modular_splurt/code/datums/interactions/lewd/lewd_datums.dm
@@ -96,6 +96,11 @@
/datum/interaction/lewd/throatfuck/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target)
. = ..()
+ if(istype(target, /mob/living) && user.is_fucking(target, CUM_TARGET_THROAT))
+ var/stat_before = target.stat
+ target.adjustOxyLoss(3)
+ if(target.stat == UNCONSCIOUS && stat_before != UNCONSCIOUS)
+ target.visible_message(message = "\The [target] passes out on \The [src]'s cock.", ignored_mobs = user.get_unconsenting())
if(!isclownjob(user))
return
@@ -358,6 +363,15 @@
playlewdinteractionsound(target, 'sound/items/bikehorn.ogg', 40, 1, -1)
+/datum/interaction/lewd/kiss/display_interaction(mob/living/user, mob/living/partner)
+ . = ..()
+ playlewdinteractionsound(partner, pick(
+ 'modular_splurt/sound/interactions/kiss/kiss1.ogg',
+ 'modular_splurt/sound/interactions/kiss/kiss2.ogg',
+ 'modular_splurt/sound/interactions/kiss/kiss3.ogg',
+ 'modular_splurt/sound/interactions/kiss/kiss4.ogg',
+ 'modular_splurt/sound/interactions/kiss/kiss5.ogg'), 50, 1, -1, ignored_mobs = user.get_unconsenting())
+
//Own stuff
/datum/interaction/lewd/oral/selfsuck
description = "Suck yourself off."
diff --git a/modular_splurt/code/datums/mood_events/dominant_events.dm b/modular_splurt/code/datums/mood_events/dominant_events.dm
new file mode 100644
index 0000000000..f8e41e6b42
--- /dev/null
+++ b/modular_splurt/code/datums/mood_events/dominant_events.dm
@@ -0,0 +1,20 @@
+/datum/mood_event/dominant/need
+ description = span_warning("I need to be someone's good THING...\n")
+ mood_change = -2
+ timeout = 3 MINUTES
+
+/datum/mood_event/dominant/good_boy
+ description = span_nicegreen("I feel like a good THING!\n")
+ mood_change = 2
+ timeout = 3 MINUTES
+
+/datum/mood_event/dominant/add_effects(param)
+ . = ..()
+ var/mob/living/carbon/human/actual_owner = owner_mob()
+ var/good_x = "pet"
+ switch(actual_owner.gender)
+ if(MALE)
+ good_x = "boy"
+ if(FEMALE)
+ good_x = "girl"
+ description = replacetextEx(description, "THING", good_x)
diff --git a/modular_splurt/code/datums/mood_events/generic_negative_events.dm b/modular_splurt/code/datums/mood_events/generic_negative_events.dm
index 2e036154fe..c66f79e83d 100644
--- a/modular_splurt/code/datums/mood_events/generic_negative_events.dm
+++ b/modular_splurt/code/datums/mood_events/generic_negative_events.dm
@@ -7,11 +7,11 @@
/datum/mood_event/creampie/cheesed/add_effects(param)
. = ..()
- if(!ishuman(owner))
- return .
- var/mob/living/carbon/human/H = owner
+ var/mob/living/carbon/human/actual_owner = owner_mob()
- if(iscatperson(H))
+ if(!ishuman(actual_owner))
+ return .
+ if(iscatperson(actual_owner))
description = span_warning("CHEESE!!! WAAAAAAAAAAAAAAAAAAAAAAAAAAAA!!!")
mood_change = -5
timeout = 5 MINUTES
diff --git a/modular_splurt/code/datums/mood_events/mood_event.dm b/modular_splurt/code/datums/mood_events/mood_event.dm
new file mode 100644
index 0000000000..961f0dc311
--- /dev/null
+++ b/modular_splurt/code/datums/mood_events/mood_event.dm
@@ -0,0 +1,8 @@
+/datum/mood_event/proc/owner_mob()
+ var/mob/living/carbon/human/actual_owner
+ var/datum/component/mood/wrong_declaration = owner
+ if(istype(wrong_declaration))
+ actual_owner = wrong_declaration.parent
+ else
+ actual_owner = owner
+ return actual_owner
diff --git a/modular_splurt/code/datums/mood_events/needs_events.dm b/modular_splurt/code/datums/mood_events/needs_events.dm
new file mode 100644
index 0000000000..da5a575ebb
--- /dev/null
+++ b/modular_splurt/code/datums/mood_events/needs_events.dm
@@ -0,0 +1,7 @@
+/datum/mood_event/fat/add_effects(param)
+ . = ..()
+ var/mob/living/carbon/human/actual_owner = owner_mob()
+ if(!HAS_TRAIT(actual_owner, TRAIT_VORACIOUS))
+ return
+ description = span_nicegreen("MORE FOOD!!! MORE FOOD!!! MORE FOOD!!!")
+ mood_change = 8
diff --git a/modular_splurt/code/datums/traits/good.dm b/modular_splurt/code/datums/traits/good.dm
index ba1fa91706..d5ab76b3be 100644
--- a/modular_splurt/code/datums/traits/good.dm
+++ b/modular_splurt/code/datums/traits/good.dm
@@ -33,3 +33,75 @@
quirk_holder.weather_immunities -= "ash"
*/
+/datum/quirk/dominant_aura
+ name = "Dominant Aura"
+ desc = "Your personality 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 on command."
+ value = 2
+ gain_text = "You feel like making someone your pet"
+ lose_text = "You feel less assertive than befpre"
+
+/datum/quirk/dominant_aura/add()
+ . = ..()
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, .proc/on_examine_holder)
+ RegisterSignal(quirk_holder, COMSIG_MOB_EMOTE, .proc/handle_snap)
+
+/datum/quirk/dominant_aura/remove()
+ . = ..()
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+ UnregisterSignal(quirk_holder, COMSIG_MOB_EMOTE)
+
+/datum/quirk/dominant_aura/proc/on_examine_holder(atom/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/sub = user
+ if(!sub.has_quirk(/datum/quirk/well_trained) || (sub == quirk_holder))
+ return
+
+ examine_list += span_lewd("\nYou can't look at [quirk_holder.p_them()] for more than three seconds before flustering away.")
+ if(!TIMER_COOLDOWN_CHECK(user, COOLDOWN_DOMINANT_EXAMINE))
+ to_chat(quirk_holder, span_notice("\The [user] tries to look at you but immediately looks away with a red face..."))
+ TIMER_COOLDOWN_START(user, COOLDOWN_DOMINANT_EXAMINE, 5 SECONDS)
+ sub.dir = turn(get_dir(sub, quirk_holder), pick(-90, 90))
+
+/datum/quirk/dominant_aura/proc/handle_snap(datum/source, list/emote_args)
+ SIGNAL_HANDLER
+
+ . = FALSE
+ var/datum/emote/E
+ E = E.emote_list[lowertext(emote_args[EMOTE_ACT])]
+ if(TIMER_COOLDOWN_CHECK(quirk_holder, COOLDOWN_DOMINANT_SNAP) || !findtext(E?.key, "snap"))
+ return
+ for(var/mob/living/carbon/human/sub in hearers(DOMINANT_DETECT_RANGE, quirk_holder))
+ if(!sub.has_quirk(/datum/quirk/well_trained) || (sub == quirk_holder))
+ continue
+ var/good_x = "pet"
+ switch(sub.gender)
+ if(MALE)
+ good_x = "boy"
+ if(FEMALE)
+ good_x = "girl"
+ switch(E?.key)
+ if("snap")
+ sub.dir = get_dir(sub, quirk_holder)
+ sub.emote(pick("blush", "pant"))
+ sub.visible_message(span_notice("\The [sub] turns shyly towards \the [quirk_holder]."),
+ span_lewd("You stare into \the [quirk_holder] submissively."))
+ if("snap2")
+ sub.dir = get_dir(sub, quirk_holder)
+ sub.KnockToFloor()
+ sub.emote(pick("blush", "pant"))
+ sub.visible_message(span_lewd("\The [sub] submissively throws [sub.p_them()]self on the floor."),
+ span_lewd("You throw yourself on the floor like a dog on [quirk_holder]'s command."))
+ if("snap3")
+ sub.KnockToFloor()
+ step(sub, get_dir(sub, quirk_holder))
+ sub.emote(pick("blush", "pant"))
+ sub.do_jitter_animation(30) //You're being moved anyways
+ sub.visible_message(span_lewd("\The [sub] crawls closer to \the [quirk_holder] in all fours, following [quirk_holder.p_their()] command"),
+ span_lewd("You get on your fours and crawl towards \the [quirk_holder] like a good, submissive [good_x]."))
+ . = TRUE
+
+ if(.)
+ TIMER_COOLDOWN_START(quirk_holder, COOLDOWN_DOMINANT_SNAP, DOMINANT_SNAP_COOLDOWN)
diff --git a/modular_splurt/code/datums/traits/negative.dm b/modular_splurt/code/datums/traits/negative.dm
index 0aa149375b..45dafa4261 100644
--- a/modular_splurt/code/datums/traits/negative.dm
+++ b/modular_splurt/code/datums/traits/negative.dm
@@ -57,3 +57,113 @@
var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
if(mood)
mood.mood_modifier -= 0.5
+
+/datum/quirk/masked_mook
+ name = "Bane Syndrome"
+ desc = "For some reason you don't feel well without wearing some kind of gas mask."
+ gain_text = "You start feeling unwell without any gas mask on."
+ lose_text = "You no longer have a need to wear some gas mask."
+ value = -2
+ 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)
+ else
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, mood_category, /datum/mood_event/masked_mook_incomplete)
+
+/datum/quirk/masked_mook/on_spawn()
+ . = ..()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/obj/item/clothing/mask/gas/gasmask = new(get_turf(quirk_holder))
+ H.equip_to_slot(gasmask, ITEM_SLOT_MASK)
+ H.regenerate_icons()
+
+/datum/quirk/well_trained
+ name = "Well-trained"
+ desc = "You absolutely love being dominated. The thought of someone with a stronger character than yours is enough to make you act up."
+ value = -2
+ gain_text = "You feel like being someone's pet"
+ lose_text = "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
+
+/datum/quirk/well_trained/add()
+ . = ..()
+ RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, .proc/on_examine_holder)
+
+/datum/quirk/well_trained/remove()
+ . = ..()
+ UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE)
+
+/datum/quirk/well_trained/proc/on_examine_holder(atom/source, mob/living/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ if(!istype(user))
+ return
+ if(!user.has_quirk(/datum/quirk/dominant_aura))
+ return
+ examine_list += span_lewd("You can sense submissiveness irradiating from [quirk_holder.p_them()]")
+
+/datum/quirk/well_trained/on_process()
+ . = ..()
+ if(!quirk_holder)
+ return
+
+ var/good_x = "pet"
+ switch(quirk_holder.gender)
+ if(MALE)
+ good_x = "boy"
+ if(FEMALE)
+ good_x = "girl"
+
+ //Check for possible doms with the dominant_aura quirk, and for the closest one if there is
+ . = FALSE
+ var/list/mob/living/carbon/human/doms = range(DOMINANT_DETECT_RANGE, quirk_holder)
+ var/closest_distance
+ for(var/mob/living/carbon/human/dom in doms)
+ if(dom != quirk_holder && dom.has_quirk(/datum/quirk/dominant_aura))
+ if(!closest_distance || get_dist(quirk_holder, dom) <= closest_distance)
+ . = dom
+ closest_distance = get_dist(quirk_holder, dom)
+
+ //Return if no dom is found
+ if(!.)
+ last_dom = null
+ return
+
+ //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)
+ else
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, mood_category, /datum/mood_event/dominant/need)
+
+ //Don't do anything if a previous dom was found
+ if(last_dom)
+ notice_delay = world.time + 15 SECONDS
+ return
+
+ last_dom = .
+
+ if(notice_delay > world.time)
+ return
+
+ //Let them know they're near
+ var/list/notices = list(
+ "You feel someone's presence making you more submissive.",
+ "The thought of being commanded floods you with lust.",
+ "You really want to be called a good [good_x].",
+ "Someone's presence is making you all flustered.",
+ "You start getting excited and sweating."
+ )
+
+ to_chat(quirk_holder, span_lewd(pick(notices)))
+ notice_delay = world.time + 15 SECONDS
diff --git a/modular_splurt/code/datums/traits/neutral.dm b/modular_splurt/code/datums/traits/neutral.dm
index d816747e71..827432936b 100644
--- a/modular_splurt/code/datums/traits/neutral.dm
+++ b/modular_splurt/code/datums/traits/neutral.dm
@@ -49,6 +49,14 @@
value = 0
medical_record_text = "Patient seems overly affectionate."
+/datum/quirk/headpat_slut/add()
+ . = ..()
+ quirk_holder.AddElement(/datum/element/wuv/headpat, null, null, /datum/mood_event/pet_animal)
+
+/datum/quirk/headpat_slut/remove()
+ . = ..()
+ quirk_holder.RemoveElement(/datum/element/wuv/headpat)
+
/datum/quirk/in_heat
name = "In Heat"
desc = "Your system burns with the desire to be bred. Satisfying your lust will make you happy, but ignoring it may cause you to become sad and needy."
@@ -284,29 +292,3 @@
. = ..()
var/obj/item/implant/genital_fluid/put_in = new
put_in.implant(quirk_holder, null, TRUE, TRUE)
-
-/datum/quirk/masked_mook
- name = "Bane Syndrome"
- desc = "For some reason you don't feel well without wearing some kind of gas mask."
- gain_text = "You start feeling unwell without any gas mask on."
- lose_text = "You no longer have a need to wear some gas mask."
- value = 0
- mood_quirk = TRUE
- medical_record_text = "Patient feels more secure when wearing a gas mask."
-
-/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_CLEAR_MOOD_EVENT, "masked_mook_incomplete")
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "masked_mook", /datum/mood_event/masked_mook)
- else
- SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "masked_mook")
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "masked_mook_incomplete", /datum/mood_event/masked_mook_incomplete)
-
-/datum/quirk/masked_mook/on_spawn()
- . = ..()
- var/mob/living/carbon/human/H = quirk_holder
- var/obj/item/clothing/mask/gas/gasmask = new(get_turf(quirk_holder))
- H.equip_to_slot(gasmask, ITEM_SLOT_MASK)
- H.regenerate_icons()
diff --git a/modular_splurt/code/game/object/structures/railings.dm b/modular_splurt/code/game/object/structures/railings.dm
deleted file mode 100644
index ca1e6e2c9e..0000000000
--- a/modular_splurt/code/game/object/structures/railings.dm
+++ /dev/null
@@ -1,198 +0,0 @@
-//ported from virgo
-
-/obj/structure/railing
- name = "railing"
- desc = "A railing to stop people from falling"
-
- icon = 'modular_splurt/icons/obj/railings.dmi'
- var/icon_modifier = "grey_"
- icon_state = "grey_railing0"
-
- density = FALSE
- layer = 4
- anchored = TRUE
- flags_1 = ON_BORDER_1
- max_integrity = 250
- var/heat_resistance = 800
- var/health = 70
- var/maxhealth = 70
-
-
- resistance_flags = ACID_PROOF
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100)
- CanAtmosPass = ATMOS_PASS_PROC
- var/real_explosion_block //ignore this, just use explosion_block
- var/breaksound = "shatter"
- var/hitsound = 'sound/effects/Glasshit.ogg'
- rad_insulation = RAD_VERY_LIGHT_INSULATION
- rad_flags = RAD_PROTECT_CONTENTS
- var/check = 0
- var/static/list/freepass = typecacheof(list(
- /obj/singularity,
- /obj/effect/projectile,
- /obj/effect/portal,
- /obj/effect/abstract,
- /obj/effect/hotspot,
- /obj/effect/landmark,
- /obj/effect/temp_visual,
- /obj/effect/light_emitter/tendril,
- /obj/effect/collapse,
- /obj/effect/particle_effect/ion_trails,
- /obj/effect/dummy/phased_mob,
- /obj/effect/immovablerod,
- /obj/effect/crystalline_reentry
- )) //Gotta make sure certain things can phase through it otherwise the railings also block them.
-
-/obj/structure/railing/CanPass(atom/movable/mover, turf/target)
- if(istype(mover) && (mover.pass_flags & PASSGLASS) || is_type_in_typecache(mover, freepass))
- return 1
- if(get_dir(loc, target) == dir)
- return 0
- return 1
-
-/obj/structure/railing/CheckExit(atom/movable/O, turf/target)
- if(istype(O) && (O.pass_flags & PASSGLASS) || is_type_in_typecache(O, freepass))
- return 1
- if(get_dir(O.loc, target) == dir)
- return 0
- return 1
-
-/obj/structure/railing/Initialize()
- . = ..()
- if(src.anchored)
- update_icon(0)
-
-/obj/structure/railing/proc/NeighborsCheck(var/UpdateNeighbors = 1)
- check = 0
- //if (!anchored) return
- var/Rturn = turn(src.dir, -90)
- var/Lturn = turn(src.dir, 90)
-
- for(var/obj/structure/railing/R in src.loc)
- if ((R.dir == Lturn) && R.anchored)
- check |= 32
- if (UpdateNeighbors)
- R.update_icon(0)
- if ((R.dir == Rturn) && R.anchored)
- check |= 2
- if (UpdateNeighbors)
- R.update_icon(0)
-
- for (var/obj/structure/railing/R in get_step(src, Lturn))
- if ((R.dir == src.dir) && R.anchored)
- check |= 16
- if (UpdateNeighbors)
- R.update_icon(0)
- for (var/obj/structure/railing/R in get_step(src, Rturn))
- if ((R.dir == src.dir) && R.anchored)
- check |= 1
- if (UpdateNeighbors)
- R.update_icon(0)
-
- for (var/obj/structure/railing/R in get_step(src, (Lturn + src.dir)))
- if ((R.dir == Rturn) && R.anchored)
- check |= 64
- if (UpdateNeighbors)
- R.update_icon(0)
- for (var/obj/structure/railing/R in get_step(src, (Rturn + src.dir)))
- if ((R.dir == Lturn) && R.anchored)
- check |= 4
- if (UpdateNeighbors)
- R.update_icon(0)
-
-/obj/structure/railing/update_icon(var/UpdateNeighgors = 1)
- NeighborsCheck(UpdateNeighgors)
- overlays.Cut()
- if (!check || !anchored)//|| !anchored
- icon_state = "[icon_modifier]railing0"
- else
- icon_state = "[icon_modifier]railing1"
- if (check & 32)
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]corneroverlay")
- if ((check & 16) || !(check & 32) || (check & 64))
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]frontoverlay_l")
- if (!(check & 2) || (check & 1) || (check & 4))
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]frontoverlay_r")
- if(check & 4)
- switch (src.dir)
- if (NORTH)
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_x = 32)
- if (SOUTH)
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_x = -32)
- if (EAST)
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_y = -32)
- if (WEST)
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_y = 32)
-
-/obj/structure/railing/examine(mob/user)
- . = ..()
- if(health < maxhealth)
- switch(health / maxhealth)
- if(0.0 to 0.5)
- . += "It looks severely damaged!"
- if(0.25 to 0.5)
- . += "It looks damaged!"
- if(0.5 to 1.0)
- . += "It has a few scrapes and dents."
-
-/obj/structure/railing/take_damage(amount)
- health -= amount
- if(health <= 0)
- visible_message("\The [src] breaks down!")
- playsound(src, 'sound/effects/grillehit.ogg', 50, 1)
- new /obj/item/stack/rods(get_turf(src))
- qdel(src)
-
-/obj/structure/railing/MouseDrop_T(mob/living/M, mob/living/user)
- if(!istype(user))
- return
- if(!isliving(user))
- return
-
- usr.visible_message("[user] starts climbing onto \the [src]!")
-
- if(!do_after(user, 15, target = src))
- return
-
- if(get_turf(user) == get_turf(src))
- usr.dir = get_dir(usr.loc, get_step(src, src.dir))//turn and face railing
- usr.forceMove(get_step(src, src.dir))
- else
- usr.dir = get_dir(usr.loc, loc)//turn and face railing
- usr.forceMove(get_turf(src))
-
- usr.visible_message("[user] climbed over \the [src]!")
- usr.do_twist(targetangle = 45, timer = 8)
-
-/obj/structure/railing/handrail
- name = "handrail"
- desc = "A waist high handrail, perhaps you could climb over it."
-
- icon = 'modular_splurt/icons/obj/railings.dmi'
- icon_modifier = "hand_"
- icon_state = "hand_railing0"
- max_integrity = 100
-
-/obj/structure/railing/handrail/update_icon(var/UpdateNeighgors = 1)
- NeighborsCheck(UpdateNeighgors)
- overlays.Cut()
- if (!check || !anchored)//|| !anchored
- icon_state = "[icon_modifier]railing0"
- else
- icon_state = "[icon_modifier]railing1"
- if (check & 32)
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]corneroverlay")
- if ((check & 16) || !(check & 32) || (check & 64))
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]frontoverlay_l")
- if (!(check & 2) || (check & 1) || (check & 4))
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]frontoverlay_r")
- if(check & 4)
- switch (src.dir)
- if (NORTH)
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_x = 32)
- if (SOUTH)
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_x = -32)
- if (EAST)
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_y = -32)
- if (WEST)
- overlays += image ('modular_splurt/icons/obj/railings.dmi', src, "[icon_modifier]mcorneroverlay", pixel_y = 32)
diff --git a/modular_splurt/code/modules/discord/toggle_notify.dm b/modular_splurt/code/modules/discord/toggle_notify.dm
index cc08974060..ddfd3dc8db 100644
--- a/modular_splurt/code/modules/discord/toggle_notify.dm
+++ b/modular_splurt/code/modules/discord/toggle_notify.dm
@@ -23,11 +23,11 @@
return
else // Linked
- for(var/member in SSdiscord.notify_members) // If they are in the list, take them out
- if(member == "[stored_id]")
- SSdiscord.notify_members -= "<@[stored_id]>" // The list uses strings because BYOND cannot handle a 17 digit integer
- to_chat(src, span_notice("You will no longer be notified when the server restarts"))
- return // This is necassary so it doesnt get added again, as it relies on the for loop being unsuccessful to tell us if they are in the list or not
+ var/mention_index = SSdiscord.notify_members.Find("<@[stored_id]>")
+ if(mention_index) // If they are in the list, take them out
+ SSdiscord.notify_members -= SSdiscord.notify_members[mention_index] // The list uses strings because BYOND cannot handle a 17 digit integer
+ to_chat(src, span_notice("You will no longer be notified when the server restarts"))
+ return // This is necassary so it doesnt get added again, as it relies on the for loop being unsuccessful to tell us if they are in the list or not
// If we got here, they arent in the list. Chuck 'em in!
to_chat(src, span_notice("You will now be notified when the server restarts"))
diff --git a/modular_splurt/code/modules/food_and_drinks/recipes/tablecraft/recipes_donut.dm b/modular_splurt/code/modules/food_and_drinks/recipes/tablecraft/recipes_donut.dm
new file mode 100644
index 0000000000..3bf6f19d95
--- /dev/null
+++ b/modular_splurt/code/modules/food_and_drinks/recipes/tablecraft/recipes_donut.dm
@@ -0,0 +1,7 @@
+/datum/crafting_recipe/food/donut/cum
+ name = "\"cream\" donut"
+ reqs = list(
+ /datum/reagent/consumable/semen = 10,
+ /obj/item/reagent_containers/food/snacks/donut/plain = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/donut/semen
diff --git a/modular_splurt/code/modules/jobs/job_types/_job_alt_titles.dm b/modular_splurt/code/modules/jobs/job_types/_job_alt_titles.dm
index 37d905e0ae..6cf1d15bee 100644
--- a/modular_splurt/code/modules/jobs/job_types/_job_alt_titles.dm
+++ b/modular_splurt/code/modules/jobs/job_types/_job_alt_titles.dm
@@ -79,7 +79,8 @@
"Tourist",
"Clerk",
"Blacksmith",
- "Bard"
+ "Bard",
+ "Snack"
)
LAZYADD(alt_titles, extra_titles)
diff --git a/modular_splurt/code/modules/language/xenocommon.dm b/modular_splurt/code/modules/language/xenocommon.dm
new file mode 100644
index 0000000000..c0cf0d69c6
--- /dev/null
+++ b/modular_splurt/code/modules/language/xenocommon.dm
@@ -0,0 +1,2 @@
+/datum/language/xenocommon
+ restricted = FALSE //sand, you sometimes know no fun fr
diff --git a/modular_splurt/code/modules/mob/emote.dm b/modular_splurt/code/modules/mob/emote.dm
new file mode 100644
index 0000000000..a8caa144f9
--- /dev/null
+++ b/modular_splurt/code/modules/mob/emote.dm
@@ -0,0 +1,3 @@
+/mob/emote(act, m_type, message, intentional)
+ . = ..()
+ SEND_SIGNAL(src, COMSIG_MOB_EMOTE, args)
diff --git a/modular_splurt/code/modules/mob/living/simple_animal/simple_animal.dm b/modular_splurt/code/modules/mob/living/simple_animal/simple_animal.dm
new file mode 100644
index 0000000000..acb57b6c00
--- /dev/null
+++ b/modular_splurt/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -0,0 +1,23 @@
+/mob/living/simple_animal/verb/toggle_gender()
+ set name = "Set Gender"
+ set desc = "Allows you to set your gender."
+ set category = "IC"
+
+ var/choice = tgui_alert(usr, "Select Gender.", "Gender", list("Both", "Male", "Female", "None"))
+ switch(choice)
+ if("Both")
+ has_penis = TRUE
+ has_balls = TRUE
+ has_vagina = TRUE
+ if("Male")
+ has_penis = TRUE
+ has_balls = TRUE
+ has_vagina = FALSE
+ if("Female")
+ has_penis = FALSE
+ has_balls = FALSE
+ has_vagina = TRUE
+ if("None")
+ has_penis = FALSE
+ has_balls = FALSE
+ has_vagina = FALSE
diff --git a/modular_splurt/code/modules/mob/splurt_emotes.dm b/modular_splurt/code/modules/mob/splurt_emotes.dm
index f7c4bbfe2b..f75a0cecca 100644
--- a/modular_splurt/code/modules/mob/splurt_emotes.dm
+++ b/modular_splurt/code/modules/mob/splurt_emotes.dm
@@ -1,3 +1,4 @@
+//Main code edits
/datum/emote/living/audio_emote/laugh/run_emote(mob/user, params)
. = ..()
if(. && iscarbon(user))
@@ -27,11 +28,8 @@
message = "farts out shitcode."
emote_type = EMOTE_AUDIBLE
-/mob/living
- var/fart_cooldown = 0
-
/datum/emote/living/fart/run_emote(mob/living/user, params, type_override, intentional)
- if(user.fart_cooldown)
+ if(TIMER_COOLDOWN_CHECK(user, COOLDOWN_EMOTE_FART))
to_chat(user, "You try your hardest, but no shart comes out.")
return
var/list/fart_emotes = list( //cope goonies
@@ -82,17 +80,8 @@
. = ..()
if(.)
playsound(user, pick(GLOB.brap_noises), 50, 1, -1)
- var/delay = 3 SECONDS
- user.fart_cooldown = TRUE
- addtimer(CALLBACK(GLOBAL_PROC, .proc/_fart_renew_msg, user), delay)
+ TIMER_COOLDOWN_START(user, COOLDOWN_EMOTE_FART, 3 SECONDS)
-/proc/_fart_renew_msg(mob/living/user)
- if(QDELETED(user))
- return
- //to_chat(user, "Your ass feels full, again.") //full o shit you mean
- user.fart_cooldown = 0
-
-// Hyperstation Emotes
/datum/emote/living/cackle
key = "cackle"
key_third_person = "cackles"
@@ -541,3 +530,18 @@
return
user.nextsoundemote = world.time + 10
playsound(user, pick('modular_splurt/sound/voice/aauugghh1.ogg', 'modular_splurt/sound/voice/aauugghh2.ogg'), 40, 1, -1)
+
+/datum/emote/living/pant
+ key = "pant"
+ key_third_person = "pants"
+ message = "pants!"
+
+/datum/emote/living/pant/run_emote(mob/user, params, type_override, intentional)
+ var/list/pants = list(
+ "pants!",
+ "pants like a dog.",
+ "lets out soft pants.",
+ "pulls [user.p_their()] tongue out, panting."
+ )
+ message = pick(pants)
+ . = ..()
diff --git a/modular_splurt/sound/interactions/kiss/kiss1.ogg b/modular_splurt/sound/interactions/kiss/kiss1.ogg
new file mode 100644
index 0000000000..f4bd734338
Binary files /dev/null and b/modular_splurt/sound/interactions/kiss/kiss1.ogg differ
diff --git a/modular_splurt/sound/interactions/kiss/kiss2.ogg b/modular_splurt/sound/interactions/kiss/kiss2.ogg
new file mode 100644
index 0000000000..18f12d6f04
Binary files /dev/null and b/modular_splurt/sound/interactions/kiss/kiss2.ogg differ
diff --git a/modular_splurt/sound/interactions/kiss/kiss3.ogg b/modular_splurt/sound/interactions/kiss/kiss3.ogg
new file mode 100644
index 0000000000..0f89617f00
Binary files /dev/null and b/modular_splurt/sound/interactions/kiss/kiss3.ogg differ
diff --git a/modular_splurt/sound/interactions/kiss/kiss4.ogg b/modular_splurt/sound/interactions/kiss/kiss4.ogg
new file mode 100644
index 0000000000..a7c47dd8af
Binary files /dev/null and b/modular_splurt/sound/interactions/kiss/kiss4.ogg differ
diff --git a/modular_splurt/sound/interactions/kiss/kiss5.ogg b/modular_splurt/sound/interactions/kiss/kiss5.ogg
new file mode 100644
index 0000000000..31a48c4d62
Binary files /dev/null and b/modular_splurt/sound/interactions/kiss/kiss5.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index ee6da4c85a..7d0486c655 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -154,6 +154,8 @@
#include "code\__DEFINES\dcs\helpers.dm"
#include "code\__DEFINES\dcs\signals.dm"
#include "code\__DEFINES\dcs\signals\signals_subsystem.dm"
+#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm"
+#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_living.dm"
#include "code\__DEFINES\mapping\maploader.dm"
#include "code\__DEFINES\material\worth.dm"
#include "code\__DEFINES\mobs\innate_abilities.dm"
@@ -245,6 +247,7 @@
#include "code\__SPLURTCODE\DEFINES\atmospherics.dm"
#include "code\__SPLURTCODE\DEFINES\cit_defines.dm"
#include "code\__SPLURTCODE\DEFINES\click.dm"
+#include "code\__SPLURTCODE\DEFINES\cooldowns.dm"
#include "code\__SPLURTCODE\DEFINES\genital_defines.dm"
#include "code\__SPLURTCODE\DEFINES\hud.dm"
#include "code\__SPLURTCODE\DEFINES\is_helpers.dm"
@@ -252,6 +255,7 @@
#include "code\__SPLURTCODE\DEFINES\login.dm"
#include "code\__SPLURTCODE\DEFINES\mobs.dm"
#include "code\__SPLURTCODE\DEFINES\pregnancy.dm"
+#include "code\__SPLURTCODE\DEFINES\quirks.dm"
#include "code\__SPLURTCODE\DEFINES\radiation.dm"
#include "code\__SPLURTCODE\DEFINES\say.dm"
#include "code\__SPLURTCODE\DEFINES\signals.dm"
@@ -261,6 +265,7 @@
#include "code\__SPLURTCODE\DEFINES\subsystems.dm"
#include "code\__SPLURTCODE\DEFINES\traits.dm"
#include "code\__SPLURTCODE\DEFINES\arousal\genitals.dm"
+#include "code\__SPLURTCODE\DEFINES\dcs\signals.dm"
#include "code\__SPLURTCODE\DEFINES\zeros\species.dm"
#include "code\__SPLURTCODE\DEFINES\zeros\traits.dm"
#include "code\_globalvars\admin.dm"
@@ -3803,7 +3808,6 @@
#include "interface\skin.dmf"
#include "modular_citadel\code\datums\components\souldeath.dm"
#include "modular_citadel\code\datums\status_effects\chems.dm"
-#include "modular_citadel\code\game\objects\cit_screenshake.dm"
#include "modular_citadel\code\game\objects\effects\temporary_visuals\souldeath.dm"
#include "modular_citadel\code\modules\admin\holder2.dm"
#include "modular_citadel\code\modules\admin\secrets.dm"
@@ -4245,14 +4249,18 @@
#include "modular_splurt\code\datums\elements\crawl_under.dm"
#include "modular_splurt\code\datums\elements\smalltalk.dm"
#include "modular_splurt\code\datums\elements\spooky.dm"
+#include "modular_splurt\code\datums\elements\wuv.dm"
#include "modular_splurt\code\datums\interactions\cuddling.dm"
#include "modular_splurt\code\datums\interactions\lewd\_lewd.dm"
#include "modular_splurt\code\datums\interactions\lewd\lewd_datums.dm"
#include "modular_splurt\code\datums\interactions\lewd\lewd_interactions.dm"
#include "modular_splurt\code\datums\looping_sounds\machinery_sounds.dm"
+#include "modular_splurt\code\datums\mood_events\dominant_events.dm"
#include "modular_splurt\code\datums\mood_events\drug_events.dm"
#include "modular_splurt\code\datums\mood_events\generic_negative_events.dm"
#include "modular_splurt\code\datums\mood_events\generic_positive_events.dm"
+#include "modular_splurt\code\datums\mood_events\mood_event.dm"
+#include "modular_splurt\code\datums\mood_events\needs_events.dm"
#include "modular_splurt\code\datums\mutations\telekenisis.dm"
#include "modular_splurt\code\datums\ruins\lavaland.dm"
#include "modular_splurt\code\datums\ruins\station.dm"
@@ -4328,7 +4336,6 @@
#include "modular_splurt\code\game\object\structures\ghost_role_spawners.dm"
#include "modular_splurt\code\game\object\structures\micro_bricks.dm"
#include "modular_splurt\code\game\object\structures\pole.dm"
-#include "modular_splurt\code\game\object\structures\railings.dm"
#include "modular_splurt\code\game\object\structures\signs.dm"
#include "modular_splurt\code\game\object\structures\statues.dm"
#include "modular_splurt\code\game\object\structures\table_frames.dm"
@@ -4484,6 +4491,7 @@
#include "modular_splurt\code\modules\food_and_drinks\food\snacks_pastry.dm"
#include "modular_splurt\code\modules\food_and_drinks\recipes\drink_recipes.dm"
#include "modular_splurt\code\modules\food_and_drinks\recipes\tablecraft\recipes_burger.dm"
+#include "modular_splurt\code\modules\food_and_drinks\recipes\tablecraft\recipes_donut.dm"
#include "modular_splurt\code\modules\hydroponics\grown\towercap.dm"
#include "modular_splurt\code\modules\jobs\job_types\_job_alt_titles.dm"
#include "modular_splurt\code\modules\jobs\job_types\blueshield.dm"
@@ -4497,10 +4505,12 @@
#include "modular_splurt\code\modules\jobs\job_types\security_officer.dm"
#include "modular_splurt\code\modules\jobs\job_types\service.dm"
#include "modular_splurt\code\modules\keybindings\keybind\movement.dm"
+#include "modular_splurt\code\modules\language\xenocommon.dm"
#include "modular_splurt\code\modules\mapping\mapping_helpers\baseturf.dm"
#include "modular_splurt\code\modules\mining\shelters.dm"
#include "modular_splurt\code\modules\mining\equipment\kinetic_crusher.dm"
#include "modular_splurt\code\modules\mining\equipment\survival_pod.dm"
+#include "modular_splurt\code\modules\mob\emote.dm"
#include "modular_splurt\code\modules\mob\mob.dm"
#include "modular_splurt\code\modules\mob\mob_defines.dm"
#include "modular_splurt\code\modules\mob\splurt_emotes.dm"
@@ -4537,6 +4547,7 @@
#include "modular_splurt\code\modules\mob\living\silicon\robot\dogborg_equipment.dm"
#include "modular_splurt\code\modules\mob\living\silicon\robot\robot.dm"
#include "modular_splurt\code\modules\mob\living\silicon\robot\robot_modules.dm"
+#include "modular_splurt\code\modules\mob\living\simple_animal\simple_animal.dm"
#include "modular_splurt\code\modules\mob\living\simple_animal\bot\medbot.dm"
#include "modular_splurt\code\modules\mob\living\simple_animal\friendly\mouse.dm"
#include "modular_splurt\code\modules\mob\living\simple_animal\gremlin\gremlin.dm"
diff --git a/tgui/packages/tgui/interfaces/MobInteraction.js b/tgui/packages/tgui/interfaces/MobInteraction.tsx
similarity index 88%
rename from tgui/packages/tgui/interfaces/MobInteraction.js
rename to tgui/packages/tgui/interfaces/MobInteraction.tsx
index 125217d898..ca7146740b 100644
--- a/tgui/packages/tgui/interfaces/MobInteraction.js
+++ b/tgui/packages/tgui/interfaces/MobInteraction.tsx
@@ -5,8 +5,69 @@ import { useBackend, useLocalState } from '../backend';
import { BlockQuote, Button, Flex, LabeledList, Icon, Input, Section, Table, Tabs, Stack } from '../components';
import { Window } from '../layouts';
+type HeaderInfo = {
+ isTargetSelf: boolean;
+ interactingWith: string;
+ selfAttributes: string[];
+ theirAttributes: string[];
+}
+
+type ContentInfo = {
+ interactions: InteractionData[];
+}
+
+type InteractionData = {
+ key: string;
+ desc: string;
+ type: number;
+}
+
+type GenitalInfo = {
+ genitals: GenitalData[];
+}
+
+type GenitalData = {
+ name: string,
+ key: string,
+ visibility: string,
+ possible_choices: string[],
+}
+
+type CharacterPrefsInfo = {
+ erp_pref: number,
+ noncon_pref: number,
+ vore_pref: number,
+ extreme_pref: number,
+ unholy_pref: number,
+ extreme_harm: boolean,
+}
+
+type ContentPrefsInfo = {
+ verb_consent: number,
+ lewd_verb_sounds: number,
+ arousable: number,
+ genital_examine: number,
+ vore_examine: number,
+ medihound_sleeper: number,
+ eating_noises: number,
+ digestion_noises: number,
+ trash_forcefeed: number,
+ forced_fem: number,
+ forced_masc: number,
+ hypno: number,
+ bimbofication: number,
+ breast_enlargement: number,
+ penis_enlargement: number,
+ butt_enlargement: number,
+ belly_inflation: number,
+ never_hypno: number,
+ no_aphro: number,
+ no_ass_slap: number,
+ no_auto_wag: number,
+}
+
export const MobInteraction = (props, context) => {
- const { act, data } = useBackend(context);
+ const { act, data } = useBackend(context);
const {
isTargetSelf,
interactingWith,
@@ -78,7 +139,7 @@ export const MobInteraction = (props, context) => {
};
const InteractionsTab = (props, context) => {
- const { act, data } = useBackend(context);
+ const { act, data } = useBackend(context);
const [
searchText,
setSearchText,
@@ -103,20 +164,20 @@ const InteractionsTab = (props, context) => {
{
interactions.length ? (
interactions.map((interaction) => (
-
+
))
) : (
-
+
{
searchText ? (
"No matching results."
@@ -124,7 +185,7 @@ const InteractionsTab = (props, context) => {
"No interactions available."
)
}
-
+
)
}
@@ -135,7 +196,8 @@ const InteractionsTab = (props, context) => {
* Interaction sorter! also search box
*/
export const sortInteractions = (interactions, searchText = '') => {
- const testSearch = createSearch(searchText, interaction => interaction.desc);
+ const testSearch = createSearch(searchText,
+ interaction => interaction.desc);
return flow([
// Optional search term
searchText && filter(testSearch),
@@ -154,14 +216,14 @@ const ModeToIcon = {
};
const GenitalVisibilityTab = (props, context) => {
- const { act, data } = useBackend(context);
+ const { act, data } = useBackend(context);
const genitals = data.genitals || [];
return (
genitals.length ? (
{genitals.map(genital => (
-
+
{genital.possible_choices.map(choice => (
- ) : ("You don't seem to have any genitals... Or any that you could modify")
+ ) : (
+
+ You don't seem to have any genitals...
+ Or any that you could modify.
+
+ )
);
};
const CharacterPrefsTab = (props, context) => {
- const { act, data } = useBackend(context);
+ const { act, data } = useBackend(context);
const {
erp_pref,
noncon_pref,
@@ -313,14 +380,14 @@ const CharacterPrefsTab = (props, context) => {