diff --git a/_maps/RandomRuins/SpaceRuins/skelter.dmm b/_maps/RandomRuins/SpaceRuins/skelter.dmm
index 47824eec63..16efa9b532 100644
--- a/_maps/RandomRuins/SpaceRuins/skelter.dmm
+++ b/_maps/RandomRuins/SpaceRuins/skelter.dmm
@@ -3257,7 +3257,7 @@
},
/obj/structure/closet,
/obj/effect/turf_decal/bot,
-/obj/item/circlegame,
+/obj/item/hand_item/circlegame,
/turf/open/floor/plasteel/dark,
/area/ruin/space/has_grav/skelter/storage)
"hE" = (
diff --git a/_maps/map_files/SpookyStation/SpookyStation.dmm b/_maps/map_files/SpookyStation/SpookyStation.dmm
index 3c1548c801..bb26270b9b 100644
--- a/_maps/map_files/SpookyStation/SpookyStation.dmm
+++ b/_maps/map_files/SpookyStation/SpookyStation.dmm
@@ -5195,7 +5195,7 @@
/obj/structure/closet/cabinet,
/obj/item/clothing/head/helmet/space/hardsuit/deathsquad,
/obj/item/clothing/suit/space/hardsuit/deathsquad,
-/obj/item/circlegame,
+/obj/item/hand_item/circlegame,
/obj/item/nullrod/scythe,
/obj/item/card/id/silver/reaper,
/turf/open/floor/wood,
diff --git a/_maps/map_files/TauStation/TauStation.dmm b/_maps/map_files/TauStation/TauStation.dmm
index ed2065aa1d..0df3dd58cb 100644
--- a/_maps/map_files/TauStation/TauStation.dmm
+++ b/_maps/map_files/TauStation/TauStation.dmm
@@ -41312,7 +41312,7 @@
/turf/open/floor/plasteel,
/area/cargo/storage)
"omF" = (
-/obj/item/circlegame,
+/obj/item/hand_item/circlegame,
/turf/open/floor/wood{
icon_state = "wood-broken2"
},
diff --git a/_maps/splurt_maps/templates/hilbertshotel.dmm b/_maps/splurt_maps/templates/hilbertshotel.dmm
index dbf63764b1..4e44fa2361 100644
--- a/_maps/splurt_maps/templates/hilbertshotel.dmm
+++ b/_maps/splurt_maps/templates/hilbertshotel.dmm
@@ -245,6 +245,8 @@
/area/hilbertshotel)
"MA" = (
/obj/structure/table/wood/fancy,
+/obj/item/storage/fancy/heart_box,
+/obj/item/paper/fluff/hilbertshotel/welcomeletter,
/turf/open/indestructible/hotelwood,
/area/hilbertshotel)
"MZ" = (
diff --git a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_1.dmm b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_1.dmm
index 4ec13e9d13..72b860f4c4 100644
--- a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_1.dmm
+++ b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_1.dmm
@@ -572,6 +572,8 @@
/obj/effect/turf_decal/siding/wood{
dir = 8
},
+/obj/item/storage/fancy/heart_box,
+/obj/item/paper/fluff/hilbertshotel/welcomeletter,
/turf/open/floor/wood/wood_tiled,
/area/hilbertshotel)
"WX" = (
diff --git a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_2.dmm b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_2.dmm
index fb9f41af27..17d46b4bd6 100644
--- a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_2.dmm
+++ b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_2.dmm
@@ -402,6 +402,12 @@
},
/turf/open/floor/wood/wood_tiled,
/area/hilbertshotel)
+"gq" = (
+/obj/structure/table/wood,
+/obj/item/storage/fancy/heart_box,
+/obj/item/paper/fluff/hilbertshotel/welcomeletter,
+/turf/open/floor/carpet,
+/area/hilbertshotel)
"jn" = (
/turf/closed/wall,
/area/hilbertshotel)
@@ -613,7 +619,7 @@ ab
ax
no
aD
-bB
+gq
bs
bB
aD
diff --git a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_3.dmm b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_3.dmm
index 5602bc1103..720b4bf1ec 100644
--- a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_3.dmm
+++ b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_3.dmm
@@ -165,6 +165,8 @@
/area/hilbertshotel)
"bl" = (
/obj/structure/table/wood,
+/obj/item/storage/fancy/heart_box,
+/obj/item/paper/fluff/hilbertshotel/welcomeletter,
/turf/open/floor/carpet,
/area/hilbertshotel)
"bn" = (
@@ -356,6 +358,10 @@
},
/turf/open/floor/mineral/plastitanium,
/area/hilbertshotel)
+"nA" = (
+/obj/structure/table/wood,
+/turf/open/floor/carpet,
+/area/hilbertshotel)
"nE" = (
/obj/effect/turf_decal/siding/wood/corner{
dir = 4
@@ -753,7 +759,7 @@ aU
YE
bh
kt
-bl
+nA
ax
LU
XP
diff --git a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_4.dmm b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_4.dmm
index 48007a5e99..6ec6b9a4a0 100644
--- a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_4.dmm
+++ b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_4.dmm
@@ -277,6 +277,8 @@
/obj/effect/turf_decal/siding/wood{
dir = 8
},
+/obj/item/storage/fancy/heart_box,
+/obj/item/paper/fluff/hilbertshotel/welcomeletter,
/turf/open/floor/carpet/royalblue,
/area/hilbertshotel)
"yJ" = (
diff --git a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_bar.dmm b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_bar.dmm
index 8ff37e4696..20d3346269 100644
--- a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_bar.dmm
+++ b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_bar.dmm
@@ -14,6 +14,8 @@
dir = 1
},
/obj/structure/table/wood/fancy/royalblack,
+/obj/item/storage/fancy/heart_box,
+/obj/item/paper/fluff/hilbertshotel/welcomeletter,
/turf/open/floor/wood/wood_tiled,
/area/hilbertshotel)
"ee" = (
@@ -102,6 +104,13 @@
/obj/structure/table/wood/poker/shadow,
/turf/open/floor/carpet/royalblack,
/area/hilbertshotel)
+"nQ" = (
+/obj/effect/turf_decal/siding/wood{
+ dir = 1
+ },
+/obj/structure/table/wood/fancy/royalblack,
+/turf/open/floor/wood/wood_tiled,
+/area/hilbertshotel)
"oH" = (
/obj/structure/table/wood,
/obj/machinery/chem_dispenser/drinks/beer{
@@ -206,6 +215,12 @@
"yz" = (
/turf/open/floor/glass/reinforced,
/area/hilbertshotel)
+"yO" = (
+/obj/effect/turf_decal/siding/wood,
+/obj/structure/table/wood/poker/shadow,
+/obj/item/choice_beacon/box/plushie,
+/turf/open/floor/carpet/royalblack,
+/area/hilbertshotel)
"BL" = (
/obj/machinery/light{
dir = 8
@@ -475,11 +490,11 @@ rY
ee
ee
pr
-nj
+yO
Wg
yz
dy
-dR
+nQ
TA
Pz
li
@@ -497,7 +512,7 @@ nj
cK
yz
dy
-dR
+nQ
af
af
lS
@@ -533,7 +548,7 @@ sm
cK
yz
dy
-dR
+nQ
af
af
fo
@@ -551,7 +566,7 @@ sm
cK
yz
dy
-dR
+nQ
af
af
af
diff --git a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_garden.dmm b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_garden.dmm
index 32e5ec86b6..d443a0b75b 100644
--- a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_garden.dmm
+++ b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_garden.dmm
@@ -345,6 +345,11 @@
/obj/structure/flora/ausbushes/brflowers,
/turf/open/floor/grass/fairy/purple,
/area/hilbertshotel)
+"JW" = (
+/obj/structure/chair/pew,
+/obj/item/choice_beacon/box/plushie,
+/turf/open/floor/wood/wood_tiled,
+/area/hilbertshotel)
"Kc" = (
/obj/structure/flora/rock,
/obj/structure/flora/ausbushes/brflowers,
@@ -487,6 +492,8 @@
"Xj" = (
/obj/structure/flora/ausbushes/lavendergrass,
/obj/structure/flora/ausbushes/ppflowers,
+/obj/item/storage/fancy/heart_box,
+/obj/item/paper/fluff/hilbertshotel/welcomeletter,
/turf/open/floor/grass/fairy/purple,
/area/hilbertshotel)
"XX" = (
@@ -661,7 +668,7 @@ Dd
uV
qq
Qc
-Gj
+JW
Kj
tY
YG
diff --git a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_sauna.dmm b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_sauna.dmm
index 5e655b153c..1c9c2a0458 100644
--- a/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_sauna.dmm
+++ b/_maps/splurt_maps/templates/hilbertshotel_templates/apartment_sauna.dmm
@@ -206,6 +206,8 @@
/area/hilbertshotel)
"qm" = (
/obj/structure/table/wood,
+/obj/item/storage/fancy/heart_box,
+/obj/item/paper/fluff/hilbertshotel/welcomeletter,
/turf/open/floor/wood,
/area/hilbertshotel)
"qv" = (
@@ -519,6 +521,7 @@
pixel_x = 5;
pixel_y = 3
},
+/obj/item/choice_beacon/box/plushie,
/turf/open/floor/carpet/red,
/area/hilbertshotel)
"ND" = (
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index dbacbcbda5..295f8f9a90 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -1,4 +1,5 @@
#define SIGNAL_TRAIT(trait_ref) "trait [trait_ref]"
+#define SIGNAL_REMOVETRAIT(trait_ref) "removetrait [trait_ref]"
// trait accessor defines
#define ADD_TRAIT(target, trait, source) \
@@ -59,6 +60,30 @@
};\
}\
} while (0)
+
+#define REMOVE_TRAITS_IN(target, sources) \
+ do { \
+ var/list/_L = target.status_traits; \
+ var/list/_S = sources; \
+ if (sources && !islist(sources)) { \
+ _S = list(sources); \
+ } else { \
+ _S = sources\
+ }; \
+ if (_L) { \
+ for (var/_T in _L) { \
+ _L[_T] -= _S;\
+ if (!length(_L[_T])) { \
+ _L -= _T; \
+ SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(_T)); \
+ }; \
+ };\
+ if (!length(_L)) { \
+ target.status_traits = null\
+ };\
+ }\
+ } while (0)
+
#define HAS_TRAIT(target, trait) (target.status_traits ? (target.status_traits[trait] ? TRUE : FALSE) : FALSE)
#define HAS_TRAIT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (source in target.status_traits[trait]) : FALSE) : FALSE)
#define HAS_TRAIT_FROM_ONLY(target, trait, source) (\
@@ -236,6 +261,12 @@
#define TRAIT_NO_STAMINA_REGENERATION "block_stamina_regen" /// Prevents stamina regeneration
#define TRAIT_ARMOR_BROKEN "armor_broken" //acts as if you are wearing no clothing when taking damage, does not affect non-clothing sources of protection
#define TRAIT_IWASBATONED "iwasbatoned" //some dastardly fellow has struck you with a baton and thought to use another to strike you again, the rogue
+//Given by social anxiety quirk
+#define TRAIT_ANXIOUS "anxious"
+/// Trait granted by lipstick
+#define LIPSTICK_TRAIT "lipstick_trait"
+/// Blowing kisses that actually do damage to the victim
+#define TRAIT_KISS_OF_DEATH "kiss_of_death"
/// forces update_density to make us not dense
#define TRAIT_LIVING_NO_DENSITY "living_no_density"
/// forces us to not render our overlays
diff --git a/code/__SPLURTCODE/DEFINES/dcs/signals.dm b/code/__SPLURTCODE/DEFINES/dcs/signals.dm
index f12c163f71..30320d2b1a 100644
--- a/code/__SPLURTCODE/DEFINES/dcs/signals.dm
+++ b/code/__SPLURTCODE/DEFINES/dcs/signals.dm
@@ -11,3 +11,6 @@
* Used by signals for determining when genitals have been updated
*/
#define COMSIG_MOB_UPDATE_GENITALS "mob_genitals_updated"
+
+///called on [/obj/item] AFTER unequip from base of [mob/proc/doUnEquip]: (force, atom/newloc, no_move, invdrop, silent)
+#define COMSIG_ITEM_POST_UNEQUIP "item_post_unequip"
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 709d558a42..09a3d052c0 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -132,6 +132,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE,
"TRAIT_VOIDSTORM_IMMUNE" = TRAIT_VOIDSTORM_IMMUNE,
"TRAIT_WEATHER_IMMUNE" = TRAIT_WEATHER_IMMUNE,
+ "TRAIT_KISS_OF_DEATH" = TRAIT_KISS_OF_DEATH,
+ "TRAIT_ANXIOUS" = TRAIT_ANXIOUS,
"TRAIT_SPACEWALK" = TRAIT_SPACEWALK,
"TRAIT_PRIMITIVE" = TRAIT_PRIMITIVE, //unable to use mechs. Given to Ash Walkers
),
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index e664f5fd7c..a59523b10c 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -5,6 +5,7 @@ SUBSYSTEM_DEF(shuttle)
wait = 10
init_order = INIT_ORDER_SHUTTLE
flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK
+ runlevels = RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME // Splurt edit: Add RUNLEVEL_POSTGAME to let shuttles move after round is over.
var/list/mobile = list()
var/list/stationary = list()
diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm
index 8dd6d80a92..475715212a 100644
--- a/code/datums/mood_events/generic_positive_events.dm
+++ b/code/datums/mood_events/generic_positive_events.dm
@@ -225,3 +225,8 @@
description = "The gods are pleased with this offering!\n"
mood_change = 5
timeout = 3 MINUTES
+
+/datum/mood_event/kiss
+ description = span_nicegreen("Someone blew a kiss at me, I must be a real catch!\n")
+ mood_change = 1.5
+ timeout = 2 MINUTES
diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm
index a30e27c8f8..20cda350d6 100644
--- a/code/datums/traits/negative.dm
+++ b/code/datums/traits/negative.dm
@@ -43,11 +43,11 @@
GLOBAL_LIST_EMPTY(family_heirlooms)
-/datum/quirk/family_heirloom/on_spawn()
+/datum/quirk/family_heirloom/on_spawn()
// Define holder and type
var/mob/living/carbon/human/human_holder = quirk_holder
var/obj/item/heirloom_type
-
+
// The quirk holder's species - we have a 50% chance, if we have a species with a set heirloom, to choose a species heirloom.
var/datum/species/holder_species = human_holder.dna?.species
if(holder_species && LAZYLEN(holder_species.family_heirlooms) && prob(50))
@@ -61,13 +61,13 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
// If we didn't find an heirloom somehow, throw them a generic one
if(!heirloom_type)
heirloom_type = pick(/obj/item/toy/cards/deck, /obj/item/lighter, /obj/item/dice/d20)
-
+
// Create the heirloom item
heirloom = new heirloom_type(get_turf(quirk_holder))
-
+
// Add to global list
GLOB.family_heirlooms += heirloom
-
+
// Determine and assign item location
var/list/slots = list(
"in your left pocket" = ITEM_SLOT_LPOCKET,
@@ -330,6 +330,7 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
gain_text = "You start worrying about what you're saying."
lose_text = "You feel easier about talking again." //if only it were that easy!
medical_record_text = "Patient is usually anxious in social encounters and prefers to avoid them."
+ mob_trait = TRAIT_ANXIOUS
var/dumb_thing = TRUE
processing_quirk = TRUE
diff --git a/code/game/objects/items/cosmetics.dm b/code/game/objects/items/cosmetics.dm
index b9a3852862..e808d3486f 100644
--- a/code/game/objects/items/cosmetics.dm
+++ b/code/game/objects/items/cosmetics.dm
@@ -7,6 +7,8 @@
w_class = WEIGHT_CLASS_TINY
var/colour = "red"
var/open = FALSE
+ /// A trait that's applied while someone has this lipstick applied, and is removed when the lipstick is removed
+ var/lipstick_trait
/obj/item/lipstick/purple
name = "purple lipstick"
@@ -21,6 +23,10 @@
name = "black lipstick"
colour = "black"
+/obj/item/lipstick/black/death
+ name = "Kiss of Death"
+ lipstick_trait = TRAIT_KISS_OF_DEATH
+
/obj/item/lipstick/random
name = "lipstick"
icon_state = "random_lipstick"
@@ -33,7 +39,7 @@
/obj/item/lipstick/attack_self(mob/user)
cut_overlays()
- to_chat(user, "You twist \the [src] [open ? "closed" : "open"].")
+ to_chat(user, span_notice("You twist \the [src] [open ? "closed" : "open"]."))
open = !open
if(open)
var/mutable_appearance/colored_overlay = mutable_appearance(icon, "lipstick_uncap_color")
@@ -44,60 +50,53 @@
icon_state = "lipstick"
/obj/item/lipstick/attack(mob/M, mob/user)
- if(!open)
+ if(!open || !ismob(M))
return
- if(!ismob(M))
+ if(!ishuman(M))
+ to_chat(user, span_warning("Where are the lips on that?"))
return
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(H.is_mouth_covered())
- to_chat(user, "Remove [ H == user ? "your" : "[H.p_their()]" ] mask!")
- return
- if(H.lip_style) //if they already have lipstick on
- to_chat(user, "You need to wipe off the old lipstick first!")
- return
- if(H == user)
- user.visible_message("[user] does [user.p_their()] lips with \the [src].", \
- "You take a moment to apply \the [src]. Perfect!")
- H.lip_style = "lipstick"
- H.lip_color = colour
- H.update_body()
- else
- user.visible_message("[user] begins to do [H]'s lips with \the [src].", \
- "You begin to apply \the [src] on [H]'s lips...")
- if(do_after(user, 20, target = H))
- user.visible_message("[user] does [H]'s lips with \the [src].", \
- "You apply \the [src] on [H]'s lips.")
- H.lip_style = "lipstick"
- H.lip_color = colour
- H.update_body()
- else
- to_chat(user, "Where are the lips on that?")
+ var/mob/living/carbon/human/target = M
+ if(target.is_mouth_covered())
+ to_chat(user, span_warning("Remove [ target == user ? "your" : "[target.p_their()]" ] mask!"))
+ return
+ if(target.lip_style) //if they already have lipstick on
+ to_chat(user, span_warning("You need to wipe off the old lipstick first!"))
+ return
+
+ if(target == user)
+ user.visible_message(span_notice("[user] does [user.p_their()] lips with \the [src]."), \
+ span_notice("You take a moment to apply \the [src]. Perfect!"))
+ target.update_lips("lipstick", colour, lipstick_trait)
+ return
+
+ user.visible_message(span_warning("[user] begins to do [target]'s lips with \the [src]."), \
+ span_notice("You begin to apply \the [src] on [target]'s lips..."))
+ if(!do_after(user, 2 SECONDS, target = target))
+ return
+ user.visible_message(span_notice("[user] does [target]'s lips with \the [src]."), \
+ span_notice("You apply \the [src] on [target]'s lips."))
+ target.update_lips("lipstick", colour, lipstick_trait)
//you can wipe off lipstick with paper!
/obj/item/paper/attack(mob/M, mob/user)
- if(user.zone_selected == BODY_ZONE_PRECISE_MOUTH)
- if(!ismob(M))
- return
+ if(user.zone_selected != BODY_ZONE_PRECISE_MOUTH || !ishuman(M))
+ return ..()
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(H == user)
- to_chat(user, "You wipe off the lipstick with [src].")
- H.lip_style = null
- H.update_body()
- else
- user.visible_message("[user] begins to wipe [H]'s lipstick off with \the [src].", \
- "You begin to wipe off [H]'s lipstick...")
- if(do_after(user, 10, target = H))
- user.visible_message("[user] wipes [H]'s lipstick off with \the [src].", \
- "You wipe off [H]'s lipstick.")
- H.lip_style = null
- H.update_body()
- else
- ..()
+ var/mob/living/carbon/human/target = M
+ if(target == user)
+ to_chat(user, span_notice("You wipe off the lipstick with [src]."))
+ target.clean_lips()
+ return
+
+ user.visible_message(span_warning("[user] begins to wipe [target]'s lipstick off with \the [src]."), \
+ span_notice("You begin to wipe off [target]'s lipstick..."))
+ if(!do_after(user, 10, target = target))
+ return
+ user.visible_message(span_notice("[user] wipes [target]'s lipstick off with \the [src]."), \
+ span_notice("You wipe off [target]'s lipstick."))
+ target.clean_lips()
/obj/item/razor
name = "electric razor"
diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm
new file mode 100644
index 0000000000..e3a3d72579
--- /dev/null
+++ b/code/game/objects/items/hand_items.dm
@@ -0,0 +1,368 @@
+/obj/item/hand_item
+ item_flags = DROPDEL | ABSTRACT | HAND_ITEM
+
+
+/obj/item/hand_item/circlegame
+ name = "circled hand"
+ desc = "If somebody looks at this while it's below your waist, you get to bop them."
+ icon_state = "madeyoulook"
+ force = 0
+ throwforce = 0
+ attack_verb = list("bopped")
+
+/obj/item/hand_item/circlegame/Initialize(mapload)
+ . = ..()
+ var/mob/living/owner = loc
+ if(!istype(owner))
+ return
+ RegisterSignal(owner, COMSIG_PARENT_EXAMINE, .proc/ownerExamined)
+
+/obj/item/hand_item/circlegame/Destroy()
+ var/mob/owner = loc
+ if(istype(owner))
+ UnregisterSignal(owner, COMSIG_PARENT_EXAMINE)
+ return ..()
+
+/obj/item/hand_item/circlegame/dropped(mob/user)
+ UnregisterSignal(user, COMSIG_PARENT_EXAMINE) //loc will have changed by the time this is called, so Destroy() can't catch it
+ // this is a dropdel item.
+ return ..()
+
+/// Stage 1: The mistake is made
+/obj/item/hand_item/circlegame/proc/ownerExamined(mob/living/owner, mob/living/sucker)
+ SIGNAL_HANDLER
+
+ if(!istype(sucker) || !in_range(owner, sucker))
+ return
+ addtimer(CALLBACK(src, .proc/waitASecond, owner, sucker), 4)
+
+/// Stage 2: Fear sets in
+/obj/item/hand_item/circlegame/proc/waitASecond(mob/living/owner, mob/living/sucker)
+ if(QDELETED(sucker) || QDELETED(src) || QDELETED(owner))
+ return
+
+ if(owner == sucker) // big mood
+ to_chat(owner, "Wait a second... you just looked at your own [src.name]!")
+ addtimer(CALLBACK(src, .proc/selfGottem, owner), 10)
+ else
+ to_chat(sucker, "Wait a second... was that a-")
+ addtimer(CALLBACK(src, .proc/GOTTEM, owner, sucker), 6)
+
+/// Stage 3A: We face our own failures
+/obj/item/hand_item/circlegame/proc/selfGottem(mob/living/owner)
+ if(QDELETED(src) || QDELETED(owner))
+ return
+
+ playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
+ owner.visible_message("[owner] shamefully bops [owner.p_them()]self with [owner.p_their()] [src.name].", "You shamefully bop yourself with your [src.name].", \
+ "You hear a dull thud!")
+ log_combat(owner, owner, "bopped", src.name, "(self)")
+ owner.do_attack_animation(owner)
+ owner.apply_damage(100, STAMINA)
+ owner.Knockdown(10)
+ qdel(src)
+
+/// Stage 3B: We face our reckoning (unless we moved away or they're incapacitated)
+/obj/item/hand_item/circlegame/proc/GOTTEM(mob/living/owner, mob/living/sucker)
+ if(QDELETED(sucker))
+ return
+
+ if(QDELETED(src) || QDELETED(owner))
+ to_chat(sucker, "Nevermind... must've been your imagination...")
+ return
+
+ if(!in_range(owner, sucker) || !(owner.mobility_flags & MOBILITY_USE))
+ to_chat(sucker, "Phew... you moved away before [owner] noticed you saw [owner.p_their()] [src.name]...")
+ return
+
+ to_chat(owner, "[sucker] looks down at your [src.name] before trying to avert [sucker.p_their()] eyes, but it's too late!")
+ to_chat(sucker, "[owner] sees the fear in your eyes as you try to look away from [owner.p_their()] [src.name]!")
+
+ owner.face_atom(sucker)
+ if(owner.client)
+ owner.client.give_award(/datum/award/achievement/misc/gottem, owner) // then everybody clapped
+
+ playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
+ owner.do_attack_animation(sucker)
+
+ if(HAS_TRAIT(owner, TRAIT_HULK))
+ owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name] much harder than intended, sending [sucker.p_them()] flying!", \
+ "You bop [sucker] with your [src.name] much harder than intended, sending [sucker.p_them()] flying!", "You hear a sickening sound of flesh hitting flesh!", ignored_mobs=list(sucker))
+ to_chat(sucker, "[owner] bops you incredibly hard with [owner.p_their()] [src.name], sending you flying!")
+ sucker.apply_damage(50, STAMINA)
+ sucker.Knockdown(50)
+ log_combat(owner, sucker, "bopped", src.name, "(setup- Hulk)")
+ var/atom/throw_target = get_edge_target_turf(sucker, owner.dir)
+ sucker.throw_at(throw_target, 6, 3, owner)
+ else
+ owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name]!", "You bop [sucker] with your [src.name]!", \
+ "You hear a dull thud!", ignored_mobs=list(sucker))
+ sucker.apply_damage(15, STAMINA)
+ log_combat(owner, sucker, "bopped", src.name, "(setup)")
+ to_chat(sucker, "[owner] bops you with [owner.p_their()] [src.name]!")
+ qdel(src)
+
+/obj/item/hand_item/slapper
+ name = "slapper"
+ desc = "This is how real men fight."
+ icon_state = "latexballon"
+ item_state = "nothing"
+ force = 0
+ throwforce = 0
+ attack_verb = list("slapped")
+ hitsound = 'sound/effects/snap.ogg'
+
+/obj/item/hand_item/slapper/attack(mob/M, mob/living/carbon/human/user)
+ if(ishuman(M))
+ var/mob/living/carbon/human/L = M
+ if(L && L.dna && L.dna.species)
+ L.dna.species.stop_wagging_tail(M)
+ user.do_attack_animation(M)
+ playsound(M, 'sound/weapons/slap.ogg', 50, TRUE, -1)
+ user.visible_message("[user] slaps [M]!",
+ "You slap [M]!",\
+ "You hear a slap.")
+ return
+
+/obj/item/hand_item/slapper/on_offered(mob/living/carbon/offerer)
+ . = TRUE
+
+ if(!(locate(/mob/living/carbon) in orange(1, offerer)))
+ visible_message(span_danger("[offerer] raises [offerer.p_their()] arm, looking around for a high-five, but there's no one around!"), \
+ span_warning("You post up, looking for a high-five, but finding no one within range!"), null, 2)
+ return
+
+ offerer.visible_message(span_notice("[offerer] raises [offerer.p_their()] arm, looking for a high-five!"), \
+ span_notice("You post up, looking for a high-five!"), null, 2)
+ offerer.apply_status_effect(STATUS_EFFECT_OFFERING, src, /atom/movable/screen/alert/give/highfive)
+
+/// Yeah broh! This is where we do the high-fiving (or high-tenning :o)
+/obj/item/hand_item/slapper/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
+ . = TRUE
+
+ var/open_hands_taker
+ var/slappers_giver
+ for(var/i in taker.held_items) // see how many hands the taker has open for high'ing
+ if(isnull(i))
+ open_hands_taker++
+
+ if(!open_hands_taker)
+ to_chat(taker, span_warning("You can't high-five [offerer] with no open hands!"))
+ SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five_full_hand) // not so successful now!
+ return
+
+ for(var/i in offerer.held_items)
+ var/obj/item/hand_item/slapper/slap_check = i
+ if(istype(slap_check))
+ slappers_giver++
+
+ if(slappers_giver >= 2) // we only check this if it's already established the taker has 2+ hands free
+ offerer.visible_message(span_notice("[taker] enthusiastically high-tens [offerer]!"), span_nicegreen("Wow! You're high-tenned [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
+ to_chat(taker, span_nicegreen("You give high-tenning [offerer] your all!"))
+ playsound(offerer, 'sound/weapons/slap.ogg', 100, TRUE, 1)
+ SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
+ SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
+ else
+ offerer.visible_message(span_notice("[taker] high-fives [offerer]!"), span_nicegreen("All right! You're high-fived by [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
+ to_chat(taker, span_nicegreen("You high-five [offerer]!"))
+ playsound(offerer, 'sound/weapons/slap.ogg', 50, TRUE, -1)
+ SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
+ SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
+ qdel(src)
+
+/// Gangster secret handshakes.
+/obj/item/hand_item/slapper/secret_handshake
+ name = "Secret Handshake"
+ icon_state = "recruit"
+ icon = 'icons/obj/gang/actions.dmi'
+ /// References the active families gamemode handler (if one exists), for adding new family members to.
+ var/datum/gang_handler/handler
+ /// The typepath of the gang antagonist datum that the person who uses the package should have added to them -- remember that the distinction between e.g. Ballas and Grove Street is on the antag datum level, not the team datum level.
+ var/gang_to_use
+ /// The team datum that the person who uses this package should be added to.
+ var/datum/team/gang/team_to_use
+
+/// Adds the user to the family that this package corresponds to, dispenses the free_clothes of that family, and adds them to the handler if it exists.
+/obj/item/hand_item/slapper/secret_handshake/proc/add_to_gang(mob/living/user, original_name)
+ var/datum/antagonist/gang/swappin_sides = new gang_to_use()
+ swappin_sides.original_name = original_name
+ swappin_sides.handler = handler
+ user.mind.add_antag_datum(swappin_sides, team_to_use)
+ var/policy = get_policy(ROLE_FAMILIES)
+ if(policy)
+ to_chat(user, policy)
+ team_to_use.add_member(user.mind)
+ swappin_sides.equip_gangster_in_inventory()
+ if (!isnull(handler) && !handler.gangbangers.Find(user.mind)) // if we have a handler and they're not tracked by it
+ handler.gangbangers += user.mind
+
+/// Checks if the user is trying to use the package of the family they are in, and if not, adds them to the family, with some differing processing depending on whether the user is already a family member.
+/obj/item/hand_item/slapper/secret_handshake/proc/attempt_join_gang(mob/living/user)
+ if(!user?.mind)
+ return
+ var/datum/antagonist/gang/is_gangster = user.mind.has_antag_datum(/datum/antagonist/gang)
+ var/real_name_backup = user.real_name
+ if(is_gangster)
+ if(is_gangster.my_gang == team_to_use)
+ return
+ real_name_backup = is_gangster.original_name
+ is_gangster.my_gang.remove_member(user.mind)
+ user.mind.remove_antag_datum(/datum/antagonist/gang)
+ add_to_gang(user, real_name_backup)
+
+/obj/item/hand_item/slapper/secret_handshake/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
+ . = TRUE
+ if (!(null in taker.held_items))
+ to_chat(taker, span_warning("You can't get taught the secret handshake if [offerer] has no free hands!"))
+ return
+
+ if(HAS_TRAIT(taker, TRAIT_MINDSHIELD))
+ to_chat(taker, "You attended a seminar on not signing up for a gang and are not interested.")
+ return
+
+ var/datum/antagonist/gang/is_gangster = taker.mind.has_antag_datum(/datum/antagonist/gang)
+ if(is_gangster?.starter_gangster)
+ if(is_gangster.my_gang == team_to_use)
+ to_chat(taker, "You started your family. You don't need to join it.")
+ return
+ to_chat(taker, "You started your family. You can't turn your back on it now.")
+ return
+
+ offerer.visible_message(span_notice("[taker] is taught the secret handshake by [offerer]!"), span_nicegreen("All right! You've taught the secret handshake to [taker]!"), span_hear("You hear a bunch of weird shuffling and flesh slapping sounds!"), ignored_mobs=taker)
+ to_chat(taker, span_nicegreen("You get taught the secret handshake by [offerer]!"))
+ var/datum/antagonist/gang/owner_gang_datum = offerer.mind.has_antag_datum(/datum/antagonist/gang)
+ handler = owner_gang_datum.handler
+ gang_to_use = owner_gang_datum.type
+ team_to_use = owner_gang_datum.my_gang
+ attempt_join_gang(taker)
+ qdel(src)
+
+/obj/item/hand_item/kisser
+ name = "kiss"
+ desc = "I want you all to know, everyone and anyone, to seal it with a kiss."
+ icon = 'icons/mob/animal.dmi'
+ icon_state = "heart"
+ /// The kind of projectile this version of the kiss blower fires
+ var/kiss_type = /obj/item/projectile/kiss
+ /// TRUE if the user was aiming anywhere but the mouth when they offer the kiss, if it's offered
+ var/cheek_kiss
+
+/obj/item/hand_item/kisser/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ // . |= AFTERATTACK_PROCESSED_ITEM
+ /*
+ if(HAS_TRAIT(user, TRAIT_GARLIC_BREATH))
+ kiss_type = /obj/projectile/kiss/french
+
+ if(HAS_TRAIT(user, TRAIT_CHEF_KISS))
+ kiss_type = /obj/projectile/kiss/chef
+ */
+
+ var/obj/item/projectile/blown_kiss = new kiss_type(get_turf(user))
+ user.visible_message("[user] blows \a [blown_kiss] at [target]!", span_notice("You blow \a [blown_kiss] at [target]!"))
+
+ //Shooting Code:
+ blown_kiss.original = target
+ blown_kiss.fired_from = user
+ blown_kiss.pixels_per_second = TILES_TO_PIXELS(7) // Speed of blown kiss
+ blown_kiss.firer = user // don't hit ourself that would be really annoying
+ blown_kiss.impacted = list(user = TRUE) // just to make sure we don't hit the wearer
+ blown_kiss.preparePixelProjectile(target, user)
+ blown_kiss.fire()
+ qdel(src)
+
+/obj/item/hand_item/kisser/death
+ name = "kiss of death"
+ desc = "If looks could kill, they'd be this."
+ color = COLOR_BLACK
+ kiss_type = /obj/item/projectile/kiss/death
+
+/obj/item/projectile/kiss
+ name = "kiss"
+ icon = 'icons/mob/animal.dmi'
+ icon_state = "heart"
+ hitsound = 'sound/effects/kiss.ogg'
+ hitsound_wall = 'sound/effects/kiss.ogg'
+ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE
+ damage_type = BRUTE
+ damage = 0
+ nodamage = TRUE // love can't actually hurt you
+ armour_penetration = 100 // but if it could, it would cut through even the thickest plate
+ flag = MAGIC // and most importantly, love is magic~
+
+/obj/item/projectile/kiss/fire(angle, atom/direct_target)
+ if(firer)
+ name = "[name] blown by [firer]"
+ return ..()
+
+/obj/item/projectile/kiss/Impact(atom/A)
+ if(damage > 0 || !isliving(A)) // if we do damage or we hit a nonliving thing, we don't have to worry about a harmless hit because we can't wrongly do damage anyway
+ return ..()
+
+ harmless_on_hit(A)
+ qdel(src)
+ return FALSE
+
+/**
+ * To get around shielded modsuits & such being set off by kisses when they shouldn't, we take a page from hallucination projectiles
+ * and simply fake our on hit effects. This lets kisses remain incorporeal without having to make some new trait for this one niche situation.
+ * This fake hit only happens if we can deal damage and if we hit a living thing. Otherwise, we just do normal on hit effects.
+ */
+/obj/item/projectile/kiss/proc/harmless_on_hit(mob/living/living_target)
+ playsound(get_turf(living_target), hitsound, 100, TRUE)
+ if(!suppressed) // direct
+ living_target.visible_message(span_danger("[living_target] is hit by \a [src]."), span_userdanger("You're hit by \a [src]!"), vision_distance=COMBAT_MESSAGE_RANGE)
+
+ /*
+ living_target.add_mob_memory(/datum/memory/kissed, deuteragonist = firer)
+ living_target.add_mood_event("kiss", /datum/mood_event/kiss, firer, suppressed)
+ if(isliving(firer))
+ var/mob/living/kisser = firer
+ kisser.add_mob_memory(/datum/memory/kissed, protagonist = living_target, deuteragonist = firer) */
+
+ try_fluster(living_target)
+
+/obj/item/projectile/kiss/proc/try_fluster(mob/living/living_target)
+ // people with the social anxiety quirk can get flustered when hit by a kiss
+ if(!HAS_TRAIT(living_target, TRAIT_ANXIOUS) || (living_target.stat > SOFT_CRIT) || living_target.is_blind())
+ return
+
+ if(HAS_TRAIT(living_target, TRAIT_FEARLESS) || prob(50)) // 50% chance for it to apply, also immune while on meds
+ return
+
+ var/other_msg
+ var/self_msg
+ var/roll = rand(1, 3)
+ switch(roll)
+ if(1)
+ other_msg = "stumbles slightly, turning a bright red!"
+ self_msg = "You lose control of your limbs for a moment as your blood rushes to your face, turning it bright red!"
+ living_target.confused += (rand(5, 10))
+ if(2)
+ other_msg = "stammers softly for a moment before choking on something!"
+ self_msg = "You feel your tongue disappear down your throat as you fight to remember how to make words!"
+ addtimer(CALLBACK(living_target, /atom/movable.proc/say, pick("Uhhh...", "O-oh, uhm...", "I- uhhhhh??", "What?")), rand(0.5 SECONDS, 1.5 SECONDS))
+ living_target.stuttering += rand(5, 15)
+ if(3)
+ other_msg = "locks up with a stunned look on [living_target.p_their()] face, staring at [firer ? firer : "the ceiling"]!"
+ self_msg = "Your brain completely fails to process what just happened, leaving you rooted in place staring [firer ? "at [firer]" : "the ceiling"] for what feels like an eternity!"
+ living_target.face_atom(firer)
+ living_target.Stun(rand(1 SECONDS, 3 SECONDS))
+
+ living_target.visible_message(span_notice("[living_target] [other_msg]"), span_userdanger("Whoa! [self_msg]"))
+
+/obj/item/projectile/kiss/death
+ name = "kiss of death"
+ nodamage = FALSE // okay i kinda lied about love not being able to hurt you
+ damage = 15
+ wound_bonus = 0
+ sharpness = SHARP_POINTY
+ color = COLOR_BLACK
+
+/obj/item/projectile/kiss/death/on_hit(atom/target, blocked, pierce_hit)
+ . = ..()
+ if(!iscarbon(target))
+ return
+ var/mob/living/carbon/heartbreakee = target
+ var/obj/item/organ/heart/dont_go_breakin_my_heart = heartbreakee.getorganslot(ORGAN_SLOT_HEART)
+ dont_go_breakin_my_heart.applyOrganDamage(15)
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index 636190b548..e4c71ee6ff 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -1117,248 +1117,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
else
qdel(target)
-/obj/item/circlegame
- name = "circled hand"
- desc = "If somebody looks at this while it's below your waist, you get to bop them."
- icon_state = "madeyoulook"
- force = 0
- throwforce = 0
- item_flags = DROPDEL | ABSTRACT | HAND_ITEM
- attack_verb = list("bopped")
-
-/obj/item/circlegame/Initialize(mapload)
- . = ..()
- var/mob/living/owner = loc
- if(!istype(owner))
- return
- RegisterSignal(owner, COMSIG_PARENT_EXAMINE, .proc/ownerExamined)
-
-/obj/item/circlegame/Destroy()
- var/mob/owner = loc
- if(istype(owner))
- UnregisterSignal(owner, COMSIG_PARENT_EXAMINE)
- return ..()
-
-/obj/item/circlegame/dropped(mob/user)
- UnregisterSignal(user, COMSIG_PARENT_EXAMINE) //loc will have changed by the time this is called, so Destroy() can't catch it
- // this is a dropdel item.
- return ..()
-
-/// Stage 1: The mistake is made
-/obj/item/circlegame/proc/ownerExamined(mob/living/owner, mob/living/sucker)
- SIGNAL_HANDLER
-
- if(!istype(sucker) || !in_range(owner, sucker))
- return
- addtimer(CALLBACK(src, .proc/waitASecond, owner, sucker), 4)
-
-/// Stage 2: Fear sets in
-/obj/item/circlegame/proc/waitASecond(mob/living/owner, mob/living/sucker)
- if(QDELETED(sucker) || QDELETED(src) || QDELETED(owner))
- return
-
- if(owner == sucker) // big mood
- to_chat(owner, "Wait a second... you just looked at your own [src.name]!")
- addtimer(CALLBACK(src, .proc/selfGottem, owner), 10)
- else
- to_chat(sucker, "Wait a second... was that a-")
- addtimer(CALLBACK(src, .proc/GOTTEM, owner, sucker), 6)
-
-/// Stage 3A: We face our own failures
-/obj/item/circlegame/proc/selfGottem(mob/living/owner)
- if(QDELETED(src) || QDELETED(owner))
- return
-
- playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
- owner.visible_message("[owner] shamefully bops [owner.p_them()]self with [owner.p_their()] [src.name].", "You shamefully bop yourself with your [src.name].", \
- "You hear a dull thud!")
- log_combat(owner, owner, "bopped", src.name, "(self)")
- owner.do_attack_animation(owner)
- owner.apply_damage(100, STAMINA)
- owner.Knockdown(10)
- qdel(src)
-
-/// Stage 3B: We face our reckoning (unless we moved away or they're incapacitated)
-/obj/item/circlegame/proc/GOTTEM(mob/living/owner, mob/living/sucker)
- if(QDELETED(sucker))
- return
-
- if(QDELETED(src) || QDELETED(owner))
- to_chat(sucker, "Nevermind... must've been your imagination...")
- return
-
- if(!in_range(owner, sucker) || !(owner.mobility_flags & MOBILITY_USE))
- to_chat(sucker, "Phew... you moved away before [owner] noticed you saw [owner.p_their()] [src.name]...")
- return
-
- to_chat(owner, "[sucker] looks down at your [src.name] before trying to avert [sucker.p_their()] eyes, but it's too late!")
- to_chat(sucker, "[owner] sees the fear in your eyes as you try to look away from [owner.p_their()] [src.name]!")
-
- owner.face_atom(sucker)
- if(owner.client)
- owner.client.give_award(/datum/award/achievement/misc/gottem, owner) // then everybody clapped
-
- playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
- owner.do_attack_animation(sucker)
-
- if(HAS_TRAIT(owner, TRAIT_HULK))
- owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name] much harder than intended, sending [sucker.p_them()] flying!", \
- "You bop [sucker] with your [src.name] much harder than intended, sending [sucker.p_them()] flying!", "You hear a sickening sound of flesh hitting flesh!", ignored_mobs=list(sucker))
- to_chat(sucker, "[owner] bops you incredibly hard with [owner.p_their()] [src.name], sending you flying!")
- sucker.apply_damage(50, STAMINA)
- sucker.Knockdown(50)
- log_combat(owner, sucker, "bopped", src.name, "(setup- Hulk)")
- var/atom/throw_target = get_edge_target_turf(sucker, owner.dir)
- sucker.throw_at(throw_target, 6, 3, owner)
- else
- owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name]!", "You bop [sucker] with your [src.name]!", \
- "You hear a dull thud!", ignored_mobs=list(sucker))
- sucker.apply_damage(15, STAMINA)
- log_combat(owner, sucker, "bopped", src.name, "(setup)")
- to_chat(sucker, "[owner] bops you with [owner.p_their()] [src.name]!")
- qdel(src)
-
-/obj/item/slapper
- name = "slapper"
- desc = "This is how real men fight."
- icon_state = "latexballon"
- item_state = "nothing"
- force = 0
- throwforce = 0
- item_flags = DROPDEL | ABSTRACT | HAND_ITEM
- attack_verb = list("slapped")
- hitsound = 'sound/effects/snap.ogg'
-
-/obj/item/slapper/attack(mob/M, mob/living/carbon/human/user)
- if(ishuman(M))
- var/mob/living/carbon/human/L = M
- if(L && L.dna && L.dna.species)
- L.dna.species.stop_wagging_tail(M)
- user.do_attack_animation(M)
- playsound(M, 'sound/weapons/slap.ogg', 50, TRUE, -1)
- user.visible_message("[user] slaps [M]!",
- "You slap [M]!",\
- "You hear a slap.")
- return
/obj/item/proc/can_trigger_gun(mob/living/user)
if(!user.can_use_guns(src))
return FALSE
return TRUE
-/obj/item/slapper/on_offered(mob/living/carbon/offerer)
- . = TRUE
-
- if(!(locate(/mob/living/carbon) in orange(1, offerer)))
- visible_message(span_danger("[offerer] raises [offerer.p_their()] arm, looking around for a high-five, but there's no one around!"), \
- span_warning("You post up, looking for a high-five, but finding no one within range!"), null, 2)
- return
-
- offerer.visible_message(span_notice("[offerer] raises [offerer.p_their()] arm, looking for a high-five!"), \
- span_notice("You post up, looking for a high-five!"), null, 2)
- offerer.apply_status_effect(STATUS_EFFECT_OFFERING, src, /atom/movable/screen/alert/give/highfive)
-
-/// Yeah broh! This is where we do the high-fiving (or high-tenning :o)
-/obj/item/slapper/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
- . = TRUE
-
- var/open_hands_taker
- var/slappers_giver
- for(var/i in taker.held_items) // see how many hands the taker has open for high'ing
- if(isnull(i))
- open_hands_taker++
-
- if(!open_hands_taker)
- to_chat(taker, span_warning("You can't high-five [offerer] with no open hands!"))
- SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five_full_hand) // not so successful now!
- return
-
- for(var/i in offerer.held_items)
- var/obj/item/slapper/slap_check = i
- if(istype(slap_check))
- slappers_giver++
-
- if(slappers_giver >= 2) // we only check this if it's already established the taker has 2+ hands free
- offerer.visible_message(span_notice("[taker] enthusiastically high-tens [offerer]!"), span_nicegreen("Wow! You're high-tenned [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
- to_chat(taker, span_nicegreen("You give high-tenning [offerer] your all!"))
- playsound(offerer, 'sound/weapons/slap.ogg', 100, TRUE, 1)
- SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
- SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
- else
- offerer.visible_message(span_notice("[taker] high-fives [offerer]!"), span_nicegreen("All right! You're high-fived by [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
- to_chat(taker, span_nicegreen("You high-five [offerer]!"))
- playsound(offerer, 'sound/weapons/slap.ogg', 50, TRUE, -1)
- SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
- SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
- qdel(src)
-
-/// Gangster secret handshakes.
-/obj/item/slapper/secret_handshake
- name = "Secret Handshake"
- icon_state = "recruit"
- icon = 'icons/obj/gang/actions.dmi'
- /// References the active families gamemode handler (if one exists), for adding new family members to.
- var/datum/gang_handler/handler
- /// The typepath of the gang antagonist datum that the person who uses the package should have added to them -- remember that the distinction between e.g. Ballas and Grove Street is on the antag datum level, not the team datum level.
- var/gang_to_use
- /// The team datum that the person who uses this package should be added to.
- var/datum/team/gang/team_to_use
-
-
-/// Adds the user to the family that this package corresponds to, dispenses the free_clothes of that family, and adds them to the handler if it exists.
-/obj/item/slapper/secret_handshake/proc/add_to_gang(mob/living/user, original_name)
- var/datum/antagonist/gang/swappin_sides = new gang_to_use()
- swappin_sides.original_name = original_name
- swappin_sides.handler = handler
- user.mind.add_antag_datum(swappin_sides, team_to_use)
- var/policy = get_policy(ROLE_FAMILIES)
- if(policy)
- to_chat(user, policy)
- team_to_use.add_member(user.mind)
- swappin_sides.equip_gangster_in_inventory()
- if (!isnull(handler) && !handler.gangbangers.Find(user.mind)) // if we have a handler and they're not tracked by it
- handler.gangbangers += user.mind
-
-/// Checks if the user is trying to use the package of the family they are in, and if not, adds them to the family, with some differing processing depending on whether the user is already a family member.
-/obj/item/slapper/secret_handshake/proc/attempt_join_gang(mob/living/user)
- if(!user?.mind)
- return
- var/datum/antagonist/gang/is_gangster = user.mind.has_antag_datum(/datum/antagonist/gang)
- var/real_name_backup = user.real_name
- if(is_gangster)
- if(is_gangster.my_gang == team_to_use)
- return
- real_name_backup = is_gangster.original_name
- is_gangster.my_gang.remove_member(user.mind)
- user.mind.remove_antag_datum(/datum/antagonist/gang)
- add_to_gang(user, real_name_backup)
-
-/obj/item/slapper/secret_handshake/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
- . = TRUE
- if (!(null in taker.held_items))
- to_chat(taker, span_warning("You can't get taught the secret handshake if [offerer] has no free hands!"))
- return
-
- if(HAS_TRAIT(taker, TRAIT_MINDSHIELD))
- to_chat(taker, "You attended a seminar on not signing up for a gang and are not interested.")
- return
-
- var/datum/antagonist/gang/is_gangster = taker.mind.has_antag_datum(/datum/antagonist/gang)
- if(is_gangster?.starter_gangster)
- if(is_gangster.my_gang == team_to_use)
- to_chat(taker, "You started your family. You don't need to join it.")
- return
- to_chat(taker, "You started your family. You can't turn your back on it now.")
- return
-
- offerer.visible_message(span_notice("[taker] is taught the secret handshake by [offerer]!"), span_nicegreen("All right! You've taught the secret handshake to [taker]!"), span_hear("You hear a bunch of weird shuffling and flesh slapping sounds!"), ignored_mobs=taker)
- to_chat(taker, span_nicegreen("You get taught the secret handshake by [offerer]!"))
- var/datum/antagonist/gang/owner_gang_datum = offerer.mind.has_antag_datum(/datum/antagonist/gang)
- handler = owner_gang_datum.handler
- gang_to_use = owner_gang_datum.type
- team_to_use = owner_gang_datum.my_gang
- attempt_join_gang(taker)
- qdel(src)
-
/obj/item/extendohand
name = "extendo-hand"
desc = "Futuristic tech has allowed these classic spring-boxing toys to essentially act as a fully functional hand-operated hand prosthetic."
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index 37afae6053..9bfb10f43c 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -546,8 +546,7 @@
if(washing_face)
if(ishuman(user))
var/mob/living/carbon/human/H = user
- H.lip_style = null //Washes off lipstick
- H.lip_color = initial(H.lip_color)
+ H.clean_lips() //Washes off lipstick
H.wash_cream()
H.wash_cum() //sandstorm edit
H.regenerate_icons()
diff --git a/code/modules/antagonists/gang/gang.dm b/code/modules/antagonists/gang/gang.dm
index 050754a91e..1dcb054153 100644
--- a/code/modules/antagonists/gang/gang.dm
+++ b/code/modules/antagonists/gang/gang.dm
@@ -239,7 +239,7 @@
if(H.stat)
return FALSE
- var/obj/item/slapper/secret_handshake/secret_handshake_item = new(owner)
+ var/obj/item/hand_item/slapper/secret_handshake/secret_handshake_item = new(owner)
if(owner.put_in_hands(secret_handshake_item))
to_chat(owner, span_notice("You ready your secret handshake."))
else
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 5cdef57673..33e3f2f276 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -547,6 +547,32 @@
if(..())
dropItemToGround(I)
+/**
+ * Used to update the makeup on a human and apply/remove lipstick traits, then store/unstore them on the head object in case it gets severed
+ */
+/mob/living/carbon/human/proc/update_lips(new_style, new_colour, apply_trait)
+ lip_style = new_style
+ lip_color = new_colour
+ update_body()
+
+ var/obj/item/bodypart/head/hopefully_a_head = get_bodypart(BODY_ZONE_HEAD)
+ REMOVE_TRAITS_IN(src, LIPSTICK_TRAIT)
+ hopefully_a_head?.stored_lipstick_trait = null
+
+ if(new_style && apply_trait)
+ ADD_TRAIT(src, apply_trait, LIPSTICK_TRAIT)
+ hopefully_a_head?.stored_lipstick_trait = apply_trait
+
+/**
+ * A wrapper for [mob/living/carbon/human/proc/update_lips] that tells us if there were lip styles to change
+ */
+
+/mob/living/carbon/human/proc/clean_lips()
+ if(isnull(lip_style) && lip_color == initial(lip_color))
+ return FALSE
+ update_lips(null)
+ return TRUE
+
/mob/living/carbon/human/clean_blood()
var/mob/living/carbon/human/H = src
if(H.gloves)
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index c2aca1f76f..cd67dd2ff4 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -195,6 +195,26 @@
/datum/emote/living/kiss
key = "kiss"
key_third_person = "kisses"
+
+/datum/emote/living/kiss/run_emote(mob/living/user, params, type_override, intentional)
+ . = ..()
+ if(!.)
+ return
+ var/kiss_type = /obj/item/hand_item/kisser
+
+ if(HAS_TRAIT(user, TRAIT_KISS_OF_DEATH))
+ kiss_type = /obj/item/hand_item/kisser/death
+
+ var/obj/item/kiss_blower = new kiss_type(user)
+ if(user.put_in_hands(kiss_blower))
+ to_chat(user, span_notice("You ready your kiss-blowing hand."))
+ else
+ qdel(kiss_blower)
+ to_chat(user, span_warning("You're incapable of blowing a kiss in your current state."))
+
+/datum/emote/living/kiss2
+ key = "kiss2"
+ key_third_person = "kisses"
message = "blows a kiss."
message_param = "blows a kiss to %t."
emote_type = EMOTE_AUDIBLE
@@ -528,7 +548,7 @@
. = ..()
if(!.)
return
- var/obj/item/circlegame/N = new(user)
+ var/obj/item/hand_item/circlegame/N = new(user)
if(user.put_in_hands(N))
to_chat(user, "You make a circle with your hand.")
else
@@ -544,7 +564,7 @@
. = ..()
if(!.)
return
- var/obj/item/slapper/N = new(user)
+ var/obj/item/hand_item/slapper/N = new(user)
if(user.put_in_hands(N))
to_chat(user, "You ready your slapping hand.")
else
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 96b9290239..35faec8bb3 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -392,8 +392,7 @@
H.hair_style = hair_style
H.facial_hair_color = facial_hair_color
H.facial_hair_style = facial_hair_style
- H.lip_style = lip_style
- H.lip_color = lip_color
+ H.update_lips(lip_style, lip_color, stored_lipstick_trait)
if(real_name)
C.real_name = real_name
real_name = ""
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index ea43740093..6b5c8b725f 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -32,6 +32,9 @@
var/lip_style = null
var/lip_color = "white"
+
+ var/stored_lipstick_trait
+
//If the head is a special sprite
var/custom_head
@@ -78,6 +81,7 @@
hair_style = "Bald"
facial_hair_style = "Shaved"
lip_style = null
+ stored_lipstick_trait = null
else if(!animal_origin)
var/mob/living/carbon/human/H = C
diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm
index a968d42073..4925cf40e7 100644
--- a/code/modules/vehicles/_vehicle.dm
+++ b/code/modules/vehicles/_vehicle.dm
@@ -54,7 +54,7 @@
return occupants
/obj/vehicle/proc/occupant_amount()
- return length(occupants)
+ return LAZYLEN(occupants)
/obj/vehicle/proc/return_amount_of_controllers_with_flag(flag)
. = 0
@@ -82,9 +82,10 @@
return !isnull(occupants[M])
/obj/vehicle/proc/add_occupant(mob/M, control_flags)
- if(!istype(M) || occupants[M])
+ if(!istype(M) || is_occupant(M))
return FALSE
- occupants[M] = NONE
+
+ LAZYSET(occupants, M, NONE)
add_control_flags(M, control_flags)
after_add_occupant(M)
grant_passenger_actions(M)
@@ -102,7 +103,7 @@
return FALSE
remove_control_flags(M, ALL)
remove_passenger_actions(M)
- occupants -= M
+ LAZYREMOVE(occupants, M)
cleanup_actions_for_mob(M)
after_remove_occupant(M)
return TRUE
@@ -140,7 +141,7 @@
return
/obj/vehicle/proc/add_control_flags(mob/controller, flags)
- if(!istype(controller) || !flags)
+ if(!is_occupant(controller) || !flags)
return FALSE
occupants[controller] |= flags
for(var/i in GLOB.bitflags)
@@ -149,7 +150,7 @@
return TRUE
/obj/vehicle/proc/remove_control_flags(mob/controller, flags)
- if(!istype(controller) || !flags)
+ if(!is_occupant(controller) || !flags)
return FALSE
occupants[controller] &= ~flags
for(var/i in GLOB.bitflags)
diff --git a/code/modules/vehicles/cars/car.dm b/code/modules/vehicles/cars/car.dm
index e024718f4c..b041ad8e79 100644
--- a/code/modules/vehicles/cars/car.dm
+++ b/code/modules/vehicles/cars/car.dm
@@ -45,7 +45,7 @@
return ..()
/obj/vehicle/sealed/car/mob_try_exit(mob/M, mob/user, silent = FALSE)
- if(M == user && (occupants[M] & VEHICLE_CONTROL_KIDNAPPED))
+ if(M == user && (LAZYACCESS(occupants, M) & VEHICLE_CONTROL_KIDNAPPED))
to_chat(user, "You push against the back of [src] trunk to try and get out.")
if(!do_after(user, escape_time, target = src))
return FALSE
@@ -73,7 +73,7 @@
if(do_after(user, 30))
if(return_amount_of_controllers_with_flag(VEHICLE_CONTROL_KIDNAPPED))
to_chat(user, "The people stuck in [src]'s trunk all come tumbling out.")
- DumpSpecificMobs(VEHICLE_CONTROL_KIDNAPPED)
+ dump_specific_mobs(VEHICLE_CONTROL_KIDNAPPED)
else
to_chat(user, "It seems [src]'s trunk was empty.")
diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm
index 8d6abb23c9..18c7a96cd8 100644
--- a/code/modules/vehicles/mecha/_mecha.dm
+++ b/code/modules/vehicles/mecha/_mecha.dm
@@ -459,7 +459,7 @@
transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION)
cabin_air.transfer_to(t_air, transfer_moles)
- if(occupants)
+ if(LAZYLEN(occupants))
for(var/i in occupants)
var/mob/living/occupant = i
if(cell)
@@ -908,13 +908,14 @@
///Handles an actual AI (simple_animal mecha pilot) entering the mech
/obj/vehicle/sealed/mecha/proc/aimob_enter_mech(mob/living/simple_animal/hostile/syndicate/mecha_pilot/pilot_mob)
- if(pilot_mob && pilot_mob.Adjacent(src))
- if(LAZYLEN(occupants))
- return
- LAZYADD(occupants, pilot_mob)
- pilot_mob.mecha = src
- pilot_mob.forceMove(src)
- update_icon()
+ if(!pilot_mob?.Adjacent(src))
+ return
+ if(LAZYLEN(occupants))
+ return
+ LAZYSET(occupants, pilot_mob, NONE)
+ pilot_mob.mecha = src
+ pilot_mob.forceMove(src)
+ update_icon()
///Handles an actual AI (simple_animal mecha pilot) exiting the mech
/obj/vehicle/sealed/mecha/proc/aimob_exit_mech(mob/living/simple_animal/hostile/syndicate/mecha_pilot/pilot_mob)
diff --git a/code/modules/vehicles/mecha/combat/durand.dm b/code/modules/vehicles/mecha/combat/durand.dm
index 8567e31b07..f7f48b01fa 100644
--- a/code/modules/vehicles/mecha/combat/durand.dm
+++ b/code/modules/vehicles/mecha/combat/durand.dm
@@ -177,7 +177,7 @@ own integrity back to max. Shield is automatically dropped if we run out of powe
/obj/durand_shield/proc/activate(datum/source, mob/owner, list/signal_args)
SIGNAL_HANDLER
currentuser = owner
- if(!chassis?.occupants)
+ if(!LAZYLEN(chassis?.occupants))
return
if(switching && !signal_args[1])
return
diff --git a/code/modules/vehicles/mecha/equipment/mecha_equipment.dm b/code/modules/vehicles/mecha/equipment/mecha_equipment.dm
index 1d104291f3..97568057ec 100644
--- a/code/modules/vehicles/mecha/equipment/mecha_equipment.dm
+++ b/code/modules/vehicles/mecha/equipment/mecha_equipment.dm
@@ -41,7 +41,7 @@
chassis.selected = null
update_chassis_page()
log_message("[src] is destroyed.", LOG_MECHA)
- if(chassis.occupants)
+ if(!LAZYLEN(chassis?.occupants))
to_chat(chassis.occupants, "[icon2html(src, chassis.occupants)][src] is destroyed!")
playsound(chassis, destroy_sound, 50)
if(!detachable) //If we're a built-in nondetachable equipment, let's lock up the slot that we were in.
diff --git a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm
index c781fa3736..9a2b69dbeb 100644
--- a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm
+++ b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm
@@ -198,7 +198,7 @@
qdel(src)
if(istype(loc, /obj/vehicle/sealed/mecha/working) && scanning_time <= world.time)
var/obj/vehicle/sealed/mecha/working/mecha = loc
- if(!mecha.occupants)
+ if(!LAZYLEN(mecha.occupants))
return
scanning_time = world.time + equip_cooldown
mineral_scan_pulse(get_turf(src))
diff --git a/code/modules/vehicles/mecha/mecha_defense.dm b/code/modules/vehicles/mecha/mecha_defense.dm
index b53156dba7..04b1d6a30c 100644
--- a/code/modules/vehicles/mecha/mecha_defense.dm
+++ b/code/modules/vehicles/mecha/mecha_defense.dm
@@ -156,7 +156,7 @@
for(var/occus in occupants)
var/mob/living/occupant = occus
occupant.update_mouse_pointer()
- if(!equipment_disabled && occupants) //prevent spamming this message with back-to-back EMPs
+ if(!equipment_disabled && LAZYLEN(occupants)) //prevent spamming this message with back-to-back EMPs
to_chat(occupants, "Error -- Connection to equipment control unit has been lost.")
addtimer(CALLBACK(src, /obj/vehicle/sealed/mecha/proc/restore_equipment), 3 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
equipment_disabled = 1
diff --git a/code/modules/vehicles/sealed.dm b/code/modules/vehicles/sealed.dm
index b5023f9729..1892613062 100644
--- a/code/modules/vehicles/sealed.dm
+++ b/code/modules/vehicles/sealed.dm
@@ -98,13 +98,14 @@
var/mob/living/carbon/Carbon = i
Carbon.DefaultCombatKnockdown(40)
-/obj/vehicle/sealed/proc/DumpSpecificMobs(flag, randomstep = TRUE)
+/obj/vehicle/sealed/proc/dump_specific_mobs(flag, randomstep = TRUE)
for(var/i in occupants)
- if((occupants[i] & flag))
- mob_exit(i, null, randomstep)
- if(iscarbon(i))
- var/mob/living/carbon/C = i
- C.DefaultCombatKnockdown(40)
+ if(!(occupants[i] & flag))
+ continue
+ mob_exit(i, null, randomstep)
+ if(iscarbon(i))
+ var/mob/living/carbon/C = i
+ C.Paralyze(40)
/obj/vehicle/sealed/AllowDrop()
diff --git a/code/modules/vehicles/vehicle_actions.dm b/code/modules/vehicles/vehicle_actions.dm
index 8b2c72008c..96c24b9adc 100644
--- a/code/modules/vehicles/vehicle_actions.dm
+++ b/code/modules/vehicles/vehicle_actions.dm
@@ -21,7 +21,7 @@
grant_controller_actions(i) //refresh
/obj/vehicle/proc/grant_action_type_to_mob(actiontype, mob/m)
- if(isnull(occupants[m]) || !actiontype)
+ if(isnull(LAZYACCESS(occupants, m)) || !actiontype)
return FALSE
LAZYINITLIST(occupant_actions[m])
if(occupant_actions[m][actiontype])
@@ -32,7 +32,7 @@
return TRUE
/obj/vehicle/proc/remove_action_type_from_mob(actiontype, mob/m)
- if(isnull(occupants[m]) || !actiontype)
+ if(isnull(LAZYACCESS(occupants, m)) || !actiontype)
return FALSE
LAZYINITLIST(occupant_actions[m])
if(occupant_actions[m][actiontype])
@@ -50,7 +50,7 @@
remove_action_type_from_mob(v, M)
/obj/vehicle/proc/grant_controller_actions(mob/M)
- if(!istype(M) || isnull(occupants[M]))
+ if(!istype(M) || isnull(LAZYACCESS(occupants, M)))
return FALSE
for(var/i in GLOB.bitflags)
if(occupants[M] & i)
@@ -58,7 +58,7 @@
return TRUE
/obj/vehicle/proc/remove_controller_actions(mob/M)
- if(!istype(M) || isnull(occupants[M]))
+ if(!istype(M) || isnull(LAZYACCESS(occupants, M)))
return FALSE
for(var/i in GLOB.bitflags)
remove_controller_actions_by_flag(M, i)
@@ -145,7 +145,7 @@
/datum/action/vehicle/sealed/dump_kidnapped_mobs/Trigger()
vehicle_entered_target.visible_message(span_danger("[vehicle_entered_target] starts dumping the people inside of it."))
- vehicle_entered_target.DumpSpecificMobs(VEHICLE_CONTROL_KIDNAPPED)
+ vehicle_entered_target.dump_specific_mobs(VEHICLE_CONTROL_KIDNAPPED)
/datum/action/vehicle/sealed/roll_the_dice
diff --git a/code/modules/vending/security.dm b/code/modules/vending/security.dm
index 52fbd7dac4..2f05de6034 100644
--- a/code/modules/vending/security.dm
+++ b/code/modules/vending/security.dm
@@ -14,7 +14,8 @@
/obj/item/storage/box/evidence = 6,
/obj/item/flashlight/seclite = 4,
/obj/item/restraints/legcuffs/bola/energy = 7,
- /obj/item/secbat = 5)
+ /obj/item/secbat = 5,
+ /obj/item/bodycam_upgrade = 10)
contraband = list(/obj/item/clothing/glasses/sunglasses = 2,
/obj/item/storage/fancy/donut_box = 2,
/obj/item/storage/belt/sabre/secbelt = 1)
diff --git a/html/changelogs/archive/2024-03.yml b/html/changelogs/archive/2024-03.yml
index 24aeebf52b..e52cf4cb41 100644
--- a/html/changelogs/archive/2024-03.yml
+++ b/html/changelogs/archive/2024-03.yml
@@ -17,3 +17,32 @@
- tweak: Removed old citadelcode from reagent container drinking, for more clean
and readable code.
- bugfix: Fixes a few bugs related to chugging.
+2024-03-07:
+ BongaTheProto:
+ - rscadd: Added a shark plushie for our donator
+ - rscadd: added the chameleon hat and mask to the donator tier 2 loadout
+ - rscadd: Adds a thicc rouny which behaves like deathclaws
+ - tweak: Ice wolf is now available in the pet capsule
+2024-03-11:
+ ariaworld:
+ - bugfix: Refactor, clean and optimize tailbag code to not be a copy/paste of wallets
+ but a subtype of it.
+ - bugfix: Proper lazylist support for vehicle occupants. NPC mecha pilots can board
+ mechas again. (And won't runtime when they try.) Mechs won't try to put themselves
+ into themselves as an occupant when boarded by simple animal mecha pilots.
+ - tweak: Removed 2 ugly pixels from the human penis size 4 erect size sprite.
+ - rscadd: "You can now blow kisses by using the *kiss emote (old emote is still\
+ \ usable with *kiss2). This puts a kiss object in your hand, which can be shot/thrown\
+ \ at other people to give them a mood boost. If you hit someone with the social\
+ \ anxiety quirk a few special things might happen\u223C!"
+ - rscadd: 'For coders: Also added a very easy way for coders to add new lipstick
+ which special traits, with this update the kiss of death was added (admin spawn
+ only), which deals damage when thrown. Have fun adding lipsticks that apply
+ crocin / increase arousal when you kiss someone / throw a kiss, or straight
+ up shooting lasers out of your mouth :P'
+2024-03-14:
+ NevilleTheGreat:
+ - rscadd: Added Thomas Plushie
+2024-03-20:
+ Just-Sim:
+ - code_imp: Made some chages following PR 1108
diff --git a/icons/obj/genitals/penis_onmob.dmi b/icons/obj/genitals/penis_onmob.dmi
index 780e982f9e..6aacb27dd4 100644
Binary files a/icons/obj/genitals/penis_onmob.dmi and b/icons/obj/genitals/penis_onmob.dmi differ
diff --git a/modular_sand/code/game/objects/items/fleshlight.dm b/modular_sand/code/game/objects/items/fleshlight.dm
index a09f5490dd..0b6ace77c0 100644
--- a/modular_sand/code/game/objects/items/fleshlight.dm
+++ b/modular_sand/code/game/objects/items/fleshlight.dm
@@ -763,7 +763,7 @@
/obj/item/paper/fluff/portallight
name = "Portal Fleshlight Instructions"
info = "Thank you for purchasing the Silver Love Portal Fleshlight!
\
- To use, simply register your new portal fleshlight with the provided underwear to link them together. The ask your lover to wear the underwear.
\
+ To use, simply register your new portal fleshlight with the provided underwear to link them together. Then ask your lover to wear the underwear.
\
Have fun lovers,
\
\
Wilhelmina Steiner."
diff --git a/modular_splurt/code/datums/components/crafting/recipes/recipes_clothing.dm b/modular_splurt/code/datums/components/crafting/recipes/recipes_clothing.dm
index 612975c5f4..41669bdbd1 100644
--- a/modular_splurt/code/datums/components/crafting/recipes/recipes_clothing.dm
+++ b/modular_splurt/code/datums/components/crafting/recipes/recipes_clothing.dm
@@ -1,14 +1,14 @@
/datum/crafting_recipe/tailbag
name = "Tailbag"
- result = /obj/item/storage/tailbag
+ result = /obj/item/storage/wallet/tailbag
reqs = list(/obj/item/stack/sheet/leather = 2)
time = 30
category = CAT_CLOTHING
/datum/crafting_recipe/tailbag_xl
name = "XL Tailbag"
- result = /obj/item/storage/tailbag/xtralg
- reqs = list(/obj/item/storage/tailbag = 1,
+ result = /obj/item/storage/wallet/tailbag/xtralg
+ reqs = list(/obj/item/storage/wallet/tailbag = 1,
/obj/item/stack/sheet/leather = 2)
time = 30
category = CAT_CLOTHING
diff --git a/modular_splurt/code/game/objects/items/pet_capsule.dm b/modular_splurt/code/game/objects/items/pet_capsule.dm
index 5bca227e05..5d611f8d84 100644
--- a/modular_splurt/code/game/objects/items/pet_capsule.dm
+++ b/modular_splurt/code/game/objects/items/pet_capsule.dm
@@ -30,7 +30,9 @@
"Femclaw" = image(icon = 'modular_splurt/icons/mob/femclaw/newclaws.dmi', icon_state = "femclaw"),
"Deathclaw" = image(icon = 'modular_splurt/icons/mob/femclaw/newclaws.dmi', icon_state = "newclaw"),
"Carp" = image(icon = 'icons/mob/animal.dmi', icon_state = "carp"),
- "Spider" = image(icon = 'icons/mob/animal.dmi', icon_state = "guard"))
+ "Spider" = image(icon = 'icons/mob/animal.dmi', icon_state = "guard"),
+ "Ice Wolf" = image(icon = 'modular_splurt/icons/mobs/werewolf.dmi', icon_state = "ice_wolf"),
+ "Rouny" = image(icon = 'modular_splurt/icons/mob/femclaw/newclaws.dmi', icon_state = "rouny"))
/obj/item/pet_capsule/alpha_capsule/InitializeSelection()
pet_icons = list(
@@ -39,7 +41,9 @@
"Carp" = image(icon = 'icons/mob/animal.dmi', icon_state = "carp"),
"Spider" = image(icon = 'icons/mob/animal.dmi', icon_state = "guard"),
"Mommyclaw" = image(icon = 'modular_splurt/icons/mob/femclaw/newclaws.dmi', icon_state = "mommyclaw"),
- "Alphaclaw" = image(icon = 'modular_splurt/icons/mob/femclaw/newclaws.dmi', icon_state = "alphaclaw"))
+ "Alphaclaw" = image(icon = 'modular_splurt/icons/mob/femclaw/newclaws.dmi', icon_state = "alphaclaw"),
+ "Ice Wolf" = image(icon = 'modular_splurt/icons/mobs/werewolf.dmi', icon_state = "Ice_Wolf_idle"),
+ "Rouny" = image(icon = 'modular_splurt/icons/mob/femclaw/newclaws.dmi', icon_state = "rouny"))
/obj/item/pet_capsule/Initialize(mapload)
. = ..()
@@ -48,42 +52,45 @@
/obj/item/pet_capsule/proc/pet_capsule_triggered(atom/location_atom, is_in_hand = FALSE, mob/user = null)
//If pet has not been chosen yet
if (!pet_picked && is_in_hand && user != null)
-
-
new_name = input(user, "New name :", "Rename your pet(Once per shift!)")
var/selected_icon = show_radial_menu(loc, loc , pet_icons, radius = 42, require_near = TRUE)
switch(selected_icon)
if("Femclaw")
selected_pet = /mob/living/simple_animal/hostile/deathclaw/funclaw/femclaw/pet_femclaw
- pet_picked = TRUE;
+ pet_picked = TRUE
if("Deathclaw")
selected_pet = /mob/living/simple_animal/hostile/deathclaw/funclaw/gentle/newclaw/pet_deathclaw
- pet_picked = TRUE;
+ pet_picked = TRUE
+ if("Rouny")
+ selected_pet = /mob/living/simple_animal/hostile/deathclaw/funclaw/femclaw/pet_femclaw/rouny
+ pet_picked = TRUE
+ if("Ice Wolf")
+ selected_pet = /mob/living/simple_animal/hostile/ice_wolf/funwolf/gentle/pet_ice_wolf
+ pet_picked = TRUE
if("Carp")
selected_pet = /mob/living/simple_animal/hostile/carp/pet_carp
- pet_picked = TRUE;
+ pet_picked = TRUE
if("Spider")
selected_pet = /mob/living/simple_animal/hostile/poison/giant_spider/pet_spider
- pet_picked = TRUE;
+ pet_picked = TRUE
if("Mommyclaw")
selected_pet = /mob/living/simple_animal/hostile/deathclaw/funclaw/femclaw/pet_femclaw/pet_mommyclaw
- pet_picked = TRUE;
+ pet_picked = TRUE
if("Alphaclaw")
selected_pet = /mob/living/simple_animal/hostile/deathclaw/funclaw/gentle/newclaw/pet_deathclaw/pet_alphaclaw
- pet_picked = TRUE;
+ pet_picked = TRUE
else
- pet_picked = FALSE;
+ pet_picked = FALSE
return FALSE
owner = user
return
else if (!open && pet_picked && is_in_hand && user != null)
- {
owner = user
to_chat(user, "You set yourself as the owner!")
- }
+
//Make pet appear if thrown on the floor
else if (!open && !is_in_hand && pet_picked)
open = TRUE
diff --git a/modular_splurt/code/game/objects/items/plushes.dm b/modular_splurt/code/game/objects/items/plushes.dm
index c6ba51a359..de27c08baa 100644
--- a/modular_splurt/code/game/objects/items/plushes.dm
+++ b/modular_splurt/code/game/objects/items/plushes.dm
@@ -43,3 +43,19 @@
icon = 'modular_splurt/icons/obj/plushes.dmi'
icon_state = "braixen"
item_state = "braixen"
+
+/obj/item/toy/plush/ethanol_shark
+ name = "Ethanol shark plushie"
+ desc = "Like a fine drink, when the good times are there, he was there to make them better."
+ icon = 'modular_splurt/icons/obj/plushes.dmi'
+ icon_state = "leo_oxto"
+ item_state = "leo_oxto"
+ squeak_override = list('modular_splurt/sound/voice/raptor_purr.ogg' = 1)
+
+/obj/item/toy/plush/thomas_plush
+ name = "Thomas The Tank Engine Plushie"
+ desc = "A plushie resembling Thomas, an anthropomorphised steam Locomotive."
+ icon = 'modular_splurt/icons/obj/plushes.dmi'
+ icon_state = "thomas_plush"
+ item_state = "thomas_plush"
+ squeak_override = list('modular_splurt/sound/misc/thomas_whistle_movie.ogg' = 1)
diff --git a/modular_splurt/code/game/objects/items/storage/tailbag.dm b/modular_splurt/code/game/objects/items/storage/tailbag.dm
deleted file mode 100644
index 0098544d8b..0000000000
--- a/modular_splurt/code/game/objects/items/storage/tailbag.dm
+++ /dev/null
@@ -1,122 +0,0 @@
-/obj/item/storage/tailbag
- name = "tailbag"
- desc = "A bag for holding small items. It fastens around the base of the tail."
- icon = 'modular_splurt/icons/obj/storage.dmi'
- icon_state = "tailbag"
- w_class = WEIGHT_CLASS_SMALL
- resistance_flags = FLAMMABLE
- slot_flags = ITEM_SLOT_ID
-
- var/obj/item/card/id/front_id = null
- var/list/combined_access
-
-/obj/item/storage/tailbag/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 6
- STR.cant_hold = typecacheof(list(/obj/item/screwdriver/power))
- STR.can_hold = typecacheof(list(
- /obj/item/stack/spacecash,
- /obj/item/holochip,
- /obj/item/card,
- /obj/item/clothing/mask/cigarette,
- /obj/item/flashlight/pen,
- /obj/item/seeds,
- /obj/item/stack/medical,
- /obj/item/toy/crayon,
- /obj/item/coin,
- /obj/item/dice,
- /obj/item/disk,
- /obj/item/implanter,
- /obj/item/lighter,
- /obj/item/lipstick,
- /obj/item/match,
- /obj/item/paper,
- /obj/item/pen,
- /obj/item/photo,
- /obj/item/reagent_containers/dropper,
- /obj/item/reagent_containers/syringe,
- /obj/item/screwdriver,
- /obj/item/multitool,
- /obj/item/wrench,
- /obj/item/wirecutters,
- /obj/item/valentine,
- /obj/item/stamp,
- /obj/item/key,
- /obj/item/cartridge,
- /obj/item/camera_film,
- /obj/item/stack/ore/bluespace_crystal,
- /obj/item/reagent_containers/food/snacks/grown/poppy,
- /obj/item/instrument/harmonica,
- /obj/item/mining_voucher,
- /obj/item/suit_voucher,
- /obj/item/reagent_containers/pill,
- /obj/item/gun/ballistic/derringer,
- /obj/item/genital_equipment/condom,
- /obj/item/restraints/handcuffs,
- /obj/item/assembly/flash,
- /obj/item/laser_pointer,
- /obj/item/pda,
- /obj/item/paicard))
-
-/obj/item/storage/tailbag/Exited(atom/movable/AM)
- . = ..()
- refreshID()
-
-/obj/item/storage/tailbag/proc/refreshID()
- LAZYCLEARLIST(combined_access)
- if(!(front_id in src))
- front_id = null
- for(var/obj/item/card/id/I in contents)
- if(!front_id)
- front_id = I
- LAZYINITLIST(combined_access)
- combined_access |= I.access
- update_icon()
-
-/obj/item/storage/tailbag/Entered(atom/movable/AM)
- . = ..()
- refreshID()
-
-/obj/item/storage/tailbag/update_icon_state()
- var/new_state = "tailbag"
- if(front_id)
- new_state = "tailbag_id"
- if(new_state != icon_state) //avoid so many icon state changes.
- icon_state = new_state
-
-/obj/item/storage/tailbag/GetID()
- return front_id
-
-/obj/item/storage/tailbag/RemoveID()
- if(!front_id)
- return
- . = front_id
- front_id.forceMove(get_turf(src))
-
-/obj/item/storage/tailbag/InsertID(obj/item/inserting_item)
- var/obj/item/card/inserting_id = inserting_item.RemoveID()
- if(!inserting_id)
- return FALSE
- attackby(inserting_id)
- if(inserting_id in contents)
- return TRUE
- return FALSE
-
-/obj/item/storage/tailbag/GetAccess()
- if(LAZYLEN(combined_access))
- return combined_access
- else
- return ..()
-
-/obj/item/storage/tailbag/xtralg
- name = "XL Tailbag"
- desc = "A larger tail bag for larger creatures"
- icon = 'modular_splurt/icons/obj/storage.dmi'
- icon_state = "tailbag_xl"
-
-/obj/item/storage/tailbag/xtralg/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 8
-
diff --git a/modular_splurt/code/game/objects/items/storage/wallets.dm b/modular_splurt/code/game/objects/items/storage/wallets.dm
index 8e08e7b456..3c10c035ce 100644
--- a/modular_splurt/code/game/objects/items/storage/wallets.dm
+++ b/modular_splurt/code/game/objects/items/storage/wallets.dm
@@ -18,3 +18,43 @@
/obj/item/storage/wallet/bluespace/update_icon_state()
// Don't update icons
return
+
+// Tailbags
+/obj/item/storage/wallet/tailbag
+ name = "tailbag"
+ desc = "A bag for holding small items. It fastens around the base of the tail."
+ icon = 'modular_splurt/icons/obj/storage.dmi'
+ icon_state = "tailbag"
+
+/obj/item/storage/wallet/tailbag/update_icon_state()
+ // Don't update icons
+ return
+
+/obj/item/storage/wallet/tailbag/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 6
+ STR.can_hold += typecacheof(list( // Extra items that can go in tailbags, more than wallets
+ /obj/item/restraints/handcuffs,
+ /obj/item/assembly/flash,
+ /obj/item/laser_pointer,
+ /obj/item/pda,
+ /obj/item/paicard
+ ))
+
+/obj/item/storage/wallet/tailbag/xtralg
+ name = "XL Tailbag"
+ desc = "A larger tail bag for larger creatures"
+ icon = 'modular_splurt/icons/obj/storage.dmi'
+ icon_state = "tailbag_xl"
+
+/obj/item/storage/wallet/tailbag/xtralg/update_icon_state()
+ // Don't update icons
+ return
+
+/obj/item/storage/wallet/tailbag/xtralg/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 8
+
+//
diff --git a/modular_splurt/code/modules/client/loadout/donator/second_tier.dm b/modular_splurt/code/modules/client/loadout/donator/second_tier.dm
index d804fd7147..bd25e19202 100644
--- a/modular_splurt/code/modules/client/loadout/donator/second_tier.dm
+++ b/modular_splurt/code/modules/client/loadout/donator/second_tier.dm
@@ -20,6 +20,15 @@
ckeywhitelist = list()
donator_group_id = DONATOR_GROUP_TIER_2
+// Head
+/datum/gear/donator/head/chameleon
+ name = "Chameleon Hat"
+ path = /obj/item/clothing/head/chameleon
+ cost = 2
+ ckeywhitelist = list()
+ donator_group_id = DONATOR_GROUP_TIER_2
+
+
// Mask
/datum/gear/donator/mask/ninja_replica
name = "Replica Ninja Mask"
@@ -27,3 +36,10 @@
cost = 1
ckeywhitelist = list()
donator_group_id = DONATOR_GROUP_TIER_2
+
+/datum/gear/donator/mask/chameleon
+ name = "Chameleon Mask"
+ path = /obj/item/clothing/mask/chameleon
+ cost = 2
+ ckeywhitelist = list()
+ donator_group_id = DONATOR_GROUP_TIER_2
diff --git a/modular_splurt/code/modules/client/loadout/hands.dm b/modular_splurt/code/modules/client/loadout/hands.dm
index bd9f3d501e..c2fd269d6b 100644
--- a/modular_splurt/code/modules/client/loadout/hands.dm
+++ b/modular_splurt/code/modules/client/loadout/hands.dm
@@ -1,24 +1,24 @@
-/datum/gear/hands/paicard
- name = "pAI device"
- description = "A personal AI device for checking and downloading various personalities."
- path = /obj/item/paicard
-
-/datum/gear/hands/tailbag
- name = "Tailbag"
- description = "A bag for holding small personal items, which fastens around the base of the tail."
- path = /obj/item/storage/tailbag
-
-/datum/gear/hands/tarotdeck
- name = "Tarot Deck"
- description = "A full 78 card deck of Tarot Cards, no refunds on false predicitons."
- path = /obj/item/toy/cards/deck/tarot
-
-//MODIFIED COSTS - MODULAR SPLURT
-/datum/gear/hands/straightrazor
- cost = 6
-
-/datum/gear/hands/flask
- cost = 1
-
-/datum/gear/hands/cigar
- cost = 2
+/datum/gear/hands/paicard
+ name = "pAI device"
+ description = "A personal AI device for checking and downloading various personalities."
+ path = /obj/item/paicard
+
+/datum/gear/hands/tailbag
+ name = "Tailbag"
+ description = "A bag for holding small personal items, which fastens around the base of the tail."
+ path = /obj/item/storage/wallet/tailbag
+
+/datum/gear/hands/tarotdeck
+ name = "Tarot Deck"
+ description = "A full 78 card deck of Tarot Cards, no refunds on false predicitons."
+ path = /obj/item/toy/cards/deck/tarot
+
+//MODIFIED COSTS - MODULAR SPLURT
+/datum/gear/hands/straightrazor
+ cost = 6
+
+/datum/gear/hands/flask
+ cost = 1
+
+/datum/gear/hands/cigar
+ cost = 2
diff --git a/modular_splurt/code/modules/clothing/misc/body_camera.dm b/modular_splurt/code/modules/clothing/misc/body_camera.dm
new file mode 100644
index 0000000000..4d57911f1a
--- /dev/null
+++ b/modular_splurt/code/modules/clothing/misc/body_camera.dm
@@ -0,0 +1,145 @@
+
+/obj/item/clothing/suit/armor/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/bodycamera_holder)
+
+/obj/item/clothing/suit/hooded/wintercoat/security/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/bodycamera_holder)
+
+/obj/item/clothing/suit/security/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/bodycamera_holder)
+
+/**
+ * The bodycamera
+ *
+ * This is the item that gets installed into items that have the bodycamera_holder element
+ */
+/obj/item/bodycam_upgrade
+ name = "\improper Body camera"
+ desc = "An armor vest upgrade, there's an instructions tag if you look a little closer..."
+ icon = 'modular_splurt/icons/obj/clothing/bodycam.dmi'
+ icon_state = "bodycamera"
+ ///The camera itself.
+ var/obj/machinery/camera/builtin_bodycamera
+
+/obj/item/bodycam_upgrade/examine_more(mob/user)
+ . = ..()
+ . += list(span_notice("Use [src] on any valid vest to quickly install."))
+ . += list(span_notice("While equipped, use your ID card on the vest to activate/deactivate the camera."))
+ . += list(span_notice("When going off duty, make sure to turn of your camera."))
+
+/obj/item/bodycam_upgrade/Destroy()
+ if(builtin_bodycamera)
+ turn_off()
+ return ..()
+
+/obj/item/bodycam_upgrade/proc/is_on()
+ if(isnull(builtin_bodycamera))
+ return FALSE
+ return TRUE
+
+/obj/item/bodycam_upgrade/proc/turn_on(mob/user, obj/item/card/id/id_card)
+ builtin_bodycamera = new(user)
+ builtin_bodycamera.internal_light = FALSE
+ builtin_bodycamera.network = list("ss13")
+ builtin_bodycamera.c_tag = "-Body Camera: [(id_card.registered_name)] ([id_card.assignment])"
+ playsound(loc, 'sound/machines/beep.ogg', get_clamped_volume(), TRUE, -1)
+ if(user)
+ user.balloon_alert(user, "bodycamera activated")
+
+/obj/item/bodycam_upgrade/proc/turn_off(mob/user)
+ if(user)
+ user.balloon_alert(user, "bodycamera deactivated")
+ playsound(loc, 'sound/machines/beep.ogg', get_clamped_volume(), TRUE, -1)
+ QDEL_NULL(builtin_bodycamera)
+
+
+/**
+ * Bodycamera component
+ *
+ * Allows anything to have a body camera inserted into it
+ */
+/datum/component/bodycamera_holder
+ ///The installed bodycamera
+ var/obj/item/bodycam_upgrade/bodycamera_installed
+ ///The clothing part this needs to be on. This could possibly just be done by checking the clothing's slot
+ var/clothingtype_required = ITEM_SLOT_OCLOTHING
+
+/datum/component/bodycamera_holder/RegisterWithParent()
+ . = ..()
+ RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/on_attackby)
+ RegisterSignal(parent, COMSIG_PARENT_EXAMINE_MORE, .proc/on_examine_more)
+
+/datum/component/bodycamera_holder/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_PARENT_ATTACKBY)
+ UnregisterSignal(parent, COMSIG_PARENT_EXAMINE)
+ QDEL_NULL(bodycamera_installed)
+ return ..()
+
+/datum/component/bodycamera_holder/proc/turn_camera_on(mob/living/user, obj/item/card)
+ RegisterSignal(parent, COMSIG_ITEM_POST_UNEQUIP, PROC_REF(on_unequip))
+ bodycamera_installed.turn_on(user, card)
+
+/datum/component/bodycamera_holder/proc/turn_camera_off(mob/living/user)
+ UnregisterSignal(parent, COMSIG_ITEM_POST_UNEQUIP)
+ bodycamera_installed.turn_off(user)
+
+/// When the camera holder is unequipped
+/datum/component/bodycamera_holder/proc/on_unequip(mob/living/source, force, atom/newloc, no_move, invdrop, silent)
+ SIGNAL_HANDLER
+ turn_camera_off()
+
+/// When examining
+/datum/component/bodycamera_holder/proc/on_examine_more(atom/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ if(bodycamera_installed)
+ examine_list += span_notice("It has [bodycamera_installed] installed.")
+ else
+ examine_list += span_notice("It has a spot to hook up a body camera onto.")
+
+/// When items are used on it. Bodycamera/ID card
+/datum/component/bodycamera_holder/proc/on_attackby(datum/source, obj/item/item, mob/living/user)
+ SIGNAL_HANDLER
+
+ if(istype(item, /obj/item/bodycam_upgrade))
+ if(bodycamera_installed)
+ user.balloon_alert(user, "camera already installed!")
+ playsound(source, 'sound/machines/buzz-two.ogg', item.get_clamped_volume(), TRUE, -1)
+ else
+ item.forceMove(source)
+ bodycamera_installed = item
+ playsound(source, 'sound/items/drill_use.ogg', item.get_clamped_volume(), TRUE, -1)
+ return
+
+ if(!bodycamera_installed)
+ return
+
+ var/obj/item/card/id/card = item.GetID()
+ if(!card)
+ return
+ var/obj/item/clothing/suit_slot = user.get_item_by_slot(clothingtype_required)
+ if(!istype(suit_slot))
+ user.balloon_alert(user, "must be wearing suit!")
+ return
+
+ //Do we have a camera on or off?
+ if(bodycamera_installed.is_on())
+ turn_camera_off(user)
+ else
+ turn_camera_on(user, card)
+
+/// When a screwdriver is used on it
+/datum/component/bodycamera_holder/proc/on_screwdriver_act(atom/source, mob/user, obj/item/tool)
+ SIGNAL_HANDLER
+
+ if(!bodycamera_installed)
+ return
+ if(bodycamera_installed.is_on())
+ turn_camera_off(user)
+ playsound(source, 'sound/items/drill_use.ogg', tool.get_clamped_volume(), TRUE, -1)
+ bodycamera_installed.forceMove(user.loc)
+ INVOKE_ASYNC(user, TYPE_PROC_REF(/mob, put_in_hands), bodycamera_installed)
+ bodycamera_installed = null
diff --git a/modular_splurt/code/modules/clothing/suits/vest.dm b/modular_splurt/code/modules/clothing/suits/vest.dm
index 20117298cf..09f9182a52 100644
--- a/modular_splurt/code/modules/clothing/suits/vest.dm
+++ b/modular_splurt/code/modules/clothing/suits/vest.dm
@@ -1,11 +1,11 @@
/obj/item/clothing/suit/brigdoc
- name = "brig physician vest"
- desc = "A dark red vests for brig physicians."
+ name = "brig physician armored vest"
+ desc = "A bullet proof vest, designed for brig physicians."
icon = 'modular_splurt/icons/obj/clothing/suits.dmi'
mob_overlay_icon = 'modular_splurt/icons/mobs/suits.dmi'
icon_state = "brigphysicianvest"
item_state = "brigphysicianvest"
- armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 10, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 50, "acid" = 50)
+ armor = list("melee" = 20, "bullet" = 10, "laser" = 0, "energy" = 10, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 50, "acid" = 50)
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/scalpel, /obj/item/surgical_drapes, /obj/item/cautery, /obj/item/hemostat, /obj/item/retractor, /obj/item/analyzer,/obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/hypospray/mkii, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic, /obj/item/soap, /obj/item/sensor_device)
diff --git a/modular_splurt/code/modules/custom_loadout/other.dm b/modular_splurt/code/modules/custom_loadout/other.dm
new file mode 100644
index 0000000000..3757f4190b
--- /dev/null
+++ b/modular_splurt/code/modules/custom_loadout/other.dm
@@ -0,0 +1,26 @@
+/obj/item/lewd_spellbook
+ name = "Lewd Spellbook"
+ desc = "A book with knowledge of spells so lewd it could get you expelled from any wizard academy."
+ icon = 'icons/obj/library.dmi'
+ icon_state ="book"
+ throw_speed = 2
+ throw_range = 5
+ attack_verb = list("bashed", "whacked", "educated")
+ w_class = WEIGHT_CLASS_TINY
+ var/obj/effect/proc_holder/spell/targeted/lewd_chems/spell = /obj/effect/proc_holder/spell/targeted/lewd_chems
+
+/obj/item/lewd_spellbook/equipped(mob/user, slot, initial)
+ . = ..()
+ if(!istype(user, /mob/living/carbon/human) || slot != ITEM_SLOT_HANDS)
+ return
+
+ var/mob/living/carbon/human/H = user
+ H.mind.AddSpell(new spell(H))
+
+/obj/item/lewd_spellbook/dropped(mob/user, silent)
+ . = ..()
+ if(!istype(user, /mob/living/carbon/human))
+ return
+
+ var/mob/living/carbon/human/H = user
+ H.mind.RemoveSpell(spell)
diff --git a/modular_splurt/code/modules/holodeck/holo_effect.dm b/modular_splurt/code/modules/holodeck/holo_effect.dm
index fe65b7471b..f267604134 100644
--- a/modular_splurt/code/modules/holodeck/holo_effect.dm
+++ b/modular_splurt/code/modules/holodeck/holo_effect.dm
@@ -14,6 +14,9 @@
/obj/effect/holodeck_effect/mobspawner/bigmommyclaw
mobtype = /mob/living/simple_animal/hostile/deathclaw/funclaw/femclaw/mommyclaw
+/obj/effect/holodeck_effect/mobspawner/rouny_claw
+ mobtype = /mob/living/simple_animal/hostile/deathclaw/funclaw/femclaw/rouny
+
// Holodeck mob: Friendly Bee
/obj/effect/holodeck_effect/mobspawner/bumbles
mobtype = /mob/living/simple_animal/pet/bumbles
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 fdaa95fc0d..57c380f601 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
@@ -4,9 +4,14 @@
"Station Director",
"Station Commander",
"Station Overseer",
- "Stationmaster",
- "Condom",
- "Senator"
+ "Station Mistress",
+ "Station Master",
+ "Cockpitain",
+ "Cuntpitain",
+ "Senator",
+ "Consul",
+ "Cap-Slut",
+ "Condom"
)
LAZYADD(alt_titles, extra_titles)
. = ..()
@@ -16,16 +21,25 @@
"Head Engineer",
"Construction Coordinator",
"Project Manager",
- "Power Plant Director"
+ "Power Plant Director",
+ "Magos",
+ "Magos Biologis"
+
)
LAZYADD(alt_titles, extra_titles)
. = ..()
/datum/job/hop/New()
var/list/extra_titles = list(
+ "Head Of Stations Pets",
+ "Head Of Cumdumps",
+ "Head Of Slutty Personnel",
+ "Headpat Of Personnel",
+ "Headgiver To Personnel",
"Personnel Manager",
"Staff Administrator",
- "Records Administrator"
+ "Records Administrator",
+ "Captain Attachment"
)
LAZYADD(alt_titles, extra_titles)
. = ..()
@@ -34,7 +48,10 @@
var/list/extra_titles = list(
"Security Commander",
"Head of Slutcurity",
+ "Division Leader",
+ "Cerberus Leader",
"Head of Studcurity",
+ "Big Iron",
"Commissar"
)
if(SSevents.holidays && SSevents.holidays[HALLOWEEN])
@@ -44,7 +61,10 @@
/datum/job/qm/New()
var/list/extra_titles = list(
- "Supply Chief"
+ "Supply Chief",
+ "Cargonia Chief",
+ "Brigadier",
+ "Manager of Shipping Sex"
)
LAZYADD(alt_titles, extra_titles)
. = ..()
@@ -52,6 +72,7 @@
/datum/job/rd/New()
var/list/extra_titles = list(
"Science Administrator",
+ "Sex Research Director",
"Research Manager"
)
LAZYADD(alt_titles, extra_titles)
@@ -61,8 +82,10 @@
alt_titles = list(
"Medical Director",
"Medical Administrator",
- "Chief Heal Slut",
- "Chief Heal Stud"
+ "Healing Fleshlight Mistress",
+ "Healing Fleshlight Master",
+ "Chief Heal Stud",
+ "Chief Heal Slut"
) // Sandcode do not have alt titles for CMO at the moment.
@@ -70,6 +93,9 @@
/datum/job/atmos/New()
var/list/extra_titles = list(
"Atmos Plumber",
+ "Anal Plumber",
+ "Atmos-Slut",
+ "Buttplug",
"Disposals Technician"
)
LAZYADD(alt_titles, extra_titles)
@@ -78,10 +104,13 @@
/datum/job/engineer/New()
var/list/extra_titles = list(
"Structural Engineer",
+ "Astromechanic",
"Station Architect",
"Hazardous Material Operator",
"Junior Engineer",
- "Apprentice Engineer"
+ "Engi-Slut",
+ "Apprentice Engineer",
+ "Techpriest Enginseer"
)
LAZYADD(alt_titles, extra_titles)
. = ..()
@@ -96,6 +125,7 @@
"Escort",
"Tourist",
"Clerk",
+ "Secretary",
"Blacksmith",
"Waiter",
"All-purpose fleshlight",
@@ -133,8 +163,10 @@
"Custodian"
)
var/list/extra_titles = list(
- "Custodial Technician",
- "Slutty Maid"
+ "Slutty Maid",
+ "Cum Cleaner",
+ "Liquidator",
+ "Custodial Technician"
)
LAZYREMOVE(alt_titles, rem_titles)
LAZYADD(alt_titles, extra_titles)
@@ -149,15 +181,18 @@
"Priestess",
"Prior",
"Monk",
+ "Tiger Cooperative Disciple",
"Nun",
- "Counselor"
+ "Keeper of Cum",
+ "Counselor",
+ "Techpriest",
)
LAZYADD(alt_titles, extra_titles)
LAZYREMOVE(alt_titles, rem_titles)
. = ..()
/datum/job/clown // Sorry, but no TWO entertainer titles.
- alt_titles = list("Jester", "Comedian")
+ alt_titles = list("Jester", "Comedian", "Cumedian", "Sexy Clown", "Performer") //Just another three title
/datum/job/cook/New()
var/list/extra_titles = list(
@@ -165,6 +200,9 @@
"Prey Prepper",
"Pred Prepper",
"Poissonier",
+ "Chef De Sexe",
+ "Boss Of This Gym",
+ "Waffle Co. Specialist",
"Baker"
)
LAZYADD(alt_titles, extra_titles)
@@ -176,6 +214,7 @@
"Archaeologist",
"Historian",
"Scholar",
+ "Hentai Artist",
"Artist"
)
LAZYADD(alt_titles, extra_titles)
@@ -186,6 +225,7 @@
"Hydroponicist",
"Farmer",
"Beekeeper",
+ "Plants Breeder",
"Vintner",
"Soiler"
)
@@ -196,6 +236,8 @@
/datum/job/lawyer/New()
var/list/extra_titles = list(
+ "Law-Slut",
+ "Internal Affairs Agent",
"Attorney"
)
LAZYADD(alt_titles, extra_titles)
@@ -204,6 +246,8 @@
/datum/job/mime/New()
var/list/extra_titles = list(
"Pantomime",
+ "Cumtomime",
+ "Sexy Mime",
"Mimic"
)
LAZYADD(alt_titles, extra_titles)
@@ -218,6 +262,7 @@
"Toxins Researcher",
"Research Intern",
"Junior Scientist",
+ "Sex Researcher",
"Rack Researcher",
"Nanite Programmer",
"Tetromino Researcher",
@@ -230,7 +275,11 @@
var/list/extra_titles = list(
"Ripperdoc",
"MOD Mechanic",
- "Synth Technician"
+ "Synth Technician",
+ "Droid Mechanic",
+ "Borgs Slut",
+ "Robo-Slut",
+ "Techpriest Biologis"
)
LAZYADD(alt_titles, extra_titles)
. = ..()
@@ -242,6 +291,8 @@
"Alchemist",
"Apothecarist",
"Chemical Plumber",
+ "Organomegaly Healer",
+ "Hexocrocin Therapist",
"Chemi-Slut",
"Chemi-Stud"
)
@@ -255,6 +306,8 @@
"Medical Resident",
"Medtech",
"Medi-Slut",
+ "Oral Doctor",
+ "Healing Fleshlight",
"Medi-Stud"
)
LAZYADD(alt_titles, extra_titles)
@@ -290,6 +343,9 @@
var/list/extra_titles = list(
"Trauma Team",
"Para-Slut",
+ "Emergency Horny Technical",
+ "Emergency Cum Receiver",
+ "Emergency Condom Team",
"Para-Stud"
)
LAZYADD(alt_titles, extra_titles)
@@ -300,6 +356,8 @@
"Microbiologist",
"Biochemist",
"Viro-Slut",
+ "Plague Doctor",
+ "Monkey Destroyer",
"Viro-Stud"
)
LAZYADD(alt_titles, extra_titles)
@@ -328,6 +386,9 @@
"Probation Officer",
"Guardsman",
"Police Officer",
+ "Civil Protection",
+ "Tyranny Lover",
+ "Cerberus",
"Slutcurity Officer",
"Studcurity Officer"
)
@@ -347,6 +408,8 @@
"Prison Administrator",
"Dungeon Master",
"Brig Superintendent",
+ "Brig Overwatch",
+ "Slutcurity Captain",
"Voreden"
)
if(SSevents.holidays && SSevents.holidays[HALLOWEEN])
@@ -364,6 +427,13 @@
"Mailroom Technician",
"Logistics Technician",
"Cryptocurrency Technician",
+ "Horny Mailer",
+ "Pleasures Deliverer",
+ "Cock Packager",
+ "Mailroom Technician",
+ "Disposal Technician",
+ "Donk Co. Specialist",
+ "Logistics Technician",
"Package Handler"
)
LAZYADD(alt_titles, extra_titles)
diff --git a/modular_splurt/code/modules/jobs/job_types/blueshield.dm b/modular_splurt/code/modules/jobs/job_types/blueshield.dm
index 8d32d2ab24..e8596d9455 100644
--- a/modular_splurt/code/modules/jobs/job_types/blueshield.dm
+++ b/modular_splurt/code/modules/jobs/job_types/blueshield.dm
@@ -14,7 +14,7 @@
exp_type = EXP_TYPE_COMMAND
considered_combat_role = TRUE //Brigger then shit yes it is
exp_type_department = EXP_TYPE_COMMAND
- alt_titles = list("Command Security", "Command Guard", "Command Bodyguard", "Blueguard", "Blueshit", "Blueslut", "Bluestud")
+ alt_titles = list("Command Security", "Command Guard", "Command Bodyguard", "Sweet Boy", "Sweet Girl", "Penis Case", "Blueguard", "Blueshit", "Captain Mattress", "Blueslut", "Bluestud")
//SPLURT CHANGES (Tells the blueshield they are command, and just a bodyguard, not security)
custom_spawn_text = "You are a member of command, meant to act as a bodyguard for all Heads of Staff. However, you are not a member of security nor a Head of Staff yourself - avoid acting as either."
outfit = /datum/outfit/job/blueshield
diff --git a/modular_splurt/code/modules/mob/living/emotes.dm b/modular_splurt/code/modules/mob/living/emotes.dm
index 34e04f4f78..bb7100aa2e 100644
--- a/modular_splurt/code/modules/mob/living/emotes.dm
+++ b/modular_splurt/code/modules/mob/living/emotes.dm
@@ -135,7 +135,7 @@
// Accepts all possible parameters
playsound(user.loc, emote_sound, emote_volume, emote_pitch_variance, emote_range, emote_falloff_exponent, emote_frequency, emote_channel, emote_check_pressure, emote_ignore_walls, emote_falloff_distance, emote_wetness, emote_dryness, emote_distance_multiplier, emote_distance_multiplier_min_range)
- // Set coodown
+ // Set cooldown
user.nextsoundemote = world.time + emote_cooldown
/datum/emote/living/surrender/run_emote(mob/user, params, type_override, intentional)
diff --git a/modular_splurt/code/modules/mob/living/simple_animal/hostile/deathclaw/rouny.dm b/modular_splurt/code/modules/mob/living/simple_animal/hostile/deathclaw/rouny.dm
new file mode 100644
index 0000000000..8eb5d186c9
--- /dev/null
+++ b/modular_splurt/code/modules/mob/living/simple_animal/hostile/deathclaw/rouny.dm
@@ -0,0 +1,12 @@
+/mob/living/simple_animal/hostile/deathclaw/funclaw/femclaw/rouny
+ name = "Rouny"
+ desc = "Why would someone staple boobs on this thing???"
+ icon_state = "rouny"
+ maxHealth = 200
+ health = 200
+ armour_penetration = 25
+
+/mob/living/simple_animal/hostile/deathclaw/funclaw/femclaw/pet_femclaw/rouny
+ name = "Rouny"
+ desc = "Why would someone staple boobs on this thing???"
+ icon_state = "rouny"
diff --git a/modular_splurt/code/modules/mob/living/simple_animal/hostile/werewolves/funwolf.dm b/modular_splurt/code/modules/mob/living/simple_animal/hostile/werewolves/funwolf.dm
index 5ecbef8b41..8c06851b30 100644
--- a/modular_splurt/code/modules/mob/living/simple_animal/hostile/werewolves/funwolf.dm
+++ b/modular_splurt/code/modules/mob/living/simple_animal/hostile/werewolves/funwolf.dm
@@ -12,6 +12,14 @@
desc = "A massive, wolf like creature with powerful muscles, razor-sharp claws, and aggression to match. This one has the bedroom eyes.."
werewolf_mode = "gentle"
+/mob/living/simple_animal/hostile/ice_wolf/funwolf/gentle/pet_ice_wolf //Donator request
+ vision_range = 0
+ aggro_vision_range = 0
+ wander = 1
+ melee_damage_lower = 0
+ melee_damage_upper = 0
+ stop_automated_movement_when_pulled = 1
+
/mob/living/simple_animal/hostile/werewolf/funwolf/abomination
name = "Exiled Werewolf"
desc = "A massive, wolf like creature with powerful muscles, razor-sharp claws, and aggression to match. This one has a strange smell for some reason.."
diff --git a/modular_splurt/code/modules/ruins/spaceruin_code/hilbertshotel.dm b/modular_splurt/code/modules/ruins/spaceruin_code/hilbertshotel.dm
index 699d0f0fb0..e4358b250f 100644
--- a/modular_splurt/code/modules/ruins/spaceruin_code/hilbertshotel.dm
+++ b/modular_splurt/code/modules/ruins/spaceruin_code/hilbertshotel.dm
@@ -47,3 +47,17 @@
/datum/map_template/hilbertshotel/apartment/sauna
name = "Apartment_sauna"
mappath = '_maps/splurt_maps/templates/hilbertshotel_templates/apartment_sauna.dmm'
+
+// Fluff - Misc
+/obj/item/paper/fluff/hilbertshotel/welcomeletter
+ name = "Welcome Letter"
+ info = "Welcome to Hilbert's Hotel!
\
+
\
+ Each room in the hotel is an unique pocket dimension: You can choose up to 5 rooms per shift, each room you select will remember your actions and belongings, allowing you to create your own personal space.
\
+ Inside the complementary guest box you'll find some sweets, each one as unique and delightful as our rooms. Savour them as you relax∼
\
+ Remember, your rooms are always available for you to return to.
\
+
\
+ Enjoy your stay∼!
\
+
\
+ With love,
\
+ Aniya ♥"
diff --git a/modular_splurt/code/modules/spell/spell_types/conjure.dm b/modular_splurt/code/modules/spells/spell_types/conjure.dm
similarity index 100%
rename from modular_splurt/code/modules/spell/spell_types/conjure.dm
rename to modular_splurt/code/modules/spells/spell_types/conjure.dm
diff --git a/modular_splurt/code/modules/spells/spell_types/lewd.dm b/modular_splurt/code/modules/spells/spell_types/lewd.dm
new file mode 100644
index 0000000000..1414b5c058
--- /dev/null
+++ b/modular_splurt/code/modules/spells/spell_types/lewd.dm
@@ -0,0 +1,35 @@
+/obj/effect/proc_holder/spell/targeted/lewd_chems
+ name = "Cast Lewd Chems"
+ desc = "Cast spells that will cause the target to feel the power of lewd chemistry."
+ include_user = TRUE
+ clothes_req = NONE
+ invocation_type = "shout"
+ invocation = "BIGGUS DICKUS"
+ charge_max = 10 SECONDS
+ cooldown_min = 5 SECONDS
+ icon = 'modular_splurt/icons/obj/syringe.dmi'
+ base_icon_state = "bombpen"
+ sparks_amt = 10
+
+/obj/effect/proc_holder/spell/targeted/lewd_chems/cast(list/targets, mob/user = usr)
+ . = ..()
+
+ var/list/datum/reagent/choices = list(
+ "Crocin" = /datum/reagent/drug/aphrodisiac,
+ "Hexacrocin" = /datum/reagent/drug/aphrodisiacplus,
+ "Succubus milk" = /datum/reagent/fermi/breast_enlarger,
+ "Incubus draft" = /datum/reagent/fermi/penis_enlarger,
+ "Denbu Tincture" = /datum/reagent/fermi/butt_enlarger,
+ "Belladine nectar" = /datum/reagent/fermi/belly_inflator,
+ "Prospacillin" = /datum/reagent/growthchem
+ )
+ var/datum/reagent/chem = input(user, "What chemical do you want to use?", "Lewd Chems") as null|anything in choices + "All of the above"
+ if(!chem)
+ return
+
+ for(var/mob/living/carbon/C in targets)
+ if(!choices.Find(chem))
+ for(var/reagent in choices - "Hexacrocin")
+ C.reagents.add_reagent(choices[reagent], 10)
+ else
+ C.reagents.add_reagent(choices[chem], 30)
diff --git a/modular_splurt/icons/mob/clothing/suit.dmi b/modular_splurt/icons/mob/clothing/suit.dmi
index d17d57d316..1bc44ff060 100644
Binary files a/modular_splurt/icons/mob/clothing/suit.dmi and b/modular_splurt/icons/mob/clothing/suit.dmi differ
diff --git a/modular_splurt/icons/mob/clothing/uniform.dmi b/modular_splurt/icons/mob/clothing/uniform.dmi
index 118d981ba0..f40aed4107 100644
Binary files a/modular_splurt/icons/mob/clothing/uniform.dmi and b/modular_splurt/icons/mob/clothing/uniform.dmi differ
diff --git a/modular_splurt/icons/mob/clothing/uniform_digi.dmi b/modular_splurt/icons/mob/clothing/uniform_digi.dmi
index 5784fdeb40..f71144e890 100644
Binary files a/modular_splurt/icons/mob/clothing/uniform_digi.dmi and b/modular_splurt/icons/mob/clothing/uniform_digi.dmi differ
diff --git a/modular_splurt/icons/mob/femclaw/newclaws.dmi b/modular_splurt/icons/mob/femclaw/newclaws.dmi
index b7bc589ac6..9caa459e7d 100644
Binary files a/modular_splurt/icons/mob/femclaw/newclaws.dmi and b/modular_splurt/icons/mob/femclaw/newclaws.dmi differ
diff --git a/modular_splurt/icons/mobs/suits.dmi b/modular_splurt/icons/mobs/suits.dmi
index 57f709fdbb..0dcaf0ac7c 100644
Binary files a/modular_splurt/icons/mobs/suits.dmi and b/modular_splurt/icons/mobs/suits.dmi differ
diff --git a/modular_splurt/icons/obj/clothing/bodycam.dmi b/modular_splurt/icons/obj/clothing/bodycam.dmi
new file mode 100644
index 0000000000..4047aca61e
Binary files /dev/null and b/modular_splurt/icons/obj/clothing/bodycam.dmi differ
diff --git a/modular_splurt/icons/obj/clothing/neck.dmi b/modular_splurt/icons/obj/clothing/neck.dmi
index 50d48a91f1..d02b839824 100644
Binary files a/modular_splurt/icons/obj/clothing/neck.dmi and b/modular_splurt/icons/obj/clothing/neck.dmi differ
diff --git a/modular_splurt/icons/obj/clothing/suits.dmi b/modular_splurt/icons/obj/clothing/suits.dmi
index dd0cdcfc84..f22a50ea61 100644
Binary files a/modular_splurt/icons/obj/clothing/suits.dmi and b/modular_splurt/icons/obj/clothing/suits.dmi differ
diff --git a/modular_splurt/icons/obj/clothing/uniforms.dmi b/modular_splurt/icons/obj/clothing/uniforms.dmi
index 25c5bb004d..c023a18ebb 100644
Binary files a/modular_splurt/icons/obj/clothing/uniforms.dmi and b/modular_splurt/icons/obj/clothing/uniforms.dmi differ
diff --git a/modular_splurt/icons/obj/plushes.dmi b/modular_splurt/icons/obj/plushes.dmi
index ba91b5434c..93de3d916e 100644
Binary files a/modular_splurt/icons/obj/plushes.dmi and b/modular_splurt/icons/obj/plushes.dmi differ
diff --git a/modular_splurt/sound/misc/thomas_whistle_movie.ogg b/modular_splurt/sound/misc/thomas_whistle_movie.ogg
new file mode 100644
index 0000000000..e1a077c30b
Binary files /dev/null and b/modular_splurt/sound/misc/thomas_whistle_movie.ogg differ
diff --git a/sound/effects/kiss.ogg b/sound/effects/kiss.ogg
new file mode 100644
index 0000000000..699b13a8de
Binary files /dev/null and b/sound/effects/kiss.ogg differ
diff --git a/sound/weapons/attributions.txt b/sound/weapons/attributions.txt
index fe2911c873..9106b7cfe2 100644
--- a/sound/weapons/attributions.txt
+++ b/sound/weapons/attributions.txt
@@ -6,4 +6,6 @@ No changes were made to the sounds, and all credit goes to kMoon.
batreflect sounds are by shadoWisp on freesound.org:
https://www.freesound.org/people/shadoWisp/sounds/252044/
-Small parts of the sound are cut out and used.
\ No newline at end of file
+Small parts of the sound are cut out and used.
+
+kiss sound: https://freesound.org/people/stereostereo/sounds/117355/
diff --git a/tgstation.dme b/tgstation.dme
index 190a24b2de..9fc105addd 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1211,6 +1211,7 @@
#include "code\game\objects\items\fluff.dm"
#include "code\game\objects\items\gift.dm"
#include "code\game\objects\items\granters.dm"
+#include "code\game\objects\items\hand_items.dm"
#include "code\game\objects\items\handcuffs.dm"
#include "code\game\objects\items\his_grace.dm"
#include "code\game\objects\items\holosign_creator.dm"
@@ -3978,6 +3979,8 @@
#include "modular_citadel\code\modules\client\loadout\donator\accessory.dm"
#include "modular_citadel\code\modules\client\loadout\donator\backpack.dm"
#include "modular_citadel\code\modules\client\loadout\donator\gloves.dm"
+#include "modular_citadel\code\modules\client\loadout\donator\hands.dm"
+#include "modular_citadel\code\modules\client\loadout\donator\head.dm"
#include "modular_citadel\code\modules\client\loadout\donator\mask.dm"
#include "modular_citadel\code\modules\client\loadout\donator\neck.dm"
#include "modular_citadel\code\modules\client\loadout\donator\shoes.dm"
@@ -4586,7 +4589,6 @@
#include "modular_splurt\code\game\objects\items\storage\boxes.dm"
#include "modular_splurt\code\game\objects\items\storage\firstaid.dm"
#include "modular_splurt\code\game\objects\items\storage\secure.dm"
-#include "modular_splurt\code\game\objects\items\storage\tailbag.dm"
#include "modular_splurt\code\game\objects\items\storage\wallets.dm"
#include "modular_splurt\code\game\objects\items\tanks\tank_types.dm"
#include "modular_splurt\code\game\objects\items\weaponry\armyknife.dm"
@@ -4760,6 +4762,7 @@
#include "modular_splurt\code\modules\clothing\masks\gasmask.dm"
#include "modular_splurt\code\modules\clothing\masks\hailer.dm"
#include "modular_splurt\code\modules\clothing\masks\miscellaneous.dm"
+#include "modular_splurt\code\modules\clothing\misc\body_camera.dm"
#include "modular_splurt\code\modules\clothing\neck\_neck.dm"
#include "modular_splurt\code\modules\clothing\outfits\ert.dm"
#include "modular_splurt\code\modules\clothing\outfits\standard.dm"
@@ -4792,6 +4795,7 @@
#include "modular_splurt\code\modules\clothing\wrists\miscellaneous.dm"
#include "modular_splurt\code\modules\crafting\eyepatchhud.dm"
#include "modular_splurt\code\modules\custom_loadout\first_tier.dm"
+#include "modular_splurt\code\modules\custom_loadout\other.dm"
#include "modular_splurt\code\modules\discord\accountlink.dm"
#include "modular_splurt\code\modules\discord\discord_link_record.dm"
#include "modular_splurt\code\modules\discord\tgs_commands.dm"
@@ -4942,6 +4946,7 @@
#include "modular_splurt\code\modules\mob\living\simple_animal\hostile\deathclaw\deathclaw.dm"
#include "modular_splurt\code\modules\mob\living\simple_animal\hostile\deathclaw\funclaw.dm"
#include "modular_splurt\code\modules\mob\living\simple_animal\hostile\deathclaw\pet_deathclaw.dm"
+#include "modular_splurt\code\modules\mob\living\simple_animal\hostile\deathclaw\rouny.dm"
#include "modular_splurt\code\modules\mob\living\simple_animal\hostile\megafauna\blood_drunk_miner.dm"
#include "modular_splurt\code\modules\mob\living\simple_animal\hostile\megafauna\king_of_goats.dm"
#include "modular_splurt\code\modules\mob\living\simple_animal\hostile\megafauna\penguinhiero.dm"
@@ -5046,7 +5051,8 @@
#include "modular_splurt\code\modules\smithing\anvil.dm"
#include "modular_splurt\code\modules\smithing\finished_items.dm"
#include "modular_splurt\code\modules\smithing\smithed_items.dm"
-#include "modular_splurt\code\modules\spell\spell_types\conjure.dm"
+#include "modular_splurt\code\modules\spells\spell_types\conjure.dm"
+#include "modular_splurt\code\modules\spells\spell_types\lewd.dm"
#include "modular_splurt\code\modules\surgery\bodyparts\_bodyparts.dm"
#include "modular_splurt\code\modules\surgery\organs\augments_arms.dm"
#include "modular_splurt\code\modules\surgery\organs\augments_eyes.dm"