diff --git a/Dockerfile b/Dockerfile
index ec3694c7e8..ad347079cd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM tgstation/byond:512.1488 as base
+FROM tgstation/byond:513.1490 as base
FROM base as build_base
diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_golem_ship.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_golem_ship.dmm
index 8015f25696..2c6eda9f90 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_golem_ship.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_golem_ship.dmm
@@ -68,7 +68,10 @@
/turf/open/floor/mineral/titanium/purple,
/area/ruin/powered/golem_ship)
"k" = (
-/obj/machinery/computer/arcade/battle,
+/obj/machinery/computer/arcade/battle{
+ icon_state = "arcade";
+ dir = 4
+ },
/turf/open/floor/mineral/titanium/purple,
/area/ruin/powered/golem_ship)
"l" = (
@@ -105,6 +108,13 @@
/obj/machinery/computer/shuttle,
/turf/open/floor/mineral/titanium/purple,
/area/ruin/powered/golem_ship)
+"s" = (
+/obj/machinery/computer/arcade/orion_trail{
+ icon_state = "arcade";
+ dir = 4
+ },
+/turf/open/floor/mineral/titanium/purple,
+/area/ruin/powered/golem_ship)
"t" = (
/obj/structure/extinguisher_cabinet{
pixel_y = 30
@@ -142,10 +152,6 @@
/obj/machinery/reagentgrinder,
/turf/open/floor/mineral/titanium/purple,
/area/ruin/powered/golem_ship)
-"y" = (
-/obj/machinery/computer/arcade/orion_trail,
-/turf/open/floor/mineral/titanium/purple,
-/area/ruin/powered/golem_ship)
"z" = (
/obj/structure/extinguisher_cabinet{
pixel_y = -30
@@ -332,7 +338,7 @@ l
l
j
l
-y
+s
b
a
a
diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm
index 8e0d9a523a..2ec5b88792 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm
@@ -167,6 +167,56 @@
},
/turf/open/floor/plating,
/area/ruin/unpowered/syndicate_lava_base/circuits)
+"av" = (
+/obj/structure/bed/roller,
+/obj/machinery/iv_drip,
+/obj/effect/decal/cleanable/dirt,
+/obj/effect/decal/cleanable/dirt,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/mob/living/carbon/monkey{
+ faction = list("neutral","Syndicate")
+ },
+/turf/open/floor/plasteel/dark,
+/area/ruin/unpowered/syndicate_lava_base/testlab)
+"aw" = (
+/obj/machinery/light/small,
+/obj/structure/bed/roller,
+/obj/machinery/iv_drip,
+/obj/effect/decal/cleanable/dirt,
+/obj/effect/decal/cleanable/dirt,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/mob/living/carbon/monkey{
+ faction = list("neutral","Syndicate")
+ },
+/turf/open/floor/plasteel/dark,
+/area/ruin/unpowered/syndicate_lava_base/testlab)
+"ax" = (
+/obj/effect/decal/cleanable/dirt,
+/obj/effect/decal/cleanable/dirt,
+/obj/machinery/computer/arcade/orion_trail{
+ icon_state = "arcade";
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/ruin/unpowered/syndicate_lava_base/bar)
"aF" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
dir = 4
@@ -790,26 +840,6 @@
/obj/structure/disposalpipe/trunk,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/syndicate_lava_base/virology)
-"ej" = (
-/obj/structure/bed/roller,
-/obj/machinery/iv_drip,
-/obj/effect/decal/cleanable/dirt,
-/obj/effect/decal/cleanable/dirt,
-/mob/living/carbon/monkey{
- faction = list("neutral","Syndicate")
- },
-/obj/effect/turf_decal/tile/neutral{
- dir = 1
- },
-/obj/effect/turf_decal/tile/neutral,
-/obj/effect/turf_decal/tile/neutral{
- dir = 4
- },
-/obj/effect/turf_decal/tile/neutral{
- dir = 8
- },
-/turf/open/floor/plasteel/dark,
-/area/ruin/unpowered/syndicate_lava_base/testlab)
"ek" = (
/obj/structure/grille,
/obj/structure/window/plastitanium,
@@ -1136,27 +1166,6 @@
/obj/structure/disposalpipe/segment,
/turf/closed/wall/mineral/plastitanium/nodiagonal,
/area/ruin/unpowered/syndicate_lava_base/virology)
-"eK" = (
-/obj/machinery/light/small,
-/obj/structure/bed/roller,
-/obj/machinery/iv_drip,
-/obj/effect/decal/cleanable/dirt,
-/obj/effect/decal/cleanable/dirt,
-/mob/living/carbon/monkey{
- faction = list("neutral","Syndicate")
- },
-/obj/effect/turf_decal/tile/neutral{
- dir = 1
- },
-/obj/effect/turf_decal/tile/neutral,
-/obj/effect/turf_decal/tile/neutral{
- dir = 4
- },
-/obj/effect/turf_decal/tile/neutral{
- dir = 8
- },
-/turf/open/floor/plasteel/dark,
-/area/ruin/unpowered/syndicate_lava_base/testlab)
"eL" = (
/obj/machinery/door/airlock/hatch{
name = "Monkey Pen";
@@ -5148,12 +5157,6 @@
/obj/item/clothing/mask/gas,
/turf/open/floor/plating,
/area/ruin/unpowered/syndicate_lava_base/arrivals)
-"mt" = (
-/obj/machinery/computer/arcade/orion_trail,
-/obj/effect/decal/cleanable/dirt,
-/obj/effect/decal/cleanable/dirt,
-/turf/open/floor/plasteel/dark,
-/area/ruin/unpowered/syndicate_lava_base/bar)
"mu" = (
/obj/item/twohanded/required/kirbyplants{
icon_state = "plant-22"
@@ -7340,8 +7343,8 @@ ae
ae
ae
ae
-ej
-eK
+av
+aw
ae
fD
ad
@@ -7362,7 +7365,7 @@ kH
jN
jZ
lU
-mt
+ax
mU
np
nP
diff --git a/_maps/RandomRuins/SpaceRuins/bigape.dmm b/_maps/RandomRuins/SpaceRuins/bigape.dmm
index 09e85e129d..602f1737e0 100644
--- a/_maps/RandomRuins/SpaceRuins/bigape.dmm
+++ b/_maps/RandomRuins/SpaceRuins/bigape.dmm
@@ -56,11 +56,9 @@
/obj/structure/chair/sofa{
dir = 4
},
-/mob/living/simple_animal/hostile/gorilla{
- AIStatus = null;
- desc = "There is no need to be upset.";
- dir = 4;
- name = "Familiar Gorilla"
+/mob/living/simple_animal/hostile/gorilla/familiar{
+ icon_state = "crawling";
+ dir = 4
},
/turf/open/floor/plating/beach/sand,
/area/ruin/powered)
diff --git a/_maps/RandomZLevels/moonoutpost19.dmm b/_maps/RandomZLevels/moonoutpost19.dmm
index c251a338a5..ddc8778ac1 100644
--- a/_maps/RandomZLevels/moonoutpost19.dmm
+++ b/_maps/RandomZLevels/moonoutpost19.dmm
@@ -16,6 +16,13 @@
heat_capacity = 1e+006
},
/area/awaymission/moonoutpost19/hive)
+"ae" = (
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/awaymission/moonoutpost19/arrivals)
"ag" = (
/obj/structure/alien/weeds,
/obj/structure/alien/weeds{
@@ -6408,10 +6415,6 @@
heat_capacity = 1e+006
},
/area/awaymission/moonoutpost19/arrivals)
-"mB" = (
-/obj/machinery/computer/arcade,
-/turf/open/floor/plasteel/dark,
-/area/awaymission/moonoutpost19/arrivals)
"mC" = (
/obj/machinery/vending/cigarette,
/obj/structure/sign/poster/contraband/smoke{
@@ -43385,7 +43388,7 @@ la
lq
jk
mj
-mB
+ae
hJ
ba
ba
@@ -43642,7 +43645,7 @@ jN
lr
jj
mj
-mB
+ae
hI
ba
ba
diff --git a/_maps/RandomZLevels/research.dmm b/_maps/RandomZLevels/research.dmm
index 3a58d85143..2499270d8f 100644
--- a/_maps/RandomZLevels/research.dmm
+++ b/_maps/RandomZLevels/research.dmm
@@ -245,10 +245,38 @@
},
/turf/open/floor/plating,
/area/awaymission/research/interior/engineering)
+"aS" = (
+/obj/effect/turf_decal/tile/purple{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/purple,
+/obj/effect/turf_decal/tile/purple{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/purple{
+ dir = 8
+ },
+/mob/living/carbon/monkey,
+/turf/open/floor/plasteel,
+/area/awaymission/research/interior/genetics)
"aT" = (
/mob/living/simple_animal/hostile/syndicate/melee/sword,
/turf/open/floor/plating,
/area/awaymission/research/interior/engineering)
+"aU" = (
+/obj/effect/turf_decal/tile/purple{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/purple,
+/obj/effect/turf_decal/tile/purple{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/purple{
+ dir = 8
+ },
+/mob/living/carbon/human,
+/turf/open/floor/plasteel,
+/area/awaymission/research/interior/genetics)
"aV" = (
/obj/item/stack/sheet/plasteel,
/obj/effect/turf_decal/tile/yellow{
@@ -326,6 +354,23 @@
icon_state = "damaged4"
},
/area/awaymission/research/interior/engineering)
+"bc" = (
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/purple{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/purple,
+/obj/effect/turf_decal/tile/purple{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/purple{
+ dir = 8
+ },
+/mob/living/carbon/monkey,
+/turf/open/floor/plasteel,
+/area/awaymission/research/interior/genetics)
"bd" = (
/turf/open/floor/plasteel{
icon_state = "damaged3"
@@ -903,6 +948,17 @@
/obj/machinery/light/small,
/turf/open/floor/plating,
/area/awaymission/research/interior/maint)
+"co" = (
+/obj/item/ammo_casing/c45,
+/obj/effect/turf_decal/tile/yellow{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/yellow{
+ dir = 8
+ },
+/mob/living/simple_animal/hostile/syndicate,
+/turf/open/floor/plasteel/white,
+/area/awaymission/research/interior)
"cp" = (
/turf/closed/wall/r_wall,
/area/awaymission/research/interior)
@@ -1195,19 +1251,19 @@
/turf/open/floor/plating,
/area/awaymission/research/interior/genetics)
"cT" = (
-/mob/living/carbon/monkey,
-/obj/effect/turf_decal/tile/purple{
+/obj/effect/turf_decal/tile/red{
dir = 1
},
-/obj/effect/turf_decal/tile/purple,
-/obj/effect/turf_decal/tile/purple{
+/obj/effect/turf_decal/tile/red,
+/obj/effect/turf_decal/tile/red{
dir = 4
},
-/obj/effect/turf_decal/tile/purple{
+/obj/effect/turf_decal/tile/red{
dir = 8
},
+/mob/living/simple_animal/hostile/nanotrasen/ranged,
/turf/open/floor/plasteel,
-/area/awaymission/research/interior/genetics)
+/area/awaymission/research/interior/security)
"cU" = (
/obj/machinery/door/window/eastright,
/obj/effect/turf_decal/tile/purple{
@@ -1253,7 +1309,9 @@
/turf/open/floor/plasteel,
/area/awaymission/research/interior/genetics)
"cX" = (
-/mob/living/carbon/human,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
/obj/effect/turf_decal/tile/purple{
dir = 1
},
@@ -1264,6 +1322,7 @@
/obj/effect/turf_decal/tile/purple{
dir = 8
},
+/mob/living/carbon/human,
/turf/open/floor/plasteel,
/area/awaymission/research/interior/genetics)
"cY" = (
@@ -1356,22 +1415,15 @@
/turf/open/floor/plasteel,
/area/awaymission/research/interior/genetics)
"di" = (
-/obj/structure/window/reinforced{
- dir = 4
- },
-/mob/living/carbon/monkey,
/obj/effect/turf_decal/tile/purple{
dir = 1
},
-/obj/effect/turf_decal/tile/purple,
/obj/effect/turf_decal/tile/purple{
dir = 4
},
-/obj/effect/turf_decal/tile/purple{
- dir = 8
- },
-/turf/open/floor/plasteel,
-/area/awaymission/research/interior/genetics)
+/mob/living/simple_animal/hostile/nanotrasen/ranged,
+/turf/open/floor/plasteel/white,
+/area/awaymission/research/interior/cryo)
"dj" = (
/obj/effect/decal/cleanable/blood,
/obj/item/stack/rods,
@@ -1416,14 +1468,14 @@
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior)
"dn" = (
-/obj/item/ammo_casing/c45,
-/mob/living/simple_animal/hostile/syndicate,
/obj/effect/turf_decal/tile/yellow{
dir = 1
},
+/obj/effect/turf_decal/tile/yellow,
/obj/effect/turf_decal/tile/yellow{
- dir = 8
+ dir = 4
},
+/mob/living/simple_animal/hostile/syndicate,
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior)
"do" = (
@@ -1675,7 +1727,6 @@
/turf/open/floor/plasteel,
/area/awaymission/research/interior/security)
"dI" = (
-/mob/living/simple_animal/hostile/nanotrasen/ranged,
/obj/effect/turf_decal/tile/red{
dir = 1
},
@@ -1686,7 +1737,8 @@
/obj/effect/turf_decal/tile/red{
dir = 8
},
-/turf/open/floor/plasteel,
+/mob/living/simple_animal/hostile/nanotrasen/ranged/smg,
+/turf/open/floor/plasteel/white,
/area/awaymission/research/interior/security)
"dJ" = (
/obj/structure/rack,
@@ -1777,11 +1829,7 @@
},
/turf/open/floor/plasteel/dark,
/area/awaymission/research/interior/genetics)
-"dQ" = (
-/obj/structure/window/reinforced{
- dir = 8
- },
-/mob/living/carbon/human,
+"dP" = (
/obj/effect/turf_decal/tile/purple{
dir = 1
},
@@ -1792,8 +1840,23 @@
/obj/effect/turf_decal/tile/purple{
dir = 8
},
+/mob/living/simple_animal/hostile/nanotrasen/ranged,
+/turf/open/floor/plasteel/white,
+/area/awaymission/research/interior/cryo)
+"dQ" = (
+/obj/effect/turf_decal/tile/purple{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/purple,
+/obj/effect/turf_decal/tile/purple{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/purple{
+ dir = 8
+ },
+/mob/living/simple_animal/hostile/nanotrasen,
/turf/open/floor/plasteel,
-/area/awaymission/research/interior/genetics)
+/area/awaymission/research/interior/cryo)
"dR" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -2144,15 +2207,15 @@
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior/cryo)
"eB" = (
-/mob/living/simple_animal/hostile/nanotrasen/ranged,
-/obj/effect/turf_decal/tile/purple{
+/obj/effect/turf_decal/tile/red{
dir = 1
},
-/obj/effect/turf_decal/tile/purple{
+/obj/effect/turf_decal/tile/red{
dir = 4
},
+/mob/living/simple_animal/hostile/nanotrasen/ranged/smg,
/turf/open/floor/plasteel/white,
-/area/awaymission/research/interior/cryo)
+/area/awaymission/research/interior/security)
"eC" = (
/obj/item/ammo_casing/c9mm,
/obj/effect/turf_decal/tile/purple{
@@ -2214,16 +2277,22 @@
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior)
"eL" = (
-/mob/living/simple_animal/hostile/syndicate,
-/obj/effect/turf_decal/tile/yellow{
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/effect/turf_decal/tile/purple{
dir = 1
},
-/obj/effect/turf_decal/tile/yellow,
-/obj/effect/turf_decal/tile/yellow{
+/obj/effect/turf_decal/tile/purple,
+/obj/effect/turf_decal/tile/purple{
dir = 4
},
-/turf/open/floor/plasteel/white,
-/area/awaymission/research/interior)
+/obj/effect/turf_decal/tile/purple{
+ dir = 8
+ },
+/mob/living/carbon/monkey,
+/turf/open/floor/plasteel,
+/area/awaymission/research/interior/genetics)
"eM" = (
/obj/item/ammo_casing/c45,
/obj/item/ammo_casing/c45,
@@ -2846,19 +2915,19 @@
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior/security)
"fY" = (
-/mob/living/simple_animal/hostile/nanotrasen/ranged/smg,
-/obj/effect/turf_decal/tile/red{
+/obj/effect/turf_decal/tile/green{
dir = 1
},
-/obj/effect/turf_decal/tile/red,
-/obj/effect/turf_decal/tile/red{
+/obj/effect/turf_decal/tile/green,
+/obj/effect/turf_decal/tile/green{
dir = 4
},
-/obj/effect/turf_decal/tile/red{
+/obj/effect/turf_decal/tile/green{
dir = 8
},
+/mob/living/simple_animal/hostile/syndicate/ranged/smg,
/turf/open/floor/plasteel/white,
-/area/awaymission/research/interior/security)
+/area/awaymission/research/interior)
"fZ" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -2966,6 +3035,23 @@
},
/turf/open/floor/plasteel/dark,
/area/awaymission/research/interior/secure)
+"gi" = (
+/obj/effect/turf_decal/tile/red{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/red{
+ dir = 4
+ },
+/mob/living/simple_animal/bot/secbot/beepsky{
+ desc = "A beefy variant of the standard securitron model.";
+ emagged = 1;
+ faction = list("nanotrasenprivate");
+ health = 50;
+ maxHealth = 50;
+ name = "Officer Genesky"
+ },
+/turf/open/floor/plasteel/white,
+/area/awaymission/research/interior/security)
"gj" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -3115,19 +3201,13 @@
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior/cryo)
"gv" = (
-/mob/living/simple_animal/hostile/nanotrasen/ranged,
-/obj/effect/turf_decal/tile/purple{
- dir = 1
- },
-/obj/effect/turf_decal/tile/purple,
-/obj/effect/turf_decal/tile/purple{
+/obj/effect/turf_decal/tile/green,
+/obj/effect/turf_decal/tile/green{
dir = 4
},
-/obj/effect/turf_decal/tile/purple{
- dir = 8
- },
+/mob/living/simple_animal/hostile/syndicate,
/turf/open/floor/plasteel/white,
-/area/awaymission/research/interior/cryo)
+/area/awaymission/research/interior)
"gw" = (
/obj/effect/mob_spawn/human/doctor{
brute_damage = 145;
@@ -3508,6 +3588,35 @@
},
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior)
+"gW" = (
+/obj/structure/sign/directions/security{
+ dir = 1;
+ pixel_x = 32;
+ pixel_y = 40
+ },
+/obj/structure/sign/directions/engineering{
+ dir = 1;
+ pixel_x = 32;
+ pixel_y = 33
+ },
+/obj/structure/sign/directions/science{
+ dir = 1;
+ pixel_x = 32;
+ pixel_y = 26
+ },
+/obj/effect/turf_decal/tile/green{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/green,
+/obj/effect/turf_decal/tile/green{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/green{
+ dir = 8
+ },
+/mob/living/simple_animal/hostile/syndicate/ranged/smg,
+/turf/open/floor/plasteel/white,
+/area/awaymission/research/interior)
"gX" = (
/obj/effect/decal/cleanable/blood/drip,
/obj/effect/turf_decal/tile/purple{
@@ -3660,19 +3769,19 @@
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior/security)
"hj" = (
-/mob/living/simple_animal/hostile/nanotrasen,
-/obj/effect/turf_decal/tile/purple{
+/obj/effect/turf_decal/tile/blue{
dir = 1
},
-/obj/effect/turf_decal/tile/purple,
-/obj/effect/turf_decal/tile/purple{
+/obj/effect/turf_decal/tile/blue,
+/obj/effect/turf_decal/tile/blue{
dir = 4
},
-/obj/effect/turf_decal/tile/purple{
+/obj/effect/turf_decal/tile/blue{
dir = 8
},
-/turf/open/floor/plasteel,
-/area/awaymission/research/interior/cryo)
+/mob/living/simple_animal/hostile/syndicate/ranged/smg,
+/turf/open/floor/plasteel/white,
+/area/awaymission/research/interior/medbay)
"hk" = (
/obj/effect/decal/cleanable/blood,
/obj/effect/turf_decal/tile/purple{
@@ -3736,15 +3845,19 @@
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior/security)
"hq" = (
-/mob/living/simple_animal/hostile/nanotrasen/ranged/smg,
-/obj/effect/turf_decal/tile/red{
+/obj/effect/turf_decal/tile/blue{
dir = 1
},
-/obj/effect/turf_decal/tile/red{
+/obj/effect/turf_decal/tile/blue,
+/obj/effect/turf_decal/tile/blue{
dir = 4
},
+/obj/effect/turf_decal/tile/blue{
+ dir = 8
+ },
+/mob/living/simple_animal/hostile/syndicate,
/turf/open/floor/plasteel/white,
-/area/awaymission/research/interior/security)
+/area/awaymission/research/interior/medbay)
"hr" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -3799,6 +3912,15 @@
},
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior/cryo)
+"hw" = (
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 8
+ },
+/turf/open/floor/plasteel/yellowsiding{
+ dir = 4
+ },
+/area/awaymission/research/interior/dorm)
"hx" = (
/obj/structure/cable{
icon_state = "2-4"
@@ -4066,23 +4188,6 @@
},
/turf/open/floor/plasteel/dark,
/area/awaymission/research/interior/genetics)
-"hW" = (
-/obj/structure/window/reinforced{
- dir = 8
- },
-/mob/living/carbon/monkey,
-/obj/effect/turf_decal/tile/purple{
- dir = 1
- },
-/obj/effect/turf_decal/tile/purple,
-/obj/effect/turf_decal/tile/purple{
- dir = 4
- },
-/obj/effect/turf_decal/tile/purple{
- dir = 8
- },
-/turf/open/floor/plasteel,
-/area/awaymission/research/interior/genetics)
"hX" = (
/obj/structure/rack,
/obj/effect/spawner/lootdrop/maintenance,
@@ -4139,20 +4244,6 @@
},
/turf/open/floor/plating,
/area/awaymission/research/interior/maint)
-"if" = (
-/mob/living/simple_animal/hostile/syndicate/ranged/smg,
-/obj/effect/turf_decal/tile/green{
- dir = 1
- },
-/obj/effect/turf_decal/tile/green,
-/obj/effect/turf_decal/tile/green{
- dir = 4
- },
-/obj/effect/turf_decal/tile/green{
- dir = 8
- },
-/turf/open/floor/plasteel/white,
-/area/awaymission/research/interior)
"ig" = (
/obj/structure/cable{
icon_state = "2-4"
@@ -4186,23 +4277,6 @@
},
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior/security)
-"ij" = (
-/mob/living/simple_animal/bot/secbot/beepsky{
- desc = "A beefy variant of the standard securitron model.";
- emagged = 1;
- faction = list("nanotrasenprivate");
- health = 50;
- maxHealth = 50;
- name = "Officer Genesky"
- },
-/obj/effect/turf_decal/tile/red{
- dir = 1
- },
-/obj/effect/turf_decal/tile/red{
- dir = 4
- },
-/turf/open/floor/plasteel/white,
-/area/awaymission/research/interior/security)
"ik" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -4546,14 +4620,6 @@
},
/turf/open/floor/plasteel/freezer,
/area/awaymission/research/interior/bathroom)
-"iQ" = (
-/mob/living/simple_animal/hostile/syndicate,
-/obj/effect/turf_decal/tile/green,
-/obj/effect/turf_decal/tile/green{
- dir = 4
- },
-/turf/open/floor/plasteel/white,
-/area/awaymission/research/interior)
"iR" = (
/turf/open/floor/plasteel,
/area/awaymission/research/interior/maint)
@@ -4723,35 +4789,6 @@
},
/turf/open/floor/plasteel,
/area/awaymission/research/interior)
-"jr" = (
-/obj/structure/sign/directions/security{
- dir = 1;
- pixel_x = 32;
- pixel_y = 40
- },
-/obj/structure/sign/directions/engineering{
- dir = 1;
- pixel_x = 32;
- pixel_y = 33
- },
-/obj/structure/sign/directions/science{
- dir = 1;
- pixel_x = 32;
- pixel_y = 26
- },
-/mob/living/simple_animal/hostile/syndicate/ranged/smg,
-/obj/effect/turf_decal/tile/green{
- dir = 1
- },
-/obj/effect/turf_decal/tile/green,
-/obj/effect/turf_decal/tile/green{
- dir = 4
- },
-/obj/effect/turf_decal/tile/green{
- dir = 8
- },
-/turf/open/floor/plasteel/white,
-/area/awaymission/research/interior)
"js" = (
/obj/effect/turf_decal/tile/blue,
/obj/effect/turf_decal/tile/blue{
@@ -4976,20 +5013,6 @@
},
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior/medbay)
-"jP" = (
-/mob/living/simple_animal/hostile/syndicate/ranged/smg,
-/obj/effect/turf_decal/tile/blue{
- dir = 1
- },
-/obj/effect/turf_decal/tile/blue,
-/obj/effect/turf_decal/tile/blue{
- dir = 4
- },
-/obj/effect/turf_decal/tile/blue{
- dir = 8
- },
-/turf/open/floor/plasteel/white,
-/area/awaymission/research/interior/medbay)
"jQ" = (
/obj/effect/decal/cleanable/blood/drip,
/obj/effect/turf_decal/tile/blue{
@@ -5591,9 +5614,6 @@
dir = 8
},
/area/awaymission/research/interior/dorm)
-"ln" = (
-/turf/open/floor/plasteel,
-/area/space/nearstation)
"lo" = (
/obj/structure/table/wood,
/obj/structure/bedsheetbin,
@@ -5613,20 +5633,6 @@
},
/turf/open/floor/plasteel/white,
/area/awaymission/research/interior/medbay)
-"lr" = (
-/mob/living/simple_animal/hostile/syndicate,
-/obj/effect/turf_decal/tile/blue{
- dir = 1
- },
-/obj/effect/turf_decal/tile/blue,
-/obj/effect/turf_decal/tile/blue{
- dir = 4
- },
-/obj/effect/turf_decal/tile/blue{
- dir = 8
- },
-/turf/open/floor/plasteel/white,
-/area/awaymission/research/interior/medbay)
"ls" = (
/obj/structure/table,
/obj/item/storage/firstaid/regular,
@@ -5867,12 +5873,6 @@
/obj/effect/landmark/awaystart,
/turf/open/floor/wood,
/area/awaymission/research/interior/dorm)
-"lU" = (
-/obj/machinery/computer/arcade,
-/turf/open/floor/plasteel/yellowsiding{
- dir = 4
- },
-/area/awaymission/research/interior/dorm)
"lV" = (
/obj/machinery/light/small{
dir = 1
@@ -38316,7 +38316,7 @@ ea
ev
fd
fE
-gv
+dP
ev
fd
fE
@@ -38565,10 +38565,10 @@ aH
cm
cw
cx
-cT
+aS
dh
dq
-cT
+aS
ea
ev
fc
@@ -38823,7 +38823,7 @@ cd
cx
cx
cU
-di
+bc
dr
dr
ea
@@ -39603,7 +39603,7 @@ ey
fG
gw
gR
-hj
+dQ
hu
hK
cK
@@ -40367,7 +40367,7 @@ cx
cW
dh
du
-dQ
+cX
ea
eA
fd
@@ -40377,7 +40377,7 @@ ev
fd
fE
ea
-hW
+eL
ib
ib
iz
@@ -40621,12 +40621,12 @@ aH
ce
cc
cx
-cX
+aU
dh
dv
dh
ea
-eB
+di
fc
fH
gx
@@ -40637,7 +40637,7 @@ ea
dh
dv
dh
-cT
+aS
cx
cm
cw
@@ -40651,7 +40651,7 @@ kr
jn
jF
jF
-ln
+jF
kc
lL
jk
@@ -41143,7 +41143,7 @@ ea
ev
fh
fI
-gv
+dP
ev
fc
fE
@@ -43224,8 +43224,8 @@ lc
lp
lp
lN
-lU
-lU
+hw
+hw
lY
aP
cd
@@ -44224,7 +44224,7 @@ dl
dz
cZ
ek
-eL
+dn
fl
eJ
eZ
@@ -44238,7 +44238,7 @@ ir
hZ
hZ
hZ
-iQ
+gv
hZ
hZ
hA
@@ -44490,7 +44490,7 @@ hl
hl
hl
hl
-if
+fY
is
hl
hl
@@ -44734,7 +44734,7 @@ cs
cC
cP
TD
-dn
+co
dB
da
em
@@ -44756,7 +44756,7 @@ ia
ia
ia
ia
-jr
+gW
jI
hl
kv
@@ -47584,7 +47584,7 @@ aO
iW
iW
ju
-jP
+hj
kh
iW
iW
@@ -48361,7 +48361,7 @@ kg
kM
jh
jw
-lr
+hq
kg
iW
cd
@@ -48855,7 +48855,7 @@ fq
fW
gK
fW
-hq
+eB
do
dH
dH
@@ -49366,7 +49366,7 @@ dT
eo
eR
fr
-fY
+dI
gK
fW
ho
@@ -49646,7 +49646,7 @@ kg
kL
kZ
lg
-lr
+hq
lD
iW
lW
@@ -50389,7 +50389,7 @@ ad
aH
aO
do
-dI
+cT
dH
dH
eS
@@ -50401,7 +50401,7 @@ fW
fW
fW
fW
-ij
+gi
do
hD
aH
@@ -50672,7 +50672,7 @@ jw
km
kB
kO
-jP
+hj
lj
lw
lI
diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index 125da46b81..cf1b2c6835 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -372,7 +372,10 @@
/turf/open/space,
/area/space/nearstation)
"aaU" = (
-/obj/machinery/computer/arcade,
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 4
+ },
/turf/open/floor/plasteel,
/area/security/prison)
"aaV" = (
@@ -6019,6 +6022,18 @@
},
/turf/open/floor/plasteel,
/area/security/processing)
+"amd" = (
+/obj/effect/turf_decal/tile/red{
+ dir = 8
+ },
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 4
+ },
+/turf/open/floor/plasteel/white/corner{
+ dir = 1
+ },
+/area/hallway/secondary/exit)
"ame" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -6225,6 +6240,17 @@
},
/turf/open/floor/plating,
/area/maintenance/solars/port/fore)
+"amB" = (
+/obj/effect/turf_decal/tile/bar,
+/obj/effect/turf_decal/tile/bar{
+ dir = 1
+ },
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 1
+ },
+/turf/open/floor/plasteel,
+/area/crew_quarters/bar)
"amC" = (
/turf/open/floor/plating,
/area/maintenance/port/fore)
@@ -19937,15 +19963,6 @@
dir = 1
},
/area/chapel/main)
-"aUL" = (
-/obj/machinery/computer/arcade,
-/obj/effect/turf_decal/tile/red{
- dir = 8
- },
-/turf/open/floor/plasteel/white/corner{
- dir = 1
- },
-/area/hallway/secondary/exit)
"aUM" = (
/obj/machinery/camera{
c_tag = "Arrivals Bay 2";
@@ -22268,14 +22285,6 @@
},
/turf/open/floor/plasteel,
/area/crew_quarters/bar)
-"baa" = (
-/obj/machinery/computer/arcade,
-/obj/effect/turf_decal/tile/bar,
-/obj/effect/turf_decal/tile/bar{
- dir = 1
- },
-/turf/open/floor/plasteel,
-/area/crew_quarters/bar)
"bab" = (
/obj/machinery/light,
/obj/machinery/firealarm{
@@ -91961,7 +91970,7 @@ aUf
aQc
aXi
aQc
-baa
+amB
aJC
bcq
bcq
@@ -106349,7 +106358,7 @@ aCR
aCR
aCR
aTl
-aUL
+amd
aVW
aXD
aZj
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index 79b7561ebb..e500c85640 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -173,6 +173,88 @@
/obj/effect/landmark/xeno_spawn,
/turf/open/space,
/area/solar/starboard/fore)
+"aav" = (
+/obj/effect/decal/cleanable/cobweb,
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 4
+ },
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
+"aaw" = (
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 4
+ },
+/turf/open/floor/plasteel/dark,
+/area/maintenance/starboard/fore)
+"aax" = (
+/obj/effect/decal/cleanable/dirt,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/maintenance/starboard/fore)
+"aay" = (
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 1
+ },
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
+"aaz" = (
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/maintenance/starboard/fore)
+"aaA" = (
+/obj/effect/decal/cleanable/dirt,
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 1
+ },
+/turf/open/floor/plating,
+/area/maintenance/starboard/fore)
+"aaB" = (
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 1
+ },
+/turf/open/floor/plating,
+/area/security/prison)
"aaE" = (
/obj/structure/lattice/catwalk,
/turf/open/space,
@@ -4563,11 +4645,6 @@
},
/turf/open/floor/plasteel,
/area/maintenance/starboard/fore)
-"anz" = (
-/obj/machinery/computer/arcade,
-/obj/effect/decal/cleanable/cobweb,
-/turf/open/floor/plating,
-/area/maintenance/starboard/fore)
"anA" = (
/obj/structure/cable/white{
icon_state = "0-2"
@@ -7122,10 +7199,6 @@
},
/turf/open/floor/plating,
/area/maintenance/starboard/fore)
-"arX" = (
-/obj/machinery/computer/arcade,
-/turf/open/floor/plating,
-/area/maintenance/starboard/fore)
"arY" = (
/obj/structure/table/wood,
/obj/item/toy/talking/codex_gigas,
@@ -7156,11 +7229,6 @@
},
/turf/open/floor/plasteel/dark,
/area/maintenance/starboard/fore)
-"asa" = (
-/obj/machinery/computer/arcade,
-/obj/effect/decal/cleanable/dirt,
-/turf/open/floor/plating,
-/area/maintenance/starboard/fore)
"asb" = (
/obj/machinery/airalarm{
dir = 1;
@@ -21997,10 +22065,6 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/plating,
/area/security/prison)
-"aRi" = (
-/obj/machinery/computer/arcade,
-/turf/open/floor/plating,
-/area/security/prison)
"aRj" = (
/obj/structure/cable/white{
icon_state = "0-2"
@@ -170617,7 +170681,7 @@ aox
apu
aqy
arb
-aoy
+aax
aig
auA
avU
@@ -170874,7 +170938,7 @@ aoy
apv
aqz
apu
-arX
+aay
aig
auB
avU
@@ -171383,8 +171447,8 @@ aiC
akU
alI
amE
-anz
-aox
+aav
+aaw
apw
aqA
apy
@@ -171902,7 +171966,7 @@ aiC
apy
apy
apu
-aox
+aaz
atw
auD
avW
@@ -172159,7 +172223,7 @@ aoB
apz
apw
apv
-asa
+aaA
atw
auE
avW
@@ -182715,7 +182779,7 @@ aFn
aMn
aNI
aPr
-aRi
+aaB
aKV
aUC
aWk
diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm
index a87c17836c..a7ab212bd6 100644
--- a/_maps/map_files/OmegaStation/OmegaStation.dmm
+++ b/_maps/map_files/OmegaStation/OmegaStation.dmm
@@ -1279,6 +1279,22 @@
},
/turf/open/floor/plasteel,
/area/science/mixing)
+"abQ" = (
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/obj/machinery/computer/arcade{
+ dir = 4
+ },
+/turf/open/floor/plasteel/dark,
+/area/maintenance/port)
"abR" = (
/obj/structure/lattice,
/turf/open/space,
@@ -37164,20 +37180,6 @@
},
/turf/open/floor/plasteel,
/area/hallway/primary/starboard)
-"dEa" = (
-/obj/machinery/computer/arcade,
-/obj/effect/turf_decal/tile/neutral{
- dir = 1
- },
-/obj/effect/turf_decal/tile/neutral,
-/obj/effect/turf_decal/tile/neutral{
- dir = 4
- },
-/obj/effect/turf_decal/tile/neutral{
- dir = 8
- },
-/turf/open/floor/plasteel/dark,
-/area/maintenance/port)
"dFV" = (
/obj/machinery/atmospherics/pipe/simple/green/visible{
dir = 4
@@ -76194,7 +76196,7 @@ aZp
bak
bbc
bbQ
-dEa
+abQ
bdD
beu
sKE
diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm
index 698eb6cd5a..9ae92b26d4 100644
--- a/_maps/map_files/PubbyStation/PubbyStation.dmm
+++ b/_maps/map_files/PubbyStation/PubbyStation.dmm
@@ -35,6 +35,30 @@
icon_state = "platingdmg3"
},
/area/maintenance/department/science)
+"aae" = (
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 8
+ },
+/turf/open/floor/plasteel/dark,
+/area/security/prison)
+"aaf" = (
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/obj/machinery/computer/arcade{
+ icon_state = "arcade";
+ dir = 1
+ },
+/turf/open/floor/plasteel/dark,
+/area/crew_quarters/heads/captain)
"abf" = (
/obj/structure/bed,
/turf/open/floor/plating,
@@ -1621,10 +1645,6 @@
},
/turf/open/floor/plasteel/dark,
/area/security/prison)
-"afI" = (
-/obj/machinery/computer/arcade,
-/turf/open/floor/plasteel/dark,
-/area/security/prison)
"afJ" = (
/obj/effect/landmark/carpspawn,
/turf/open/space/basic,
@@ -13685,20 +13705,6 @@
},
/turf/open/floor/plasteel/dark,
/area/crew_quarters/heads/captain)
-"aGm" = (
-/obj/machinery/computer/arcade,
-/obj/effect/turf_decal/tile/neutral{
- dir = 1
- },
-/obj/effect/turf_decal/tile/neutral,
-/obj/effect/turf_decal/tile/neutral{
- dir = 4
- },
-/obj/effect/turf_decal/tile/neutral{
- dir = 8
- },
-/turf/open/floor/plasteel/dark,
-/area/crew_quarters/heads/captain)
"aGn" = (
/obj/item/twohanded/required/kirbyplants/photosynthetic{
layer = 3.1
@@ -83601,7 +83607,7 @@ aeu
aeI
lGp
aeU
-afI
+aae
aeU
dUk
agy
@@ -90832,7 +90838,7 @@ aCC
aDG
aBm
aFz
-aGm
+aaf
awR
aHQ
aIO
diff --git a/_maps/map_files/generic/City_of_Cogs.dmm b/_maps/map_files/generic/City_of_Cogs.dmm
index 584564ba2a..e1e7659ce3 100644
--- a/_maps/map_files/generic/City_of_Cogs.dmm
+++ b/_maps/map_files/generic/City_of_Cogs.dmm
@@ -26979,9 +26979,9 @@ ab
ab
ab
ab
-ab
-ab
-ab
+ae
+ae
+ae
ab
ab
ab
@@ -27235,11 +27235,11 @@ ab
ab
ab
ab
-ab
-ab
-ab
-ab
-ab
+ae
+ag
+ag
+ag
+ae
ab
ab
ab
@@ -27492,11 +27492,11 @@ ab
ab
ab
ab
-ab
-ab
-ab
-ab
-ab
+ae
+ag
+ag
+ag
+ae
aF
aF
aF
@@ -27747,8 +27747,8 @@ ab
ab
ab
ab
-ae
-ae
+ab
+ab
ah
ah
ai
@@ -28003,9 +28003,9 @@ ab
ab
ab
ab
-ae
-ag
-ag
+ab
+ab
+ab
ah
ar
aj
@@ -28260,9 +28260,9 @@ ab
ab
ab
ab
-ae
-ag
-ag
+ab
+ab
+ab
ah
as
aj
@@ -28517,9 +28517,9 @@ ab
ab
ab
ab
-ae
-ag
-ag
+ab
+ab
+ab
ah
VP
aj
@@ -28775,8 +28775,8 @@ ab
ab
ab
ab
-ae
-ae
+ab
+ab
ah
ah
ay
@@ -29033,7 +29033,7 @@ ab
ab
ab
ab
-ae
+ab
an
au
aj
@@ -33657,7 +33657,7 @@ ab
ab
ab
ab
-ae
+ab
ah
al
aj
@@ -33914,7 +33914,7 @@ ab
ab
ab
ab
-ae
+ab
ai
bE
aj
@@ -34171,7 +34171,7 @@ ab
ab
ab
ab
-ae
+ab
ah
al
aj
@@ -34688,11 +34688,11 @@ ab
ab
ab
ab
-ab
-ab
-ab
-ab
-ab
+ae
+ae
+ag
+ae
+ae
aF
aF
aF
diff --git a/_maps/shuttles/emergency_cere.dmm b/_maps/shuttles/emergency_cere.dmm
index 23de211226..f49e70aeb9 100644
--- a/_maps/shuttles/emergency_cere.dmm
+++ b/_maps/shuttles/emergency_cere.dmm
@@ -591,6 +591,22 @@
/obj/machinery/recharger,
/turf/open/floor/mineral/plastitanium/red/brig,
/area/shuttle/escape)
+"bh" = (
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/obj/machinery/computer/arcade{
+ dir = 4
+ },
+/turf/open/floor/plasteel,
+/area/shuttle/escape)
"bj" = (
/turf/open/floor/mech_bay_recharge_floor,
/area/shuttle/escape)
@@ -1181,20 +1197,6 @@
},
/turf/open/floor/plasteel/white,
/area/shuttle/escape)
-"cz" = (
-/obj/machinery/computer/arcade,
-/obj/effect/turf_decal/tile/neutral{
- dir = 1
- },
-/obj/effect/turf_decal/tile/neutral,
-/obj/effect/turf_decal/tile/neutral{
- dir = 4
- },
-/obj/effect/turf_decal/tile/neutral{
- dir = 8
- },
-/turf/open/floor/plasteel,
-/area/shuttle/escape)
"cA" = (
/obj/machinery/light{
dir = 8
@@ -1815,8 +1817,8 @@ ab
ad
cn
cn
-cz
-cz
+bh
+bh
cn
cn
ab
diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm
index 60fb49f780..4052dce40d 100644
--- a/code/__DEFINES/DNA.dm
+++ b/code/__DEFINES/DNA.dm
@@ -131,6 +131,11 @@
#define ORGAN_SLOT_BRAIN_ANTISTUN "brain_antistun"
#define ORGAN_SLOT_TAIL "tail"
#define ORGAN_SLOT_PENIS "penis"
+#define ORGAN_SLOT_WOMB "womb"
+#define ORGAN_SLOT_VAGINA "vagina"
+#define ORGAN_SLOT_TESTICLES "testicles"
+#define ORGAN_SLOT_BREASTS "breasts"
+
////organ defines
#define STANDARD_ORGAN_THRESHOLD 100
diff --git a/code/__DEFINES/citadel_defines.dm b/code/__DEFINES/citadel_defines.dm
index 3301def4a1..e3cafd4613 100644
--- a/code/__DEFINES/citadel_defines.dm
+++ b/code/__DEFINES/citadel_defines.dm
@@ -15,9 +15,26 @@
#define ui_boxvore "EAST-5:22,SOUTH+1:6"
//Filters
-#define CIT_FILTER_STAMINACRIT filter(type="drop_shadow", x=0, y=0, size=-3, border=0, color="#04080F")
+#define CIT_FILTER_STAMINACRIT filter(type="drop_shadow", x=0, y=0, size=-3, color="#04080F")
//organ defines
+#define VAGINA_LAYER_INDEX 1
+#define TESTICLES_LAYER_INDEX 2
+#define GENITAL_LAYER_INDEX 3
+#define PENIS_LAYER_INDEX 4
+
+#define GENITAL_LAYER_INDEX_LENGTH 4 //keep it updated with each new index added, thanks.
+
+//genital flags
+#define GENITAL_BLACKLISTED (1<<0) //for genitals that shouldn't be added to GLOB.genitals_list.
+#define GENITAL_INTERNAL (1<<1)
+#define GENITAL_HIDDEN (1<<2)
+#define GENITAL_THROUGH_CLOTHES (1<<3)
+#define GENITAL_FUID_PRODUCTION (1<<4)
+#define CAN_MASTURBATE_WITH (1<<5)
+#define MASTURBATE_LINKED_ORGAN (1<<6) //used to pass our mission to the linked organ
+#define CAN_CLIMAX_WITH (1<<7)
+
#define COCK_SIZE_MIN 1
#define COCK_SIZE_MAX 20
@@ -50,27 +67,6 @@
#define BREASTS_VOLUME_BASE 50 //base volume for the reagents in the breasts, multiplied by the size then multiplier. 50u for A cups, 850u for HH cups.
#define BREASTS_VOLUME_MULT 1 //global multiplier for breast volume.
-#define BREASTS_SIZE_FLAT 0
-#define BREASTS_SIZE_A 1
-#define BREASTS_SIZE_AA 1.5
-#define BREASTS_SIZE_B 2
-#define BREASTS_SIZE_BB 2.5
-#define BREASTS_SIZE_C 3
-#define BREASTS_SIZE_CC 3.5
-#define BREASTS_SIZE_D 4
-#define BREASTS_SIZE_DD 4.5
-#define BREASTS_SIZE_E 5
-#define BREASTS_SIZE_EE 5.5
-#define BREASTS_SIZE_F 6
-#define BREASTS_SIZE_FF 6.5
-#define BREASTS_SIZE_G 7
-#define BREASTS_SIZE_GG 7.5//Are these even real sizes? The world may never know because cup sizes make no fucking sense.
-#define BREASTS_SIZE_H 8
-#define BREASTS_SIZE_HH 8.5//Largest size, ever. For now.
-
-#define BREASTS_SIZE_MIN BREASTS_SIZE_A
-#define BREASTS_SIZE_DEF BREASTS_SIZE_C
-#define BREASTS_SIZE_MAX BREASTS_SIZE_HH
#define MILK_RATE 5
#define MILK_RATE_MULT 1
diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index 7ed8ee558b..3a680d1c90 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -157,6 +157,10 @@
// /mob/living/carbon signals
#define COMSIG_CARBON_SOUNDBANG "carbon_soundbang" //from base of mob/living/carbon/soundbang_act(): (list(intensity))
+// /mob/living/simple_animal/hostile signals
+#define COMSIG_HOSTILE_ATTACKINGTARGET "hostile_attackingtarget"
+ #define COMPONENT_HOSTILE_NO_ATTACK 1
+
// /obj signals
#define COMSIG_OBJ_DECONSTRUCT "obj_deconstruct" //from base of obj/deconstruct(): (disassembled)
#define COMSIG_OBJ_BREAK "obj_break" //from base of /obj/obj_break(): (damage_flag)
@@ -206,6 +210,9 @@
// /obj/item/pen signals
#define COMSIG_PEN_ROTATED "pen_rotated" //called after rotation in /obj/item/pen/attack_self(): (rotation, mob/living/carbon/user)
+// /obj/item/projectile signals (sent to the firer)
+#define COMSIG_PROJECTILE_ON_HIT "projectile_on_hit" // from base of /obj/item/projectile/proc/on_hit(): (atom/movable/firer, atom/target, Angle)
+#define COMSIG_PROJECTILE_BEFORE_FIRE "projectile_before_fire" // from base of /obj/item/projectile/proc/fire(): (obj/item/projectile, atom/original_target)
// /mob/living/carbon/human signals
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target)
diff --git a/code/__DEFINES/fantasy_affixes.dm b/code/__DEFINES/fantasy_affixes.dm
new file mode 100644
index 0000000000..709d414d11
--- /dev/null
+++ b/code/__DEFINES/fantasy_affixes.dm
@@ -0,0 +1,5 @@
+#define AFFIX_PREFIX (1 << 0)
+#define AFFIX_SUFFIX (1 << 1)
+
+#define AFFIX_GOOD (1 << 0)
+#define AFFIX_EVIL (1 << 1)
\ No newline at end of file
diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm
index eb9ed377a1..632e4adef9 100644
--- a/code/__DEFINES/inventory.dm
+++ b/code/__DEFINES/inventory.dm
@@ -243,3 +243,6 @@ GLOBAL_LIST_INIT(security_wintercoat_allowed, typecacheof(list(
//Internals checker
#define GET_INTERNAL_SLOTS(C) list(C.head, C.wear_mask)
+
+//Slots that won't trigger humans' update_genitals() on equip().
+GLOBAL_LIST_INIT(no_genitals_update_slots, list(SLOT_L_STORE, SLOT_R_STORE, SLOT_S_STORE, SLOT_IN_BACKPACK, SLOT_LEGCUFFED, SLOT_HANDCUFFED, SLOT_HANDS, SLOT_GENERC_DEXTROUS_STORAGE))
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 0dee8bd1e8..70010eeffc 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -219,6 +219,10 @@ GLOBAL_LIST_INIT(pointed_types, typecacheof(list(
#define isbodypart(A) (istype(A, /obj/item/bodypart))
+#define isprojectile(A) (istype(A, /obj/item/projectile))
+
+#define isgun(A) (istype(A, /obj/item/gun))
+
//Assemblies
#define isassembly(O) (istype(O, /obj/item/assembly))
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 94a189ff07..4b76f41c61 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -51,24 +51,25 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s
//Human Overlays Indexes/////////
//LOTS OF CIT CHANGES HERE. BE CAREFUL WHEN UPSTREAM ADDS MORE LAYERS
-#define MUTATIONS_LAYER 31 //mutations. Tk headglows, cold resistance glow, etc
-#define GENITALS_BEHIND_LAYER 30 //Some genitalia needs to be behind everything, such as with taurs (Taurs use body_behind_layer
-#define BODY_BEHIND_LAYER 29 //certain mutantrace features (tail when looking south) that must appear behind the body parts
-#define BODYPARTS_LAYER 28 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag
-#define MARKING_LAYER 27 //Matrixed body markings because clashing with snouts?
-#define BODY_ADJ_LAYER 26 //certain mutantrace features (snout, body markings) that must appear above the body parts
-#define GENITALS_FRONT_LAYER 25 //Draws some genitalia above clothes and the TAUR body if need be.
-#define BODY_LAYER 24 //underwear, undershirts, socks, eyes, lips(makeup)
-#define FRONT_MUTATIONS_LAYER 23 //mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes)
-#define DAMAGE_LAYER 22 //damage indicators (cuts and burns)
-#define UNIFORM_LAYER 21
-#define ID_LAYER 20
+#define MUTATIONS_LAYER 32 //mutations. Tk headglows, cold resistance glow, etc
+#define GENITALS_BEHIND_LAYER 31 //Some genitalia needs to be behind everything, such as with taurs (Taurs use body_behind_layer
+#define BODY_BEHIND_LAYER 30 //certain mutantrace features (tail when looking south) that must appear behind the body parts
+#define BODYPARTS_LAYER 29 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag
+#define MARKING_LAYER 28 //Matrixed body markings because clashing with snouts?
+#define BODY_ADJ_LAYER 27 //certain mutantrace features (snout, body markings) that must appear above the body parts
+#define GENITALS_FRONT_LAYER 26 //Draws some genitalia above clothes and the TAUR body if need be.
+#define BODY_LAYER 25 //underwear, undershirts, socks, eyes, lips(makeup)
+#define FRONT_MUTATIONS_LAYER 24 //mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes)
+#define DAMAGE_LAYER 23 //damage indicators (cuts and burns)
+#define UNIFORM_LAYER 22
+#define ID_LAYER 21
#define HANDS_PART_LAYER 20
#define SHOES_LAYER 19
#define GLOVES_LAYER 18
#define EARS_LAYER 17
#define BODY_TAUR_LAYER 16
#define SUIT_LAYER 15
+#define GENITALS_EXPOSED_LAYER 14
#define GLASSES_LAYER 13
#define BELT_LAYER 12 //Possible make this an overlay of somethign required to wear a belt?
#define SUIT_STORE_LAYER 11
@@ -82,7 +83,7 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s
#define HANDS_LAYER 3
#define BODY_FRONT_LAYER 2
#define FIRE_LAYER 1 //If you're on fire
-#define TOTAL_LAYERS 30 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_;
+#define TOTAL_LAYERS 32 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_;
//Human Overlay Index Shortcuts for alternate_worn_layer, layers
//Because I *KNOW* somebody will think layer+1 means "above"
@@ -489,7 +490,7 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
#define PDAIMG(what) {""}
//Filters
-#define AMBIENT_OCCLUSION list("type"="drop_shadow","x"=0,"y"=-2,"size"=4,"border"=4,"color"="#04080FAA")
+#define AMBIENT_OCCLUSION list("type"="drop_shadow","x"=0,"y"=-2,"size"=4,"color"="#04080FAA")
#define EYE_BLUR(size) list("type"="blur", "size"=size)
#define GRAVITY_MOTION_BLUR list("type"="motion_blur","x"=0,"y"=0)
diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index 332d36f2f4..ff19976cff 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -74,6 +74,9 @@
#define STATUS_EFFECT_ICHORIAL_STAIN /datum/status_effect/ichorial_stain //Prevents a servant from being revived by vitality matrices for one minute.
+#define STATUS_EFFECT_BREASTS_ENLARGEMENT /datum/status_effect/chem/breast_enlarger //Applied slowdown due to the ominous bulk.
+
+#define STATUS_EFFECT_PENIS_ENLARGEMENT /datum/status_effect/chem/penis_enlarger //More applied slowdown, just like the above.
/////////////
// NEUTRAL //
/////////////
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index ef8adfcaf2..1f37556d1d 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -145,13 +145,14 @@
#define TRAIT_SKITTISH "skittish"
#define TRAIT_POOR_AIM "poor_aim"
#define TRAIT_PROSOPAGNOSIA "prosopagnosia"
-#define TRAIT_DRUNK_HEALING "drunk_healing"
-#define TRAIT_TAGGER "tagger"
-#define TRAIT_PHOTOGRAPHER "photographer"
-#define TRAIT_MUSICIAN "musician"
-#define TRAIT_CROCRIN_IMMUNE "crocin_immune"
+#define TRAIT_DRUNK_HEALING "drunk_healing"
+#define TRAIT_TAGGER "tagger"
+#define TRAIT_PHOTOGRAPHER "photographer"
+#define TRAIT_MUSICIAN "musician"
+#define TRAIT_CROCRIN_IMMUNE "crocin_immune"
#define TRAIT_NYMPHO "nymphomania"
#define TRAIT_MASO "masochism"
+#define TRAIT_EXHIBITIONIST "exhibitionist"
#define TRAIT_HIGH_BLOOD "high_blood"
#define TRAIT_PHARMA "hepatic_pharmacokinesis"
#define TRAIT_PARA "paraplegic"
diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm
index 86d0a34e3e..8134494306 100644
--- a/code/__HELPERS/_cit_helpers.dm
+++ b/code/__HELPERS/_cit_helpers.dm
@@ -56,6 +56,7 @@ GLOBAL_LIST_EMPTY(ipc_screens_list)
GLOBAL_LIST_EMPTY(ipc_antennas_list)
//Genitals and Arousal Lists
+GLOBAL_LIST_EMPTY(genitals_list)
GLOBAL_LIST_EMPTY(cock_shapes_list)//global_lists.dm for the list initializations //Now also _DATASTRUCTURES globals.dm
GLOBAL_LIST_EMPTY(cock_shapes_icons) //Associated list for names->icon_states for cockshapes.
GLOBAL_LIST_EMPTY(gentlemans_organ_names)
@@ -131,53 +132,53 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/mob/living/carbon/proc/has_penis()
- if(getorganslot("penis"))//slot shared with ovipositor
- if(istype(getorganslot("penis"), /obj/item/organ/genital/penis))
- return TRUE
+ var/obj/item/organ/genital/G = getorganslot(ORGAN_SLOT_PENIS)
+ if(G && istype(G, /obj/item/organ/genital/penis))
+ return TRUE
return FALSE
/mob/living/carbon/proc/has_balls()
- if(getorganslot("balls"))
- if(istype(getorganslot("balls"), /obj/item/organ/genital/testicles))
- return TRUE
+ var/obj/item/organ/genital/G = getorganslot(ORGAN_SLOT_TESTICLES)
+ if(G && istype(G, /obj/item/organ/genital/testicles))
+ return TRUE
return FALSE
/mob/living/carbon/proc/has_vagina()
- if(getorganslot("vagina"))
+ if(getorganslot(ORGAN_SLOT_VAGINA))
return TRUE
return FALSE
/mob/living/carbon/proc/has_breasts()
- if(getorganslot("breasts"))
+ if(getorganslot(ORGAN_SLOT_BREASTS))
return TRUE
return FALSE
/mob/living/carbon/proc/has_ovipositor()
- if(getorganslot("penis"))//shared slot
- if(istype(getorganslot("penis"), /obj/item/organ/genital/ovipositor))
- return TRUE
+ var/obj/item/organ/genital/G = getorganslot(ORGAN_SLOT_PENIS)
+ if(G && istype(G, /obj/item/organ/genital/ovipositor))
+ return TRUE
return FALSE
/mob/living/carbon/human/proc/has_eggsack()
- if(getorganslot("balls"))
- if(istype(getorganslot("balls"), /obj/item/organ/genital/eggsack))
- return TRUE
+ var/obj/item/organ/genital/G = getorganslot(ORGAN_SLOT_TESTICLES)
+ if(G && istype(G, /obj/item/organ/genital/eggsack))
+ return TRUE
return FALSE
-/mob/living/carbon/human/proc/is_bodypart_exposed(bodypart)
-
-/mob/living/carbon/proc/is_groin_exposed(var/list/L)
+/mob/living/carbon/proc/is_groin_exposed(list/L)
if(!L)
L = get_equipped_items()
- for(var/obj/item/I in L)
+ for(var/A in L)
+ var/obj/item/I = A
if(I.body_parts_covered & GROIN)
return FALSE
return TRUE
-/mob/living/carbon/proc/is_chest_exposed(var/list/L)
+/mob/living/carbon/proc/is_chest_exposed(list/L)
if(!L)
L = get_equipped_items()
- for(var/obj/item/I in L)
+ for(var/A in L)
+ var/obj/item/I = A
if(I.body_parts_covered & CHEST)
return FALSE
return TRUE
@@ -195,9 +196,9 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE)
message_admins("[src] gave everyone genitals.")
for(var/mob/living/carbon/human/H in GLOB.mob_list)
if(H.gender == MALE)
- H.give_penis()
- H.give_balls()
+ H.give_genital(/obj/item/organ/genital/penis)
+ H.give_genital(/obj/item/organ/genital/testicles)
else
- H.give_vagina()
- H.give_womb()
- H.give_breasts()
+ H.give_genital(/obj/item/organ/genital/vagina)
+ H.give_genital(/obj/item/organ/genital/womb)
+ H.give_genital(/obj/item/organ/genital/breasts)
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 6fca4eed3d..7852251230 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -72,6 +72,10 @@
var/datum/sprite_accessory/testicles/value = GLOB.balls_shapes_list[K]
GLOB.balls_shapes_icons[K] = value.icon_state
+ for(var/gpath in subtypesof(/obj/item/organ/genital))
+ var/obj/item/organ/genital/G = gpath
+ if(!CHECK_BITFIELD(initial(G.genital_flags), GENITAL_BLACKLISTED))
+ GLOB.genitals_list[initial(G.name)] = gpath
//END OF CIT CHANGES
//Species
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 19d126f08f..8e7938a312 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -160,7 +160,6 @@
"xenodorsal" = "Standard",
"xenohead" = "Standard",
"xenotail" = "Xenomorph Tail",
- "exhibitionist" = FALSE,
"genitals_use_skintone" = FALSE,
"has_cock" = FALSE,
"cock_shape" = pick(GLOB.cock_shapes_list),
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index d2846a68d3..91f5105553 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -202,4 +202,14 @@ GLOBAL_LIST_INIT(bitfields, list(
"ORGAN_VITAL" = ORGAN_VITAL,
"ORGAN_NO_SPOIL" = ORGAN_NO_SPOIL
),
- ))
+ "genital_flags" = list(
+ "GENITAL_BLACKLISTED" = GENITAL_BLACKLISTED,
+ "GENITAL_INTERNAL" = GENITAL_INTERNAL,
+ "GENITAL_HIDDEN" = GENITAL_HIDDEN,
+ "GENITAL_THROUGH_CLOTHES" = GENITAL_THROUGH_CLOTHES,
+ "GENITAL_FUID_PRODUCTION" = GENITAL_FUID_PRODUCTION,
+ "CAN_MASTURBATE_WITH" = CAN_MASTURBATE_WITH,
+ "MASTURBATE_LINKED_ORGAN" = MASTURBATE_LINKED_ORGAN,
+ "CAN_CLIMAX_WITH" = CAN_CLIMAX_WITH
+ )
+ ))
\ No newline at end of file
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 72ea0ecbd6..36b57a4b7c 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -380,6 +380,22 @@
config_entry_value = 6
min_val = 1
+/datum/config_entry/number/dynamic_midround_delay_min
+ config_entry_value = 15
+ min_val = 1
+
+/datum/config_entry/number/dynamic_midround_delay_max
+ config_entry_value = 35
+ min_val = 1
+
+/datum/config_entry/number/dynamic_latejoin_delay_min
+ config_entry_value = 5
+ min_val = 1
+
+/datum/config_entry/number/dynamic_latejoin_delay_max
+ config_entry_value = 25
+ min_val = 1
+
/datum/config_entry/keyed_list/dynamic_cost
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_NUM
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index ea7e7ce812..efa6b8b59d 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -4,7 +4,7 @@ SUBSYSTEM_DEF(job)
flags = SS_NO_FIRE
var/list/occupations = list() //List of all jobs
- var/list/name_occupations = list() //Dict of all jobs, keys are titles
+ var/list/datum/job/name_occupations = list() //Dict of all jobs, keys are titles
var/list/type_occupations = list() //Dict of all jobs, keys are types
var/list/unassigned = list() //Players who need jobs
var/initial_players_to_assign = 0 //used for checking against population caps
@@ -14,6 +14,8 @@ SUBSYSTEM_DEF(job)
var/overflow_role = "Assistant"
+ var/list/level_order = list(JP_HIGH,JP_MEDIUM,JP_LOW)
+
/datum/controller/subsystem/job/Initialize(timeofday)
SSmapping.HACK_LoadMapConfig()
if(!occupations.len)
@@ -119,7 +121,7 @@ SUBSYSTEM_DEF(job)
if(player.mind && job.title in player.mind.restricted_roles)
JobDebug("FOC incompatible with antagonist role, Player: [player]")
continue
- if(player.client.prefs.job_preferences["[job.title]"] == level)
+ if(player.client.prefs.job_preferences[job.title] == level)
JobDebug("FOC pass, Player: [player], Level:[level]")
candidates += player
return candidates
@@ -181,7 +183,7 @@ SUBSYSTEM_DEF(job)
//it locates a head or runs out of levels to check
//This is basically to ensure that there's atleast a few heads in the round
/datum/controller/subsystem/job/proc/FillHeadPosition()
- for(var/level = 1 to 3)
+ for(var/level in level_order)
for(var/command_position in GLOB.command_positions)
var/datum/job/job = GetJob(command_position)
if(!job)
@@ -218,7 +220,7 @@ SUBSYSTEM_DEF(job)
if(!job)
return 0
for(var/i = job.total_positions, i > 0, i--)
- for(var/level = 1 to 3)
+ for(var/level in level_order)
var/list/candidates = list()
candidates = FindOccupationCandidates(job, level)
if(candidates.len)
@@ -304,8 +306,7 @@ SUBSYSTEM_DEF(job)
// Loop through all levels from high to low
var/list/shuffledoccupations = shuffle(occupations)
- var/list/levels = list(JP_HIGH,JP_MEDIUM,JP_LOW)
- for(var/level in levels)
+ for(var/level in level_order)
//Check the head jobs first each level
CheckHeadPositions(level)
@@ -334,7 +335,7 @@ SUBSYSTEM_DEF(job)
if(job.required_playtime_remaining(player.client))
JobDebug("DO player not enough xp, Player: [player], Job:[job.title]")
continue
-
+
if(!player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features))
JobDebug("DO non-human failed, Player: [player], Job:[job.title]")
continue
@@ -344,7 +345,7 @@ SUBSYSTEM_DEF(job)
continue
// If the player wants that job on this level, then try give it to him.
- if(player.client.prefs.job_preferences["[job.title]"] == level)
+ if(player.client.prefs.job_preferences[job.title] == level)
// If the job isn't filled
if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)
JobDebug("DO pass, Player: [player], Level:[level], Job:[job.title]")
@@ -475,7 +476,6 @@ SUBSYSTEM_DEF(job)
to_chat(M, "[job.custom_spawn_text]")
if(CONFIG_GET(number/minimal_access_threshold))
to_chat(M, "As this station was initially staffed with a [CONFIG_GET(flag/jobs_have_minimal_access) ? "full crew, only your job's necessities" : "skeleton crew, additional access may"] have been added to your ID card.")
-
if(job && H)
if(job.dresscodecompliant)// CIT CHANGE - dress code compliance
equip_loadout(N, H) // CIT CHANGE - allows players to spawn with loadout items
@@ -551,7 +551,7 @@ SUBSYSTEM_DEF(job)
if(job.required_playtime_remaining(player.client))
young++
continue
- switch(player.client.prefs.job_preferences["[job.title]"])
+ switch(player.client.prefs.job_preferences[job.title])
if(JP_HIGH)
high++
if(JP_MEDIUM)
@@ -698,4 +698,4 @@ SUBSYSTEM_DEF(job)
. |= player.mind
/datum/controller/subsystem/job/proc/JobDebug(message)
- log_job_debug(message)
+ log_job_debug(message)
\ No newline at end of file
diff --git a/code/datums/components/bane.dm b/code/datums/components/bane.dm
new file mode 100644
index 0000000000..84f8010270
--- /dev/null
+++ b/code/datums/components/bane.dm
@@ -0,0 +1,45 @@
+/datum/component/bane
+ dupe_mode = COMPONENT_DUPE_ALLOWED
+
+ var/mobtype
+ var/speciestype
+ var/damage_multiplier
+
+/datum/component/bane/Initialize(mobtype, damage_multiplier=1)
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ if(ispath(mobtype, /mob/living))
+ src.mobtype = mobtype
+ else if(ispath(mobtype, /datum/species))
+ speciestype = mobtype
+ else
+ return COMPONENT_INCOMPATIBLE
+
+ src.damage_multiplier = damage_multiplier
+
+/datum/component/bane/RegisterWithParent()
+ if(speciestype)
+ RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/speciesCheck)
+ else
+ RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/mobCheck)
+
+/datum/component/bane/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_ITEM_AFTERATTACK)
+
+/datum/component/bane/proc/speciesCheck(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
+ if(!is_species(target, speciestype))
+ return
+ activate(source, target, user)
+
+/datum/component/bane/proc/mobCheck(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
+ if(!istype(target, mobtype))
+ return
+ activate(source, target, user)
+
+/datum/component/bane/proc/activate(obj/item/source, mob/living/target, mob/attacker)
+ if(attacker.a_intent != INTENT_HARM)
+ return
+
+ var/extra_damage = max(0, source.force * damage_multiplier)
+ target.apply_damage(extra_damage, source.damtype, attacker.zone_selected)
diff --git a/code/datums/components/fantasy/_fantasy.dm b/code/datums/components/fantasy/_fantasy.dm
new file mode 100644
index 0000000000..86e016784a
--- /dev/null
+++ b/code/datums/components/fantasy/_fantasy.dm
@@ -0,0 +1,140 @@
+/datum/component/fantasy
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+
+ var/quality
+
+ var/canFail
+ var/announce
+
+ var/originalName
+ var/list/affixes
+ var/list/appliedComponents
+
+ var/static/list/affixListing
+
+/datum/component/fantasy/Initialize(quality, list/affixes = list(), canFail=FALSE, announce=FALSE)
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.quality = quality || randomQuality()
+ src.canFail = canFail
+ src.announce = announce
+
+ src.affixes = affixes
+ appliedComponents = list()
+ randomAffixes()
+
+/datum/component/fantasy/Destroy()
+ unmodify()
+ affixes = null
+ return ..()
+
+/datum/component/fantasy/RegisterWithParent()
+ var/obj/item/master = parent
+ originalName = master.name
+ modify()
+
+/datum/component/fantasy/UnregisterFromParent()
+ unmodify()
+
+/datum/component/fantasy/InheritComponent(datum/component/fantasy/newComp, original, list/arguments)
+ unmodify()
+ if(newComp)
+ quality += newComp.quality
+ canFail = newComp.canFail
+ announce = newComp.announce
+ else
+ arguments.len = 5 // This is done to replicate what happens when an arglist smaller than the necessary arguments is given
+ quality += arguments[1]
+ canFail = arguments[4] || canFail
+ announce = arguments[5] || announce
+ modify()
+
+/datum/component/fantasy/proc/randomQuality()
+ var/quality = pick(1;15, 2;14, 2;13, 2;12, 3;11, 3;10, 3;9, 4;8, 4;7, 4;6, 5;5, 5;4, 5;3, 6;2, 6;1, 6;0)
+ if(prob(50))
+ quality = -quality
+ return quality
+
+/datum/component/fantasy/proc/randomAffixes(force)
+ if(!affixListing)
+ affixListing = list()
+ for(var/T in subtypesof(/datum/fantasy_affix))
+ var/datum/fantasy_affix/affix = new T
+ affixListing[affix] = affix.weight
+
+ if(length(affixes))
+ if(!force)
+ return
+ affixes = list()
+
+ var/alignment
+ if(quality >= 0)
+ alignment |= AFFIX_GOOD
+ if(quality <= 0)
+ alignment |= AFFIX_EVIL
+
+ var/usedSlots = NONE
+ for(var/i in 1 to max(1, abs(quality))) // We want at least 1 affix applied
+ var/datum/fantasy_affix/affix = pickweight(affixListing)
+ if(affix.placement & usedSlots)
+ continue
+ if(!(affix.alignment & alignment))
+ continue
+ if(!affix.validate(src))
+ continue
+ affixes += affix
+ usedSlots |= affix.placement
+
+/datum/component/fantasy/proc/modify()
+ var/obj/item/master = parent
+
+ master.force = max(0, master.force + quality)
+ master.throwforce = max(0, master.throwforce + quality)
+ master.armor = master.armor?.modifyAllRatings(quality)
+
+ var/newName = originalName
+ for(var/i in affixes)
+ var/datum/fantasy_affix/affix = i
+ newName = affix.apply(src, newName)
+
+ if(quality != 0)
+ newName = "[newName] [quality > 0 ? "+" : ""][quality]"
+
+ if(canFail && prob((quality - 9)*10))
+ var/turf/place = get_turf(parent)
+ place.visible_message("[parent] violently glows blue for a while, then evaporates.")
+ master.burn()
+ return
+ else if(announce)
+ announce()
+
+ master.name = newName
+
+/datum/component/fantasy/proc/unmodify()
+ var/obj/item/master = parent
+
+ for(var/i in affixes)
+ var/datum/fantasy_affix/affix = i
+ affix.remove(src)
+ for(var/i in appliedComponents)
+ qdel(i)
+
+ master.force = max(0, master.force - quality)
+ master.throwforce = max(0, master.throwforce - quality)
+ master.armor = master.armor?.modifyAllRatings(-quality)
+
+ master.name = originalName
+
+/datum/component/fantasy/proc/announce()
+ var/turf/location = get_turf(parent)
+ var/span
+ var/effect_description
+ if(quality >= 0)
+ span = ""
+ effect_description = "shimmering golden glow"
+ else
+ span = ""
+ effect_description = "mottled black glow"
+
+ location.visible_message("[span][originalName] is covered by a [effect_description] and then transforms into [parent]!")
diff --git a/code/datums/components/fantasy/affix.dm b/code/datums/components/fantasy/affix.dm
new file mode 100644
index 0000000000..ad1f44ce4d
--- /dev/null
+++ b/code/datums/components/fantasy/affix.dm
@@ -0,0 +1,13 @@
+/datum/fantasy_affix
+ var/placement // A bitflag of "slots" this affix takes up, for example pre/suffix
+ var/alignment
+ var/weight = 10
+
+// For those occasional affixes which only make sense in certain circumstances
+/datum/fantasy_affix/proc/validate(datum/component/fantasy/comp)
+ return TRUE
+
+/datum/fantasy_affix/proc/apply(datum/component/fantasy/comp, newName)
+ return newName
+
+/datum/fantasy_affix/proc/remove(datum/component/fantasy/comp)
diff --git a/code/datums/components/fantasy/prefixes.dm b/code/datums/components/fantasy/prefixes.dm
new file mode 100644
index 0000000000..702ec9329a
--- /dev/null
+++ b/code/datums/components/fantasy/prefixes.dm
@@ -0,0 +1,68 @@
+/datum/fantasy_affix/cosmetic_prefixes
+ placement = AFFIX_PREFIX
+ alignment = AFFIX_GOOD | AFFIX_EVIL
+
+ var/list/goodPrefixes
+ var/list/badPrefixes
+
+/datum/fantasy_affix/cosmetic_prefixes/New()
+ goodPrefixes = list(
+ "greater",
+ "major",
+ "blessed",
+ "superior",
+ "empowered",
+ "honed",
+ "true",
+ "glorious",
+ "robust",
+ )
+ badPrefixes = list(
+ "lesser",
+ "minor",
+ "blighted",
+ "inferior",
+ "enfeebled",
+ "rusted",
+ "unsteady",
+ "tragic",
+ "gimped",
+ "cursed",
+ )
+
+ weight = (length(goodPrefixes) + length(badPrefixes)) * 10
+
+/datum/fantasy_affix/cosmetic_prefixes/apply(datum/component/fantasy/comp, newName)
+ if(comp.quality > 0 || (comp.quality == 0 && prob(50)))
+ return "[pick(goodPrefixes)] [newName]"
+ else
+ return "[pick(badPrefixes)] [newName]"
+
+/datum/fantasy_affix/tactical
+ placement = AFFIX_PREFIX
+ alignment = AFFIX_GOOD
+ weight = 1 // Very powerful, no one should have such power
+
+/datum/fantasy_affix/tactical/apply(datum/component/fantasy/comp, newName)
+ var/obj/item/master = comp.parent
+ comp.appliedComponents += master.AddComponent(/datum/component/tactical)
+ return "tactical [newName]"
+
+/datum/fantasy_affix/pyromantic
+ placement = AFFIX_PREFIX
+ alignment = AFFIX_GOOD
+
+/datum/fantasy_affix/pyromantic/apply(datum/component/fantasy/comp, newName)
+ var/obj/item/master = comp.parent
+ comp.appliedComponents += master.AddComponent(/datum/component/igniter, CLAMP(comp.quality, 1, 10))
+ return "pyromantic [newName]"
+
+/datum/fantasy_affix/vampiric
+ placement = AFFIX_PREFIX
+ alignment = AFFIX_GOOD
+ weight = 5
+
+/datum/fantasy_affix/vampiric/apply(datum/component/fantasy/comp, newName)
+ var/obj/item/master = comp.parent
+ comp.appliedComponents += master.AddComponent(/datum/component/lifesteal, comp.quality)
+ return "vampiric [newName]"
\ No newline at end of file
diff --git a/code/datums/components/fantasy/suffixes.dm b/code/datums/components/fantasy/suffixes.dm
new file mode 100644
index 0000000000..f32ca50595
--- /dev/null
+++ b/code/datums/components/fantasy/suffixes.dm
@@ -0,0 +1,170 @@
+/datum/fantasy_affix/cosmetic_suffixes
+ placement = AFFIX_SUFFIX
+ alignment = AFFIX_GOOD | AFFIX_EVIL
+
+ var/list/goodSuffixes
+ var/list/badSuffixes
+
+/datum/fantasy_affix/cosmetic_suffixes/New()
+ goodSuffixes = list(
+ "dexterity",
+ "constitution",
+ "intelligence",
+ "wisdom",
+ "charisma",
+ "the forest",
+ "the hills",
+ "the plains",
+ "the sea",
+ "the sun",
+ "the moon",
+ "the void",
+ "the world",
+ "many secrets",
+ "many tales",
+ "many colors",
+ "rending",
+ "sundering",
+ "the night",
+ "the day",
+ )
+ badSuffixes = list(
+ "draining",
+ "burden",
+ "discomfort",
+ "awkwardness",
+ "poor hygiene",
+ "timidity",
+ )
+
+ weight = (length(goodSuffixes) + length(badSuffixes)) * 10
+
+/datum/fantasy_affix/cosmetic_suffixes/apply(datum/component/fantasy/comp, newName)
+ if(comp.quality > 0 || (comp.quality == 0 && prob(50)))
+ return "[newName] of [pick(goodSuffixes)]"
+ else
+ return "[newName] of [pick(badSuffixes)]"
+
+//////////// Good suffixes
+/datum/fantasy_affix/bane
+ placement = AFFIX_SUFFIX
+ alignment = AFFIX_GOOD
+ weight = 20
+
+/datum/fantasy_affix/bane/apply(datum/component/fantasy/comp, newName)
+ . = ..()
+ // This is set up to be easy to add to these lists as I expect it will need modifications
+ var/static/list/possible_mobtypes
+ if(!possible_mobtypes)
+ // The base list of allowed mob/species types
+ possible_mobtypes = typecacheof(list(
+ /mob/living/simple_animal,
+ /mob/living/carbon,
+ /datum/species,
+ ))
+ // Some particular types to disallow if they're too broad/abstract
+ possible_mobtypes -= list(
+ /mob/living/simple_animal/hostile,
+ )
+ // Some types to remove them and their subtypes
+ possible_mobtypes -= typecacheof(list(
+ /mob/living/carbon/human/species,
+ ))
+
+ var/mob/picked_mobtype = pick(possible_mobtypes)
+ // This works even with the species picks since we're only accessing the name
+
+ var/obj/item/master = comp.parent
+ comp.appliedComponents += master.AddComponent(/datum/component/bane, picked_mobtype)
+ return "[newName] of [initial(picked_mobtype.name)] slaying"
+
+/datum/fantasy_affix/summoning
+ placement = AFFIX_SUFFIX
+ alignment = AFFIX_GOOD
+ weight = 5
+
+/datum/fantasy_affix/summoning/apply(datum/component/fantasy/comp, newName)
+ . = ..()
+ // This is set up to be easy to add to these lists as I expect it will need modifications
+ var/static/list/possible_mobtypes
+ if(!possible_mobtypes)
+ // The base list of allowed mob/species types
+ possible_mobtypes = typecacheof(list(
+ /mob/living/simple_animal,
+ /mob/living/carbon,
+ /datum/species,
+ ))
+ // Some particular types to disallow if they're too broad/abstract
+ possible_mobtypes -= list(
+ /mob/living/simple_animal/hostile,
+ )
+ // Some types to remove them and their subtypes
+ possible_mobtypes -= typecacheof(list(
+ /mob/living/carbon/human/species,
+ /mob/living/simple_animal/hostile/megafauna,
+ ))
+
+ var/mob/picked_mobtype = pick(possible_mobtypes)
+ // This works even with the species picks since we're only accessing the name
+
+ var/obj/item/master = comp.parent
+ var/max_mobs = max(CEILING(comp.quality/2, 1), 1)
+ var/spawn_delay = 300 - 30 * comp.quality
+ comp.appliedComponents += master.AddComponent(/datum/component/summoning, list(picked_mobtype), 100, max_mobs, spawn_delay)
+ return "[newName] of [initial(picked_mobtype.name)] summoning"
+
+/datum/fantasy_affix/shrapnel
+ placement = AFFIX_SUFFIX
+ alignment = AFFIX_GOOD
+
+/datum/fantasy_affix/shrapnel/validate(datum/component/fantasy/comp)
+ if(isgun(comp.parent))
+ return TRUE
+ return FALSE
+
+/datum/fantasy_affix/shrapnel/apply(datum/component/fantasy/comp, newName)
+ . = ..()
+ // higher means more likely
+ var/list/weighted_projectile_types = list(/obj/item/projectile/meteor = 1,
+ /obj/item/projectile/energy/nuclear_particle = 1,
+ /obj/item/projectile/beam/pulse = 1,
+ /obj/item/projectile/bullet/honker = 15,
+ /obj/item/projectile/temp = 15,
+ /obj/item/projectile/ion = 15,
+ /obj/item/projectile/magic/door = 15,
+ /obj/item/projectile/magic/locker = 15,
+// /obj/item/projectile/magic/fetch = 15,
+ /obj/item/projectile/beam/emitter = 15,
+// /obj/item/projectile/magic/flying = 15,
+ /obj/item/projectile/energy/net = 15,
+ /obj/item/projectile/bullet/incendiary/c9mm = 15,
+ /obj/item/projectile/temp/hot = 15,
+ /obj/item/projectile/beam/disabler = 15)
+
+ var/obj/item/projectile/picked_projectiletype = pickweight(weighted_projectile_types)
+
+ var/obj/item/master = comp.parent
+ comp.appliedComponents += master.AddComponent(/datum/component/shrapnel, picked_projectiletype)
+ return "[newName] of [initial(picked_projectiletype.name)] shrapnel"
+
+/datum/fantasy_affix/strength
+ placement = AFFIX_SUFFIX
+ alignment = AFFIX_GOOD
+
+/datum/fantasy_affix/strength/apply(datum/component/fantasy/comp, newName)
+ . = ..()
+ var/obj/item/master = comp.parent
+ comp.appliedComponents += master.AddComponent(/datum/component/knockback, CEILING(comp.quality/2, 1), FLOOR(comp.quality/10, 1))
+ return "[newName] of strength"
+
+//////////// Bad suffixes
+
+/datum/fantasy_affix/fool
+ placement = AFFIX_SUFFIX
+ alignment = AFFIX_EVIL
+
+/datum/fantasy_affix/fool/apply(datum/component/fantasy/comp, newName)
+ . = ..()
+ var/obj/item/master = comp.parent
+ comp.appliedComponents += master.AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 50)
+ return "[newName] of the fool"
diff --git a/code/datums/components/igniter.dm b/code/datums/components/igniter.dm
new file mode 100644
index 0000000000..b40383e828
--- /dev/null
+++ b/code/datums/components/igniter.dm
@@ -0,0 +1,37 @@
+
+/datum/component/igniter
+ var/fire_stacks
+
+/datum/component/igniter/Initialize(fire_stacks=1)
+ if(!isitem(parent) && !ishostile(parent) && !isgun(parent) && !ismachinery(parent) && !isstructure(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.fire_stacks = fire_stacks
+
+/datum/component/igniter/RegisterWithParent()
+ if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
+ RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
+ else if(isitem(parent))
+ RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/item_afterattack)
+ else if(ishostile(parent))
+ RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
+
+/datum/component/igniter/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
+
+/datum/component/igniter/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
+ if(!proximity_flag)
+ return
+ do_igniter(target)
+
+/datum/component/igniter/proc/hostile_attackingtarget(mob/living/simple_animal/hostile/attacker, atom/target)
+ do_igniter(target)
+
+/datum/component/igniter/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
+ do_igniter(target)
+
+/datum/component/igniter/proc/do_igniter(atom/target)
+ if(isliving(target))
+ var/mob/living/L = target
+ L.adjust_fire_stacks(fire_stacks)
+ L.IgniteMob()
\ No newline at end of file
diff --git a/code/datums/components/knockback.dm b/code/datums/components/knockback.dm
new file mode 100644
index 0000000000..b4fcaa2dd8
--- /dev/null
+++ b/code/datums/components/knockback.dm
@@ -0,0 +1,44 @@
+/datum/component/knockback
+ var/throw_distance
+ var/throw_anchored
+
+/datum/component/knockback/Initialize(throw_distance=1)
+ if(!isitem(parent) && !ishostile(parent) && !isgun(parent) && !ismachinery(parent) && !isstructure(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.throw_distance = throw_distance
+ src.throw_anchored = throw_anchored
+
+/datum/component/knockback/RegisterWithParent()
+ if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
+ RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
+ else if(isitem(parent))
+ RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/item_afterattack)
+ else if(ishostile(parent))
+ RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
+
+/datum/component/knockback/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
+
+/datum/component/knockback/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
+ if(!proximity_flag)
+ return
+ do_knockback(target, user, get_dir(source, target))
+
+/datum/component/knockback/proc/hostile_attackingtarget(mob/living/simple_animal/hostile/attacker, atom/target)
+ do_knockback(target, attacker, get_dir(attacker, target))
+
+/datum/component/knockback/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
+ do_knockback(target, null, angle2dir(Angle))
+
+/datum/component/knockback/proc/do_knockback(atom/target, mob/thrower, throw_dir)
+ if(!ismovableatom(target) || throw_dir == null)
+ return
+ var/atom/movable/throwee = target
+ if(throwee.anchored && !throw_anchored)
+ return
+ if(throw_distance < 0)
+ throw_dir = turn(throw_dir, 180)
+ throw_distance *= -1
+ var/atom/throw_target = get_edge_target_turf(throwee, throw_dir)
+ throwee.safe_throw_at(throw_target, throw_distance, 1, thrower)
diff --git a/code/datums/components/lifesteal.dm b/code/datums/components/lifesteal.dm
new file mode 100644
index 0000000000..c7a78e10a3
--- /dev/null
+++ b/code/datums/components/lifesteal.dm
@@ -0,0 +1,39 @@
+
+/datum/component/lifesteal
+ var/flat_heal // heals a constant amount every time a hit occurs
+ var/static/list/damage_heal_order = list(BRUTE, BURN, OXY)
+
+/datum/component/lifesteal/Initialize(flat_heal=0)
+ if(!isitem(parent) && !ishostile(parent) && !isgun(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.flat_heal = flat_heal
+
+/datum/component/lifesteal/RegisterWithParent()
+ if(isgun(parent))
+ RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
+ else if(isitem(parent))
+ RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/item_afterattack)
+ else if(ishostile(parent))
+ RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
+
+/datum/component/lifesteal/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
+
+/datum/component/lifesteal/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
+ if(!proximity_flag)
+ return
+ do_lifesteal(user, target)
+
+/datum/component/lifesteal/proc/hostile_attackingtarget(mob/living/simple_animal/hostile/attacker, atom/target)
+ do_lifesteal(attacker, target)
+
+/datum/component/lifesteal/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
+ do_lifesteal(firer, target)
+
+/datum/component/lifesteal/proc/do_lifesteal(atom/heal_target, atom/damage_target)
+ if(isliving(heal_target) && isliving(damage_target))
+ var/mob/living/healing = heal_target
+ var/mob/living/damaging = damage_target
+ if(damaging.stat != DEAD)
+ healing.heal_ordered_damage(flat_heal, damage_heal_order)
diff --git a/code/datums/components/phantomthief.dm b/code/datums/components/phantomthief.dm
index ff1c48936f..7afe64994f 100644
--- a/code/datums/components/phantomthief.dm
+++ b/code/datums/components/phantomthief.dm
@@ -7,33 +7,24 @@
var/filter_x
var/filter_y
var/filter_size
- var/filter_border
var/filter_color
-/datum/component/wearertargeting/phantomthief/Initialize(_x = -2, _y = 0, _size = 0, _border = 0, _color = "#E62111", list/_valid_slots = list(SLOT_GLASSES))
+/datum/component/wearertargeting/phantomthief/Initialize(_x = -2, _y = 0, _size = 0, _color = "#E62111", list/_valid_slots = list(SLOT_GLASSES))
. = ..()
if(. == COMPONENT_INCOMPATIBLE)
return
filter_x = _x
filter_y = _y
filter_size = _size
- filter_border = _border
filter_color = _color
valid_slots = _valid_slots
/datum/component/wearertargeting/phantomthief/proc/handlefilterstuff(datum/source, mob/user, combatmodestate)
- if(istype(user))
- var/thefilter = filter(type = "drop_shadow", x = filter_x, y = filter_y, size = filter_size, border = filter_border, color = filter_color)
- if(!combatmodestate)
- user.filters -= thefilter
- else
- user.filters += thefilter
-
-/datum/component/wearertargeting/phantomthief/proc/stripdesiredfilter(mob/user)
- if(istype(user))
- var/thefilter = filter(type = "drop_shadow", x = filter_x, y = filter_y, size = filter_size, border = filter_border, color = filter_color)
- user.filters -= thefilter
+ if(!combatmodestate)
+ user.remove_filter("phantomthief")
+ else
+ user.add_filter("phantomthief", 4, list(type = "drop_shadow", x = filter_x, y = filter_y, size = filter_size, color = filter_color))
/datum/component/wearertargeting/phantomthief/on_drop(datum/source, mob/user)
. = ..()
- stripdesiredfilter(user)
+ user.remove_filter("phantomthief")
diff --git a/code/datums/components/riding.dm b/code/datums/components/riding.dm
index 0af90f694c..dcdbd5ca4b 100644
--- a/code/datums/components/riding.dm
+++ b/code/datums/components/riding.dm
@@ -18,6 +18,7 @@
var/ride_check_rider_incapacitated = FALSE
var/ride_check_rider_restrained = FALSE
var/ride_check_ridden_incapacitated = FALSE
+ var/list/offhands = list() // keyed list containing all the current riding offsets associated by mob
/datum/component/riding/Initialize()
if(!ismovableatom(parent))
@@ -299,36 +300,34 @@
M.throw_at(target, 14, 5, AM)
M.Knockdown(60)
-/datum/component/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1, riding_target_override = null)
- var/atom/movable/AM = parent
- var/amount_equipped = 0
+/datum/component/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1, mob/living/riding_target_override)
+ var/list/equipped
+ var/mob/living/L = riding_target_override ? riding_target_override : user
for(var/amount_needed = amount_required, amount_needed > 0, amount_needed--)
- var/obj/item/riding_offhand/inhand = new /obj/item/riding_offhand(user)
- if(!riding_target_override)
- inhand.rider = user
- else
- inhand.rider = riding_target_override
- inhand.parent = AM
- if(user.put_in_hands(inhand, TRUE))
- amount_equipped++
- else
+ var/obj/item/riding_offhand/inhand = new
+ inhand.rider = L
+ inhand.parent = parent
+ if(!user.put_in_hands(inhand, TRUE))
+ qdel(inhand) // it isn't going to be added to offhands anyway
break
+ LAZYADD(equipped, src)
+ var/amount_equipped = LAZYLEN(equipped)
+ if(amount_equipped)
+ LAZYADD(offhands[L], amount_equipped)
if(amount_equipped >= amount_required)
return TRUE
- else
- unequip_buckle_inhands(user)
- return FALSE
+ unequip_buckle_inhands(L)
+ return FALSE
/datum/component/riding/proc/unequip_buckle_inhands(mob/living/carbon/user)
- var/atom/movable/AM = parent
- for(var/obj/item/riding_offhand/O in user.contents)
- if(O.parent != AM)
- CRASH("RIDING OFFHAND ON WRONG MOB")
- continue
- if(O.selfdeleting)
- continue
- else
- qdel(O)
+ for(var/a in offhands[user])
+ LAZYREMOVE(offhands[user], a)
+ if(a) //edge cases null entries
+ var/obj/item/riding_offhand/O = a
+ if(O.parent != parent)
+ CRASH("RIDING OFFHAND ON WRONG MOB")
+ else if(!O.selfdeleting)
+ qdel(O)
return TRUE
/obj/item/riding_offhand
diff --git a/code/datums/components/shrapnel.dm b/code/datums/components/shrapnel.dm
new file mode 100644
index 0000000000..a911221f26
--- /dev/null
+++ b/code/datums/components/shrapnel.dm
@@ -0,0 +1,39 @@
+
+/datum/component/shrapnel
+ var/projectile_type
+ var/radius // shoots a projectile for every turf on this radius from the hit target
+ var/override_projectile_range
+
+/datum/component/shrapnel/Initialize(projectile_type, radius=1, override_projectile_range)
+ if(!isgun(parent) && !ismachinery(parent) && !isstructure(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.projectile_type = projectile_type
+ src.radius = radius
+ src.override_projectile_range = override_projectile_range
+
+/datum/component/shrapnel/RegisterWithParent()
+ if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
+ RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
+
+/datum/component/shrapnel/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_PROJECTILE_ON_HIT))
+
+/datum/component/shrapnel/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
+ do_shrapnel(firer, target)
+
+/datum/component/shrapnel/proc/do_shrapnel(mob/firer, atom/target)
+ if(radius < 1)
+ return
+ var/turf/target_turf = get_turf(target)
+ for(var/turf/shootat_turf in RANGE_TURFS(radius, target) - RANGE_TURFS(radius-1, target))
+ var/obj/item/projectile/P = new projectile_type(target_turf)
+
+ //Shooting Code:
+ P.range = radius+1
+ if(override_projectile_range)
+ P.range = override_projectile_range
+ P.preparePixelProjectile(shootat_turf, target)
+ P.firer = firer // don't hit ourself that would be really annoying
+ P.permutated += target // don't hit the target we hit already with the flak
+ P.fire()
diff --git a/code/datums/components/summoning.dm b/code/datums/components/summoning.dm
new file mode 100644
index 0000000000..552959603d
--- /dev/null
+++ b/code/datums/components/summoning.dm
@@ -0,0 +1,69 @@
+
+/datum/component/summoning
+ var/list/mob_types = list()
+ var/spawn_chance // chance for the mob to spawn on hit in percent
+ var/max_mobs
+ var/spawn_delay // delay in spawning between mobs (deciseconds)
+ var/spawn_text
+ var/spawn_sound
+ var/list/faction
+
+ var/last_spawned_time = 0
+ var/list/spawned_mobs = list()
+
+/datum/component/summoning/Initialize(mob_types, spawn_chance=100, max_mobs=3, spawn_delay=100, spawn_text="appears out of nowhere", spawn_sound='sound/magic/summon_magic.ogg', faction)
+ if(!isitem(parent) && !ishostile(parent) && !isgun(parent) && !ismachinery(parent) && !isstructure(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.mob_types = mob_types
+ src.spawn_chance = spawn_chance
+ src.max_mobs = max_mobs
+ src.spawn_delay = spawn_delay
+ src.spawn_text = spawn_text
+ src.spawn_sound = spawn_sound
+ src.faction = faction
+
+/datum/component/summoning/RegisterWithParent()
+ if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
+ RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
+ else if(isitem(parent))
+ RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/item_afterattack)
+ else if(ishostile(parent))
+ RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
+
+/datum/component/summoning/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
+
+/datum/component/summoning/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
+ if(!proximity_flag)
+ return
+ do_spawn_mob(get_turf(target), user)
+
+/datum/component/summoning/proc/hostile_attackingtarget(mob/living/simple_animal/hostile/attacker, atom/target)
+ do_spawn_mob(get_turf(target), attacker)
+
+/datum/component/summoning/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
+ do_spawn_mob(get_turf(target), firer)
+
+/datum/component/summoning/proc/do_spawn_mob(atom/spawn_location, summoner)
+ if(spawned_mobs.len >= max_mobs)
+ return 0
+ if(last_spawned_time > world.time)
+ return 0
+ if(!prob(spawn_chance))
+ return 0
+ last_spawned_time = world.time + spawn_delay
+ var/chosen_mob_type = pick(mob_types)
+ var/mob/living/simple_animal/L = new chosen_mob_type(spawn_location)
+ if(ishostile(L))
+ var/mob/living/simple_animal/hostile/H = L
+ H.friends += summoner // do not attack our summon boy
+ spawned_mobs += L
+ if(faction != null)
+ L.faction = faction
+ RegisterSignal(L, COMSIG_MOB_DEATH, .proc/on_spawned_death) // so we can remove them from the list, etc (for mobs with corpses)
+ playsound(spawn_location,spawn_sound, 50, 1)
+ spawn_location.visible_message("[L] [spawn_text].")
+
+/datum/component/summoning/proc/on_spawned_death(mob/killed, gibbed)
+ spawned_mobs -= killed
\ No newline at end of file
diff --git a/code/datums/components/tactical.dm b/code/datums/components/tactical.dm
new file mode 100644
index 0000000000..5917fc3009
--- /dev/null
+++ b/code/datums/components/tactical.dm
@@ -0,0 +1,42 @@
+
+/datum/component/tactical
+ var/allowed_slot
+
+/datum/component/tactical/Initialize(allowed_slot)
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.allowed_slot = allowed_slot
+
+/datum/component/tactical/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/modify)
+ RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/unmodify)
+
+/datum/component/tactical/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
+ unmodify()
+
+/datum/component/fantasy/Destroy()
+ unmodify()
+ return ..()
+
+/datum/component/tactical/proc/modify(obj/item/source, mob/user, slot)
+ if(allowed_slot && slot != allowed_slot)
+ unmodify()
+ return
+
+ var/obj/item/master = parent
+ var/image/I = image(icon = master.icon, icon_state = master.icon_state, loc = user)
+ I.copy_overlays(master)
+ I.override = TRUE
+ source.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, "sneaking_mission", I)
+ I.layer = ABOVE_MOB_LAYER
+
+/datum/component/tactical/proc/unmodify(obj/item/source, mob/user)
+ var/obj/item/master = source || parent
+ if(!user)
+ if(!ismob(master.loc))
+ return
+ user = master.loc
+
+ user.remove_alt_appearance("sneaking_mission")
diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm
index 1ab3456c22..8b205db756 100644
--- a/code/datums/diseases/advance/symptoms/heal.dm
+++ b/code/datums/diseases/advance/symptoms/heal.dm
@@ -79,7 +79,7 @@
if(M.getToxLoss() && prob(5))
to_chat(M, "Your skin tingles as the starlight seems to heal you.")
- M.adjustToxLoss(-(4 * heal_amt)) //most effective on toxins
+ M.adjustToxLoss(-(4 * heal_amt), forced = TRUE) //most effective on toxins
var/list/parts = M.get_damaged_bodyparts(1,1)
@@ -368,7 +368,7 @@
level = 8
passive_message = "You feel an odd attraction to plasma."
var/temp_rate = 1
- threshold_desc = "Transmission 6: Increases temperature adjustment rate.
\
+ threshold_desc = "Transmission 6: Increases temperature adjustment rate and heals toxin lovers.
\
Stage Speed 7: Increases healing speed."
/datum/symptom/heal/plasma/Start(datum/disease/advance/A)
@@ -410,7 +410,7 @@
if(prob(5))
to_chat(M, "You feel warmer.")
- M.adjustToxLoss(-heal_amt)
+ M.adjustToxLoss(-heal_amt, forced = (temp_rate == 4))
var/list/parts = M.get_damaged_bodyparts(1,1)
if(!parts.len)
@@ -435,7 +435,7 @@
symptom_delay_max = 1
passive_message = "Your skin glows faintly for a moment."
var/cellular_damage = FALSE
- threshold_desc = "Transmission 6: Additionally heals cellular damage.
\
+ threshold_desc = "Transmission 6: Additionally heals cellular damage and toxin lovers.
\
Resistance 7: Increases healing speed."
/datum/symptom/heal/radiation/Start(datum/disease/advance/A)
@@ -468,7 +468,7 @@
if(cellular_damage)
M.adjustCloneLoss(-heal_amt * 0.5)
- M.adjustToxLoss(-(2 * heal_amt))
+ M.adjustToxLoss(-(2 * heal_amt), forced = cellular_damage)
var/list/parts = M.get_damaged_bodyparts(1,1)
diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm
index f70e3a3c68..c015dba5ec 100644
--- a/code/datums/traits/neutral.dm
+++ b/code/datums/traits/neutral.dm
@@ -94,6 +94,51 @@
lose_text = "You don't feel as prudish as before."
medical_record_text = "Patient exhibits a special gene that makes them immune to Crocin and Hexacrocin."
+/datum/quirk/libido
+ name = "Nymphomania"
+ desc = "You're always feeling a bit in heat. Also, you get aroused faster than usual."
+ value = 0
+ mob_trait = TRAIT_NYMPHO
+ gain_text = "You are feeling extra wild."
+ lose_text = "You don't feel that burning sensation anymore."
+
+/datum/quirk/libido/add()
+ var/mob/living/M = quirk_holder
+ M.min_arousal = 16
+ M.arousal_rate = 3
+
+/datum/quirk/libido/remove()
+ var/mob/living/M = quirk_holder
+ M.min_arousal = initial(M.min_arousal)
+ M.arousal_rate = initial(M.arousal_rate)
+
+/datum/quirk/maso
+ name = "Masochism"
+ desc = "You are aroused by pain."
+ value = 0
+ mob_trait = TRAIT_MASO
+ gain_text = "You desire to be hurt."
+ lose_text = "Pain has become less exciting for you."
+
+/datum/quirk/exhibitionism
+ name = "Exhibitionism"
+ desc = "You don't mind showing off your bare body to strangers, in fact you find it quite satistying."
+ value = 0
+ medical_record_text = "Patient has been diagnosed with exhibitionistic disorder."
+ mob_trait = TRAIT_EXHIBITIONIST
+ gain_text = "You feel like exposing yourself to the world."
+ lose_text = "Indecent exposure doesn't sound as charming to you anymore."
+
+/datum/quirk/pharmacokinesis //Prevents unwanted organ additions.
+ name = "Acute hepatic pharmacokinesis"
+ desc = "You've a rare genetic disorder that causes Incubus draft and Sucubus milk to be absorbed by your liver instead."
+ value = 0
+ mob_trait = TRAIT_PHARMA
+ lose_text = "Your liver feels different."
+ var/active = FALSE
+ var/power = 0
+ var/cachedmoveCalc = 1
+
/datum/quirk/assblastusa
name = "Buns of Steel"
desc = "You've never skipped ass day. With this trait, you are completely immune to all forms of ass slapping and anyone who tries to slap your rock hard ass usually gets a broken hand."
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 6efa6c974a..ca009f76ad 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -111,6 +111,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
..()
pop_per_requirement = CONFIG_GET(number/dynamic_pop_per_requirement)
GLOB.dynamic_high_pop_limit = CONFIG_GET(number/dynamic_high_pop_limit)
+ GLOB.dynamic_latejoin_delay_min = CONFIG_GET(number/dynamic_latejoin_delay_min)*600
+ GLOB.dynamic_latejoin_delay_max = CONFIG_GET(number/dynamic_latejoin_delay_max)*600
+ GLOB.dynamic_midround_delay_min = CONFIG_GET(number/dynamic_midround_delay_min)*600
+ GLOB.dynamic_midround_delay_max = CONFIG_GET(number/dynamic_midround_delay_max)*600
/datum/game_mode/dynamic/admin_panel()
var/list/dat = list("Game Mode PanelGame Mode Panel
")
diff --git a/code/game/machinery/computer/_computer.dm b/code/game/machinery/computer/_computer.dm
index 3c3f0e7f0f..fb46229bdb 100644
--- a/code/game/machinery/computer/_computer.dm
+++ b/code/game/machinery/computer/_computer.dm
@@ -34,9 +34,9 @@
/obj/machinery/computer/ratvar_act()
if(!clockwork)
clockwork = TRUE
- icon_screen = "ratvar[rand(1, 4)]"
- icon_keyboard = "ratvar_key[rand(1, 6)]"
- icon_state = "ratvarcomputer[rand(1, 4)]"
+ icon_screen = "ratvar[rand(1, 3)]"
+ icon_keyboard = "ratvar_key[rand(1, 2)]"
+ icon_state = "ratvarcomputer"
update_icon()
/obj/machinery/computer/narsie_act()
diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm
index b988bd8d35..8d950f8458 100644
--- a/code/game/machinery/computer/arcade.dm
+++ b/code/game/machinery/computer/arcade.dm
@@ -86,7 +86,8 @@
/obj/item/circuitboard/computer/arcade/amputation = 2)
var/thegame = pickweight(gameodds)
var/obj/item/circuitboard/CB = new thegame()
- new CB.build_path(loc, CB)
+ var/obj/machinery/computer/arcade/A = new CB.build_path(loc, CB)
+ A.setDir(dir)
return INITIALIZE_HINT_QDEL
//The below object acts as a spawner with a wide array of possible picks, most being uninspired references to past/current player characters.
//Nevertheless, this keeps its ratio constant with the sum of all the others prizes.
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index 5cbd2d8975..43cfbdb33b 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -298,13 +298,15 @@
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
say("Initiating scan...")
-
+ var/prev_locked = scanner.locked
+ scanner.locked = TRUE
spawn(20)
src.scan_occupant(scanner.occupant)
loading = 0
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
+ scanner.locked = prev_locked
//No locking an open scanner.
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 893f36acb3..79aef8ba7b 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -24,6 +24,8 @@ Possible to do for anyone motivated enough:
* Holopad
*/
+GLOBAL_LIST_EMPTY(network_holopads)
+
#define HOLOPAD_PASSIVE_POWER_USAGE 1
#define HOLOGRAM_POWER_USAGE 2
@@ -55,7 +57,6 @@ Possible to do for anyone motivated enough:
var/record_user //user that inititiated the recording
var/obj/effect/overlay/holo_pad_hologram/replay_holo //replay hologram
var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging
- var/static/list/holopads = list()
var/obj/effect/overlay/holoray/ray
var/ringing = FALSE
var/offset = FALSE
@@ -96,7 +97,7 @@ Possible to do for anyone motivated enough:
/obj/machinery/holopad/Initialize()
. = ..()
if(on_network)
- holopads += src
+ GLOB.network_holopads += src
/obj/machinery/holopad/Destroy()
if(outgoing_call)
@@ -116,7 +117,7 @@ Possible to do for anyone motivated enough:
QDEL_NULL(disk)
- holopads -= src
+ GLOB.network_holopads -= src
return ..()
/obj/machinery/holopad/power_change()
@@ -260,7 +261,7 @@ Possible to do for anyone motivated enough:
temp += "Main Menu"
if(usr.loc == loc)
var/list/callnames = list()
- for(var/I in holopads)
+ for(var/I in GLOB.network_holopads)
var/area/A = get_area(I)
if(A)
LAZYADD(callnames[A], I)
@@ -474,7 +475,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
var/obj/effect/overlay/holo_pad_hologram/h = masters[holo_owner]
if(!h || h.HC) //Holocalls can't change source.
return FALSE
- for(var/pad in holopads)
+ for(var/pad in GLOB.network_holopads)
var/obj/machinery/holopad/another = pad
if(another == src)
continue
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index 165170cf0f..7aaab0d8ea 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -562,6 +562,7 @@
//Shooting Code:
A.preparePixelProjectile(target, T)
A.firer = src
+ A.fired_from = src
A.fire()
return A
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index 693d32e545..9c3df5395f 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -89,7 +89,6 @@
buckled_mob.clear_alert("buckled")
buckled_mobs -= buckled_mob
SEND_SIGNAL(src, COMSIG_MOVABLE_UNBUCKLE, buckled_mob, force)
- SEND_SIGNAL(src, COMSIG_MOVABLE_UNBUCKLE, src, force)
post_unbuckle_mob(.)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 9ce2e66abf..4f81a875f1 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -97,9 +97,6 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
// non-clothing items
var/datum/dog_fashion/dog_fashion = null
- var/datum/rpg_loot/rpg_loot = null
-
-
//Tooltip vars
var/force_string //string form of an item's force. Edit this var only to set a custom force string
var/last_force_string_check = 0
@@ -124,7 +121,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
actions_types = null
if(GLOB.rpg_loot_items)
- rpg_loot = new(src)
+ AddComponent(/datum/component/fantasy)
if(force_string)
item_flags |= FORCE_STRING_OVERRIDE
@@ -149,7 +146,6 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
m.temporarilyRemoveItemFromInventory(src, TRUE)
for(var/X in actions)
qdel(X)
- QDEL_NULL(rpg_loot)
return ..()
/obj/item/proc/check_allowed_items(atom/target, not_inside, target_self)
diff --git a/code/game/objects/items/devices/compressionkit.dm b/code/game/objects/items/devices/compressionkit.dm
index 5ac958328d..a5a9377690 100644
--- a/code/game/objects/items/devices/compressionkit.dm
+++ b/code/game/objects/items/devices/compressionkit.dm
@@ -89,30 +89,23 @@
else
to_chat(user, "Anomalous error. Summon a coder.")
- if(istype(target, /mob/living))
- var/mob/living/victim = target
- if(istype(victim, /mob/living/carbon/human))
- if(user.zone_selected == "groin") // pp smol. There's probably a smarter way to do this but im retarded. If you have a simpler method let me know.
- var/list/organs = victim.getorganszone("groin")
- for(var/internal_organ in organs)
- if(istype(internal_organ, /obj/item/organ/genital/penis))
- var/obj/item/organ/genital/penis/O = internal_organ
- playsound(get_turf(src), 'sound/weapons/flash.ogg', 50, 1)
- victim.visible_message("[user] is preparing to shrink [victim]\'s [O.name] with their bluespace compression kit!")
- if(do_mob(user, victim, 40) && charges > 0 && O.length > 0)
- victim.visible_message("[user] has shrunk [victim]\'s [O.name]!")
- playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 50, 1)
- sparks()
- flash_lighting_fx(3, 3, LIGHT_COLOR_CYAN)
- charges -= 1
- O.length -= 5
- if(O.length < 1)
- victim.visible_message("[user]\'s [O.name] vanishes!")
- qdel(O) // no pp for you
- else
- O.update_size()
- O.update_appearance()
-
+ else if(ishuman(target) && user.zone_selected == BODY_ZONE_PRECISE_GROIN)
+ var/mob/living/carbon/human/H = target
+ var/obj/item/organ/genital/penis/P = H.getorganslot(ORGAN_SLOT_PENIS)
+ if(!P)
+ return
+ playsound(get_turf(src), 'sound/weapons/flash.ogg', 50, 1)
+ H.visible_message("[user] is preparing to shrink [H]\'s [P.name] with their bluespace compression kit!")
+ if(do_mob(user, H, 40) && charges > 0 && P.length > 0)
+ H.visible_message("[user] has shrunk [H]\'s [P.name]!")
+ playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 50, 1)
+ sparks()
+ flash_lighting_fx(3, 3, LIGHT_COLOR_CYAN)
+ charges -= 1
+ var/p_name = P.name
+ P.modify_size(-5)
+ if(QDELETED(P))
+ H.visible_message("[H]\'s [p_name] vanishes!")
/obj/item/compressionkit/attackby(obj/item/I, mob/user, params)
diff --git a/code/game/objects/items/devices/dogborg_sleeper.dm b/code/game/objects/items/devices/dogborg_sleeper.dm
index c1a9136f76..3c945f464e 100644
--- a/code/game/objects/items/devices/dogborg_sleeper.dm
+++ b/code/game/objects/items/devices/dogborg_sleeper.dm
@@ -6,8 +6,7 @@
icon = 'icons/mob/dogborg.dmi'
icon_state = "sleeper"
w_class = WEIGHT_CLASS_TINY
- var/mob/living/carbon/patient = null
- var/mob/living/silicon/robot/hound = null
+ var/mob/living/carbon/patient
var/inject_amount = 10
var/min_health = -100
var/cleaning = FALSE
@@ -61,8 +60,18 @@
/obj/item/dogborg/sleeper/Exit(atom/movable/O)
return 0
+/obj/item/dogborg/sleeper/proc/get_host()
+ if(!loc)
+ return
+ if(iscyborg(loc))
+ return loc
+ else if(iscyborg(loc.loc))
+ return loc.loc //cursed cyborg code
+
/obj/item/dogborg/sleeper/afterattack(mob/living/carbon/target, mob/living/silicon/user, proximity)
- hound = loc
+ var/mob/living/silicon/robot/hound = get_host()
+ if(!hound)
+ return
if(!proximity)
return
if(!iscarbon(target))
@@ -82,7 +91,8 @@
if(!in_range(src, target)) //Proximity is probably old news by now, do a new check.
return //If they moved away, you can't eat them.
- if(patient) return //If you try to eat two people at once, you can only eat one.
+ if(patient)
+ return //If you try to eat two people at once, you can only eat one.
else //If you don't have someone in you, proceed.
if(!isjellyperson(target) && ("toxin" in injection_chems))
@@ -94,14 +104,17 @@
target.forceMove(src)
target.reset_perspective(src)
target.ExtinguishMob() //The tongue already puts out fire stacks but being put into the sleeper shouldn't allow you to keep burning.
- update_gut()
+ update_gut(hound)
user.visible_message("[voracious ? "[hound]'s [src.name] lights up and expands as [target] slips inside into their [src.name]." : "[hound]'s sleeper indicator lights up as [target] is scooped up into [hound.p_their()] [src]."]", \
"Your [voracious ? "[src.name] lights up as [target] slips into" : "sleeper indicator light shines brightly as [target] is scooped inside"] your [src]. Life support functions engaged.")
message_admins("[key_name(hound)] has sleeper'd [key_name(patient)] as a dogborg. [ADMIN_JMP(src)]")
playsound(hound, 'sound/effects/bin_close.ogg', 100, 1)
/obj/item/dogborg/sleeper/container_resist(mob/living/user)
- hound = loc
+ var/mob/living/silicon/robot/hound = get_host()
+ if(!hound)
+ go_out(user)
+ return
user.changeNext_move(CLICK_CD_BREAKOUT)
user.last_special = world.time + CLICK_CD_BREAKOUT
if(user.a_intent == INTENT_HELP)
@@ -113,55 +126,41 @@
"[voracious ? "You start struggling inside of [src]'s tight, flexible confines," : "You start pounding against the metallic walls of [src],"] trying to trigger the release... (this will take about [DisplayTimeText(breakout_time)].)", \
"You hear a [voracious ? "couple of thumps" : "loud banging noise"] coming from within [hound].")
if(do_after(user, breakout_time, target = src))
- if(!user || user.stat != CONSCIOUS || user.loc != src )
- return
user.visible_message("[user] successfully broke out of [hound.name]!", \
"You successfully break out of [hound.name]!")
- go_out()
+ go_out(user, hound)
-/obj/item/dogborg/sleeper/proc/go_out(var/target)
- hound = loc
- hound.setClickCooldown(50)
- var/voracious = TRUE
- if(!hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
- voracious = FALSE
- else
- for(var/mob/M in contents)
- if(!M.client || !(M.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
- voracious = FALSE
- if(length(contents) > 0)
- hound.visible_message("[voracious ? "[hound] empties out [hound.p_their()] contents via [hound.p_their()] release port." : "[hound]'s underside slides open with an audible clunk before [hound.p_their()] [src] flips over, carelessly dumping its contents onto the ground below [hound.p_them()] before closing right back up again."]", \
- "[voracious ? "You empty your contents via your release port." : "You open your sleeper hatch, quickly releasing all of the contents within before closing it again."]")
- if(target)
- if(iscarbon(target))
- var/mob/living/carbon/person = target
- person.forceMove(get_turf(src))
- person.reset_perspective()
- else
- var/obj/T = target
- T.loc = hound.loc
+/obj/item/dogborg/sleeper/proc/go_out(atom/movable/target, mob/living/silicon/robot/hound)
+ var/voracious = hound ? TRUE : FALSE
+ var/list/targets = target && hound ? list(target) : contents
+ if(hound)
+ hound.setClickCooldown(50)
+ if(!hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
+ voracious = FALSE
else
- for(var/C in contents)
- if(iscarbon(C))
- var/mob/living/carbon/person = C
- person.forceMove(get_turf(src))
- person.reset_perspective()
- else
- var/obj/T = C
- T.loc = hound.loc
- items_preserved.Cut()
- update_gut()
- cleaning = FALSE
+ for(var/mob/M in targets)
+ if(!M.client || !(M.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
+ voracious = FALSE
+ if(length(targets))
+ if(hound)
+ hound.visible_message("[voracious ? "[hound] empties out [hound.p_their()] contents via [hound.p_their()] release port." : "[hound]'s underside slides open with an audible clunk before [hound.p_their()] [src] flips over, carelessly dumping its contents onto the ground below [hound.p_them()] before closing right back up again."]", \
+ "[voracious ? "You empty your contents via your release port." : "You open your sleeper hatch, quickly releasing all of the contents within before closing it again."]")
+ for(var/a in contents)
+ var/atom/movable/AM = a
+ AM.forceMove(get_turf(src))
+ if(ismob(AM))
+ var/mob/M = AM
+ M.reset_perspective()
playsound(loc, voracious ? 'sound/effects/splat.ogg' : 'sound/effects/bin_close.ogg', 50, 1)
-
- else //You clicked eject with nothing in you, let's just reset stuff to be sure.
- items_preserved.Cut()
- cleaning = FALSE
- update_gut()
+ items_preserved.Cut()
+ cleaning = FALSE
+ if(hound)
+ update_gut(hound)
/obj/item/dogborg/sleeper/attack_self(mob/user)
- if(..())
+ . = ..()
+ if(. || !iscyborg(user))
return
ui_interact(user)
@@ -219,30 +218,31 @@
return data
/obj/item/dogborg/sleeper/ui_act(action, params)
- if(..())
+ . = ..()
+ if(. || !iscyborg(usr))
return
switch(action)
if("eject")
- go_out()
+ go_out(null, usr)
. = TRUE
if("inject")
var/chem = params["chem"]
if(!patient)
return
- inject_chem(chem)
+ inject_chem(chem, usr)
. = TRUE
if("cleaning")
if(!contents)
to_chat(src, "Your [src] is already cleaned.")
return
if(patient)
- to_chat(patient, "[hound.name]'s [src] fills with caustic enzymes around you!")
+ to_chat(patient, "[usr.name]'s [src] fills with caustic enzymes around you!")
to_chat(src, "Cleaning process enabled.")
- clean_cycle()
+ clean_cycle(usr)
. = TRUE
-/obj/item/dogborg/sleeper/proc/update_gut()
+/obj/item/dogborg/sleeper/proc/update_gut(mob/living/silicon/robot/hound)
//Well, we HAD one, what happened to them?
var/prociconupdate = FALSE
var/currentenvy = hound.sleeper_nv
@@ -291,7 +291,7 @@
//Update icon and return new patient
hound.update_icons()
- return(C)
+ return
//Cleaning looks better with red on, even with nobody in it
if(cleaning && !patient)
@@ -307,8 +307,10 @@
hound.update_icons()
//Gurgleborg process
-/obj/item/dogborg/sleeper/proc/clean_cycle()
+/obj/item/dogborg/sleeper/proc/clean_cycle(mob/living/silicon/robot/hound)
//Sanity
+ if(!hound)
+ return
for(var/I in items_preserved)
if(!(I in contents))
items_preserved -= I
@@ -320,13 +322,12 @@
if(cleaning_cycles)
cleaning_cycles--
cleaning = TRUE
- for(var/mob/living/carbon/human/T in (touchable_items))
- if((T.status_flags & GODMODE) || !T.digestable)
- items_preserved += T
+ for(var/mob/living/carbon/C in (touchable_items))
+ if((C.status_flags & GODMODE) || !C.digestable)
+ items_preserved += C
else
- T.adjustBruteLoss(2)
- T.adjustFireLoss(3)
- update_gut()
+ C.adjustBruteLoss(2)
+ C.adjustFireLoss(3)
if(contents)
var/atom/target = pick(touchable_items)
if(iscarbon(target)) //Handle the target being a mob
@@ -359,7 +360,6 @@
if(!T.dropItemToGround(W))
qdel(W)
qdel(T)
- update_gut()
//Handle the target being anything but a mob
else if(isobj(target))
var/obj/T = target
@@ -374,12 +374,10 @@
cleaning_cycles = initial(cleaning_cycles)
cleaning = FALSE
to_chat(hound, "Your [src] chimes it ends its self-cleaning cycle.")//Belly is entirely empty
- update_gut()
if(!length(contents))
to_chat(hound, "Your [src] is now clean. Ending self-cleaning cycle.")
cleaning = FALSE
- update_gut()
//sound effects
if(prob(50))
@@ -397,13 +395,17 @@
else if(H in contents)
H.playsound_local(source, null, 65, falloff = 0, S = prey_digest)
+ update_gut(hound)
+
if(cleaning)
- addtimer(CALLBACK(src, .proc/clean_cycle), 50)
+ addtimer(CALLBACK(src, .proc/clean_cycle, hound), 50)
/obj/item/dogborg/sleeper/proc/CheckAccepted(obj/item/I)
return is_type_in_typecache(I, important_items)
-/obj/item/dogborg/sleeper/proc/inject_chem(chem)
+/obj/item/dogborg/sleeper/proc/inject_chem(chem, mob/living/silicon/robot/hound)
+ if(!hound)
+ return
if(hound.cell.charge <= 800) //This is so borgs don't kill themselves with it. Remember, 750 charge used every injection.
to_chat(hound, "You don't have enough power to synthesize fluids.")
return
@@ -438,40 +440,27 @@
else
. = ..()
-/obj/item/dogborg/sleeper/K9/afterattack(var/atom/movable/target, mob/living/silicon/user, proximity)
- hound = loc
+/obj/item/dogborg/sleeper/K9/afterattack(mob/living/carbon/target, mob/living/silicon/user, proximity)
+ var/mob/living/silicon/robot/hound = get_host()
+ if(!hound || !istype(target) || !proximity || target.anchored)
+ return
+ if (!target.devourable)
+ to_chat(user, "The target registers an error code. Unable to insert into [src].")
+ return
+ if(target)
+ to_chat(user,"Your [src] is already occupied.")
+ return
+ if(target.buckled)
+ to_chat(user,"[target] is buckled and can not be put into your [src].")
+ return
+ user.visible_message("[hound.name] is ingesting [target] into their [src].", "You start ingesting [target] into your [src.name]...")
+ if(do_after(user, 30, target = target) && !patient && !target.buckled)
+ target.forceMove(src)
+ target.reset_perspective(src)
+ update_gut(hound)
+ user.visible_message("[hound.name]'s mobile brig clunks in series as [target] slips inside.", "Your mobile brig groans lightly as [target] slips inside.")
+ playsound(hound, 'sound/effects/bin_close.ogg', 80, 1) // Really don't need ERP sound effects for robots
- if(!istype(target))
- return
- if(!proximity)
- return
- if(target.anchored)
- return
- if(isobj(target))
- to_chat(user, "You are above putting such trash inside of yourself.")
- return
- if(iscarbon(target))
- var/mob/living/carbon/brigman = target
- if (!brigman.devourable)
- to_chat(user, "The target registers an error code. Unable to insert into [src].")
- return
- if(patient)
- to_chat(user,"Your [src] is already occupied.")
- return
- if(brigman.buckled)
- to_chat(user,"[brigman] is buckled and can not be put into your [src].")
- return
- user.visible_message("[hound.name] is ingesting [brigman] into their [src].", "You start ingesting [brigman] into your [src.name]...")
- if(do_after(user, 30, target = brigman) && !patient && !brigman.buckled)
- if(!in_range(src, brigman)) //Proximity is probably old news by now, do a new check.
- return //If they moved away, you can't eat them.
- brigman.forceMove(src)
- brigman.reset_perspective(src)
- update_gut()
- user.visible_message("[hound.name]'s mobile brig clunks in series as [brigman] slips inside.", "Your mobile brig groans lightly as [brigman] slips inside.")
- playsound(hound, 'sound/effects/bin_close.ogg', 80, 1) // Really don't need ERP sound effects for robots
- return
- return
/obj/item/dogborg/sleeper/compactor //Janihound gut.
name = "garbage processor"
@@ -489,31 +478,25 @@
else
. = ..()
-/obj/item/dogborg/sleeper/compactor/afterattack(var/atom/movable/target, mob/living/silicon/user, proximity)//GARBO NOMS
- hound = loc
- var/obj/item/target_obj = target
- if(!istype(target))
- return
- if(!proximity)
- return
- if(target.anchored)
+/obj/item/dogborg/sleeper/compactor/afterattack(atom/movable/target, mob/living/silicon/user, proximity)//GARBO NOMS
+ var/mob/living/silicon/robot/hound = get_host()
+ if(!hound || !istype(target) || !proximity || target.anchored)
return
if(length(contents) > (max_item_count - 1))
to_chat(user,"Your [src] is full. Eject or process contents to continue.")
return
- if(isobj(target))
- if(CheckAccepted(target))
- to_chat(user,"\The [target] registers an error code to your [src]")
+ if(isitem(target))
+ var/obj/item/I = target
+ if(CheckAccepted(I))
+ to_chat(user,"[I] registers an error code to your [src]")
return
- if(target_obj.w_class > WEIGHT_CLASS_NORMAL)
- to_chat(user,"\The [target] is too large to fit into your [src]")
+ if(I.w_class > WEIGHT_CLASS_NORMAL)
+ to_chat(user,"[I] is too large to fit into your [src]")
return
- user.visible_message("[hound.name] is ingesting [target.name] into their [src.name].", "You start ingesting [target] into your [src.name]...")
+ user.visible_message("[hound.name] is ingesting [I] into their [src.name].", "You start ingesting [target] into your [src.name]...")
if(do_after(user, 15, target = target) && length(contents) < max_item_count)
- if(!in_range(src, target)) //Proximity is probably old news by now, do a new check.
- return //If they moved away, you can't eat them. This still applies to items, don't magically eat things I picked up already.
- target.forceMove(src)
- user.visible_message("[hound.name]'s garbage processor groans lightly as [target.name] slips inside.", "Your garbage compactor groans lightly as [target] slips inside.")
+ I.forceMove(src)
+ I.visible_message("[hound.name]'s garbage processor groans lightly as [I] slips inside.", "Your garbage compactor groans lightly as [I] slips inside.")
playsound(hound, 'sound/machines/disposalflush.ogg', 50, 1)
if(length(contents) > 11) //grow that tum after a certain junk amount
hound.sleeper_r = 1
@@ -523,9 +506,9 @@
hound.update_icons()
return
- else if(iscarbon(target))
- var/mob/living/carbon/trashman = target
- if (!trashman.devourable)
+ if(iscarbon(target) || issilicon(target))
+ var/mob/living/trashman = target
+ if(!trashman.devourable)
to_chat(user, "[target] registers an error code to your [src]")
return
if(patient)
@@ -536,34 +519,8 @@
return
user.visible_message("[hound.name] is ingesting [trashman] into their [src].", "You start ingesting [trashman] into your [src.name]...")
if(do_after(user, 30, target = trashman) && !patient && !trashman.buckled && length(contents) < max_item_count)
- if(!in_range(src, trashman)) //Proximity is probably old news by now, do a new check.
- return //If they moved away, you can't eat them.
trashman.forceMove(src)
trashman.reset_perspective(src)
update_gut()
user.visible_message("[hound.name]'s garbage processor groans lightly as [trashman] slips inside.", "Your garbage compactor groans lightly as [trashman] slips inside.")
playsound(hound, 'sound/effects/bin_close.ogg', 80, 1)
- return
- else if(issilicon(target))
- var/mob/living/silicon/trashbot = target
- if (!trashbot.devourable)
- to_chat(user, "[target] registers an error code to your [src]")
- return
- if(patient)
- to_chat(user,"Your [src] is already occupied.")
- return
- if(trashbot.buckled)
- to_chat(user,"[trashbot] is buckled and can not be put into your [src].")
- return
- user.visible_message("[hound.name] is ingesting [trashbot] into their [src].", "You start ingesting [trashbot] into your [src.name]...")
- if(do_after(user, 30, target = trashbot) && !patient && !trashbot.buckled && length(contents) < max_item_count)
- if(!in_range(src, trashbot)) //Proximity is probably old news by now, do a new check.
- return //If they moved away, you can't eat them.
- trashbot.forceMove(src)
- trashbot.reset_perspective(src)
- update_gut()
- user.visible_message("[hound.name]'s garbage processor groans lightly as [trashbot] slips inside.", "Your garbage compactor groans lightly as [trashbot] slips inside.")
- playsound(hound, 'sound/effects/bin_close.ogg', 80, 1)
- return
-
- return
diff --git a/code/game/objects/items/implants/implant_storage.dm b/code/game/objects/items/implants/implant_storage.dm
index 1f44b5318e..1dfc0b4580 100644
--- a/code/game/objects/items/implants/implant_storage.dm
+++ b/code/game/objects/items/implants/implant_storage.dm
@@ -12,7 +12,7 @@
/obj/item/implant/storage/removed(source, silent = FALSE, special = 0)
if(!special)
- qdel(pocket)
+ QDEL_NULL(pocket)
else
pocket?.moveToNullspace()
return ..()
@@ -29,7 +29,7 @@
return FALSE
. = ..()
if(.)
- if(pocket)
+ if(!QDELETED(pocket))
pocket.forceMove(target)
else
pocket = new(target)
@@ -41,7 +41,6 @@
desc = "A tiny yet spacious pocket, usually found implanted inside sneaky syndicate agents and nowhere else."
component_type = /datum/component/storage/concrete/implant
resistance_flags = INDESTRUCTIBLE //A bomb!
- item_flags = DROPDEL
/obj/item/implanter/storage
name = "implanter (storage)"
diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm
index d6effe0727..77bc7ed810 100644
--- a/code/game/objects/items/robot/robot_items.dm
+++ b/code/game/objects/items/robot/robot_items.dm
@@ -430,7 +430,7 @@
A.BB.nodamage = FALSE
A.BB.speed = 0.5
playsound(src.loc, 'sound/machines/click.ogg', 50, 1)
- A.fire_casing(target, user, params, 0, 0, null, 0)
+ A.fire_casing(target, user, params, 0, 0, null, 0, src)
user.visible_message("[user] blasts a flying lollipop at [target]!")
check_amount()
@@ -446,7 +446,7 @@
A.BB.speed = 0.5
A.BB.color = rgb(rand(0, 255), rand(0, 255), rand(0, 255))
playsound(src.loc, 'sound/weapons/bulletflyby3.ogg', 50, 1)
- A.fire_casing(target, user, params, 0, 0, null, 0)
+ A.fire_casing(target, user, params, 0, 0, null, 0, src)
user.visible_message("[user] shoots a high-velocity gumball at [target]!")
check_amount()
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index 7c73a1fd8c..12529fb9e0 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -300,18 +300,9 @@
throw_speed = 2
throw_range = 4
-
-/obj/item/twohanded/required/kirbyplants/equipped(mob/living/user)
- var/image/I = image(icon = 'icons/obj/flora/plants.dmi' , icon_state = src.icon_state, loc = user)
- I.copy_overlays(src)
- I.override = 1
- add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, "sneaking_mission", I)
- I.layer = ABOVE_MOB_LAYER
- ..()
-
-/obj/item/twohanded/required/kirbyplants/dropped(mob/living/user)
- ..()
- user.remove_alt_appearance("sneaking_mission")
+/obj/item/twohanded/required/kirbyplants/Initialize()
+ . = ..()
+ AddComponent(/datum/component/tactical)
/obj/item/twohanded/required/kirbyplants/random
icon = 'icons/obj/flora/_flora.dmi'
diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm
index cff7faadd8..eae59fcb93 100644
--- a/code/modules/admin/create_mob.dm
+++ b/code/modules/admin/create_mob.dm
@@ -25,6 +25,9 @@
H.facial_hair_color = H.hair_color
H.eye_color = random_eye_color()
H.dna.blood_type = random_blood_type()
+ H.saved_underwear = H.underwear
+ H.saved_undershirt = H.undershirt
+ H.saved_socks = H.socks
// Mutant randomizing, doesn't affect the mob appearance unless it's the specific mutant.
H.dna.features["mcolor"] = random_short_color()
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 47c3293c35..edec4d74a6 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -113,7 +113,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
"xenohead" = "Standard",
"xenotail" = "Xenomorph Tail",
"taur" = "None",
- "exhibitionist" = FALSE,
"genitals_use_skintone" = FALSE,
"has_cock" = FALSE,
"cock_shape" = "Human",
@@ -826,7 +825,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat +="
"
dat += "Citadel Preferences" //Because fuck me if preferences can't be fucking modularized and expected to update in a reasonable timeframe.
dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"] "
- dat += "Exhibitionist:[features["exhibitionist"] == TRUE ? "Yes" : "No"] "
dat += "Voracious MediHound sleepers: [(cit_toggles & MEDIHOUND_SLEEPER) ? "Yes" : "No"] "
dat += "Hear Vore Sounds: [(cit_toggles & EATING_NOISES) ? "Yes" : "No"] "
dat += "Hear Vore Digestion Sounds: [(cit_toggles & DIGESTION_NOISES) ? "Yes" : "No"] "
@@ -2057,8 +2055,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["has_womb"] = FALSE
if("has_womb")
features["has_womb"] = !features["has_womb"]
- if("exhibitionist")
- features["exhibitionist"] = !features["exhibitionist"]
if("widescreenpref")
widescreenpref = !widescreenpref
user.client.change_view(CONFIG_GET(string/default_view))
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 01a46a44a0..98ef2ed0e8 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -5,7 +5,7 @@
// You do not need to raise this if you are adding new values that have sane defaults.
// Only raise this value when changing the meaning/format/name/layout of an existing value
// where you would want the updater procs below to run
-#define SAVEFILE_VERSION_MAX 23
+#define SAVEFILE_VERSION_MAX 24
/*
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
@@ -109,6 +109,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
else if(current_version < 23) // we are fixing a gamebreaking bug.
job_preferences = list() //It loaded null from nonexistant savefile field.
+ if(current_version < 24 && S["feature_exhibitionist"])
+ var/datum/quirk/exhibitionism/E
+ var/quirk_name = initial(E.name)
+ neutral_quirks += quirk_name
+ all_quirks += quirk_name
+
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
if(!ckey)
return
diff --git a/code/modules/clothing/head/vg_hats.dm b/code/modules/clothing/head/vg_hats.dm
index dc245cd39a..87f64baf13 100644
--- a/code/modules/clothing/head/vg_hats.dm
+++ b/code/modules/clothing/head/vg_hats.dm
@@ -82,13 +82,6 @@
item_state = "nr_helmet"
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-/obj/item/clothing/head/stalhelm
- name = "Stalhelm"
- desc = "Ein Helm, um die Nazi-Interesse an fremden Raumstationen zu sichern."
- icon_state = "stalhelm"
- item_state = "stalhelm"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
/obj/item/clothing/head/panzer
name = "Panzer Cap"
desc = "Command any mech in style."
@@ -96,13 +89,6 @@
item_state = "panzercap"
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-/obj/item/clothing/head/naziofficer
- name = "Officer Cap"
- desc = "Style is all that matters."
- icon_state = "officercap"
- item_state = "officercap"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
/obj/item/clothing/head/russobluecamohat
name = "russian blue camo beret"
desc = "A symbol of discipline, honor, and lots and lots of removal of some type of skewered food."
diff --git a/code/modules/clothing/spacesuits/vg_spess.dm b/code/modules/clothing/spacesuits/vg_spess.dm
index e6b1c7a1ee..517539f3af 100644
--- a/code/modules/clothing/spacesuits/vg_spess.dm
+++ b/code/modules/clothing/spacesuits/vg_spess.dm
@@ -1,24 +1,5 @@
//VG Ports
-/obj/item/clothing/head/helmet/space/hardsuit/nazi
- name = "nazi hardhelmet"
- desc = "This is the face of das vaterland's top elite. Gas or energy are your only escapes."
- item_state = "hardsuit0-nazi"
- icon_state = "hardsuit0-nazi"
- armor = list(melee = 40, bullet = 30, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 20)
- item_color = "nazi"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/suit/space/hardsuit/nazi
- name = "nazi hardsuit"
- desc = "The attire of a true krieger. All shall fall, and only das vaterland will remain."
- item_state = "hardsuit-nazi"
- icon_state = "hardsuit-nazi"
- slowdown = 1
- armor = list(melee = 40, bullet = 30, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 20)
- allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/)
- helmettype = /obj/item/clothing/head/helmet/space/hardsuit/nazi
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
/obj/item/clothing/head/helmet/space/hardsuit/soviet
name = "soviet hardhelmet"
diff --git a/code/modules/events/wizard/rpgloot.dm b/code/modules/events/wizard/rpgloot.dm
index 3d560aefb8..bf3104450e 100644
--- a/code/modules/events/wizard/rpgloot.dm
+++ b/code/modules/events/wizard/rpgloot.dm
@@ -8,8 +8,11 @@
/datum/round_event/wizard/rpgloot/start()
var/upgrade_scroll_chance = 0
for(var/obj/item/I in world)
- if(!istype(I.rpg_loot))
- I.rpg_loot = new(I)
+ CHECK_TICK
+ if(!(I.flags_1 & INITIALIZED_1))
+ continue
+
+ I.AddComponent(/datum/component/fantasy)
if(istype(I, /obj/item/storage))
var/obj/item/storage/S = I
@@ -31,86 +34,20 @@
var/upgrade_amount = 1
var/can_backfire = TRUE
- var/one_use = TRUE
+ var/uses = 1
/obj/item/upgradescroll/afterattack(obj/item/target, mob/user , proximity)
. = ..()
if(!proximity || !istype(target))
return
- var/datum/rpg_loot/rpg_loot_datum = target.rpg_loot
- if(!istype(rpg_loot_datum))
- target.rpg_loot = rpg_loot_datum = new /datum/rpg_loot(target)
+ target.AddComponent(/datum/component/fantasy, upgrade_amount, null, null, can_backfire, TRUE)
- var/quality = rpg_loot_datum.quality
-
- if(can_backfire && (quality > 9 && prob((quality - 9)*10)))
- to_chat(user, "[target] violently glows blue for a while, then evaporates.")
- target.burn()
- else
- to_chat(user, "[target] glows blue and seems vaguely \"better\"!")
- rpg_loot_datum.modify(upgrade_amount)
-
- if(one_use)
+ if(--uses <= 0)
qdel(src)
/obj/item/upgradescroll/unlimited
name = "unlimited foolproof item fortification scroll"
desc = "Somehow, this piece of paper can be applied to items to make them \"better\". This scroll is made from the tongues of dead paper wizards, and can be used an unlimited number of times, with no drawbacks."
- one_use = FALSE
+ uses = INFINITY
can_backfire = FALSE
-
-/datum/rpg_loot
- var/positive_prefix = "okay"
- var/negative_prefix = "weak"
- var/suffix = "something profound"
- var/quality = 0
-
- var/obj/item/attached
- var/original_name
-
-/datum/rpg_loot/New(attached_item=null)
- attached = attached_item
-
- randomise()
-
-/datum/rpg_loot/Destroy()
- attached = null
-
-/datum/rpg_loot/proc/randomise()
- var/static/list/prefixespositive = list("greater", "major", "blessed", "superior", "empowered", "honed", "true", "glorious", "robust")
- var/static/list/prefixesnegative = list("lesser", "minor", "blighted", "inferior", "enfeebled", "rusted", "unsteady", "tragic", "gimped")
- var/static/list/suffixes = list("orc slaying", "elf slaying", "corgi slaying", "strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma", "the forest", "the hills", "the plains", "the sea", "the sun", "the moon", "the void", "the world", "the fool", "many secrets", "many tales", "many colors", "rending", "sundering", "the night", "the day")
-
- var/new_quality = pick(1;15, 2;14, 2;13, 2;12, 3;11, 3;10, 3;9, 4;8, 4;7, 4;6, 5;5, 5;4, 5;3, 6;2, 6;1, 6;0)
-
- suffix = pick(suffixes)
- positive_prefix = pick(prefixespositive)
- negative_prefix = pick(prefixesnegative)
-
- if(prob(50))
- new_quality = -new_quality
-
- modify(new_quality)
-
-/datum/rpg_loot/proc/rename()
- var/obj/item/I = attached
- if(!original_name)
- original_name = I.name
- if(quality < 0)
- I.name = "[negative_prefix] [original_name] of [suffix] [quality]"
- else if(quality == 0)
- I.name = "[original_name] of [suffix]"
- else if(quality > 0)
- I.name = "[positive_prefix] [original_name] of [suffix] +[quality]"
-
-/datum/rpg_loot/proc/modify(quality_mod)
- var/obj/item/I = attached
- quality += quality_mod
-
- I.force = max(0,I.force + quality_mod)
- I.throwforce = max(0,I.throwforce + quality_mod)
-
- I.armor = I.armor.modifyAllRatings(quality)
-
- rename()
diff --git a/code/modules/holiday/halloween/jacqueen.dm b/code/modules/holiday/halloween/jacqueen.dm
index ed9254c295..f67c099309 100644
--- a/code/modules/holiday/halloween/jacqueen.dm
+++ b/code/modules/holiday/halloween/jacqueen.dm
@@ -95,45 +95,34 @@
last_poof = world.realtime
var/datum/reagents/R = new/datum/reagents(100)//Hey, just in case.
var/datum/effect_system/smoke_spread/chem/s = new()
- R.add_reagent("secretcatchem", (10))
+ R.add_reagent("secretcatchem", 10)
s.set_up(R, 0, loc)
s.start()
visible_message("[src] disappears in a puff of smoke!")
canmove = TRUE
health = 25
- var/hp_list = list()
- for(var/obj/machinery/holopad/hp in world)
- hp_list += hp
+ //Try to go to populated areas
+ var/list/pop_areas = list()
+ for(var/mob/living/L in GLOB.player_list)
+ var/area/A = get_area(L)
+ pop_areas += A
- var/nono_areas = list("AI ")
-
- for(var/i = 0, i <= 6, i+=1) //Attempts a jump 6 times.
- var/obj/machinery/holopad/hp = pick(hp_list)
- if(forceMove(pick(hp.loc)))
-
- var/jacq_please_no = FALSE
- for(var/no_area in nono_areas)
- var/turf/L1 = hp.loc
- if(!L1) //Incase the area isn't a turf (i.e. in a locker)
- continue
- var/area/L2 = L1.loc
- if(L2)
- if(findtext(L2.name, no_area))
- jacq_please_no = TRUE
-
- if(jacq_please_no)
- i-=1
- continue
-
- //Try to go to populated areas
- var/list/seen = viewers(8, get_turf(src))
- for(var/victim in seen)
- if(ishuman(victim))
- if(z == cached_z)
- return TRUE
+ var/list/targets = list()
+ for(var/H in GLOB.network_holopads)
+ var/area/A = get_area(H)
+ if(findtextEx(A, "AI") || !(A in pop_areas) || !is_station_level(H))
+ continue
+ targets += H
+ if(!targets)
+ targets = GLOB.generic_event_spawns
+ for(var/i in 1 to 6) //Attempts a jump up to 6 times.
+ var/atom/A = pick(targets)
+ if(do_teleport(src, A, channel = TELEPORT_CHANNEL_MAGIC))
+ return TRUE
+ targets -= A
return FALSE
/mob/living/simple_animal/jacq/proc/gender_check(mob/living/carbon/C)
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index d35df6b789..084dbd83ef 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -79,7 +79,8 @@
//This is an UNSAFE proc. Use mob_can_equip() before calling this one! Or rather use equip_to_slot_if_possible() or advanced_equip_to_slot_if_possible()
/mob/living/carbon/human/equip_to_slot(obj/item/I, slot)
- if(!..()) //a check failed or the item has already found its slot
+ . = ..()
+ if(!.) //a check failed or the item has already found its slot
return
var/not_handled = FALSE //Added in case we make this type path deeper one day
@@ -136,6 +137,7 @@
update_inv_s_store()
else
to_chat(src, "You are trying to equip this item to an unsupported inventory slot. Report this to a coder!")
+ not_handled = TRUE
//Item is handled and in slot, valid to call callback, for this proc should always be true
if(!not_handled)
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 9395283aeb..be52a8289b 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -18,7 +18,7 @@
#define THERMAL_PROTECTION_HAND_LEFT 0.025
#define THERMAL_PROTECTION_HAND_RIGHT 0.025
-/mob/living/carbon/human/Life()
+/mob/living/carbon/human/Life(seconds, times_fired)
set invisibility = 0
if (notransform)
return
@@ -41,7 +41,7 @@
if(stat != DEAD)
//process your dick energy
- handle_arousal()
+ handle_arousal(times_fired)
//Update our name based on whether our face is obscured/disfigured
name = get_visible_name()
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 1f67f2b13c..025e933356 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -545,7 +545,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(H.hidden_underwear)
H.underwear = "Nude"
else
- H.saved_underwear = H.underwear
+ H.underwear = H.saved_underwear
var/datum/sprite_accessory/underwear/bottom/B = GLOB.underwear_list[H.underwear]
if(B)
var/mutable_appearance/MA = mutable_appearance(B.icon, B.icon_state, -BODY_LAYER)
@@ -557,7 +557,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(H.hidden_undershirt)
H.undershirt = "Nude"
else
- H.saved_undershirt = H.undershirt
+ H.undershirt = H.saved_undershirt
var/datum/sprite_accessory/underwear/top/T = GLOB.undershirt_list[H.undershirt]
if(T)
var/mutable_appearance/MA
@@ -573,7 +573,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(H.hidden_socks)
H.socks = "Nude"
else
- H.saved_socks = H.socks
+ H.socks = H.saved_socks
var/datum/sprite_accessory/underwear/socks/S = GLOB.socks_list[H.socks]
if(S)
var/digilegs = (DIGITIGRADE in species_traits) ? "_d" : ""
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index 79ebdd1a68..71eaa5ae22 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -472,37 +472,25 @@
H.hair_style = new_style
H.update_hair()
else if (select_alteration == "Genitals")
- var/list/organs = list()
var/operation = input("Select organ operation.", "Organ Manipulation", "cancel") in list("add sexual organ", "remove sexual organ", "cancel")
switch(operation)
if("add sexual organ")
- var/new_organ = input("Select sexual organ:", "Organ Manipulation") in list("Penis", "Testicles", "Breasts", "Vagina", "Womb", "Cancel")
- if(new_organ == "Penis")
- H.give_penis()
- else if(new_organ == "Testicles")
- H.give_balls()
- else if(new_organ == "Breasts")
- H.give_breasts()
- else if(new_organ == "Vagina")
- H.give_vagina()
- else if(new_organ == "Womb")
- H.give_womb()
- else
+ var/new_organ = input("Select sexual organ:", "Organ Manipulation") as null|anything in GLOB.genitals_list
+ if(!new_organ)
return
+ H.give_genital(GLOB.genitals_list[new_organ])
+
if("remove sexual organ")
+ var/list/organs = list()
for(var/obj/item/organ/genital/X in H.internal_organs)
var/obj/item/organ/I = X
organs["[I.name] ([I.type])"] = I
- var/obj/item/organ = input("Select sexual organ:", "Organ Manipulation", null) in organs
- organ = organs[organ]
- if(!organ)
+ var/obj/item/O = input("Select sexual organ:", "Organ Manipulation", null) as null|anything in organs
+ var/obj/item/organ/genital/G = organs[O]
+ if(!G)
return
- var/obj/item/organ/genital/O
- if(isorgan(organ))
- O = organ
- O.Remove(H)
- organ.forceMove(get_turf(H))
- qdel(organ)
+ G.forceMove(get_turf(H))
+ qdel(G)
H.update_genitals()
else if (select_alteration == "Ears")
@@ -592,8 +580,8 @@
if(new_shape)
H.dna.features["cock_shape"] = new_shape
H.update_genitals()
- H.give_balls()
- H.give_penis()
+ H.give_genital(/obj/item/organ/genital/testicles)
+ H.give_genital(/obj/item/organ/genital/penis)
H.apply_overlay()
@@ -605,8 +593,8 @@
if(new_shape)
H.dna.features["vag_shape"] = new_shape
H.update_genitals()
- H.give_womb()
- H.give_vagina()
+ H.give_genital(/obj/item/organ/genital/womb)
+ H.give_genital(/obj/item/organ/genital/vagina)
H.apply_overlay()
else if (select_alteration == "Penis Length")
@@ -618,8 +606,8 @@
H.dna.features["cock_length"] = max(min( round(text2num(new_length)), COCK_SIZE_MAX),COCK_SIZE_MIN)
H.update_genitals()
H.apply_overlay()
- H.give_balls()
- H.give_penis()
+ H.give_genital(/obj/item/organ/genital/testicles)
+ H.give_genital(/obj/item/organ/genital/penis)
else if (select_alteration == "Breast Size")
for(var/obj/item/organ/genital/breasts/X in H.internal_organs)
@@ -630,7 +618,7 @@
H.dna.features["breasts_size"] = new_size
H.update_genitals()
H.apply_overlay()
- H.give_breasts()
+ H.give_genital(/obj/item/organ/genital/breasts)
else if (select_alteration == "Breast Shape")
for(var/obj/item/organ/genital/breasts/X in H.internal_organs)
@@ -641,7 +629,7 @@
H.dna.features["breasts_shape"] = new_shape
H.update_genitals()
H.apply_overlay()
- H.give_breasts()
+ H.give_genital(/obj/item/organ/genital/breasts)
else
return
diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm
index a1dce4fb0f..c50677c54c 100644
--- a/code/modules/mob/living/carbon/human/species_types/zombies.dm
+++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm
@@ -66,7 +66,7 @@
playsound(C, pick(spooks), 50, TRUE, 10)
//Congrats you somehow died so hard you stopped being a zombie
-/datum/species/zombie/infectious/spec_death(mob/living/carbon/C)
+/datum/species/zombie/infectious/spec_death(gibbed, mob/living/carbon/C)
. = ..()
var/obj/item/organ/zombie_infection/infection
infection = C.getorganslot(ORGAN_SLOT_ZOMBIE)
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 5b18e95235..ca139cace7 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -98,7 +98,7 @@
var/datum/gas_mixture/breath
if(!getorganslot(ORGAN_SLOT_BREATHING_TUBE))
- if(health <= HEALTH_THRESHOLD_FULLCRIT || (pulledby && pulledby.grab_state >= GRAB_KILL) || lungs.organ_flags & ORGAN_FAILING)
+ if(health <= HEALTH_THRESHOLD_FULLCRIT || (pulledby && pulledby.grab_state >= GRAB_KILL) || !lungs || lungs.organ_flags & ORGAN_FAILING)
losebreath++ //You can't breath at all when in critical or when being choked, so you're going to miss a breath
else if(health <= crit_threshold)
diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
index 5d1db8d35e..6866df01d5 100644
--- a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
+++ b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
@@ -2,7 +2,7 @@
#define GORILLA_TOTAL_LAYERS 1
/mob/living/simple_animal/hostile/gorilla
- name = "Gorilla"
+ name = "gorilla"
desc = "A ground-dwelling, predominantly herbivorous ape that inhabits the forests of central Africa."
icon = 'icons/mob/gorilla.dmi'
icon_state = "crawling"
@@ -108,3 +108,10 @@
playsound(src, 'sound/creatures/gorilla.ogg', 200)
oogas = 0
+/mob/living/simple_animal/hostile/gorilla/familiar
+ name = "familiar gorilla"
+ desc = "There is no need to be upset."
+ unique_name = FALSE
+ AIStatus = AI_OFF
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index 368c5ad4a8..9cd0504315 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -344,6 +344,7 @@
/mob/living/simple_animal/hostile/proc/AttackingTarget()
+ SEND_SIGNAL(src, COMSIG_HOSTILE_ATTACKINGTARGET, target)
in_melee = TRUE
if(vore_active)
if(isliving(target))
@@ -426,12 +427,13 @@
if(casingtype)
var/obj/item/ammo_casing/casing = new casingtype(startloc)
playsound(src, projectilesound, 100, 1)
- casing.fire_casing(targeted_atom, src, null, null, null, ran_zone())
+ casing.fire_casing(targeted_atom, src, null, null, null, ran_zone(), src)
else if(projectiletype)
var/obj/item/projectile/P = new projectiletype(startloc)
playsound(src, projectilesound, 100, 1)
P.starting = startloc
P.firer = src
+ P.fired_from = src
P.yo = targeted_atom.y - startloc.y
P.xo = targeted_atom.x - startloc.x
if(AIStatus != AI_ON)//Don't want mindless mobs to have their movement screwed up firing in space
diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm
index 4d6ede69d1..71a8565d26 100644
--- a/code/modules/power/singularity/emitter.dm
+++ b/code/modules/power/singularity/emitter.dm
@@ -199,6 +199,7 @@
if(prob(35))
sparks.start()
P.firer = user ? user : src
+ P.fired_from = src
if(last_projectile_params)
P.p_x = last_projectile_params[2]
P.p_y = last_projectile_params[3]
diff --git a/code/modules/projectiles/ammunition/_firing.dm b/code/modules/projectiles/ammunition/_firing.dm
index 441088c78c..a83042c90a 100644
--- a/code/modules/projectiles/ammunition/_firing.dm
+++ b/code/modules/projectiles/ammunition/_firing.dm
@@ -1,8 +1,8 @@
-/obj/item/ammo_casing/proc/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread)
+/obj/item/ammo_casing/proc/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from)
distro += variance
for (var/i = max(1, pellets), i > 0, i--)
var/targloc = get_turf(target)
- ready_proj(target, user, quiet, zone_override)
+ ready_proj(target, user, quiet, zone_override, fired_from)
if(distro) //We have to spread a pixel-precision bullet. throw_proj was called before so angles should exist by now...
if(randomspread)
spread = round((rand() - 0.5) * distro)
@@ -20,11 +20,12 @@
update_icon()
return 1
-/obj/item/ammo_casing/proc/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
+/obj/item/ammo_casing/proc/ready_proj(atom/target, mob/living/user, quiet, zone_override = "", fired_from)
if (!BB)
return
BB.original = target
BB.firer = user
+ BB.fired_from = fired_from
if (zone_override)
BB.def_zone = zone_override
else
diff --git a/code/modules/projectiles/ammunition/caseless/_caseless.dm b/code/modules/projectiles/ammunition/caseless/_caseless.dm
index a6b65f79e3..11f7b8670d 100644
--- a/code/modules/projectiles/ammunition/caseless/_caseless.dm
+++ b/code/modules/projectiles/ammunition/caseless/_caseless.dm
@@ -3,7 +3,7 @@
firing_effect_type = null
heavy_metal = FALSE
-/obj/item/ammo_casing/caseless/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread)
+/obj/item/ammo_casing/caseless/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from)
if (..()) //successfully firing
moveToNullspace()
QDEL_NULL(src)
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 4004bc81ae..6694d42dce 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -233,7 +233,7 @@
else //Smart spread
sprd = round((((rand_spr/burst_size) * iteration) - (0.5 + (rand_spr * 0.25))) * (randomized_gun_spread + randomized_bonus_spread), 1)
- if(!chambered.fire_casing(target, user, params, ,suppressed, zone_override, sprd))
+ if(!chambered.fire_casing(target, user, params, ,suppressed, zone_override, sprd, src))
shoot_with_empty_chamber(user)
firing_burst = FALSE
return FALSE
@@ -280,7 +280,7 @@
to_chat(user, " [src] is lethally chambered! You don't want to risk harming anyone...")
return
sprd = round((rand() - 0.5) * DUALWIELD_PENALTY_EXTRA_MULTIPLIER * (randomized_gun_spread + randomized_bonus_spread))
- if(!chambered.fire_casing(target, user, params, , suppressed, zone_override, sprd))
+ if(!chambered.fire_casing(target, user, params, , suppressed, zone_override, sprd, src))
shoot_with_empty_chamber(user)
return
else
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index df8eba00ed..646b4bd57d 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -331,9 +331,9 @@
uninstall(KA)
/obj/item/borg/upgrade/modkit/proc/uninstall(obj/item/gun/energy/kinetic_accelerator/KA, forcemove = TRUE)
+ KA.modkits -= src
if(forcemove)
forceMove(get_turf(KA))
- KA.modkits -= src
/obj/item/borg/upgrade/modkit/proc/modify_projectile(obj/item/projectile/kinetic/K)
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index dca7a6087c..e1122f7eaa 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -17,6 +17,7 @@
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/def_zone = "" //Aiming at
var/atom/movable/firer = null//Who shot it
+ var/atom/fired_from = null // the atom that the projectile was fired from (gun, turret)
var/suppressed = FALSE //Attack message
var/candink = FALSE //Can this projectile play the dink sound when hitting the head?
var/yo = null
@@ -131,6 +132,8 @@
return TRUE
/obj/item/projectile/proc/on_hit(atom/target, blocked = FALSE)
+ if(fired_from)
+ SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_ON_HIT, firer, target, Angle)
var/turf/target_loca = get_turf(target)
var/hitx
@@ -356,6 +359,8 @@
pixel_move(1, FALSE)
/obj/item/projectile/proc/fire(angle, atom/direct_target)
+ if(fired_from)
+ SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_BEFORE_FIRE, src, original)
//If no angle needs to resolve it from xo/yo!
if(!log_override && firer && original)
log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]")
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 40d151e021..af6a65ebe1 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -2265,24 +2265,6 @@
M.emote("nya")
..()
-//Kept for legacy, I think it will break everything if you enable it.
-/datum/reagent/penis_enlargement
- name = "Penis Enlargement"
- id = "penis_enlargement"
- description = "A patented chemical forumula by Doctor Ronald Hyatt that is guaranteed to bring maximum GROWTH and LENGTH to your penis, today!"
- color = "#888888"
- taste_description = "chinese dragon powder"
- metabolization_rate = INFINITY //So it instantly removes all of itself. Don't want to put strain on the system.
-
-/datum/reagent/penis_enlargement/on_mob_life(mob/living/carbon/C)
- var/obj/item/organ/genital/penis/P = C.getorganslot(ORGAN_SLOT_PENIS)
- if(P)
- var/added_length = round(volume/30,0.01) //Every 30u gives an extra inch. Rounded to the nearest 0.01 so float fuckery doesn't occur with the division by 30.
- if(added_length >= 0.20) //Only add the length if it's greater than or equal to 0.2. This is to prevent people from smoking the reagents and causing the penis to update constantly.
- P.length += added_length
- P.update()
- ..()
-
/datum/reagent/changeling_string
name = "UNKNOWN"
id = "changeling_sting_real"
diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm
index 95258c709a..faaa1c63ad 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -338,7 +338,7 @@
/obj/item/organ/eyes/robotic/glow/proc/start_visuals()
if(!islist(eye_lighting))
regenerate_light_effects()
- if((eye_lighting.len < light_beam_distance) || !on_mob)
+ if((LAZYLEN(eye_lighting) < light_beam_distance) || !on_mob)
regenerate_light_effects()
sync_light_effects()
update_visuals()
diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm
index 593614372a..aaea914e73 100644
--- a/code/modules/surgery/organs/organ_internal.dm
+++ b/code/modules/surgery/organs/organ_internal.dm
@@ -31,7 +31,7 @@
/obj/item/organ/proc/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
if(!iscarbon(M) || owner == M)
- return
+ return FALSE
var/obj/item/organ/replaced = M.getorganslot(slot)
if(replaced)
@@ -53,6 +53,8 @@
A.Grant(M)
STOP_PROCESSING(SSobj, src)
+ return TRUE
+
//Special is for instant replacement like autosurgeons
/obj/item/organ/proc/Remove(mob/living/carbon/M, special = FALSE)
owner = null
@@ -67,6 +69,8 @@
A.Remove(M)
START_PROCESSING(SSobj, src)
+ return TRUE
+
/obj/item/organ/proc/on_find(mob/living/finder)
return
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index e81cbf528f..a920ea765a 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -92,7 +92,7 @@
return TRUE
if(iscyborg(user) && user.a_intent != INTENT_HARM) //to save asimov borgs a LOT of heartache
return TRUE
- if(tool.item_flags & SURGICAL_TOOL) //Just because you used the wrong tool it doesn't mean you meant to whack the patient with it
+ if(tool && tool.item_flags & SURGICAL_TOOL) //Just because you used the wrong tool it doesn't mean you meant to whack the patient with it
to_chat(user, "This step requires a different tool!")
return TRUE
diff --git a/code/modules/vore/eating/belly_obj.dm b/code/modules/vore/eating/belly_obj.dm
index 741aff5f9a..5c2b45dace 100644
--- a/code/modules/vore/eating/belly_obj.dm
+++ b/code/modules/vore/eating/belly_obj.dm
@@ -158,7 +158,7 @@
/obj/belly/Destroy()
SSbellies.belly_list -= src
- if(owner)
+ if(owner?.vore_organs)
owner.vore_organs -= src
owner = null
. = ..()
diff --git a/config/game_options.txt b/config/game_options.txt
index 0843e7c3d1..c06845cde3 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -258,6 +258,12 @@ EVENTS_MIN_PLAYERS_MUL 1
### DYNAMIC MODE ###
+## Injection delays: how long (in minutes) will pass before a midround or latejoin antag is injected.
+DYNAMIC_MIDROUND_DELAY_MIN 15
+DYNAMIC_MIDROUND_DELAY_MAX 35
+DYNAMIC_LATEJOIN_DELAY_MIN 5
+DYNAMIC_LATEJOIN_DELAY_MAX 25
+
## How many roundstart players required for high population override to take effect.
DYNAMIC_HIGH_POP_LIMIT 55
diff --git a/html/changelogs/AutoChangeLog-pr-8948.yml b/html/changelogs/AutoChangeLog-pr-8948.yml
new file mode 100644
index 0000000000..af3d28eb34
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8948.yml
@@ -0,0 +1,9 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - code_imp: "Cleaned up the absolute state of the arousal module."
+ - refactor: "refactored exhibitionism into a quirk."
+ - tweak: "arousal states won't persist after death."
+ - bugfix: "Fixes testicles size adjective thing."
+ - bugfix: "undergarments toggling now works instead of just making underwear disappear and not come back."
+ - tweak: "The \"Always visible\" genitals setting will now display them above clothes."
diff --git a/html/changelogs/AutoChangeLog-pr-9654.yml b/html/changelogs/AutoChangeLog-pr-9654.yml
new file mode 100644
index 0000000000..420697aec5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-9654.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - config: "Added dynamic midround/latejoin antag injection to the config."
diff --git a/html/changelogs/AutoChangeLog-pr-9658.yml b/html/changelogs/AutoChangeLog-pr-9658.yml
new file mode 100644
index 0000000000..199f847c4e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-9658.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed dogborg sleepers. Just don't tell me what is exactly fixed, cause I don't want to find out."
diff --git a/html/changelogs/AutoChangeLog-pr-9659.yml b/html/changelogs/AutoChangeLog-pr-9659.yml
new file mode 100644
index 0000000000..2590aec672
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-9659.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Buffed the deep space familiar gorilla against runtimes."
diff --git a/html/changelogs/AutoChangeLog-pr-9668.yml b/html/changelogs/AutoChangeLog-pr-9668.yml
new file mode 100644
index 0000000000..b892f7c3ea
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-9668.yml
@@ -0,0 +1,5 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - balance: "Made starlight condensation not kill slime people."
+ - balance: "Added not-killing-slime-people to the transmission threshold of plasma fixation and radioactive resonance."
diff --git a/html/changelogs/AutoChangeLog-pr-9674.yml b/html/changelogs/AutoChangeLog-pr-9674.yml
new file mode 100644
index 0000000000..9ca51c9b0e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-9674.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - imageadd: "Updated ratvarian computer sprites."
diff --git a/html/changelogs/AutoChangeLog-pr-9675.yml b/html/changelogs/AutoChangeLog-pr-9675.yml
new file mode 100644
index 0000000000..35d9110f7b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-9675.yml
@@ -0,0 +1,4 @@
+author: "PersianXerxes"
+delete-after: True
+changes:
+ - tweak: "Relocates cult catwalks outside the Reebe dressing room."
diff --git a/html/changelogs/AutoChangeLog-pr-9677.yml b/html/changelogs/AutoChangeLog-pr-9677.yml
new file mode 100644
index 0000000000..8b8608ff29
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-9677.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - tweak: "Made the clone scanner lock while it's scanning."
diff --git a/html/changelogs/AutoChangeLog-pr-9679.yml b/html/changelogs/AutoChangeLog-pr-9679.yml
new file mode 100644
index 0000000000..b2b3a718c4
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-9679.yml
@@ -0,0 +1,5 @@
+author: "ninjanomnom and WhoneedSpacee"
+delete-after: True
+changes:
+ - rscadd: "Some rpg affixes now have special effects"
+ - rscadd: "New RPGLoot modifiers: Vampirism which heals you when you attack, Pyromantic which sets things you hit on fire. Shrapnel which causes projectiles fired from a gun to fire projectiles in a radius when they hit something. Finally, Summoning which summons mobs that sometimes aid you in combat."
diff --git a/html/changelogs/AutoChangeLog-pr-9686.yml b/html/changelogs/AutoChangeLog-pr-9686.yml
new file mode 100644
index 0000000000..18e411c3dc
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-9686.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed storage implant transplant."
diff --git a/html/changelogs/AutoChangeLog-pr-9687.yml b/html/changelogs/AutoChangeLog-pr-9687.yml
new file mode 100644
index 0000000000..3bc1644806
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-9687.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Refactored how Jacqueen teleportation destination is selected, preventing them from teleporting on off-station holopads."
diff --git a/icons/obj/citvending.dmi b/icons/obj/citvending.dmi
index 37c2c8fcf1..8f836b7eb5 100644
Binary files a/icons/obj/citvending.dmi and b/icons/obj/citvending.dmi differ
diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi
index d22002935f..74ca148039 100644
Binary files a/icons/obj/computer.dmi and b/icons/obj/computer.dmi differ
diff --git a/icons/obj/machines/mining_machines.dmi b/icons/obj/machines/mining_machines.dmi
index 28d608125d..aa60e7100c 100644
Binary files a/icons/obj/machines/mining_machines.dmi and b/icons/obj/machines/mining_machines.dmi differ
diff --git a/modular_citadel/code/datums/status_effects/chems.dm b/modular_citadel/code/datums/status_effects/chems.dm
index 4c01725120..abf68cc398 100644
--- a/modular_citadel/code/datums/status_effects/chems.dm
+++ b/modular_citadel/code/datums/status_effects/chems.dm
@@ -37,64 +37,62 @@
alert_type = null
var/moveCalc = 1
var/cachedmoveCalc = 1
+ var/last_checked_size //used to prevent potential cpu waste from happening every tick.
-/datum/status_effect/chem/breast_enlarger/on_apply(mob/living/carbon/human/H)//Removes clothes, they're too small to contain you. You belong to space now.
+/datum/status_effect/chem/breast_enlarger/on_apply()//Removes clothes, they're too small to contain you. You belong to space now.
log_game("FERMICHEM: [owner]'s breasts has reached comical sizes. ID: [owner.key]")
- var/mob/living/carbon/human/o = owner
- var/items = o.get_contents()
- for(var/obj/item/W in items)
- if(W == o.w_uniform || W == o.wear_suit)
- o.dropItemToGround(W, TRUE)
- playsound(o.loc, 'sound/items/poster_ripped.ogg', 50, 1)
- to_chat(o, "Your clothes give, ripping into peices under the strain of your swelling breasts! Unless you manage to reduce the size of your breasts, there's no way you're going to be able to put anything on over these melons..!")
- o.visible_message("[o]'s chest suddenly bursts forth, ripping their clothes off!'")
- else
- to_chat(o, "Your bountiful bosom is so rich with mass, you seriously doubt you'll be able to fit any clothes over it.")
- return ..()
+ var/mob/living/carbon/human/H = owner
+ var/message = FALSE
+ if(H.w_uniform)
+ H.dropItemToGround(H.w_uniform, TRUE)
+ message = TRUE
+ if(H.wear_suit)
+ H.dropItemToGround(H.wear_suit, TRUE)
+ message = TRUE
+ if(message)
+ playsound(H.loc, 'sound/items/poster_ripped.ogg', 50, 1)
+ H.visible_message("[H]'s chest suddenly bursts forth, ripping their clothes off!'", \
+ "Your clothes give, ripping into peices under the strain of your swelling breasts! Unless you manage to reduce the size of your breasts, there's no way you're going to be able to put anything on over these melons..!")
+ else
+ to_chat(H, "Your bountiful bosom is so rich with mass, you seriously doubt you'll be able to fit any clothes over it.")
+ return ..()
-/datum/status_effect/chem/breast_enlarger/tick(mob/living/carbon/human/H)//If you try to wear clothes, you fail. Slows you down if you're comically huge
- var/mob/living/carbon/human/o = owner
- var/obj/item/organ/genital/breasts/B = o.getorganslot("breasts")
- moveCalc = 1+((round(B.cached_size) - 9)/3) //Afffects how fast you move, and how often you can click.
+/datum/status_effect/chem/breast_enlarger/tick()//If you try to wear clothes, you fail. Slows you down if you're comically huge
+ var/mob/living/carbon/human/H = owner
+ var/obj/item/organ/genital/breasts/B = H.getorganslot(ORGAN_SLOT_BREASTS)
if(!B)
- o.remove_movespeed_modifier(BREAST_MOVEMENT_SPEED)
- sizeMoveMod(1)
- owner.remove_status_effect(src)
- var/items = o.get_contents()
- for(var/obj/item/W in items)
- if(W == o.w_uniform || W == o.wear_suit)
- o.dropItemToGround(W, TRUE)
- playsound(o.loc, 'sound/items/poster_ripped.ogg', 50, 1)
- to_chat(owner, "Your enormous breasts are way too large to fit anything over them!")
+ H.remove_status_effect(src)
+ return
+ moveCalc = 1+((round(B.cached_size) - 9)/3) //Afffects how fast you move, and how often you can click.
+ var/message = FALSE
+ if(H.w_uniform)
+ H.dropItemToGround(H.w_uniform, TRUE)
+ message = TRUE
+ if(H.wear_suit)
+ H.dropItemToGround(H.wear_suit, TRUE)
+ message = TRUE
+ if(message)
+ playsound(H.loc, 'sound/items/poster_ripped.ogg', 50, 1)
+ to_chat(H, "Your enormous breasts are way too large to fit anything over them!")
+
+ if(last_checked_size != B.cached_size)
+ H.add_movespeed_modifier(BREAST_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
+ sizeMoveMod(moveCalc)
+
if (B.size == "huge")
if(prob(1))
to_chat(owner, "Your back is feeling sore.")
- var/target = o.get_bodypart(BODY_ZONE_CHEST)
- o.apply_damage(0.1, BRUTE, target)
- if(!B.cached_size == B.breast_values[B.prev_size])
- o.add_movespeed_modifier(BREAST_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
- sizeMoveMod(moveCalc)
- return ..()
- else if (B.breast_values[B.size] > B.breast_values[B.prev_size])
- o.add_movespeed_modifier(BREAST_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
- sizeMoveMod(moveCalc)
- else if (B.breast_values[B.size] < B.breast_values[B.prev_size])
- o.add_movespeed_modifier(BREAST_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
- sizeMoveMod(moveCalc)
- if((B.cached_size) < 16)
- switch(round(B.cached_size))
- if(9)
- if (B.breast_values[B.prev_size] != B.breast_values[B.size])
- to_chat(o, "Your expansive chest has become a more managable size, liberating your movements.")
- if(10 to INFINITY)
- if (B.breast_values[B.prev_size] != B.breast_values[B.size])
- to_chat(H, "Your indulgent busom is so substantial, it's affecting your movements!")
+ var/target = H.get_bodypart(BODY_ZONE_CHEST)
+ H.apply_damage(0.1, BRUTE, target)
+ else
if(prob(1))
- to_chat(owner, "Your back is feeling a little sore.")
- ..()
+ to_chat(H, "Your back is feeling a little sore.")
+ last_checked_size = B.cached_size
+ ..()
-/datum/status_effect/chem/breast_enlarger/on_remove(mob/living/carbon/M)
+/datum/status_effect/chem/breast_enlarger/on_remove()
log_game("FERMICHEM: [owner]'s breasts has reduced to an acceptable size. ID: [owner.key]")
+ to_chat(owner, "Your expansive chest has become a more managable size, liberating your movements.")
owner.remove_movespeed_modifier(BREAST_MOVEMENT_SPEED)
sizeMoveMod(1)
@@ -112,51 +110,57 @@
alert_type = null
var/bloodCalc
var/moveCalc
+ var/last_checked_size //used to prevent potential cpu waste, just like the above.
-/datum/status_effect/chem/penis_enlarger/on_apply(mob/living/carbon/human/H)//Removes clothes, they're too small to contain you. You belong to space now.
+/datum/status_effect/chem/penis_enlarger/on_apply()//Removes clothes, they're too small to contain you. You belong to space now.
log_game("FERMICHEM: [owner]'s dick has reached comical sizes. ID: [owner.key]")
- var/mob/living/carbon/human/o = owner
- var/items = o.get_contents()
- if(o.w_uniform || o.wear_suit)
- to_chat(o, "Your clothes give, ripping into peices under the strain of your swelling pecker! Unless you manage to reduce the size of your emancipated trouser snake, there's no way you're going to be able to put anything on over this girth..!")
- owner.visible_message("[o]'s schlong suddenly bursts forth, ripping their clothes off!'")
+ var/mob/living/carbon/human/H = owner
+ var/message = FALSE
+ if(H.w_uniform)
+ H.dropItemToGround(H.w_uniform, TRUE)
+ message = TRUE
+ if(H.wear_suit)
+ H.dropItemToGround(H.wear_suit, TRUE)
+ message = TRUE
+ if(message)
+ playsound(H.loc, 'sound/items/poster_ripped.ogg', 50, 1)
+ H.visible_message("[H]'s schlong suddenly bursts forth, ripping their clothes off!'", \
+ "Your clothes give, ripping into peices under the strain of your swelling pecker! Unless you manage to reduce the size of your emancipated trouser snake, there's no way you're going to be able to put anything on over this girth..!")
else
- to_chat(o, "Your emancipated trouser snake is so ripe with girth, you seriously doubt you'll be able to fit any clothes over it.")
- for(var/obj/item/W in items)
- if(W == o.w_uniform || W == o.wear_suit)
- o.dropItemToGround(W, TRUE)
- playsound(o.loc, 'sound/items/poster_ripped.ogg', 50, 1)
+ to_chat(H, "Your emancipated trouser snake is so ripe with girth, you seriously doubt you'll be able to fit any clothes over it.")
return ..()
-/datum/status_effect/chem/penis_enlarger/tick(mob/living/carbon/M)
- var/mob/living/carbon/human/o = owner
- var/obj/item/organ/genital/penis/P = o.getorganslot("penis")
+/datum/status_effect/chem/penis_enlarger/tick()
+ var/mob/living/carbon/human/H = owner
+ var/obj/item/organ/genital/penis/P = H.getorganslot(ORGAN_SLOT_PENIS)
+ if(!P)
+ owner.remove_status_effect(src)
+ return
moveCalc = 1+((round(P.length) - 21)/3) //effects how fast you can move
bloodCalc = 1+((round(P.length) - 21)/15) //effects how much blood you need (I didn' bother adding an arousal check because I'm spending too much time on this organ already.)
- if(!P)
- o.remove_movespeed_modifier(DICK_MOVEMENT_SPEED)
- o.ResetBloodVol()
- owner.remove_status_effect(src)
- var/items = o.get_contents()
- for(var/obj/item/W in items)
- if(W == o.w_uniform || W == o.wear_suit)
- o.dropItemToGround(W, TRUE)
- playsound(o.loc, 'sound/items/poster_ripped.ogg', 50, 1)
- to_chat(owner, "Your enormous package is way to large to fit anything over!")
- switch(round(P.cached_length))
- if(21)
- to_chat(o, "Your rascally willy has become a more managable size, liberating your movements.")
- o.remove_movespeed_modifier(DICK_MOVEMENT_SPEED)
- o.AdjustBloodVol(bloodCalc)
- if(22 to INFINITY)
- if(prob(2))
- to_chat(o, "Your indulgent johnson is so substantial, it's taking all your blood and affecting your movements!")
- o.add_movespeed_modifier(DICK_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
- o.AdjustBloodVol(bloodCalc)
+
+ var/message = FALSE
+ if(H.w_uniform)
+ H.dropItemToGround(H.w_uniform, TRUE)
+ message = TRUE
+ if(H.wear_suit)
+ H.dropItemToGround(H.wear_suit, TRUE)
+ message = TRUE
+ if(message)
+ playsound(H.loc, 'sound/items/poster_ripped.ogg', 50, 1)
+ to_chat(H, "Your enormous package is way to large to fit anything over!")
+
+ if(P.length < 22 && H.has_movespeed_modifier(DICK_MOVEMENT_SPEED))
+ to_chat(owner, "Your rascally willy has become a more managable size, liberating your movements.")
+ H.remove_movespeed_modifier(DICK_MOVEMENT_SPEED)
+ else if(P.length >= 22 && !H.has_movespeed_modifier(DICK_MOVEMENT_SPEED))
+ to_chat(H, "Your indulgent johnson is so substantial, it's taking all your blood and affecting your movements!")
+ H.add_movespeed_modifier(DICK_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
+ H.AdjustBloodVol(bloodCalc)
..()
-/datum/status_effect/chem/penis_enlarger/on_remove(mob/living/carbon/human/o)
+/datum/status_effect/chem/penis_enlarger/on_remove()
log_game("FERMICHEM: [owner]'s dick has reduced to an acceptable size. ID: [owner.key]")
owner.remove_movespeed_modifier(DICK_MOVEMENT_SPEED)
owner.ResetBloodVol()
diff --git a/modular_citadel/code/datums/traits/neutral.dm b/modular_citadel/code/datums/traits/neutral.dm
deleted file mode 100644
index 197c9b94e1..0000000000
--- a/modular_citadel/code/datums/traits/neutral.dm
+++ /dev/null
@@ -1,43 +0,0 @@
-// Citadel-specific Neutral Traits
-
-/datum/quirk/libido
- name = "Nymphomania"
- desc = "You're always feeling a bit in heat. Also, you get aroused faster than usual."
- value = 0
- mob_trait = TRAIT_NYMPHO
- gain_text = "You are feeling extra wild."
- lose_text = "You don't feel that burning sensation anymore."
-
-/datum/quirk/libido/add()
- var/mob/living/M = quirk_holder
- M.min_arousal = 16
- M.arousal_rate = 3
-
-/datum/quirk/libido/remove()
- var/mob/living/M = quirk_holder
- M.min_arousal = initial(M.min_arousal)
- M.arousal_rate = initial(M.arousal_rate)
-
-/datum/quirk/libido/on_process()
- var/mob/living/M = quirk_holder
- if(M.canbearoused == FALSE)
- to_chat(quirk_holder, "Having high libido is useless when you can't feel arousal at all!")
- qdel(src)
-
-/datum/quirk/maso
- name = "Masochism"
- desc = "You are aroused by pain."
- value = 0
- mob_trait = TRAIT_MASO
- gain_text = "You desire to be hurt."
- lose_text = "Pain has become less exciting for you."
-
-/datum/quirk/pharmacokinesis //Prevents unwanted organ additions.
- name = "Acute hepatic pharmacokinesis"
- desc = "You've a rare genetic disorder that causes Incubus draft and Sucubus milk to be absorbed by your liver instead."
- value = 0
- mob_trait = TRAIT_PHARMA
- lose_text = "Your liver feels different."
- var/active = FALSE
- var/power = 0
- var/cachedmoveCalc = 1
diff --git a/modular_citadel/code/game/machinery/vending.dm b/modular_citadel/code/game/machinery/vending.dm
index 6a91810173..a71175c345 100755
--- a/modular_citadel/code/game/machinery/vending.dm
+++ b/modular_citadel/code/game/machinery/vending.dm
@@ -72,32 +72,7 @@
/obj/item/reagent_containers/glass/bottle/hexacrocin = 10
)
refill_canister = /obj/item/vending_refill/kink
-/*
-/obj/machinery/vending/nazivend
- name = "Nazivend"
- desc = "A vending machine containing Nazi German supplies. A label reads: \"Remember the gorrilions lost.\""
- icon = 'icons/obj/citvending.dmi'
- icon_state = "nazi"
- vend_reply = "SIEG HEIL!"
- product_slogans = "Das Vierte Reich wird zuruckkehren!;ENTFERNEN JUDEN!;Billiger als die Juden jemals geben!;Rader auf dem adminbus geht rund und rund.;Warten Sie, warum wir wieder hassen Juden?- *BZZT*"
- products = list(
- /obj/item/clothing/head/stalhelm = 20,
- /obj/item/clothing/head/panzer = 20,
- /obj/item/clothing/suit/soldiercoat = 20,
- // /obj/item/clothing/under/soldieruniform = 20,
- /obj/item/clothing/shoes/jackboots = 20
- )
- contraband = list(
- /obj/item/clothing/head/naziofficer = 10,
- // /obj/item/clothing/suit/officercoat = 10,
- // /obj/item/clothing/under/officeruniform = 10,
- /obj/item/clothing/suit/space/hardsuit/nazi = 3,
- /obj/item/gun/energy/plasma/MP40k = 4
- )
- premium = list()
- refill_canister = /obj/item/vending_refill/nazi
-*/
/obj/machinery/vending/sovietvend
name = "KomradeVendtink"
desc = "Rodina-mat' zovyot!"
@@ -137,10 +112,6 @@
icon = 'modular_citadel/icons/vending_restock.dmi'
icon_state = "refill_kink"
-/obj/item/vending_refill/nazi
- machine_name = "nazivend"
- icon_state = "refill_nazi"
-
/obj/item/vending_refill/soviet
machine_name = "sovietvend"
icon_state = "refill_soviet"
diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm
index 27f7576e7f..6c9da17289 100644
--- a/modular_citadel/code/modules/arousal/arousal.dm
+++ b/modular_citadel/code/modules/arousal/arousal.dm
@@ -19,11 +19,6 @@
var/hidden_undershirt = FALSE
var/hidden_socks = FALSE
-/mob/living/carbon/human/New()
- ..()
- saved_underwear = underwear
- saved_undershirt = undershirt
-
//Species vars
/datum/species
var/arousal_gain_rate = AROUSAL_START_VALUE //Rate at which this species becomes aroused
@@ -35,60 +30,51 @@
//Mob procs
/mob/living/carbon/human/proc/underwear_toggle()
set name = "Toggle undergarments"
- set category = "Object"
- if(ishuman(src))
- var/mob/living/carbon/human/humz = src
- var/confirm = input(src, "Select what part of your form to alter", "Undergarment Toggling", "Cancel") in list("Top", "Bottom", "Socks", "All", "Cancel")
- if(confirm == "Top")
- humz.hidden_undershirt = !humz.hidden_undershirt
+ set category = "IC"
- if(confirm == "Bottom")
- humz.hidden_underwear = !humz.hidden_underwear
+ var/confirm = input(src, "Select what part of your form to alter", "Undergarment Toggling") as null|anything in list("Top", "Bottom", "Socks", "All")
+ if(!confirm)
+ return
+ if(confirm == "Top")
+ hidden_undershirt = !hidden_undershirt
- if(confirm == "Socks")
- humz.hidden_socks = !humz.hidden_socks
+ if(confirm == "Bottom")
+ hidden_underwear = !hidden_underwear
- if(confirm == "All")
- humz.hidden_undershirt = !humz.hidden_undershirt
- humz.hidden_underwear = !humz.hidden_underwear
- humz.hidden_socks = !humz.hidden_socks
+ if(confirm == "Socks")
+ hidden_socks = !hidden_socks
- if(confirm == "Cancel")
- return
- src.update_body()
+ if(confirm == "All")
+ var/on_off = (hidden_undershirt || hidden_underwear || hidden_socks) ? FALSE : TRUE
+ hidden_undershirt = on_off
+ hidden_underwear = on_off
+ hidden_socks = on_off
- else
- to_chat(src, "Humans only. How the fuck did you get this verb anyway.")
+ update_body()
-/mob/living/proc/handle_arousal()
-
-
-/mob/living/carbon/handle_arousal()
- if(canbearoused && dna)
- var/datum/species/S
- S = dna.species
- if(S && !(SSmobs.times_fired % 36) && getArousalLoss() < max_arousal)//Totally stolen from breathing code. Do this every 36 ticks.
- adjustArousalLoss(arousal_rate * S.arousal_gain_rate)
- if(dna.features["exhibitionist"] && client)
- var/amt_nude = 0
- if(is_chest_exposed() && (getorganslot("breasts")))
- amt_nude++
- if(is_groin_exposed())
- if(getorganslot("penis"))
- amt_nude++
- if(getorganslot("vagina"))
- amt_nude++
- if(amt_nude)
- var/watchers = 0
- for(var/mob/_M in view(world.view, src))
- var/mob/living/M = _M
- if(!istype(M))
- continue
- if(M.client && !M.stat && !M.eye_blind && (locate(src) in viewers(world.view,M)))
- watchers++
- if(watchers)
- adjustArousalLoss((amt_nude * watchers) + S.arousal_gain_rate)
+/mob/living/proc/handle_arousal(times_fired)
+ return
+/mob/living/carbon/handle_arousal(times_fired)
+ if(!canbearoused || !dna)
+ return
+ var/datum/species/S = dna.species
+ if(!S || (times_fired % 36) || !getArousalLoss() >= max_arousal)//Totally stolen from breathing code. Do this every 36 ticks.
+ return
+ var/our_loss = arousal_rate * S.arousal_gain_rate
+ if(HAS_TRAIT(src, TRAIT_EXHIBITIONIST) && client)
+ var/amt_nude = 0
+ for(var/obj/item/organ/genital/G in internal_organs)
+ if(G.is_exposed())
+ amt_nude++
+ if(amt_nude)
+ var/watchers = 0
+ for(var/mob/living/L in view(src))
+ if(L.client && !L.stat && !L.eye_blind && (src in view(L)))
+ watchers++
+ if(watchers)
+ our_loss += (amt_nude * watchers) + S.arousal_gain_rate
+ adjustArousalLoss(our_loss)
/mob/living/proc/getArousalLoss()
return arousalloss
@@ -138,8 +124,6 @@
S = GLOB.breasts_shapes_list[G.shape]
if(S?.alt_aroused)
G.aroused_state = isPercentAroused(G.aroused_amount)
- if(getArousalLoss() >= ((max_arousal / 100) * 33))
- G.aroused_state = TRUE
else
G.aroused_state = FALSE
G.update_appearance()
@@ -147,54 +131,16 @@
/mob/living/proc/update_arousal_hud()
return FALSE
-/datum/species/proc/update_arousal_hud(mob/living/carbon/human/H)
- return FALSE
-
/mob/living/carbon/human/update_arousal_hud()
- if(!client || !hud_used)
- return FALSE
- if(dna.species.update_arousal_hud())
+ if(!client || !(hud_used?.arousal))
return FALSE
if(!canbearoused)
hud_used.arousal.icon_state = ""
return FALSE
else
- if(hud_used.arousal)
- if(stat == DEAD)
- hud_used.arousal.icon_state = "arousal0"
- return TRUE
- if(getArousalLoss() == max_arousal)
- hud_used.arousal.icon_state = "arousal100"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 90)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal90"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 80)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal80"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 70)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal70"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 60)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal60"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 50)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal50"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 40)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal40"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 30)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal30"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 20)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal10"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 10)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal10"
- return TRUE
- else
- hud_used.arousal.icon_state = "arousal0"
+ var/value = FLOOR(getPercentAroused(), 10)
+ hud_used.arousal.icon_state = "arousal[value]"
+ return TRUE
/obj/screen/arousal
name = "arousal"
@@ -213,7 +159,6 @@
to_chat(M, "Arousal is disabled. Feature is unavailable.")
-
/mob/living/proc/mob_climax()//This is just so I can test this shit without being forced to add actual content to get rid of arousal. Will be a very basic proc for a while.
set name = "Masturbate"
set category = "IC"
@@ -221,225 +166,187 @@
if(mb_cd_timer <= world.time)
//start the cooldown even if it fails
mb_cd_timer = world.time + mb_cd_length
- if(getArousalLoss() >= ((max_arousal / 100) * 33))//33% arousal or greater required
- src.visible_message("[src] starts masturbating!", \
+ if(getArousalLoss() >= 33)//one third of average max_arousal or greater required
+ visible_message("[src] starts masturbating!", \
"You start masturbating.")
if(do_after(src, 30, target = src))
- src.visible_message("[src] relieves [p_them()]self!", \
+ visible_message("[src] relieves [p_them()]self!", \
"You have relieved yourself.")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
setArousalLoss(min_arousal)
else
to_chat(src, "You aren't aroused enough for that.")
+/obj/item/organ/genital/proc/climaxable(mob/living/carbon/human/H, silent = FALSE) //returns the fluid source (ergo reagents holder) if found.
+ if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
+ . = reagents
+ else
+ if(linked_organ)
+ . = linked_organ.reagents
+ if(!. && !silent)
+ to_chat(H, "Your [name] is unable to produce it's own fluids, it's missing the organs for it.")
+
+/mob/living/carbon/human/proc/do_climax(datum/reagents/R, atom/target, obj/item/organ/genital/G, spill = TRUE)
+ if(!G)
+ return
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ setArousalLoss(min_arousal)
+ if(!target || !R)
+ return
+ var/turfing = isturf(target)
+ if(spill & R.total_volume >= 5)
+ R.reaction(turfing ? target : target.loc, TOUCH, 1, 0)
+ if(!turfing)
+ R.trans_to(target, R.total_volume * (spill ? G.fluid_transfer_factor : 1))
+ R.clear_reagents()
//These are various procs that we'll use later, split up for readability instead of having one, huge proc.
//For all of these, we assume the arguments given are proper and have been checked beforehand.
/mob/living/carbon/human/proc/mob_masturbate(obj/item/organ/genital/G, mb_time = 30) //Masturbation, keep it gender-neutral
- var/total_fluids = 0
- var/datum/reagents/fluid_source = null
-
- if(G.producing) //Can it produce its own fluids, such as breasts?
- fluid_source = G.reagents
- else
- if(!G.linked_organ)
- to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.")
- return
- fluid_source = G.linked_organ.reagents
- total_fluids = fluid_source.total_volume
+ var/datum/reagents/fluid_source = G.climaxable(src)
+ if(!fluid_source)
+ return
+ var/obj/item/organ/genital/PP = CHECK_BITFIELD(G.genital_flags, MASTURBATE_LINKED_ORGAN) ? G.linked_organ : G
+ if(!PP)
+ to_chat(src, "You shudder, unable to cum with your [name].")
if(mb_time)
- src.visible_message("[src] starts to [G.masturbation_verb] [p_their()] [G.name].", \
- "You start to [G.masturbation_verb] your [G.name].", \
+ visible_message("[src] starts to [G.masturbation_verb] [p_their()] [G.name].", \
"You start to [G.masturbation_verb] your [G.name].")
-
- if(do_after(src, mb_time, target = src))
- if(total_fluids > 5)
- fluid_source.reaction(src.loc, TOUCH, 1, 0)
- fluid_source.clear_reagents()
- src.visible_message("[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!", \
- "You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].", \
- "You have relieved yourself.")
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
- if(G.can_climax)
- setArousalLoss(min_arousal)
-
+ if(!do_after(src, mb_time, target = src) || !G.climaxable(src, TRUE))
+ return
+ visible_message("[src] orgasms, [PP.orgasm_verb][isturf(loc) ? " onto [loc]" : ""] with [p_their()] [PP.name]!", \
+ "You orgasm, [PP.orgasm_verb][isturf(loc) ? " onto [loc]" : ""] with your [PP.name].")
+ do_climax(fluid_source, loc, G)
/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30) //This is used for forced orgasms and other hands-free climaxes
- var/total_fluids = 0
- var/datum/reagents/fluid_source = null
- var/unable_to_come = FALSE
-
- if(G.producing) //Can it produce its own fluids, such as breasts?
- fluid_source = G.reagents
- total_fluids = fluid_source.total_volume
- else
- if(!G.linked_organ)
- unable_to_come = TRUE
- else
- fluid_source = G.linked_organ.reagents
- total_fluids = fluid_source.total_volume
-
- if(unable_to_come)
- src.visible_message("[src] shudders, their [G.name] unable to cum.", \
- "Your [G.name] cannot cum, giving no relief.", \
+ var/datum/reagents/fluid_source = G.climaxable(src, TRUE)
+ if(!fluid_source)
+ visible_message("[src] shudders, their [G.name] unable to cum.", \
"Your [G.name] cannot cum, giving no relief.")
- else
- total_fluids = fluid_source.total_volume
- if(mb_time) //as long as it's not instant, give a warning
- src.visible_message("[src] looks like they're about to cum.", \
- "You feel yourself about to orgasm.", \
- "You feel yourself about to orgasm.")
- if(do_after(src, mb_time, target = src))
- if(total_fluids > 5)
- fluid_source.reaction(src.loc, TOUCH, 1, 0)
- fluid_source.clear_reagents()
- src.visible_message("[src] orgasms[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""], using [p_their()] [G.name]!", \
- "You climax[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""] with your [G.name].", \
- "You climax using your [G.name].")
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
- if(G.can_climax)
- setArousalLoss(min_arousal)
-
+ return
+ if(mb_time) //as long as it's not instant, give a warning
+ visible_message("[src] looks like they're about to cum.", \
+ "You feel yourself about to orgasm.")
+ if(!do_after(src, mb_time, target = src) || !G.climaxable(src, TRUE))
+ return
+ visible_message("[src] orgasms[isturf(loc) ? " onto [loc]" : ""], using [p_their()] [G.name]!", \
+ "You climax[isturf(loc) ? " onto [loc]" : ""] with your [G.name].")
+ do_climax(fluid_source, loc, G)
/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, mob/living/L, spillage = TRUE, mb_time = 30) //Used for climaxing with any living thing
- var/total_fluids = 0
- var/datum/reagents/fluid_source = null
-
- if(G.producing) //Can it produce its own fluids, such as breasts?
- fluid_source = G.reagents
- else
- if(!G.linked_organ)
- to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.")
- return
- fluid_source = G.linked_organ.reagents
- total_fluids = fluid_source.total_volume
+ var/datum/reagents/fluid_source = G.climaxable(src)
+ if(!fluid_source)
+ return
if(mb_time) //Skip warning if this is an instant climax.
- src.visible_message("[src] is about to climax with [L]!", \
- "You're about to climax with [L]!", \
- "You're preparing to climax with someone!")
+ visible_message("[src] is about to climax with [L]!", \
+ "You're about to climax with [L]!")
+ if(!do_after(src, mb_time, target = src) || !in_range(src, L) || !G.climaxable(src, TRUE))
+ return
if(spillage)
- if(do_after(src, mb_time, target = src) && in_range(src, L))
- fluid_source.trans_to(L, total_fluids*G.fluid_transfer_factor)
- total_fluids -= total_fluids*G.fluid_transfer_factor
- if(total_fluids > 5)
- fluid_source.reaction(L.loc, TOUCH, 1, 0)
- fluid_source.clear_reagents()
- src.visible_message("[src] climaxes with [L][spillage ? ", overflowing and spilling":""], using [p_their()] [G.name]!", \
- "You orgasm with [L][spillage ? ", spilling out of them":""], using your [G.name].", \
- "You have climaxed with someone[spillage ? ", spilling out of them":""], using your [G.name].")
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
- SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
- if(G.can_climax)
- setArousalLoss(min_arousal)
+ visible_message("[src] climaxes with [L], overflowing and spilling, using [p_their()] [G.name]!", \
+ "You orgasm with [L], spilling out of them, using your [G.name].")
else //knots and other non-spilling orgasms
- if(do_after(src, mb_time, target = src) && in_range(src, L))
- fluid_source.trans_to(L, total_fluids)
- total_fluids = 0
- src.visible_message("[src] climaxes with [L], [p_their()] [G.name] spilling nothing!", \
- "You ejaculate with [L], your [G.name] spilling nothing.", \
- "You have climaxed inside someone, your [G.name] spilling nothing.")
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
- SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
- if(G.can_climax)
- setArousalLoss(min_arousal)
+ visible_message("[src] climaxes with [L], [p_their()] [G.name] spilling nothing!", \
+ "You ejaculate with [L], your [G.name] spilling nothing.")
+ SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ do_climax(fluid_source, spillage ? loc : L, G, spillage)
/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/G, obj/item/reagent_containers/container, mb_time = 30) //For beaker-filling, beware the bartender
- var/total_fluids = 0
- var/datum/reagents/fluid_source = null
-
- if(G.producing) //Can it produce its own fluids, such as breasts?
- fluid_source = G.reagents
- else
- if(!G.linked_organ)
- to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.")
+ var/datum/reagents/fluid_source = G.climaxable(src)
+ if(!fluid_source)
+ return
+ if(mb_time)
+ visible_message("[src] starts to [G.masturbation_verb] their [G.name] over [container].", \
+ "You start to [G.masturbation_verb] your [G.name] over [container].")
+ if(!do_after(src, mb_time, target = src) || !in_range(src, container) || !G.climaxable(src, TRUE))
return
- fluid_source = G.linked_organ.reagents
- total_fluids = fluid_source.total_volume
+ visible_message("[src] uses [p_their()] [G.name] to fill [container]!", \
+ "You used your [G.name] to fill [container].")
+ do_climax(fluid_source, container, G, FALSE)
- //if(!container) //Something weird happened
- // to_chat(src, "You need a container to do this!")
- // return
-
- src.visible_message("[src] starts to [G.masturbation_verb] their [G.name] over [container].", \
- "You start to [G.masturbation_verb] your [G.name] over [container].", \
- "You start to [G.masturbation_verb] your [G.name] over something.")
- if(do_after(src, mb_time, target = src) && in_range(src, container))
- fluid_source.trans_to(container, total_fluids)
- src.visible_message("[src] uses [p_their()] [G.name] to fill [container]!", \
- "You used your [G.name] to fill [container].", \
- "You have relieved some pressure.")
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
- if(G.can_climax)
- setArousalLoss(min_arousal)
-
-/mob/living/carbon/human/proc/pick_masturbate_genitals()
- var/obj/item/organ/genital/ret_organ
- var/list/genitals_list = list()
+/mob/living/carbon/human/proc/pick_masturbate_genitals(silent = FALSE)
+ var/list/genitals_list
var/list/worn_stuff = get_equipped_items()
for(var/obj/item/organ/genital/G in internal_organs)
- if(G.can_masturbate_with) //filter out what you can't masturbate with
- if(G.is_exposed(worn_stuff)) //Nude or through_clothing
- genitals_list += G
- if(genitals_list.len)
- ret_organ = input(src, "with what?", "Masturbate", null) as null|obj in genitals_list
+ if(CHECK_BITFIELD(G.genital_flags, CAN_MASTURBATE_WITH) && G.is_exposed(worn_stuff)) //filter out what you can't masturbate with
+ if(CHECK_BITFIELD(G.genital_flags, MASTURBATE_LINKED_ORGAN) && !G.linked_organ)
+ continue
+ LAZYADD(genitals_list, G)
+ if(LAZYLEN(genitals_list))
+ var/obj/item/organ/genital/ret_organ = input(src, "with what?", "Masturbate", null) as null|obj in genitals_list
return ret_organ
- return null //error stuff
+ else if(!silent)
+ to_chat(src, "You cannot masturbate without available genitals.")
-
-/mob/living/carbon/human/proc/pick_climax_genitals()
- var/obj/item/organ/genital/ret_organ
- var/list/genitals_list = list()
+/mob/living/carbon/human/proc/pick_climax_genitals(silent = FALSE)
+ var/list/genitals_list
var/list/worn_stuff = get_equipped_items()
for(var/obj/item/organ/genital/G in internal_organs)
- if(G.can_climax) //filter out what you can't masturbate with
- if(G.is_exposed(worn_stuff)) //Nude or through_clothing
- genitals_list += G
- if(genitals_list.len)
- ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
+ if(CHECK_BITFIELD(G.genital_flags, CAN_CLIMAX_WITH) && G.is_exposed(worn_stuff)) //filter out what you can't masturbate with
+ LAZYADD(genitals_list, G)
+ if(LAZYLEN(genitals_list))
+ var/obj/item/organ/genital/ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
return ret_organ
- return null //error stuff
+ else if(!silent)
+ to_chat(src, "You cannot climax without available genitals.")
-
-/mob/living/carbon/human/proc/pick_partner()
+/mob/living/carbon/human/proc/pick_partner(silent = FALSE)
var/list/partners = list()
- if(src.pulling)
- partners += src.pulling //Yes, even objects for now
- if(src.pulledby)
- partners += src.pulledby
+ if(pulling)
+ partners += pulling
+ if(pulledby)
+ partners += pulledby
//Now we got both of them, let's check if they're proper
- for(var/I in partners)
- if(isliving(I))
- if(iscarbon(I))
- var/mob/living/carbon/C = I
- if(!C.exposed_genitals.len) //Nothing through_clothing
- if(!C.is_groin_exposed()) //No pants undone
- if(!C.is_chest_exposed()) //No chest exposed
- partners -= I //Then not proper, remove them
- else
- partners -= I //No fucking objects
+ for(var/mob/living/L in partners)
+ if(iscarbon(L))
+ var/mob/living/carbon/C = L
+ if(!C.exposed_genitals.len && !C.is_groin_exposed() && !C.is_chest_exposed()) //Nothing through_clothing, no proper partner.
+ partners -= C
//NOW the list should only contain correct partners
if(!partners.len)
- return null //No one left.
- return input(src, "With whom?", "Sexual partner", null) in partners //pick one, default to null
+ if(!silent)
+ to_chat(src, "You cannot do this alone.")
+ return //No one left.
+ var/mob/living/target = input(src, "With whom?", "Sexual partner", null) as null|anything in partners //pick one, default to null
+ if(target && in_range(src, target))
+ return target
-/mob/living/carbon/human/proc/pick_climax_container()
- var/obj/item/reagent_containers/SC = null
+/mob/living/carbon/human/proc/pick_climax_container(silent = FALSE)
var/list/containers_list = list()
- for(var/obj/item/reagent_containers/container in held_items)
- if(container.is_open_container() || istype(container, /obj/item/reagent_containers/food/snacks))
- containers_list += container
+ for(var/obj/item/reagent_containers/C in held_items)
+ if(C.is_open_container() || istype(C, /obj/item/reagent_containers/food/snacks))
+ containers_list += C
+ for(var/obj/item/reagent_containers/C in range(1, src))
+ if((C.is_open_container() || istype(C, /obj/item/reagent_containers/food/snacks)) && CanReach(C))
+ containers_list += C
if(containers_list.len)
- SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list
- if(SC)
- if(in_range(src, SC))
- return SC
- return null //If nothing correct, give null.
+ var/obj/item/reagent_containers/SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list
+ if(SC && CanReach(SC))
+ return SC
+ else if(!silent)
+ to_chat(src, "You cannot do this without an appropriate container.")
+/mob/living/carbon/human/proc/available_rosie_palms(silent = FALSE, list/whitelist_typepaths = list(/obj/item/dildo))
+ if(restrained(TRUE)) //TRUE ignores grabs
+ if(!silent)
+ to_chat(src, "You can't do that while restrained!")
+ return FALSE
+ if(!get_num_arms() || !get_empty_held_indexes())
+ if(whitelist_typepaths)
+ if(!islist(whitelist_typepaths))
+ whitelist_typepaths = list(whitelist_typepaths)
+ for(var/path in whitelist_typepaths)
+ if(is_holding_item_of_type(path))
+ return TRUE
+ if(!silent)
+ to_chat(src, "You need at least one free arm.")
+ return FALSE
+ return TRUE
//Here's the main proc itself
/mob/living/carbon/human/mob_climax(forced_climax=FALSE) //Forced is instead of the other proc, makes you cum if you have the tools for it, ignoring restraints
@@ -447,156 +354,97 @@
if(!forced_climax) //Don't spam the message to the victim if forced to come too fast
to_chat(src, "You need to wait [DisplayTimeText((mb_cd_timer - world.time), TRUE)] before you can do that again!")
return
- mb_cd_timer = (world.time + mb_cd_length)
-
- if(canbearoused && has_dna())
- if(stat==2)
+ if(!canbearoused || !has_dna())
+ return
+ if(stat == DEAD)
+ if(!forced_climax)
to_chat(src, "You can't do that while dead!")
- return
- if(forced_climax) //Something forced us to cum, this is not a masturbation thing and does not progress to the other checks
- for(var/obj/item/organ/O in internal_organs)
- if(istype(O, /obj/item/organ/genital))
- var/obj/item/organ/genital/G = O
- if(!G.can_climax) //Skip things like wombs and testicles
- continue
- var/mob/living/partner
- var/check_target
- var/list/worn_stuff = get_equipped_items()
+ return
+ if(forced_climax) //Something forced us to cum, this is not a masturbation thing and does not progress to the other checks
+ for(var/obj/item/organ/genital/G in internal_organs)
+ if(!CHECK_BITFIELD(G.genital_flags, CAN_CLIMAX_WITH)) //Skip things like wombs and testicles
+ continue
+ var/mob/living/partner
+ var/check_target
+ var/list/worn_stuff = get_equipped_items()
- if(G.is_exposed(worn_stuff))
- if(src.pulling) //Are we pulling someone? Priority target, we can't be making option menus for this, has to be quick
- if(isliving(src.pulling)) //Don't fuck objects
- check_target = src.pulling
- if(src.pulledby && !check_target) //prioritise pulled over pulledby
- if(isliving(src.pulledby))
- check_target = src.pulledby
- //Now we should have a partner, or else we have to come alone
- if(check_target)
- if(iscarbon(check_target)) //carbons can have clothes
- var/mob/living/carbon/C = check_target
- if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) //Are they naked enough?
- partner = C
- else //A cat is fine too
- partner = check_target
- if(partner) //Did they pass the clothing checks?
- mob_climax_partner(G, partner, mb_time = 0) //Instant climax due to forced
- continue //You've climaxed once with this organ, continue on
- //not exposed OR if no partner was found while exposed, climax alone
- mob_climax_outside(G, mb_time = 0) //removed climax timer for sudden, forced orgasms
- //Now all genitals that could climax, have.
- //Since this was a forced climax, we do not need to continue with the other stuff
- return
- //If we get here, then this is not a forced climax and we gotta check a few things.
+ if(G.is_exposed(worn_stuff))
+ if(pulling) //Are we pulling someone? Priority target, we can't be making option menus for this, has to be quick
+ if(isliving(pulling)) //Don't fuck objects
+ check_target = pulling
+ if(pulledby && !check_target) //prioritise pulled over pulledby
+ if(isliving(pulledby))
+ check_target = pulledby
+ //Now we should have a partner, or else we have to come alone
+ if(check_target)
+ if(iscarbon(check_target)) //carbons can have clothes
+ var/mob/living/carbon/C = check_target
+ if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) //Are they naked enough?
+ partner = C
+ else //A cat is fine too
+ partner = check_target
+ if(partner) //Did they pass the clothing checks?
+ mob_climax_partner(G, partner, mb_time = 0) //Instant climax due to forced
+ continue //You've climaxed once with this organ, continue on
+ //not exposed OR if no partner was found while exposed, climax alone
+ mob_climax_outside(G, mb_time = 0) //removed climax timer for sudden, forced orgasms
+ //Now all genitals that could climax, have.
+ //Since this was a forced climax, we do not need to continue with the other stuff
+ mb_cd_timer = world.time + mb_cd_length
+ return
+ //If we get here, then this is not a forced climax and we gotta check a few things.
- if(stat==1) //No sleep-masturbation, you're unconscious.
- to_chat(src, "You must be conscious to do that!")
- return
- if(getArousalLoss() < 33) //flat number instead of percentage
- to_chat(src, "You aren't aroused enough for that!")
- return
+ if(stat == UNCONSCIOUS) //No sleep-masturbation, you're unconscious.
+ to_chat(src, "You must be conscious to do that!")
+ return
+ if(getArousalLoss() < 33) //flat number instead of percentage
+ to_chat(src, "You aren't aroused enough for that!")
+ return
- //Ok, now we check what they want to do.
- var/choice = input(src, "Select sexual activity", "Sexual activity:") in list("Masturbate", "Climax alone", "Climax with partner", "Fill container")
+ //Ok, now we check what they want to do.
+ var/choice = input(src, "Select sexual activity", "Sexual activity:") as null|anything in list("Masturbate", "Climax alone", "Climax with partner", "Fill container")
+ if(!choice)
+ return
- switch(choice)
- if("Masturbate")
- if(restrained(TRUE)) //TRUE ignores grabs
- to_chat(src, "You can't do that while restrained!")
- return
- var/free_hands = get_num_arms()
- if(!free_hands)
- to_chat(src, "You need at least one free arm.")
- return
- for(var/helditem in held_items)//how many hands are free
- if(isobj(helditem))
- free_hands--
- if(free_hands <= 0)
- to_chat(src, "You're holding too many things.")
- return
- //We got hands, let's pick an organ
- var/obj/item/organ/genital/picked_organ
- picked_organ = pick_masturbate_genitals()
- if(picked_organ)
- mob_masturbate(picked_organ)
- return
- else //They either lack organs that can masturbate, or they didn't pick one.
- to_chat(src, "You cannot masturbate without choosing genitals.")
- return
+ switch(choice)
+ if("Masturbate")
+ if(!available_rosie_palms())
+ return
+ //We got hands, let's pick an organ
+ var/obj/item/organ/genital/picked_organ = pick_masturbate_genitals()
+ if(picked_organ && available_rosie_palms(TRUE))
+ mob_masturbate(picked_organ)
+ return
- if("Climax alone")
- if(restrained(TRUE)) //TRUE ignores grabs
- to_chat(src, "You can't do that while restrained!")
- return
- var/free_hands = get_num_arms()
- if(!free_hands)
- to_chat(src, "You need at least one free arm.")
- return
- for(var/helditem in held_items)//how many hands are free
- if(isobj(helditem))
- free_hands--
- if(free_hands <= 0)
- to_chat(src, "You're holding too many things.")
- return
- //We got hands, let's pick an organ
- var/obj/item/organ/genital/picked_organ
- picked_organ = pick_climax_genitals()
- if(picked_organ)
- mob_climax_outside(picked_organ)
- return
- else //They either lack organs that can masturbate, or they didn't pick one.
- to_chat(src, "You cannot climax without choosing genitals.")
- return
+ if("Climax alone")
+ if(!available_rosie_palms())
+ return
+ var/obj/item/organ/genital/picked_organ = pick_climax_genitals()
+ if(picked_organ && available_rosie_palms(TRUE))
+ mob_climax_outside(picked_organ)
- if("Climax with partner")
- //We need no hands, we can be restrained and so on, so let's pick an organ
- var/obj/item/organ/genital/picked_organ
- picked_organ = pick_climax_genitals()
- if(picked_organ)
- var/mob/living/partner = pick_partner() //Get someone
- if(partner)
- var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
- if(spillage == "Yes")
- mob_climax_partner(picked_organ, partner, TRUE)
- else
- mob_climax_partner(picked_organ, partner, FALSE)
- return
- else
- to_chat(src, "You cannot do this alone.")
- return
- else //They either lack organs that can masturbate, or they didn't pick one.
- to_chat(src, "You cannot climax without choosing genitals.")
- return
+ if("Climax with partner")
+ //We need no hands, we can be restrained and so on, so let's pick an organ
+ var/obj/item/organ/genital/picked_organ = pick_climax_genitals()
+ if(picked_organ)
+ var/mob/living/partner = pick_partner() //Get someone
+ if(partner)
+ var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as null|anything in list("Yes", "No")
+ if(spillage && in_range(src, partner))
+ mob_climax_partner(picked_organ, partner, spillage == "Yes" ? TRUE : FALSE)
- if("Fill container")
- //We'll need hands and no restraints.
- if(restrained(TRUE)) //TRUE ignores grabs
- to_chat(src, "You can't do that while restrained!")
- return
- var/free_hands = get_num_arms()
- if(!free_hands)
- to_chat(src, "You need at least one free arm.")
- return
- for(var/helditem in held_items)//how many hands are free
- if(isobj(helditem))
- free_hands--
- if(free_hands <= 0)
- to_chat(src, "You're holding too many things.")
- return
- //We got hands, let's pick an organ
- var/obj/item/organ/genital/picked_organ
- picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
- if(picked_organ)
- //Good, got an organ, time to pick a container
- var/obj/item/reagent_containers/fluid_container = pick_climax_container()
- if(fluid_container)
- mob_fill_container(picked_organ, fluid_container)
- return
- else
- to_chat(src, "You cannot do this without anything to fill.")
- return
- else //They either lack organs that can climax, or they didn't pick one.
- to_chat(src, "You cannot fill anything without choosing genitals.")
- return
- else //Somehow another option was taken, maybe something interrupted the selection or it was cancelled
- return //Just end it in that case.
+ if("Fill container")
+ //We'll need hands and no restraints.
+ if(!available_rosie_palms(FALSE, /obj/item/reagent_containers))
+ return
+ //We got hands, let's pick an organ
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
+ if(picked_organ)
+ //Good, got an organ, time to pick a container
+ var/obj/item/reagent_containers/fluid_container = pick_climax_container()
+ if(fluid_container && available_rosie_palms(TRUE, /obj/item/reagent_containers))
+ mob_fill_container(picked_organ, fluid_container)
+
+ mb_cd_timer = world.time + mb_cd_length
\ No newline at end of file
diff --git a/modular_citadel/code/modules/arousal/genitals.dm b/modular_citadel/code/modules/arousal/genitals.dm
new file mode 100644
index 0000000000..9ecf67e4b0
--- /dev/null
+++ b/modular_citadel/code/modules/arousal/genitals.dm
@@ -0,0 +1,339 @@
+/obj/item/organ/genital
+ color = "#fcccb3"
+ w_class = WEIGHT_CLASS_NORMAL
+ var/shape = "human"
+ var/sensitivity = AROUSAL_START_VALUE
+ var/genital_flags //see citadel_defines.dm
+ var/masturbation_verb = "masturbate"
+ var/orgasm_verb = "cumming" //present continous
+ var/fluid_transfer_factor = 0 //How much would a partner get in them if they climax using this?
+ var/size = 2 //can vary between num or text, just used in icon_state strings
+ var/fluid_id = null
+ var/fluid_max_volume = 50
+ var/fluid_efficiency = 1
+ var/fluid_rate = CUM_RATE
+ var/fluid_mult = 1
+ var/aroused_state = FALSE //Boolean used in icon_state strings
+ var/aroused_amount = 50 //This is a num from 0 to 100 for arousal percentage for when to use arousal state icons.
+ var/obj/item/organ/genital/linked_organ
+ var/linked_organ_slot //used for linking an apparatus' organ to its other half on update_link().
+ var/layer_index = GENITAL_LAYER_INDEX //Order should be very important. FIRST vagina, THEN testicles, THEN penis, as this affects the order they are rendered in.
+
+/obj/item/organ/genital/Initialize(mapload, mob/living/carbon/human/H)
+ . = ..()
+ if(fluid_id)
+ create_reagents(fluid_max_volume)
+ if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
+ reagents.add_reagent(fluid_id, fluid_max_volume)
+ if(H)
+ get_features(H)
+ Insert(H)
+ else
+ update()
+
+/obj/item/organ/genital/Destroy()
+ if(linked_organ)
+ update_link(TRUE)//this should remove any other links it has
+ if(owner)
+ Remove(owner, TRUE)//this should remove references to it, so it can be GCd correctly
+ return ..()
+
+/obj/item/organ/genital/proc/update(removing = FALSE)
+ if(QDELETED(src))
+ return
+ update_size()
+ update_appearance()
+ if(linked_organ_slot || (linked_organ && removing))
+ update_link(removing)
+
+//exposure and through-clothing code
+/mob/living/carbon
+ var/list/exposed_genitals = list() //Keeping track of them so we don't have to iterate through every genitalia and see if exposed
+
+/obj/item/organ/genital/proc/is_exposed()
+ if(!owner || CHECK_BITFIELD(genital_flags, GENITAL_INTERNAL) || CHECK_BITFIELD(genital_flags, GENITAL_HIDDEN))
+ return FALSE
+ if(CHECK_BITFIELD(genital_flags, GENITAL_THROUGH_CLOTHES))
+ return TRUE
+
+ switch(zone) //update as more genitals are added
+ if(BODY_ZONE_CHEST)
+ return owner.is_chest_exposed()
+ if(BODY_ZONE_PRECISE_GROIN)
+ return owner.is_groin_exposed()
+
+/obj/item/organ/genital/proc/toggle_visibility(visibility)
+ switch(visibility)
+ if("Always visible")
+ ENABLE_BITFIELD(genital_flags, GENITAL_THROUGH_CLOTHES)
+ DISABLE_BITFIELD(genital_flags, GENITAL_HIDDEN)
+ if(!(src in owner.exposed_genitals))
+ owner.exposed_genitals += src
+ if("Hidden by clothes")
+ DISABLE_BITFIELD(genital_flags, GENITAL_THROUGH_CLOTHES)
+ DISABLE_BITFIELD(genital_flags, GENITAL_HIDDEN)
+ if(src in owner.exposed_genitals)
+ owner.exposed_genitals -= src
+ if("Always hidden")
+ DISABLE_BITFIELD(genital_flags, GENITAL_THROUGH_CLOTHES)
+ ENABLE_BITFIELD(genital_flags, GENITAL_HIDDEN)
+ if(src in owner.exposed_genitals)
+ owner.exposed_genitals -= src
+
+ if(ishuman(owner)) //recast to use update genitals proc
+ var/mob/living/carbon/human/H = owner
+ H.update_genitals()
+
+/mob/living/carbon/verb/toggle_genitals()
+ set category = "IC"
+ set name = "Expose/Hide genitals"
+ set desc = "Allows you to toggle which genitals should show through clothes or not."
+
+ var/list/genital_list = list()
+ for(var/obj/item/organ/O in internal_organs)
+ if(isgenital(O))
+ var/obj/item/organ/genital/G = O
+ if(!CHECK_BITFIELD(G.genital_flags, GENITAL_INTERNAL))
+ genital_list += G
+ if(!genital_list.len) //There is nothing to expose
+ return
+ //Full list of exposable genitals created
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = input(src, "Choose which genitalia to expose/hide", "Expose/Hide genitals", null) in genital_list
+ if(picked_organ)
+ var/picked_visibility = input(src, "Choose visibility setting", "Expose/Hide genitals", "Hidden by clothes") in list("Always visible", "Hidden by clothes", "Always hidden")
+ picked_organ.toggle_visibility(picked_visibility)
+ return
+
+/obj/item/organ/genital/proc/modify_size(modifier, min = -INFINITY, max = INFINITY)
+ return
+
+/obj/item/organ/genital/proc/update_size()
+ return
+
+/obj/item/organ/genital/proc/update_appearance()
+ if(!owner || owner.stat == DEAD)
+ aroused_state = FALSE
+
+/obj/item/organ/genital/on_life()
+ if(!reagents || !owner)
+ return
+ reagents.maximum_volume = fluid_max_volume
+ if(fluid_id && CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
+ generate_fluid()
+
+/obj/item/organ/genital/proc/generate_fluid()
+ var/amount = fluid_rate
+ if(!reagents.total_volume && amount < 0.1) // Apparently, 0.015 gets rounded down to zero and no reagents are created if we don't start it with 0.1 in the tank.
+ amount += 0.1
+ var/multiplier = fluid_mult
+ if(reagents.total_volume >= 5)
+ multiplier *= 0.5
+ if(reagents.total_volume < reagents.maximum_volume)
+ reagents.isolate_reagent(fluid_id)//remove old reagents if it changed and just clean up generally
+ reagents.add_reagent(fluid_id, (amount * multiplier))//generate the cum
+ return TRUE
+ return FALSE
+
+/obj/item/organ/genital/proc/update_link(removing = FALSE)
+ if(!removing && owner)
+ if(linked_organ)
+ return
+ linked_organ = owner.getorganslot(linked_organ_slot)
+ if(linked_organ)
+ linked_organ.linked_organ = src
+ linked_organ.upon_link()
+ upon_link()
+ return TRUE
+ else
+ if(linked_organ)
+ linked_organ.linked_organ = null
+ linked_organ = null
+ return FALSE
+
+//post organ duo making arrangements.
+/obj/item/organ/genital/proc/upon_link()
+ return
+
+/obj/item/organ/genital/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
+ . = ..()
+ if(.)
+ update()
+ RegisterSignal(owner, COMSIG_MOB_DEATH, .proc/update_appearance)
+
+/obj/item/organ/genital/Remove(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
+ . = ..()
+ if(.)
+ update(TRUE)
+ UnregisterSignal(M, COMSIG_MOB_DEATH)
+
+//proc to give a player their genitals and stuff when they log in
+/mob/living/carbon/human/proc/give_genitals(clean = FALSE)//clean will remove all pre-existing genitals. proc will then give them any genitals that are enabled in their DNA
+ if(clean)
+ for(var/obj/item/organ/genital/G in internal_organs)
+ qdel(G)
+ if (NOGENITALS in dna.species.species_traits)
+ return
+ if(dna.features["has_vag"])
+ give_genital(/obj/item/organ/genital/vagina)
+ if(dna.features["has_womb"])
+ give_genital(/obj/item/organ/genital/womb)
+ if(dna.features["has_balls"])
+ give_genital(/obj/item/organ/genital/testicles)
+ if(dna.features["has_breasts"])
+ give_genital(/obj/item/organ/genital/breasts)
+ if(dna.features["has_cock"])
+ give_genital(/obj/item/organ/genital/penis)
+ /*
+ if(dna.features["has_ovi"])
+ give_genital(/obj/item/organ/genital/ovipositor)
+ if(dna.features["has_eggsack"])
+ give_genital(/obj/item/organ/genital/eggsack)
+ */
+
+/mob/living/carbon/human/proc/give_genital(obj/item/organ/genital/G)
+ if(!dna || (NOGENITALS in dna.species.species_traits) || getorganslot(initial(G.slot)))
+ return FALSE
+ G = new G(null, src)
+ return G
+
+/obj/item/organ/genital/proc/get_features(mob/living/carbon/human/H)
+ return
+
+/datum/species/proc/genitals_layertext(layer)
+ switch(layer)
+ if(GENITALS_BEHIND_LAYER)
+ return "BEHIND"
+ if(GENITALS_FRONT_LAYER)
+ return "FRONT"
+
+//procs to handle sprite overlays being applied to humans
+
+/mob/living/carbon/human/equip_to_slot(obj/item/I, slot)
+ . = ..()
+ if(!. && I && slot && !(slot in GLOB.no_genitals_update_slots)) //the item was successfully equipped, and the chosen slot wasn't merely storage, hands or cuffs.
+ update_genitals()
+
+/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE)
+ var/no_update = FALSE
+ if(!I || I == l_store || I == r_store || I == s_store || I == handcuffed || I == legcuffed || get_held_index_of_item(I)) //stops storages, cuffs and held items from triggering it.
+ no_update = TRUE
+ . = ..()
+ if(!. || no_update)
+ return
+ update_genitals()
+
+/mob/living/carbon/human/proc/update_genitals()
+ if(!QDELETED(src))
+ dna.species.handle_genitals(src)
+
+//Checks to see if organs are new on the mob, and changes their colours so that they don't get crazy colours.
+/mob/living/carbon/human/proc/emergent_genital_call()
+ if(!canbearoused)
+ return FALSE
+
+ var/organCheck = locate(/obj/item/organ/genital) in internal_organs
+ var/breastCheck = getorganslot(ORGAN_SLOT_BREASTS)
+ var/willyCheck = getorganslot(ORGAN_SLOT_PENIS)
+
+ if(organCheck == FALSE)
+ if(ishuman(src) && dna.species.id == "human")
+ dna.features["genitals_use_skintone"] = TRUE
+ dna.species.use_skintones = TRUE
+ if(MUTCOLORS)
+ if(src.dna.species.fixed_mut_color)
+ dna.features["cock_color"] = "[dna.species.fixed_mut_color]"
+ dna.features["breasts_color"] = "[dna.species.fixed_mut_color]"
+ return
+ //So people who haven't set stuff up don't get rainbow surprises.
+ dna.features["cock_color"] = "[dna.features["mcolor"]]"
+ dna.features["breasts_color"] = "[dna.features["mcolor"]]"
+ else //If there's a new organ, make it the same colour.
+ if(breastCheck == FALSE)
+ dna.features["breasts_color"] = dna.features["cock_color"]
+ else if (willyCheck == FALSE)
+ dna.features["cock_color"] = dna.features["breasts_color"]
+ return TRUE
+
+/datum/species/proc/handle_genitals(mob/living/carbon/human/H)//more like handle sadness
+ if(!H)//no args
+ CRASH("H = null")
+ if(!LAZYLEN(H.internal_organs) || ((NOGENITALS in species_traits) && !H.genital_override) || HAS_TRAIT(H, TRAIT_HUSK))
+ return
+ var/list/relevant_layers = list(GENITALS_BEHIND_LAYER, GENITALS_FRONT_LAYER)
+
+ for(var/L in relevant_layers) //Less hardcode
+ H.remove_overlay(L)
+ H.remove_overlay(GENITALS_EXPOSED_LAYER)
+ //start scanning for genitals
+
+ var/list/gen_index[GENITAL_LAYER_INDEX_LENGTH]
+ var/list/genitals_to_add
+ var/list/fully_exposed
+ for(var/obj/item/organ/genital/G in H.internal_organs)
+ if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes
+ LAZYADD(gen_index[G.layer_index], G)
+ for(var/L in gen_index)
+ if(L) //skip nulls
+ LAZYADD(genitals_to_add, L)
+ if(!genitals_to_add)
+ return
+ //Now we added all genitals that aren't internal and should be rendered
+ //start applying overlays
+ for(var/layer in relevant_layers)
+ var/list/standing = list()
+ var/layertext = genitals_layertext(layer)
+ for(var/A in genitals_to_add)
+ var/obj/item/organ/genital/G = A
+ var/datum/sprite_accessory/S
+ var/size = G.size
+ var/aroused_state = G.aroused_state
+ switch(G.type)
+ if(/obj/item/organ/genital/penis)
+ S = GLOB.cock_shapes_list[G.shape]
+ if(/obj/item/organ/genital/testicles)
+ S = GLOB.balls_shapes_list[G.shape]
+ if(/obj/item/organ/genital/vagina)
+ S = GLOB.vagina_shapes_list[G.shape]
+ if(/obj/item/organ/genital/breasts)
+ S = GLOB.breasts_shapes_list[G.shape]
+
+ if(!S || S.icon_state == "none")
+ continue
+
+ var/mutable_appearance/genital_overlay = mutable_appearance(S.icon, layer = -layer)
+ genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]_[aroused_state]_[layertext]"
+
+ if(S.center)
+ genital_overlay = center_image(genital_overlay, S.dimension_x, S.dimension_y)
+
+ if(use_skintones && H.dna.features["genitals_use_skintone"])
+ genital_overlay.color = "#[skintone2hex(H.skin_tone)]"
+ genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]-s_[aroused_state]_[layertext]"
+ else
+ switch(S.color_src)
+ if("cock_color")
+ genital_overlay.color = "#[H.dna.features["cock_color"]]"
+ if("balls_color")
+ genital_overlay.color = "#[H.dna.features["balls_color"]]"
+ if("breasts_color")
+ genital_overlay.color = "#[H.dna.features["breasts_color"]]"
+ if("vag_color")
+ genital_overlay.color = "#[H.dna.features["vag_color"]]"
+
+ if(layer == GENITALS_FRONT_LAYER && CHECK_BITFIELD(G.genital_flags, GENITAL_THROUGH_CLOTHES))
+ genital_overlay.layer = -GENITALS_EXPOSED_LAYER
+ LAZYADD(fully_exposed, genital_overlay) // to be added to a layer with higher priority than clothes, hence the name of the bitflag.
+ else
+ standing += genital_overlay
+
+ if(LAZYLEN(standing))
+ H.overlays_standing[layer] = standing
+
+ if(LAZYLEN(fully_exposed))
+ H.overlays_standing[GENITALS_EXPOSED_LAYER] = fully_exposed
+ H.apply_overlay(GENITALS_EXPOSED_LAYER)
+
+ for(var/L in relevant_layers)
+ H.apply_overlay(L)
+
diff --git a/modular_citadel/code/modules/arousal/organs/genitals_sprite_accessories.dm b/modular_citadel/code/modules/arousal/genitals_sprite_accessories.dm
similarity index 89%
rename from modular_citadel/code/modules/arousal/organs/genitals_sprite_accessories.dm
rename to modular_citadel/code/modules/arousal/genitals_sprite_accessories.dm
index b37bef7dbc..0c1763b8e2 100644
--- a/modular_citadel/code/modules/arousal/organs/genitals_sprite_accessories.dm
+++ b/modular_citadel/code/modules/arousal/genitals_sprite_accessories.dm
@@ -5,11 +5,9 @@
//DICKS,COCKS,PENISES,WHATEVER YOU WANT TO CALL THEM
/datum/sprite_accessory/penis
icon = 'modular_citadel/icons/obj/genitals/penis_onmob.dmi'
- icon_state = null
name = "penis" //the preview name of the accessory
- gender_specific = 0 //Might be needed somewhere down the list.
color_src = "cock_color"
- locked = 0
+ alt_aroused = TRUE
/datum/sprite_accessory/penis/human
icon_state = "human"
@@ -75,27 +73,21 @@
icon_state = "testicle"
name = "testicle" //the preview name of the accessory
color_src = "balls_color"
- locked = 0
/datum/sprite_accessory/testicles/hidden
- icon_state = "hidden"
+ icon_state = "none"
name = "Hidden"
- alt_aroused = TRUE
/datum/sprite_accessory/testicles/single
icon_state = "single"
name = "Single"
- alt_aroused = TRUE
//Vaginas
/datum/sprite_accessory/vagina
icon = 'modular_citadel/icons/obj/genitals/vagina_onmob.dmi'
icon_state = null
name = "vagina"
- gender_specific = 0
color_src = "vag_color"
- locked = 0
- alt_aroused = FALSE //if this is TRUE, then the genitals will use an alternate sprite for aroused states
/datum/sprite_accessory/vagina/human
icon_state = "human"
@@ -131,35 +123,28 @@
//BREASTS BE HERE
/datum/sprite_accessory/breasts
icon = 'modular_citadel/icons/obj/genitals/breasts_onmob.dmi'
- icon_state = null
name = "breasts"
- gender_specific = 0
color_src = "breasts_color"
- locked = 0
+ alt_aroused = TRUE
/datum/sprite_accessory/breasts/pair
icon_state = "pair"
name = "Pair"
- alt_aroused = TRUE
/datum/sprite_accessory/breasts/quad
icon_state = "quad"
name = "Quad"
- alt_aroused = TRUE
/datum/sprite_accessory/breasts/sextuple
icon_state = "sextuple"
name = "Sextuple"
- alt_aroused = TRUE
//OVIPOSITORS BE HERE
/datum/sprite_accessory/ovipositor
icon = 'modular_citadel/icons/obj/genitals/penis_onmob.dmi'
icon_state = null
name = "Ovipositor" //the preview name of the accessory
- gender_specific = 0 //Might be needed somewhere down the list.
color_src = "cock_color"
- locked = 0
/datum/sprite_accessory/ovipositor/knotted
icon_state = "knotted"
diff --git a/modular_citadel/code/modules/arousal/organs/breasts.dm b/modular_citadel/code/modules/arousal/organs/breasts.dm
index 3df2218766..6299f68b6c 100644
--- a/modular_citadel/code/modules/arousal/organs/breasts.dm
+++ b/modular_citadel/code/modules/arousal/organs/breasts.dm
@@ -1,50 +1,29 @@
/obj/item/organ/genital/breasts
- name = "breasts"
- desc = "Female milk producing organs."
- icon_state = "breasts"
- icon = 'modular_citadel/icons/obj/genitals/breasts.dmi'
- zone = "chest"
- slot = "breasts"
- w_class = 3
- size = BREASTS_SIZE_DEF //SHOULD BE A LETTER, starts as a number...???
- var/cached_size = null //for enlargement SHOULD BE A NUMBER
- var/prev_size //For flavour texts SHOULD BE A LETTER
- //var/breast_sizes = list ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "huge", "flat")
- var/breast_values = list ("a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5, "f" = 6, "g" = 7, "h" = 8, "i" = 9, "j" = 10, "k" = 11, "l" = 12, "m" = 13, "n" = 14, "o" = 15, "huge" = 16, "flat" = 0)
- var/statuscheck = FALSE
- fluid_id = "milk"
- var/amount = 2
- producing = TRUE
- shape = "Pair"
- can_masturbate_with = TRUE
- masturbation_verb = "massage"
- can_climax = TRUE
- fluid_transfer_factor = 0.5
+ name = "breasts"
+ desc = "Female milk producing organs."
+ icon_state = "breasts"
+ icon = 'modular_citadel/icons/obj/genitals/breasts.dmi'
+ zone = BODY_ZONE_CHEST
+ slot = ORGAN_SLOT_BREASTS
+ size = "c" //refer to the breast_values static list below for the cups associated number values
+ fluid_id = "milk"
+ shape = "pair"
+ genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_FUID_PRODUCTION
+ masturbation_verb = "massage"
+ orgasm_verb = "leaking"
+ fluid_transfer_factor = 0.5
+ var/static/list/breast_values = list("a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5, "f" = 6, "g" = 7, "h" = 8, "i" = 9, "j" = 10, "k" = 11, "l" = 12, "m" = 13, "n" = 14, "o" = 15, "huge" = 16, "flat" = 0)
+ var/cached_size //these two vars pertain size modifications and so should be expressed in NUMBERS.
+ var/prev_size //former cached_size value, to allow update_size() to early return should be there no significant changes.
-/obj/item/organ/genital/breasts/on_life()
- if(QDELETED(src))
- return
- if(!reagents || !owner)
- return
- reagents.maximum_volume = fluid_max_volume
- if(fluid_id && producing)
- if(reagents.total_volume == 0) // Apparently, 0.015 gets rounded down to zero and no reagents are created if we don't start it with 0.1 in the tank.
- fluid_rate = 0.1
- else
- fluid_rate = CUM_RATE
- if(reagents.total_volume >= 5)
- fluid_mult = 0.5
- else
- fluid_mult = 1
- generate_milk()
-
-/obj/item/organ/genital/breasts/proc/generate_milk()
- if(owner.stat == DEAD)
- return FALSE
- reagents.isolate_reagent(fluid_id)
- reagents.add_reagent(fluid_id, (fluid_mult * fluid_rate))
+/obj/item/organ/genital/breasts/Initialize(mapload, mob/living/carbon/human/H)
+ if(!H)
+ cached_size = breast_values[size]
+ prev_size = cached_size
+ return ..()
/obj/item/organ/genital/breasts/update_appearance()
+ . = ..()
var/lowershape = lowertext(shape)
switch(lowershape)
if("pair")
@@ -55,16 +34,15 @@
desc = "You see three sets of breasts, running from their chest to their belly."
else
desc = "You see some breasts, they seem to be quite exotic."
- if(cached_size > 16)
+ if(size == "huge")
desc = "You see [pick("some serious honkers", "a real set of badonkers", "some dobonhonkeros", "massive dohoonkabhankoloos", "two big old tonhongerekoogers", "a couple of giant bonkhonagahoogs", "a pair of humongous hungolomghnonoloughongous")]. Their volume is way beyond cupsize now, measuring in about [round(cached_size)]cm in diameter."
- else if (!isnum(size))
+ else
if (size == "flat")
desc += " They're very small and flatchested, however."
else
desc += " You estimate that they're [uppertext(size)]-cups."
- //string = "breasts_[lowertext(shape)]_[size]-s"
- if(producing && aroused_state)
+ if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION) && aroused_state)
desc += " They're leaking [fluid_id]."
var/string
if(owner)
@@ -80,57 +58,70 @@
var/mob/living/carbon/human/H = owner
icon_state = sanitize_text(string)
H.update_genitals()
-
icon_state = sanitize_text(string)
-
//Allows breasts to grow and change size, with sprite changes too.
//maximum wah
//Comical sizes slow you down in movement and actions.
//Rediculous sizes makes you more cumbersome.
//this is far too lewd wah
-/obj/item/organ/genital/breasts/update_size()//wah
-
- if(!ishuman(owner) || !owner)
+/obj/item/organ/genital/breasts/modify_size(modifier, min = -INFINITY, max = INFINITY)
+ var/new_value = CLAMP(cached_size + modifier, min, max)
+ if(new_value == cached_size)
return
- if(cached_size < 0)//I don't actually know what round() does to negative numbers, so to be safe!!fixed
- to_chat(owner, "You feel your breasts shrinking away from your body as your chest flattens out.")
- src.Remove(owner)
- switch(round(cached_size))
- if(0) //If flatchested
- size = "flat"
- if(owner.has_status_effect(/datum/status_effect/chem/breast_enlarger))
- owner.remove_status_effect(/datum/status_effect/chem/breast_enlarger)
- statuscheck = FALSE
- if(1 to 8) //If modest size
- size = breast_values[round(cached_size)]
- if(owner.has_status_effect(/datum/status_effect/chem/breast_enlarger))
- owner.remove_status_effect(/datum/status_effect/chem/breast_enlarger)
- statuscheck = FALSE
- if(9 to 15) //If massive
- size = breast_values[round(cached_size)]
- if(!owner.has_status_effect(/datum/status_effect/chem/breast_enlarger))
- owner.apply_status_effect(/datum/status_effect/chem/breast_enlarger)
- statuscheck = TRUE
- if(16 to INFINITY) //if Rediculous
- size = cached_size
+ prev_size = cached_size
+ cached_size = new_value
+ update()
- if(round(cached_size) < 16)//Because byond doesn't count from 0, I have to do this.
- if (prev_size == 0)
- prev_size = "flat"
- if(size == 0)//Bloody byond with it's counting from 1
+/obj/item/organ/genital/breasts/update_size()//wah
+ var/rounded_cached = round(cached_size)
+ if(cached_size < 0)//I don't actually know what round() does to negative numbers, so to be safe!!fixed
+ if(owner)
+ to_chat(owner, "You feel your breasts shrinking away from your body as your chest flattens out.")
+ QDEL_IN(src, 1)
+ return
+ var/enlargement = FALSE
+ switch(rounded_cached)
+ if(0) //flatchested
size = "flat"
- if(isnum(prev_size))
- prev_size = breast_values[prev_size]
- if (breast_values[size] > breast_values[prev_size])
- to_chat(owner, "Your breasts [pick("swell up to", "flourish into", "expand into", "burst forth into", "grow eagerly into", "amplify into")] a [uppertext(size)]-cup.")
- var/mob/living/carbon/human/H = owner
- H.Force_update_genitals()
- else if ((breast_values[size] < breast_values[prev_size]) && (breast_values[size] > 0.5))
- to_chat(owner, "Your breasts [pick("shrink down to", "decrease into", "diminish into", "deflate into", "shrivel regretfully into", "contracts into")] a [uppertext(size)]-cup.")
- var/mob/living/carbon/human/H = owner
- H.Force_update_genitals()
- prev_size = size
- else if (cached_size >= 16)
- size = "huge"
+ if(1 to 8) //modest
+ size = breast_values[rounded_cached]
+ if(9 to 15) //massive
+ size = breast_values[rounded_cached]
+ enlargement = TRUE
+ if(16 to INFINITY) //rediculous
+ size = "huge"
+ enlargement = TRUE
+ if(owner)
+ var/status_effect = owner.has_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
+ if(enlargement && !status_effect)
+ owner.apply_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
+ else if(!enlargement && status_effect)
+ owner.remove_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
+
+ if(rounded_cached < 16 && owner)//Because byond doesn't count from 0, I have to do this.
+ var/mob/living/carbon/human/H = owner
+ var/r_prev_size = round(prev_size)
+ if (rounded_cached > r_prev_size)
+ to_chat(H, "Your breasts [pick("swell up to", "flourish into", "expand into", "burst forth into", "grow eagerly into", "amplify into")] a [uppertext(size)]-cup.")
+ else if (rounded_cached < r_prev_size)
+ to_chat(H, "Your breasts [pick("shrink down to", "decrease into", "diminish into", "deflate into", "shrivel regretfully into", "contracts into")] a [uppertext(size)]-cup.")
+
+/obj/item/organ/genital/breasts/get_features(mob/living/carbon/human/H)
+ var/datum/dna/D = H.dna
+ if(D.species.use_skintones && D.features["genitals_use_skintone"])
+ color = "#[skintone2hex(H.skin_tone)]"
+ else
+ color = "#[D.features["breasts_color"]]"
+ size = D.features["breasts_size"]
+ shape = D.features["breasts_shape"]
+ fluid_id = D.features["breasts_fluid"]
+ if(!D.features["breasts_producing"])
+ DISABLE_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION)
+ if(!isnum(size))
+ cached_size = breast_values[size]
+ else
+ cached_size = size
+ size = breast_values[size]
+ prev_size = cached_size
diff --git a/modular_citadel/code/modules/arousal/organs/eggsack.dm b/modular_citadel/code/modules/arousal/organs/eggsack.dm
index 402d246e40..ebdefd2371 100644
--- a/modular_citadel/code/modules/arousal/organs/eggsack.dm
+++ b/modular_citadel/code/modules/arousal/organs/eggsack.dm
@@ -1,14 +1,14 @@
/obj/item/organ/genital/eggsack
- name = "Egg sack"
- desc = "An egg producing reproductive organ."
- icon_state = "egg_sack"
- icon = 'modular_citadel/icons/obj/genitals/ovipositor.dmi'
- zone = "groin"
- slot = "testicles"
- color = null //don't use the /genital color since it already is colored
- internal = TRUE
+ name = "Egg sack"
+ desc = "An egg producing reproductive organ."
+ icon_state = "egg_sack"
+ icon = 'modular_citadel/icons/obj/genitals/ovipositor.dmi'
+ zone = BODY_ZONE_PRECISE_GROIN
+ slot = ORGAN_SLOT_TESTICLES
+ genital_flags = GENITAL_INTERNAL|GENITAL_BLACKLISTED //unimplemented
+ linked_organ_slot = ORGAN_SLOT_PENIS
+ color = null //don't use the /genital color since it already is colored
var/egg_girth = EGG_GIRTH_DEF
var/cum_mult = CUM_RATE_MULT
var/cum_rate = CUM_RATE
var/cum_efficiency = CUM_EFFICIENCY
- var/obj/item/organ/ovipositor/linked_ovi
diff --git a/modular_citadel/code/modules/arousal/organs/genitals.dm b/modular_citadel/code/modules/arousal/organs/genitals.dm
deleted file mode 100644
index 3abe7ecb1f..0000000000
--- a/modular_citadel/code/modules/arousal/organs/genitals.dm
+++ /dev/null
@@ -1,414 +0,0 @@
-/obj/item/organ/genital
- color = "#fcccb3"
- w_class = WEIGHT_CLASS_NORMAL
- var/shape = "Human" //Changed to be uppercase, let me know if this breaks everything..!!
- var/sensitivity = AROUSAL_START_VALUE
- var/list/genital_flags = list()
- var/can_masturbate_with = FALSE
- var/masturbation_verb = "masturbate"
- var/can_climax = FALSE
- var/fluid_transfer_factor = 0.0 //How much would a partner get in them if they climax using this?
- var/size = 2 //can vary between num or text, just used in icon_state strings
- var/fluid_id = null
- var/fluid_max_volume = 15
- var/fluid_efficiency = 1
- var/fluid_rate = 1
- var/fluid_mult = 1
- var/producing = FALSE
- var/aroused_state = FALSE //Boolean used in icon_state strings
- var/aroused_amount = 50 //This is a num from 0 to 100 for arousal percentage for when to use arousal state icons.
- var/obj/item/organ/genital/linked_organ
- var/through_clothes = FALSE
- var/internal = FALSE
- var/hidden = FALSE
-
-/obj/item/organ/genital/Initialize()
- . = ..()
- if(!reagents)
- create_reagents(fluid_max_volume)
- update()
-
-/obj/item/organ/genital/Destroy()
- remove_ref()
- if(owner)
- Remove(owner, 1)//this should remove references to it, so it can be GCd correctly
- update_link()//this should remove any other links it has
- return ..()
-
-/obj/item/organ/genital/proc/update()
- if(QDELETED(src))
- return
- update_size()
- update_appearance()
- update_link()
-
-//exposure and through-clothing code
-/mob/living/carbon
- var/list/exposed_genitals = list() //Keeping track of them so we don't have to iterate through every genitalia and see if exposed
-
-/obj/item/organ/genital/proc/is_exposed()
- if(!owner)
- return FALSE
- if(hidden)
- return FALSE
- if(internal)
- return FALSE
- if(through_clothes)
- return TRUE
-
- switch(zone) //update as more genitals are added
- if("chest")
- return owner.is_chest_exposed()
- if("groin")
- return owner.is_groin_exposed()
-
- return FALSE
-
-/obj/item/organ/genital/proc/toggle_visibility(visibility)
- switch(visibility)
- if("Always visible")
- through_clothes = TRUE
- hidden = FALSE
- if(!(src in owner.exposed_genitals))
- owner.exposed_genitals += src
- if("Hidden by clothes")
- through_clothes = FALSE
- hidden = TRUE
- if(src in owner.exposed_genitals)
- owner.exposed_genitals -= src
- if("Always hidden")
- through_clothes = FALSE
- hidden = TRUE
- if(src in owner.exposed_genitals)
- owner.exposed_genitals -= src
-
- if(ishuman(owner)) //recast to use update genitals proc
- var/mob/living/carbon/human/H = owner
- H.update_genitals()
-
-/mob/living/carbon/verb/toggle_genitals()
- set category = "IC"
- set name = "Expose/Hide genitals"
- set desc = "Allows you to toggle which genitals should show through clothes or not."
-
- var/list/genital_list = list()
- for(var/obj/item/organ/O in internal_organs)
- if(isgenital(O))
- var/obj/item/organ/genital/G = O
- if(!G.internal)
- genital_list += G
- if(!genital_list.len) //There is nothing to expose
- return
- //Full list of exposable genitals created
- var/obj/item/organ/genital/picked_organ
- picked_organ = input(src, "Choose which genitalia to expose/hide", "Expose/Hide genitals", null) in genital_list
- if(picked_organ)
- var/picked_visibility = input(src, "Choose visibility setting", "Expose/Hide genitals", "Hidden by clothes") in list("Always visible", "Hidden by clothes", "Always hidden")
- picked_organ.toggle_visibility(picked_visibility)
- return
-
-
-
-
-/obj/item/organ/genital/proc/update_size()
- return
-
-/obj/item/organ/genital/proc/update_appearance()
- return
-
-/obj/item/organ/genital/proc/update_link()
- return
-
-/obj/item/organ/genital/proc/remove_ref()
- if(linked_organ)
- linked_organ.linked_organ = null
- linked_organ = null
-
-/obj/item/organ/genital/Insert(mob/living/carbon/M, special = 0)
- ..()
- update()
-
-/obj/item/organ/genital/Remove(mob/living/carbon/M, special = 0)
- ..()
- update()
-
-//proc to give a player their genitals and stuff when they log in
-/mob/living/carbon/human/proc/give_genitals(clean=0)//clean will remove all pre-existing genitals. proc will then give them any genitals that are enabled in their DNA
- if(clean)
- var/obj/item/organ/genital/GtoClean
- for(GtoClean in internal_organs)
- qdel(GtoClean)
- if (NOGENITALS in dna.species.species_traits)
- return
- //Order should be very important. FIRST vagina, THEN testicles, THEN penis, as this affects the order they are rendered in.
- if(dna.features["has_vag"])
- give_vagina()
- if(dna.features["has_womb"])
- give_womb()
- if(dna.features["has_balls"])
- give_balls()
- if(dna.features["has_breasts"]) // since we have multi-boobs as a thing, we'll want to at least draw over these. but not over the pingas.
- give_breasts()
- if(dna.features["has_cock"])
- give_penis()
- if(dna.features["has_ovi"])
- give_ovipositor()
- if(dna.features["has_eggsack"])
- give_eggsack()
-
-/mob/living/carbon/human/proc/give_penis()
- if(!dna)
- return FALSE
- if(NOGENITALS in dna.species.species_traits)
- return FALSE
- if(!getorganslot("penis"))
- var/obj/item/organ/genital/penis/P = new
- P.Insert(src)
- if(P)
- if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
- P.color = "#[skintone2hex(skin_tone)]"
- else
- P.color = "#[dna.features["cock_color"]]"
- P.length = dna.features["cock_length"]
- P.girth_ratio = dna.features["cock_girth_ratio"]
- P.shape = dna.features["cock_shape"]
- P.prev_length = P.length
- P.cached_length = P.length
- P.update()
-
-/mob/living/carbon/human/proc/give_balls()
- if(!dna)
- return FALSE
- if(NOGENITALS in dna.species.species_traits)
- return FALSE
- if(!getorganslot("testicles"))
- var/obj/item/organ/genital/testicles/T = new
- T.Insert(src)
- if(T)
- if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
- T.color = "#[skintone2hex(skin_tone)]"
- else
- T.color = "#[dna.features["balls_color"]]"
- T.size = dna.features["balls_size"]
- T.sack_size = dna.features["balls_sack_size"]
- T.shape = dna.features["balls_shape"]
- if(dna.features["balls_shape"] == "Hidden")
- T.internal = TRUE
- else
- T.internal = FALSE
- T.fluid_id = dna.features["balls_fluid"]
- T.fluid_rate = dna.features["balls_cum_rate"]
- T.fluid_mult = dna.features["balls_cum_mult"]
- T.fluid_efficiency = dna.features["balls_efficiency"]
- T.update()
-
-/mob/living/carbon/human/proc/give_breasts()
- if(!dna)
- return FALSE
- if(NOGENITALS in dna.species.species_traits)
- return FALSE
- if(!getorganslot("breasts"))
- var/obj/item/organ/genital/breasts/B = new
- B.Insert(src)
- if(B)
- if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
- B.color = "#[skintone2hex(skin_tone)]"
- else
- B.color = "#[dna.features["breasts_color"]]"
- B.size = dna.features["breasts_size"]
- if(!isnum(B.size))
- if(B.size == "flat")
- B.cached_size = 0
- B.prev_size = 0
- else if (B.cached_size == "huge")
- B.prev_size = "huge"
- else
- B.cached_size = B.breast_values[B.size]
- B.prev_size = B.size
- else
- B.cached_size = B.size
- B.prev_size = B.size
- B.shape = dna.features["breasts_shape"]
- B.fluid_id = dna.features["breasts_fluid"]
- B.producing = dna.features["breasts_producing"]
- B.update()
-
-
-/mob/living/carbon/human/proc/give_ovipositor()
- return
-/mob/living/carbon/human/proc/give_eggsack()
- return
-
-/mob/living/carbon/human/proc/give_vagina()
- if(!dna)
- return FALSE
- if(NOGENITALS in dna.species.species_traits)
- return FALSE
- if(!getorganslot("vagina"))
- var/obj/item/organ/genital/vagina/V = new
- V.Insert(src)
- if(V)
- if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
- V.color = "#[skintone2hex(skin_tone)]"
- else
- V.color = "[dna.features["vag_color"]]"
- V.shape = "[dna.features["vag_shape"]]"
- V.update()
-
-/mob/living/carbon/human/proc/give_womb()
- if(!dna)
- return FALSE
- if(NOGENITALS in dna.species.species_traits)
- return FALSE
- if(!getorganslot("womb"))
- var/obj/item/organ/genital/womb/W = new
- W.Insert(src)
- if(W)
- W.update()
-
-
-/datum/species/proc/genitals_layertext(layer)
- switch(layer)
- if(GENITALS_BEHIND_LAYER)
- return "BEHIND"
- /*if(GENITALS_ADJ_LAYER)
- return "ADJ"*/
- if(GENITALS_FRONT_LAYER)
- return "FRONT"
-
-//procs to handle sprite overlays being applied to humans
-
-/obj/item/equipped(mob/user, slot)
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- H.update_genitals()
- ..()
-
-/mob/living/carbon/human/doUnEquip(obj/item/I, force)
- . = ..()
- if(!.)
- return
- update_genitals()
-
-/mob/living/carbon/human/proc/update_genitals()
- if(src && !QDELETED(src))
- dna.species.handle_genitals(src)
-
-//fermichem procs
-/mob/living/carbon/human/proc/Force_update_genitals(mob/living/carbon/human/H) //called in fermiChem
- dna.species.handle_genitals(src)//should work.
- //dna.species.handle_breasts(src)
-
-//Checks to see if organs are new on the mob, and changes their colours so that they don't get crazy colours.
-/mob/living/carbon/human/proc/emergent_genital_call()
- var/organCheck = FALSE
- var/breastCheck = FALSE
- var/willyCheck = FALSE
- if(!canbearoused)
- ADD_TRAIT(src, TRAIT_PHARMA, "pharma")//Prefs prevent unwanted organs.
- return
- for(var/obj/item/organ/O in internal_organs)
- if(istype(O, /obj/item/organ/genital))
- organCheck = TRUE
- if(/obj/item/organ/genital/penis)
- //dna.features["has_cock"] = TRUE
- willyCheck = TRUE
- if(/obj/item/organ/genital/breasts)
- //dna.features["has_breasts"] = TRUE//Goddamnit get in there.
- breastCheck = TRUE
- if(organCheck == FALSE)
- if(ishuman(src) && dna.species.id == "human")
- dna.features["genitals_use_skintone"] = TRUE
- dna.species.use_skintones = TRUE
- if(MUTCOLORS)
- if(src.dna.species.fixed_mut_color)
- dna.features["cock_color"] = "[src.dna.species.fixed_mut_color]"
- dna.features["breasts_color"] = "[src.dna.species.fixed_mut_color]"
- return
- //So people who haven't set stuff up don't get rainbow surprises.
- dna.features["cock_color"] = "[dna.features["mcolor"]]"
- dna.features["breasts_color"] = "[dna.features["mcolor"]]"
- else //If there's a new organ, make it the same colour.
- if(breastCheck == FALSE)
- dna.features["breasts_color"] = dna.features["cock_color"]
- else if (willyCheck == FALSE)
- dna.features["cock_color"] = dna.features["breasts_color"]
- return
-
-/datum/species/proc/handle_genitals(mob/living/carbon/human/H)//more like handle sadness
- if(!H)//no args
- CRASH("H = null")
- if(!LAZYLEN(H.internal_organs))//if they have no organs, we're done
- return
- if((NOGENITALS in species_traits) && (H.genital_override = FALSE))//golems and such - things that shouldn't
- return
- if(HAS_TRAIT(H, TRAIT_HUSK))
- return
- var/list/genitals_to_add = list()
- var/list/relevant_layers = list(GENITALS_BEHIND_LAYER, GENITALS_FRONT_LAYER) //GENITALS_ADJ_LAYER removed
- var/list/standing = list()
- var/size
- var/aroused_state
-
- for(var/L in relevant_layers) //Less hardcode
- H.remove_overlay(L)
- //start scanning for genitals
- for(var/obj/item/organ/O in H.internal_organs)
- if(isgenital(O))
- var/obj/item/organ/genital/G = O
- if(G.hidden)
- return //we're gunna just hijack this for updates.
- if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes
- genitals_to_add += H.getorganslot(G.slot)
- //Now we added all genitals that aren't internal and should be rendered
- //start applying overlays
- for(var/layer in relevant_layers)
- var/layertext = genitals_layertext(layer)
- for(var/obj/item/organ/genital/G in genitals_to_add)
- var/datum/sprite_accessory/S
- size = G.size
- aroused_state = G.aroused_state
- switch(G.type)
- if(/obj/item/organ/genital/penis)
- S = GLOB.cock_shapes_list[G.shape]
- if(/obj/item/organ/genital/testicles)
- S = GLOB.balls_shapes_list[G.shape]
- if(/obj/item/organ/genital/vagina)
- S = GLOB.vagina_shapes_list[G.shape]
- if(/obj/item/organ/genital/breasts)
- S = GLOB.breasts_shapes_list[G.shape]
-
-
-
-
- if(!S || S.icon_state == "none")
- continue
-
- var/mutable_appearance/genital_overlay = mutable_appearance(S.icon, layer = -layer)
- genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]_[aroused_state]_[layertext]"
-
- if(S.center)
- genital_overlay = center_image(genital_overlay, S.dimension_x, S.dimension_y)
-
- if(use_skintones && H.dna.features["genitals_use_skintone"])
- genital_overlay.color = "#[skintone2hex(H.skin_tone)]"
- genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]-s_[aroused_state]_[layertext]"
- else
- switch(S.color_src)
- if("cock_color")
- genital_overlay.color = "#[H.dna.features["cock_color"]]"
- if("balls_color")
- genital_overlay.color = "#[H.dna.features["balls_color"]]"
- if("breasts_color")
- genital_overlay.color = "#[H.dna.features["breasts_color"]]"
- if("vag_color")
- genital_overlay.color = "#[H.dna.features["vag_color"]]"
-
- standing += genital_overlay
-
- if(LAZYLEN(standing))
- H.overlays_standing[layer] = standing.Copy()
- standing = list()
-
- for(var/L in relevant_layers)
- H.apply_overlay(L)
diff --git a/modular_citadel/code/modules/arousal/organs/ovipositor.dm b/modular_citadel/code/modules/arousal/organs/ovipositor.dm
index 76bf60d93c..c26424d296 100644
--- a/modular_citadel/code/modules/arousal/organs/ovipositor.dm
+++ b/modular_citadel/code/modules/arousal/organs/ovipositor.dm
@@ -3,14 +3,14 @@
desc = "An egg laying reproductive organ."
icon_state = "ovi_knotted_2"
icon = 'modular_citadel/icons/obj/genitals/ovipositor.dmi'
- zone = "groin"
- slot = "penis"
- w_class = 3
+ zone = BODY_ZONE_PRECISE_GROIN
+ slot = ORGAN_SLOT_PENIS
+ genital_flags = GENITAL_BLACKLISTED //unimplemented
shape = "knotted"
size = 3
+ layer_index = PENIS_LAYER_INDEX
var/length = 6 //inches
var/girth = 0
var/girth_ratio = COCK_GIRTH_RATIO_DEF //citadel_defines.dm for these defines
var/knot_girth_ratio = KNOT_GIRTH_RATIO_DEF
var/list/oviflags = list()
- var/obj/item/organ/eggsack/linked_eggsack
diff --git a/modular_citadel/code/modules/arousal/organs/penis.dm b/modular_citadel/code/modules/arousal/organs/penis.dm
index 43a512acf6..17cd35c144 100644
--- a/modular_citadel/code/modules/arousal/organs/penis.dm
+++ b/modular_citadel/code/modules/arousal/organs/penis.dm
@@ -1,77 +1,78 @@
/obj/item/organ/genital/penis
- name = "penis"
- desc = "A male reproductive organ."
- icon_state = "penis"
- icon = 'modular_citadel/icons/obj/genitals/penis.dmi'
- zone = "groin"
- slot = ORGAN_SLOT_PENIS
- can_masturbate_with = TRUE
- masturbation_verb = "stroke"
- can_climax = TRUE
- fluid_transfer_factor = 0.5
- size = 2 //arbitrary value derived from length and girth for sprites.
- var/length = 6 //inches
- var/cached_length //used to detect a change in length
- var/girth = 4.38
- var/girth_ratio = COCK_GIRTH_RATIO_DEF //0.73; check citadel_defines.dm
- var/knot_girth_ratio = KNOT_GIRTH_RATIO_DEF
- var/list/dickflags = list()
- var/list/knotted_types = list("knotted", "barbed, knotted")
- var/prev_length = 6 //really should be renamed to prev_length
+ name = "penis"
+ desc = "A male reproductive organ."
+ icon_state = "penis"
+ icon = 'modular_citadel/icons/obj/genitals/penis.dmi'
+ zone = BODY_ZONE_PRECISE_GROIN
+ slot = ORGAN_SLOT_PENIS
+ masturbation_verb = "stroke"
+ genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH
+ linked_organ_slot = ORGAN_SLOT_TESTICLES
+ fluid_transfer_factor = 0.5
+ size = 2 //arbitrary value derived from length and girth for sprites.
+ layer_index = PENIS_LAYER_INDEX
+ var/length = 6 //inches
+ var/prev_length = 6 //really should be renamed to prev_length
+ var/girth = 4.38
+ var/girth_ratio = COCK_GIRTH_RATIO_DEF //0.73; check citadel_defines.dm
-/obj/item/organ/genital/penis/Initialize()
- . = ..()
- /* I hate genitals.*/
-
-/obj/item/organ/genital/penis/update_size()
- var/mob/living/carbon/human/o = owner
- if(!ishuman(o) || !o)
+/obj/item/organ/genital/penis/modify_size(modifier, min = -INFINITY, max = INFINITY)
+ var/new_value = CLAMP(length + modifier, min, max)
+ if(new_value == length)
return
- if(cached_length < 0)//I don't actually know what round() does to negative numbers, so to be safe!!
- var/obj/item/organ/genital/penis/P = o.getorganslot("penis")
- to_chat(o, "You feel your tallywacker shrinking away from your body as your groin flattens out!")
- P.Remove(o)
- switch(round(cached_length))
- if(0 to 4) //If modest size
- length = cached_length
- size = 1
- if(owner.has_status_effect(/datum/status_effect/chem/penis_enlarger))
- o.remove_status_effect(/datum/status_effect/chem/penis_enlarger)
- if(5 to 10) //If modest size
- length = cached_length
- size = 2
- if(owner.has_status_effect(/datum/status_effect/chem/penis_enlarger))
- o.remove_status_effect(/datum/status_effect/chem/penis_enlarger)
- if(11 to 20) //If massive
- length = cached_length
- size = 3
- if(owner.has_status_effect(/datum/status_effect/chem/penis_enlarger))
- o.remove_status_effect(/datum/status_effect/chem/penis_enlarger)
- if(21 to 35) //If massive and due for large effects
- length = cached_length
- size = 3
- if(!owner.has_status_effect(/datum/status_effect/chem/penis_enlarger))
- o.apply_status_effect(/datum/status_effect/chem/penis_enlarger)
- if(36 to INFINITY) //If comical
- length = cached_length
- size = 4 //no new sprites for anything larger yet
- if(!owner.has_status_effect(/datum/status_effect/chem/penis_enlarger))
- o.apply_status_effect(/datum/status_effect/chem/penis_enlarger)
-
- if (round(length) > round(prev_length))
- to_chat(o, "Your [pick(GLOB.gentlemans_organ_names)] [pick("swells up to", "flourishes into", "expands into", "bursts forth into", "grows eagerly into", "amplifys into")] a [uppertext(round(length))] inch penis.")
- else if ((round(length) < round(prev_length)) && (length > 0.5))
- to_chat(o, "Your [pick(GLOB.gentlemans_organ_names)] [pick("shrinks down to", "decreases into", "diminishes into", "deflates into", "shrivels regretfully into", "contracts into")] a [uppertext(round(length))] inch penis.")
prev_length = length
+ length = CLAMP(length + modifier, min, max)
+ update()
+
+/obj/item/organ/genital/penis/update_size(modified = FALSE)
+ if(length < 0)//I don't actually know what round() does to negative numbers, so to be safe!!
+ if(owner)
+ to_chat(owner, "You feel your tallywacker shrinking away from your body as your groin flattens out!")
+ QDEL_IN(src, 1)
+ if(linked_organ)
+ QDEL_IN(linked_organ, 1)
+ return
+ var/rounded_length = round(length)
+ var/new_size
+ var/enlargement = FALSE
+ switch(rounded_length)
+ if(0 to 6) //If modest size
+ new_size = 1
+ if(7 to 11) //If large
+ new_size = 2
+ if(12 to 20) //If massive
+ new_size = 3
+ if(21 to 34) //If massive and due for large effects
+ new_size = 3
+ enlargement = TRUE
+ if(35 to INFINITY) //If comical
+ new_size = 4 //no new sprites for anything larger yet
+ enlargement = TRUE
+ if(owner)
+ var/status_effect = owner.has_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
+ if(enlargement && !status_effect)
+ owner.apply_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
+ else if(!enlargement && status_effect)
+ owner.remove_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
+ if(linked_organ)
+ linked_organ.size = CLAMP(size + new_size, BALLS_SIZE_MIN, BALLS_SIZE_MAX)
+ linked_organ.update()
+ size = new_size
+
+ if(owner)
+ if (round(length) > round(prev_length))
+ to_chat(owner, "Your [pick(GLOB.gentlemans_organ_names)] [pick("swells up to", "flourishes into", "expands into", "bursts forth into", "grows eagerly into", "amplifys into")] a [uppertext(round(length))] inch penis.")
+ else if ((round(length) < round(prev_length)) && (length > 0.5))
+ to_chat(owner, "Your [pick(GLOB.gentlemans_organ_names)] [pick("shrinks down to", "decreases into", "diminishes into", "deflates into", "shrivels regretfully into", "contracts into")] a [uppertext(round(length))] inch penis.")
icon_state = sanitize_text("penis_[shape]_[size]")
girth = (length * girth_ratio)//Is it just me or is this ludicous, why not make it exponentially decay?
- //I have no idea on how to update sprites and I hate it
/obj/item/organ/genital/penis/update_appearance()
+ . = ..()
var/string
var/lowershape = lowertext(shape)
- desc = "You see [aroused_state ? "an erect" : "a flaccid"] [lowershape] penis. You estimate it's about [round(length, 0.25)] inch[round(length, 0.25) != 1 ? "es" : ""] long and [round(girth, 0.25)] inch[round(girth, 0.25) != 1 ? "es" : ""] in girth."
+ desc = "You see [aroused_state ? "an erect" : "a flaccid"] [lowershape] [name]. You estimate it's about [round(length, 0.25)] inch[round(length, 0.25) != 1 ? "es" : ""] long and [round(girth, 0.25)] inch[round(girth, 0.25) != 1 ? "es" : ""] in girth."
if(owner)
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
@@ -87,13 +88,13 @@
icon_state = sanitize_text(string)
H.update_genitals()
-/obj/item/organ/genital/penis/update_link()
- if(owner)
- linked_organ = (owner.getorganslot("testicles"))
- if(linked_organ)
- linked_organ.linked_organ = src
- linked_organ.size = size
+/obj/item/organ/genital/penis/get_features(mob/living/carbon/human/H)
+ var/datum/dna/D = H.dna
+ if(D.species.use_skintones && D.features["genitals_use_skintone"])
+ color = "#[skintone2hex(H.skin_tone)]"
else
- if(linked_organ)
- linked_organ.linked_organ = null
- linked_organ = null
+ color = "#[D.features["cock_color"]]"
+ length = D.features["cock_length"]
+ girth_ratio = D.features["cock_girth_ratio"]
+ shape = D.features["cock_shape"]
+ prev_length = length
diff --git a/modular_citadel/code/modules/arousal/organs/testicles.dm b/modular_citadel/code/modules/arousal/organs/testicles.dm
index f4ef4b5064..547674a5f1 100644
--- a/modular_citadel/code/modules/arousal/organs/testicles.dm
+++ b/modular_citadel/code/modules/arousal/organs/testicles.dm
@@ -1,82 +1,46 @@
/obj/item/organ/genital/testicles
- name = "testicles"
- desc = "A male reproductive organ."
- icon_state = "testicles"
- icon = 'modular_citadel/icons/obj/genitals/testicles.dmi'
- zone = "groin"
- slot = "testicles"
- size = BALLS_SIZE_MIN
- var/size_name = "average"
- shape = "single"
- var/sack_size = BALLS_SACK_SIZE_DEF
- fluid_id = "semen"
- producing = TRUE
- can_masturbate_with = FALSE
- masturbation_verb = "massage"
- can_climax = TRUE
- var/sent_full_message = TRUE //defaults to 1 since they're full to start
+ name = "testicles"
+ desc = "A male reproductive organ."
+ icon_state = "testicles"
+ icon = 'modular_citadel/icons/obj/genitals/testicles.dmi'
+ zone = BODY_ZONE_PRECISE_GROIN
+ slot = ORGAN_SLOT_TESTICLES
+ size = BALLS_SIZE_MIN
+ linked_organ_slot = ORGAN_SLOT_PENIS
+ genital_flags = CAN_MASTURBATE_WITH|MASTURBATE_LINKED_ORGAN|GENITAL_FUID_PRODUCTION
+ var/size_name = "average"
+ shape = "Single"
+ var/sack_size = BALLS_SACK_SIZE_DEF
+ fluid_id = "semen"
+ masturbation_verb = "massage"
+ layer_index = TESTICLES_LAYER_INDEX
-/obj/item/organ/genital/testicles/on_life()
- if(QDELETED(src))
- return
- if(reagents && producing)
- if(reagents.total_volume == 0) // Apparently, 0.015 gets rounded down to zero and no reagents are created if we don't start it with 0.1 in the tank.
- fluid_rate = 0.1
- else
- fluid_rate = CUM_RATE
- if(reagents.total_volume >= 5)
- fluid_mult = 0.5
- else
- fluid_mult = 1
- generate_cum()
-
-/obj/item/organ/genital/testicles/proc/generate_cum()
- reagents.maximum_volume = fluid_max_volume
- if(reagents.total_volume >= reagents.maximum_volume)
- if(!sent_full_message)
- send_full_message()
- sent_full_message = TRUE
+/obj/item/organ/genital/testicles/generate_fluid()
+ if(!linked_organ && !update_link())
return FALSE
- sent_full_message = FALSE
- update_link()
- if(!linked_organ)
- return FALSE
- reagents.isolate_reagent(fluid_id)//remove old reagents if it changed and just clean up generally
- reagents.add_reagent(fluid_id, (fluid_mult * fluid_rate))//generate the cum
+ . = ..()
+ if(. && reagents.holder_full())
+ to_chat(owner, "Your balls finally feel full, again.")
-/obj/item/organ/genital/testicles/update_link()
- if(owner && !QDELETED(src))
- linked_organ = (owner.getorganslot("penis"))
- if(linked_organ)
- linked_organ.linked_organ = src
- size = linked_organ.size
+/obj/item/organ/genital/testicles/upon_link()
+ size = linked_organ.size
+ update_size()
+ update_appearance()
- else
- if(linked_organ)
- linked_organ.linked_organ = null
- linked_organ = null
-
-/obj/item/organ/genital/testicles/proc/send_full_message(msg = "Your balls finally feel full, again.")
- if(owner && istext(msg))
- to_chat(owner, msg)
- return TRUE
-
-/obj/item/organ/genital/testicles/update_appearance()
+/obj/item/organ/genital/testicles/update_size(modified = FALSE)
switch(size)
- if(0.1 to 1)
+ if(BALLS_SIZE_MIN)
size_name = "average"
- if(1.1 to 2)
+ if(BALLS_SIZE_DEF)
size_name = "enlarged"
- if(2.1 to INFINITY)
+ if(BALLS_SIZE_MAX)
size_name = "engorged"
else
size_name = "nonexistant"
- if(!internal)
- desc = "You see an [size_name] pair of testicles."
- else
- desc = "They don't have any testicles you can see."
-
+/obj/item/organ/genital/testicles/update_appearance()
+ . = ..()
+ desc = "You see an [size_name] pair of testicles."
if(owner)
var/string
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
@@ -91,3 +55,18 @@
var/mob/living/carbon/human/H = owner
icon_state = sanitize_text(string)
H.update_genitals()
+
+/obj/item/organ/genital/testicles/get_features(mob/living/carbon/human/H)
+ var/datum/dna/D = H.dna
+ if(D.species.use_skintones && D.features["genitals_use_skintone"])
+ color = "#[skintone2hex(H.skin_tone)]"
+ else
+ color = "#[D.features["balls_color"]]"
+ sack_size = D.features["balls_sack_size"]
+ shape = D.features["balls_shape"]
+ if(D.features["balls_shape"] == "Hidden")
+ ENABLE_BITFIELD(genital_flags, GENITAL_INTERNAL)
+ fluid_id = D.features["balls_fluid"]
+ fluid_rate = D.features["balls_cum_rate"]
+ fluid_mult = D.features["balls_cum_mult"]
+ fluid_efficiency = D.features["balls_efficiency"]
diff --git a/modular_citadel/code/modules/arousal/organs/vagina.dm b/modular_citadel/code/modules/arousal/organs/vagina.dm
index 8c15aa5437..0df954fd79 100644
--- a/modular_citadel/code/modules/arousal/organs/vagina.dm
+++ b/modular_citadel/code/modules/arousal/organs/vagina.dm
@@ -1,26 +1,25 @@
/obj/item/organ/genital/vagina
- name = "vagina"
- desc = "A female reproductive organ."
- icon = 'modular_citadel/icons/obj/genitals/vagina.dmi'
- icon_state = "vagina"
- zone = "groin"
- slot = "vagina"
- size = 1 //There is only 1 size right now
- can_masturbate_with = TRUE
- masturbation_verb = "finger"
- can_climax = TRUE
+ name = "vagina"
+ desc = "A female reproductive organ."
+ icon = 'modular_citadel/icons/obj/genitals/vagina.dmi'
+ icon_state = ORGAN_SLOT_VAGINA
+ zone = BODY_ZONE_PRECISE_GROIN
+ slot = "vagina"
+ size = 1 //There is only 1 size right now
+ genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH
+ masturbation_verb = "finger"
fluid_transfer_factor = 0.1 //Yes, some amount is exposed to you, go get your AIDS
- w_class = 3
- var/cap_length = 8//D E P T H (cap = capacity)
- var/cap_girth = 12
+ layer_index = VAGINA_LAYER_INDEX
+ var/cap_length = 8//D E P T H (cap = capacity)
+ var/cap_girth = 12
var/cap_girth_ratio = 1.5
- var/clits = 1
- var/clit_diam = 0.25
- var/clit_len = 0.25
+ var/clits = 1
+ var/clit_diam = 0.25
+ var/clit_len = 0.25
var/list/vag_types = list("tentacle", "dentata", "hairy", "spade", "furred")
-
/obj/item/organ/genital/vagina/update_appearance()
+ . = ..()
var/string //Keeping this code here, so making multiple sprites for the different kinds is easier.
var/lowershape = lowertext(shape)
var/details
@@ -63,12 +62,10 @@
icon_state = sanitize_text(string)
H.update_genitals()
-/obj/item/organ/genital/vagina/update_link()
- if(owner)
- linked_organ = (owner.getorganslot("womb"))
- if(linked_organ)
- linked_organ.linked_organ = src
+/obj/item/organ/genital/vagina/get_features(mob/living/carbon/human/H)
+ var/datum/dna/D = H.dna
+ if(D.species.use_skintones && D.features["genitals_use_skintone"])
+ color = "#[skintone2hex(H.skin_tone)]"
else
- if(linked_organ)
- linked_organ.linked_organ = null
- linked_organ = null
+ color = "[D.features["vag_color"]]"
+ shape = "[D.features["vag_shape"]]"
diff --git a/modular_citadel/code/modules/arousal/organs/womb.dm b/modular_citadel/code/modules/arousal/organs/womb.dm
index 3f190b72ac..9dfe811571 100644
--- a/modular_citadel/code/modules/arousal/organs/womb.dm
+++ b/modular_citadel/code/modules/arousal/organs/womb.dm
@@ -1,45 +1,10 @@
/obj/item/organ/genital/womb
- name = "womb"
- desc = "A female reproductive organ."
- icon = 'modular_citadel/icons/obj/genitals/vagina.dmi'
- icon_state = "womb"
- zone = "groin"
- slot = "womb"
- internal = TRUE
- fluid_id = "femcum"
- producing = TRUE
-
-/obj/item/organ/genital/womb/on_life()
- if(QDELETED(src))
- return
- if(reagents && producing)
- if(reagents.total_volume == 0) // Apparently, 0.015 gets rounded down to zero and no reagents are created if we don't start it with 0.1 in the tank.
- fluid_rate = 0.1
- else
- fluid_rate = CUM_RATE
- if(reagents.total_volume >= 5)
- fluid_mult = 0.5
- else
- fluid_mult = 1
- generate_femcum()
-
-/obj/item/organ/genital/womb/proc/generate_femcum()
- reagents.maximum_volume = fluid_max_volume
- update_link()
- if(!linked_organ)
- return FALSE
- reagents.isolate_reagent(fluid_id)//remove old reagents if it changed and just clean up generally
- reagents.add_reagent(fluid_id, (fluid_mult * fluid_rate))//generate the cum
-
-/obj/item/organ/genital/womb/update_link()
- if(owner)
- linked_organ = (owner.getorganslot("vagina"))
- if(linked_organ)
- linked_organ.linked_organ = src
- else
- if(linked_organ)
- linked_organ.linked_organ = null
- linked_organ = null
-
-/obj/item/organ/genital/womb/Destroy()
- return ..()
+ name = "womb"
+ desc = "A female reproductive organ."
+ icon = 'modular_citadel/icons/obj/genitals/vagina.dmi'
+ icon_state = "womb"
+ zone = BODY_ZONE_PRECISE_GROIN
+ slot = ORGAN_SLOT_WOMB
+ genital_flags = GENITAL_INTERNAL|GENITAL_FUID_PRODUCTION
+ fluid_id = "femcum"
+ linked_organ_slot = ORGAN_SLOT_VAGINA
diff --git a/modular_citadel/code/modules/client/preferences_savefile.dm b/modular_citadel/code/modules/client/preferences_savefile.dm
index 0a353e3f5d..5584181a70 100644
--- a/modular_citadel/code/modules/client/preferences_savefile.dm
+++ b/modular_citadel/code/modules/client/preferences_savefile.dm
@@ -32,7 +32,6 @@
WRITE_FILE(S["feature_ipc_antenna"], features["ipc_antenna"])
//Citadel
WRITE_FILE(S["feature_genitals_use_skintone"], features["genitals_use_skintone"])
- WRITE_FILE(S["feature_exhibitionist"], features["exhibitionist"])
WRITE_FILE(S["feature_mcolor2"], features["mcolor2"])
WRITE_FILE(S["feature_mcolor3"], features["mcolor3"])
WRITE_FILE(S["feature_mam_body_markings"], features["mam_body_markings"])
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/enlargement.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/enlargement.dm
index 69f76e380b..ee90f985c6 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/enlargement.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/enlargement.dm
@@ -30,17 +30,15 @@
inverse_chem_val = 0.35
inverse_chem = "BEsmaller" //At really impure vols, it just becomes 100% inverse
can_synth = FALSE
+ var/message_spam = FALSE
-/datum/reagent/fermi/breast_enlarger/on_mob_add(mob/living/carbon/M)
+/datum/reagent/fermi/breast_enlarger/on_mob_metabolize(mob/living/M)
. = ..()
if(!ishuman(M)) //The monkey clause
if(volume >= 15) //To prevent monkey breast farms
var/turf/T = get_turf(M)
var/obj/item/organ/genital/breasts/B = new /obj/item/organ/genital/breasts(T)
- var/list/seen = viewers(8, T)
- for(var/mob/S in seen)
- to_chat(S, "A pair of breasts suddenly fly out of the [M]!")
- //var/turf/T2 = pick(turf in view(5, M))
+ M.visible_message("A pair of breasts suddenly fly out of the [M]!")
var/T2 = get_random_station_turf()
M.adjustBruteLoss(25)
M.Knockdown(50)
@@ -48,94 +46,82 @@
B.throw_at(T2, 8, 1)
M.reagents.remove_reagent(id, volume)
return
- log_game("FERMICHEM: [M] ckey: [M.key] has ingested Sucubus milk")
var/mob/living/carbon/human/H = M
- H.genital_override = TRUE
- var/obj/item/organ/genital/breasts/B = H.getorganslot("breasts")
- if(!B)
- H.emergent_genital_call()
- return
- if(!B.size == "huge")
- var/sizeConv = list("a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5)
- B.prev_size = B.size
- B.cached_size = sizeConv[B.size]
+ if(!H.getorganslot(ORGAN_SLOT_BREASTS) && H.emergent_genital_call())
+ H.genital_override = TRUE
/datum/reagent/fermi/breast_enlarger/on_mob_life(mob/living/carbon/M) //Increases breast size
if(!ishuman(M))//Just in case
return..()
var/mob/living/carbon/human/H = M
- var/obj/item/organ/genital/breasts/B = M.getorganslot("breasts")
+ var/obj/item/organ/genital/breasts/B = M.getorganslot(ORGAN_SLOT_BREASTS)
if(!B) //If they don't have breasts, give them breasts.
//If they have Acute hepatic pharmacokinesis, then route processing though liver.
- if(HAS_TRAIT(M, TRAIT_PHARMA))
- var/obj/item/organ/liver/L = M.getorganslot("liver")
+ if(HAS_TRAIT(H, TRAIT_PHARMA) || !H.canbearoused)
+ var/obj/item/organ/liver/L = H.getorganslot(ORGAN_SLOT_LIVER)
if(L)
- L.swelling+= 0.05
- return..()
+ L.swelling += 0.05
else
- M.adjustToxLoss(1)
- return..()
+ H.adjustToxLoss(1)
+ return..()
//otherwise proceed as normal
- var/obj/item/organ/genital/breasts/nB = new
- nB.Insert(M)
- if(nB)
- if(M.dna.species.use_skintones && M.dna.features["genitals_use_skintone"])
- nB.color = skintone2hex(H.skin_tone)
- else if(M.dna.features["breasts_color"])
- nB.color = "#[M.dna.features["breasts_color"]]"
- else
- nB.color = skintone2hex(H.skin_tone)
- nB.size = "flat"
- nB.cached_size = 0
- nB.prev_size = 0
- to_chat(M, "Your chest feels warm, tingling with newfound sensitivity.")
- M.reagents.remove_reagent(id, 5)
- B = nB
+ B = new
+ if(H.dna.species.use_skintones && H.dna.features["genitals_use_skintone"])
+ B.color = skintone2hex(H.skin_tone)
+ else if(M.dna.features["breasts_color"])
+ B.color = "#[M.dna.features["breasts_color"]]"
+ else
+ B.color = skintone2hex(H.skin_tone)
+ B.size = "flat"
+ B.cached_size = 0
+ B.prev_size = 0
+ to_chat(H, "Your chest feels warm, tingling with newfound sensitivity.")
+ H.reagents.remove_reagent(id, 5)
+ B.Insert(H)
+
//If they have them, increase size. If size is comically big, limit movement and rip clothes.
- B.cached_size = B.cached_size + 0.05
- if (B.cached_size >= 8.5 && B.cached_size < 9)
- if(H.w_uniform || H.wear_suit)
- var/target = M.get_bodypart(BODY_ZONE_CHEST)
- to_chat(M, "Your breasts begin to strain against your clothes tightly!")
- M.adjustOxyLoss(5, 0)
- M.apply_damage(1, BRUTE, target)
- B.update()
- ..()
+ B.modify_size(0.05)
+
+ if (ISINRANGE_EX(B.cached_size, 8.5, 9) && (H.w_uniform || H.wear_suit))
+ var/target = H.get_bodypart(BODY_ZONE_CHEST)
+ if(!message_spam)
+ to_chat(H, "Your breasts begin to strain against your clothes tightly!")
+ message_spam = TRUE
+ H.adjustOxyLoss(5, 0)
+ H.apply_damage(1, BRUTE, target)
+ return ..()
/datum/reagent/fermi/breast_enlarger/overdose_process(mob/living/carbon/M) //Turns you into a female if male and ODing, doesn't touch nonbinary and object genders.
//Acute hepatic pharmacokinesis.
- if(HAS_TRAIT(M, TRAIT_PHARMA))
- var/obj/item/organ/liver/L = M.getorganslot("liver")
+ if(HAS_TRAIT(M, TRAIT_PHARMA) || !M.canbearoused)
+ var/obj/item/organ/liver/L = M.getorganslot(ORGAN_SLOT_LIVER)
L.swelling+= 0.05
return ..()
- var/obj/item/organ/genital/penis/P = M.getorganslot("penis")
- var/obj/item/organ/genital/testicles/T = M.getorganslot("testicles")
- var/obj/item/organ/genital/vagina/V = M.getorganslot("vagina")
- var/obj/item/organ/genital/womb/W = M.getorganslot("womb")
+ var/obj/item/organ/genital/penis/P = M.getorganslot(ORGAN_SLOT_PENIS)
+ var/obj/item/organ/genital/testicles/T = M.getorganslot(ORGAN_SLOT_TESTICLES)
+ var/obj/item/organ/genital/vagina/V = M.getorganslot(ORGAN_SLOT_VAGINA)
+ var/obj/item/organ/genital/womb/W = M.getorganslot(ORGAN_SLOT_WOMB)
if(M.gender == MALE)
M.gender = FEMALE
M.visible_message("[M] suddenly looks more feminine!", "You suddenly feel more feminine!")
if(P)
- P.cached_length = P.cached_length - 0.05
- P.update()
+ P.modify_size(-0.05)
if(T)
- T.Remove(M)
+ qdel(T)
if(!V)
- var/obj/item/organ/genital/vagina/nV = new
- nV.Insert(M)
- V = nV
+ V = new
+ V.Insert(M)
if(!W)
- var/obj/item/organ/genital/womb/nW = new
- nW.Insert(M)
- W = nW
- ..()
+ W = new
+ W.Insert(M)
+ return ..()
/datum/reagent/fermi/BEsmaller
name = "Modesty milk"
@@ -147,19 +133,18 @@
can_synth = FALSE
/datum/reagent/fermi/BEsmaller/on_mob_life(mob/living/carbon/M)
- var/obj/item/organ/genital/breasts/B = M.getorganslot("breasts")
+ var/obj/item/organ/genital/breasts/B = M.getorganslot(ORGAN_SLOT_BREASTS)
if(!B)
//Acute hepatic pharmacokinesis.
- if(HAS_TRAIT(M, TRAIT_PHARMA))
- var/obj/item/organ/liver/L = M.getorganslot("liver")
+ if(HAS_TRAIT(M, TRAIT_PHARMA) || !M.canbearoused)
+ var/obj/item/organ/liver/L = M.getorganslot(ORGAN_SLOT_LIVER)
L.swelling-= 0.05
return ..()
//otherwise proceed as normal
return..()
- B.cached_size = B.cached_size - 0.05
- B.update()
- ..()
+ B.modify_size(-0.05)
+ return ..()
/datum/reagent/fermi/BEsmaller_hypo
name = "Rectify milk" //Rectify
@@ -171,31 +156,28 @@
var/sizeConv = list("a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5)
can_synth = TRUE
-/datum/reagent/fermi/BEsmaller_hypo/on_mob_add(mob/living/carbon/M)
+/datum/reagent/fermi/BEsmaller_hypo/on_mob_metabolize(mob/living/M)
. = ..()
- if(!M.getorganslot("vagina"))
- if(M.dna.features["has_vag"])
- var/obj/item/organ/genital/vagina/nV = new
- nV.Insert(M)
- if(!M.getorganslot("womb"))
- if(M.dna.features["has_womb"])
- var/obj/item/organ/genital/womb/nW = new
- nW.Insert(M)
+ if(!ishuman(M))
+ return
+ var/mob/living/carbon/human/H = M
+ if(!H.getorganslot(ORGAN_SLOT_VAGINA) && H.dna.features["has_vag"])
+ H.give_genital(/obj/item/organ/genital/vagina)
+ if(!H.getorganslot(ORGAN_SLOT_WOMB) && H.dna.features["has_womb"])
+ H.give_genital(/obj/item/organ/genital/womb)
/datum/reagent/fermi/BEsmaller_hypo/on_mob_life(mob/living/carbon/M)
- var/obj/item/organ/genital/breasts/B = M.getorganslot("breasts")
+ var/obj/item/organ/genital/breasts/B = M.getorganslot(ORGAN_SLOT_BREASTS)
if(!B)
return..()
- if(!M.dna.features["has_breasts"])//Fast fix for those who don't want it.
- B.cached_size = B.cached_size - 0.1
- B.update()
- else if(B.cached_size > (sizeConv[M.dna.features["breasts_size"]]+0.1))
- B.cached_size = B.cached_size - 0.05
- B.update()
- else if(B.cached_size < (sizeConv[M.dna.features["breasts_size"]])+0.1)
- B.cached_size = B.cached_size + 0.05
- B.update()
- ..()
+ var/optimal_size = B.breast_values[M.dna.features["breasts_size"]]
+ if(!optimal_size)//Fast fix for those who don't want it.
+ B.modify_size(-0.1)
+ else if(B.cached_size > optimal_size)
+ B.modify_size(-0.05, optimal_size)
+ else if(B.cached_size < optimal_size)
+ B.modify_size(0.05, 0, optimal_size)
+ return ..()
////////////////////////////////////////////////////////////////////////////////////////////////////
// PENIS ENLARGE
@@ -215,16 +197,15 @@
inverse_chem_val = 0.35
inverse_chem = "PEsmaller" //At really impure vols, it just becomes 100% inverse and shrinks instead.
can_synth = FALSE
+ var/message_spam = FALSE
-/datum/reagent/fermi/penis_enlarger/on_mob_add(mob/living/carbon/M)
+/datum/reagent/fermi/penis_enlarger/on_mob_metabolize(mob/living/M)
. = ..()
if(!ishuman(M)) //Just monkeying around.
if(volume >= 15) //to prevent monkey penis farms
var/turf/T = get_turf(M)
var/obj/item/organ/genital/penis/P = new /obj/item/organ/genital/penis(T)
- var/list/seen = viewers(8, T)
- for(var/mob/S in seen)
- to_chat(S, "A penis suddenly flies out of the [M]!")
+ M.visible_message("A penis suddenly flies out of the [M]!")
var/T2 = get_random_station_turf()
M.adjustBruteLoss(25)
M.Knockdown(50)
@@ -233,80 +214,71 @@
M.reagents.remove_reagent(id, volume)
return
var/mob/living/carbon/human/H = M
- H.genital_override = TRUE
- var/obj/item/organ/genital/penis/P = M.getorganslot("penis")
- if(!P)
- H.emergent_genital_call()
- return
- P.prev_length = P.length
- P.cached_length = P.length
+ if(!H.getorganslot(ORGAN_SLOT_PENIS) && H.emergent_genital_call())
+ H.genital_override = TRUE
/datum/reagent/fermi/penis_enlarger/on_mob_life(mob/living/carbon/M) //Increases penis size, 5u = +1 inch.
if(!ishuman(M))
- return
+ return ..()
var/mob/living/carbon/human/H = M
- var/obj/item/organ/genital/penis/P = M.getorganslot("penis")
+ var/obj/item/organ/genital/penis/P = H.getorganslot(ORGAN_SLOT_PENIS)
if(!P)//They do have a preponderance for escapism, or so I've heard.
//If they have Acute hepatic pharmacokinesis, then route processing though liver.
- if(HAS_TRAIT(M, TRAIT_PHARMA))
- var/obj/item/organ/liver/L = M.getorganslot("liver")
+ if(HAS_TRAIT(H, TRAIT_PHARMA) || !H.canbearoused)
+ var/obj/item/organ/liver/L = H.getorganslot(ORGAN_SLOT_LIVER)
if(L)
- L.swelling+= 0.05
- return..()
+ L.swelling += 0.05
else
- M.adjustToxLoss(1)
- return..()
+ H.adjustToxLoss(1)
+ return ..()
//otherwise proceed as normal
- var/obj/item/organ/genital/penis/nP = new
- nP.Insert(M)
- if(nP)
- nP.length = 1
- to_chat(M, "Your groin feels warm, as you feel a newly forming bulge down below.")
- nP.cached_length = 1
- nP.prev_length = 1
- M.reagents.remove_reagent(id, 5)
- P = nP
+ P = new
+ P.length = 1
+ to_chat(H, "Your groin feels warm, as you feel a newly forming bulge down below.")
+ P.prev_length = 1
+ H.reagents.remove_reagent(id, 5)
+ P.Insert(H)
- P.cached_length = P.cached_length + 0.1
- if (P.cached_length >= 20.5 && P.cached_length < 21)
- if(H.w_uniform || H.wear_suit)
- var/target = M.get_bodypart(BODY_ZONE_CHEST)
- to_chat(M, "Your cock begin to strain against your clothes tightly!")
- M.apply_damage(2.5, BRUTE, target)
+ P.modify_size(0.1)
+ if (ISINRANGE_EX(P.length, 20.5, 21) && (H.w_uniform || H.wear_suit))
+ var/target = H.get_bodypart(BODY_ZONE_CHEST)
+ if(!message_spam)
+ to_chat(H, "Your cock begin to strain against your clothes tightly!")
+ message_spam = TRUE
+ H.apply_damage(2.5, BRUTE, target)
- P.update()
- ..()
+ return ..()
-/datum/reagent/fermi/penis_enlarger/overdose_process(mob/living/carbon/M) //Turns you into a male if female and ODing, doesn't touch nonbinary and object genders.
+/datum/reagent/fermi/penis_enlarger/overdose_process(mob/living/carbon/human/M) //Turns you into a male if female and ODing, doesn't touch nonbinary and object genders.
+ if(!istype(M))
+ return ..()
//Acute hepatic pharmacokinesis.
- if(HAS_TRAIT(M, TRAIT_PHARMA))
- var/obj/item/organ/liver/L = M.getorganslot("liver")
+ if(HAS_TRAIT(M, TRAIT_PHARMA) || !M.canbearoused)
+ var/obj/item/organ/liver/L = M.getorganslot(ORGAN_SLOT_LIVER)
L.swelling+= 0.05
return..()
- var/obj/item/organ/genital/breasts/B = M.getorganslot("breasts")
- var/obj/item/organ/genital/testicles/T = M.getorganslot("testicles")
- var/obj/item/organ/genital/vagina/V = M.getorganslot("vagina")
- var/obj/item/organ/genital/womb/W = M.getorganslot("womb")
+ var/obj/item/organ/genital/breasts/B = M.getorganslot(ORGAN_SLOT_BREASTS)
+ var/obj/item/organ/genital/testicles/T = M.getorganslot(ORGAN_SLOT_TESTICLES)
+ var/obj/item/organ/genital/vagina/V = M.getorganslot(ORGAN_SLOT_VAGINA)
+ var/obj/item/organ/genital/womb/W = M.getorganslot(ORGAN_SLOT_WOMB)
if(M.gender == FEMALE)
M.gender = MALE
M.visible_message("[M] suddenly looks more masculine!", "You suddenly feel more masculine!")
if(B)
- B.cached_size = B.cached_size - 0.05
- B.update()
- if(V)
- V.Remove(M)
+ B.modify_size(-0.05)
+ if(M.getorganslot(ORGAN_SLOT_VAGINA))
+ qdel(V)
if(W)
- W.Remove(M)
+ qdel(W)
if(!T)
- var/obj/item/organ/genital/testicles/nT = new
- nT.Insert(M)
- T = nT
- ..()
+ T = new
+ T.Insert(M)
+ return ..()
/datum/reagent/fermi/PEsmaller // Due to cozmo's request...!
name = "Chastity draft"
@@ -318,19 +290,18 @@
can_synth = FALSE
/datum/reagent/fermi/PEsmaller/on_mob_life(mob/living/carbon/M)
+ if(!ishuman(M))
+ return ..()
var/mob/living/carbon/human/H = M
- var/obj/item/organ/genital/penis/P = H.getorganslot("penis")
+ var/obj/item/organ/genital/penis/P = H.getorganslot(ORGAN_SLOT_PENIS)
if(!P)
//Acute hepatic pharmacokinesis.
if(HAS_TRAIT(M, TRAIT_PHARMA))
- var/obj/item/organ/liver/L = M.getorganslot("liver")
+ var/obj/item/organ/liver/L = M.getorganslot(ORGAN_SLOT_LIVER)
L.swelling-= 0.05
- return..()
-
- //otherwise proceed as normal
return..()
- P.cached_length = P.cached_length - 0.1
- P.update()
+
+ P.modify_size(-0.1)
..()
/datum/reagent/fermi/PEsmaller_hypo
@@ -342,24 +313,25 @@
metabolization_rate = 0.5
can_synth = TRUE
-/datum/reagent/fermi/PEsmaller_hypo/on_mob_add(mob/living/carbon/M)
+/datum/reagent/fermi/PEsmaller_hypo/on_mob_metabolize(mob/living/M)
. = ..()
- if(!M.getorganslot("testicles"))
- if(M.dna.features["has_balls"])
- var/obj/item/organ/genital/testicles/nT = new
- nT.Insert(M)
+ if(!ishuman(M))
+ return
+ var/mob/living/carbon/human/H = M
+ if(!H.getorganslot(ORGAN_SLOT_PENIS) && H.dna.features["has_cock"])
+ H.give_genital(/obj/item/organ/genital/penis)
+ if(!H.getorganslot(ORGAN_SLOT_TESTICLES) && H.dna.features["has_balls"])
+ H.give_genital(/obj/item/organ/genital/testicles)
/datum/reagent/fermi/PEsmaller_hypo/on_mob_life(mob/living/carbon/M)
- var/obj/item/organ/genital/penis/P = M.getorganslot("penis")
+ var/obj/item/organ/genital/penis/P = M.getorganslot(ORGAN_SLOT_PENIS)
if(!P)
return ..()
- if(!M.dna.features["has_cock"])//Fast fix for those who don't want it.
- P.cached_length = P.cached_length - 0.2
- P.update()
- else if(P.cached_length > (M.dna.features["cock_length"]+0.1))
- P.cached_length = P.cached_length - 0.1
- P.update()
- else if(P.cached_length < (M.dna.features["cock_length"]+0.1))
- P.cached_length = P.cached_length + 0.1
- P.update()
- ..()
+ var/optimal_size = M.dna.features["cock_length"]
+ if(!optimal_size)//Fast fix for those who don't want it.
+ P.modify_size(-0.2)
+ else if(P.length > optimal_size)
+ P.modify_size(-0.1, optimal_size)
+ else if(P.length < optimal_size)
+ P.modify_size(0.1, 0, optimal_size)
+ return ..()
diff --git a/modular_citadel/icons/obj/genitals/hud.dmi b/modular_citadel/icons/obj/genitals/hud.dmi
index bf2adcb1fc..f8c8643fe0 100644
Binary files a/modular_citadel/icons/obj/genitals/hud.dmi and b/modular_citadel/icons/obj/genitals/hud.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/testicles_onmob.dmi b/modular_citadel/icons/obj/genitals/testicles_onmob.dmi
index 33659cf13c..65eb24268f 100644
Binary files a/modular_citadel/icons/obj/genitals/testicles_onmob.dmi and b/modular_citadel/icons/obj/genitals/testicles_onmob.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index c13db8171d..0ab92d863d 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -45,6 +45,7 @@
#include "code\__DEFINES\donator_groupings.dm"
#include "code\__DEFINES\events.dm"
#include "code\__DEFINES\exports.dm"
+#include "code\__DEFINES\fantasy_affixes.dm"
#include "code\__DEFINES\flags.dm"
#include "code\__DEFINES\food.dm"
#include "code\__DEFINES\footsteps.dm"
@@ -344,6 +345,7 @@
#include "code\datums\components\_component.dm"
#include "code\datums\components\anti_magic.dm"
#include "code\datums\components\armor_plate.dm"
+#include "code\datums\components\bane.dm"
#include "code\datums\components\bouncy.dm"
#include "code\datums\components\butchering.dm"
#include "code\datums\components\caltrop.dm"
@@ -357,9 +359,12 @@
#include "code\datums\components\empprotection.dm"
#include "code\datums\components\footstep.dm"
#include "code\datums\components\forced_gravity.dm"
+#include "code\datums\components\igniter.dm"
#include "code\datums\components\infective.dm"
#include "code\datums\components\jousting.dm"
+#include "code\datums\components\knockback.dm"
#include "code\datums\components\knockoff.dm"
+#include "code\datums\components\lifesteal.dm"
#include "code\datums\components\lockon_aiming.dm"
#include "code\datums\components\magnetic_catch.dm"
#include "code\datums\components\material_container.dm"
@@ -375,16 +380,23 @@
#include "code\datums\components\remote_materials.dm"
#include "code\datums\components\riding.dm"
#include "code\datums\components\rotation.dm"
+#include "code\datums\components\shrapnel.dm"
#include "code\datums\components\slippery.dm"
#include "code\datums\components\spooky.dm"
#include "code\datums\components\squeak.dm"
#include "code\datums\components\stationloving.dm"
+#include "code\datums\components\summoning.dm"
#include "code\datums\components\swarming.dm"
+#include "code\datums\components\tactical.dm"
#include "code\datums\components\thermite.dm"
#include "code\datums\components\uplink.dm"
#include "code\datums\components\virtual_reality.dm"
#include "code\datums\components\wearertargeting.dm"
#include "code\datums\components\wet_floor.dm"
+#include "code\datums\components\fantasy\_fantasy.dm"
+#include "code\datums\components\fantasy\affix.dm"
+#include "code\datums\components\fantasy\prefixes.dm"
+#include "code\datums\components\fantasy\suffixes.dm"
#include "code\datums\components\storage\storage.dm"
#include "code\datums\components\storage\concrete\_concrete.dm"
#include "code\datums\components\storage\concrete\bag_of_holding.dm"
@@ -2960,7 +2972,6 @@
#include "modular_citadel\code\datums\status_effects\chems.dm"
#include "modular_citadel\code\datums\status_effects\debuffs.dm"
#include "modular_citadel\code\datums\traits\negative.dm"
-#include "modular_citadel\code\datums\traits\neutral.dm"
#include "modular_citadel\code\datums\wires\airlock.dm"
#include "modular_citadel\code\datums\wires\autoylathe.dm"
#include "modular_citadel\code\game\area\cit_areas.dm"
@@ -3018,10 +3029,10 @@
#include "modular_citadel\code\modules\admin\secrets.dm"
#include "modular_citadel\code\modules\admin\topic.dm"
#include "modular_citadel\code\modules\arousal\arousal.dm"
+#include "modular_citadel\code\modules\arousal\genitals.dm"
+#include "modular_citadel\code\modules\arousal\genitals_sprite_accessories.dm"
#include "modular_citadel\code\modules\arousal\organs\breasts.dm"
#include "modular_citadel\code\modules\arousal\organs\eggsack.dm"
-#include "modular_citadel\code\modules\arousal\organs\genitals.dm"
-#include "modular_citadel\code\modules\arousal\organs\genitals_sprite_accessories.dm"
#include "modular_citadel\code\modules\arousal\organs\ovipositor.dm"
#include "modular_citadel\code\modules\arousal\organs\penis.dm"
#include "modular_citadel\code\modules\arousal\organs\testicles.dm"
diff --git a/tools/mapmerge2/requirements.txt b/tools/mapmerge2/requirements.txt
index adb42438e7..d24cb40dcc 100644
--- a/tools/mapmerge2/requirements.txt
+++ b/tools/mapmerge2/requirements.txt
@@ -1,3 +1,3 @@
pygit2==0.27.2
bidict==0.13.1
-Pillow==5.1.0
+Pillow==6.2.0
|