diff --git a/_maps/RandomRuins/SpaceRuins/spacehermit.dmm b/_maps/RandomRuins/SpaceRuins/spacehermit.dmm index 31d83c730..25c093c8c 100644 --- a/_maps/RandomRuins/SpaceRuins/spacehermit.dmm +++ b/_maps/RandomRuins/SpaceRuins/spacehermit.dmm @@ -6,11 +6,13 @@ /turf/closed/mineral/random/low_chance, /area/ruin/unpowered) "ac" = ( -/turf/open/floor/plating/asteroid, +/obj/machinery/hydroponics/soil, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "ad" = ( -/obj/machinery/hydroponics/soil, -/turf/open/floor/plating/asteroid, +/obj/structure/table/wood, +/obj/item/shovel, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "ae" = ( /turf/closed/wall/mineral/titanium, @@ -26,9 +28,7 @@ /turf/closed/wall/mineral/titanium, /area/ruin/powered) "ah" = ( -/obj/structure/table/wood, -/obj/item/shovel, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "ai" = ( /obj/structure/lattice, @@ -47,9 +47,10 @@ /turf/closed/wall/mineral/titanium, /area/ruin/powered) "am" = ( -/obj/item/circuitboard/computer/rdconsole, -/obj/item/circuitboard/machine/protolathe/department/science, -/turf/open/floor/plating/asteroid, +/obj/item/seeds/tower, +/obj/item/seeds/tower, +/obj/item/seeds/tower, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "an" = ( /turf/open/floor/mineral/titanium, @@ -91,61 +92,52 @@ /turf/closed/wall/mineral/titanium, /area/ruin/powered) "au" = ( -/obj/item/seeds/tower, -/obj/item/seeds/tower, -/obj/item/seeds/tower, -/turf/open/floor/plating/asteroid, +/obj/item/stack/sheet/mineral/wood, +/obj/item/stack/sheet/mineral/wood, +/obj/item/stack/sheet/mineral/wood, +/obj/item/stack/sheet/mineral/wood, +/obj/item/stack/sheet/mineral/wood, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "av" = ( -/obj/structure/table/wood, -/obj/item/paper/crumpled{ - info = "

So this is it, I guess.. Always thought I'd go out doing something stupid, but.. not like this.
Should anyone find this note, tell Jeremy Clarke he can kiss my ass. Launching the pod unprepared like that..
Well. Now that I'm keeling over here, I guess I should write how I came to be the last one standing here.
Four of us took this pod.. only three of us woke up though.. fourth pod is locked from the inside, we couldn't figure out how to get it open.
Me and the clown set up a farm s'we can survive at least a while.. though, he didn't seem to understand that man cannot live on banana alone.
Had a bunch of circuits on him for some reason when he eventually passed, whispered to me something about a chem dispenser.. I.. don't know what he meant by that. I was never good with machinary. Maybe he wanted space lube?
The assistant that we gave the space suit to go out and find help never came back.. I'm betting he found salvation and left us behind.. never can trust those grubby greytiders." - }, -/obj/item/pen, -/obj/item/bluespace_thread{ - pixel_x = -1; - pixel_y = 1 - }, -/turf/open/floor/plating/asteroid, +/obj/item/twohanded/spear, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aw" = ( -/obj/item/stack/sheet/mineral/wood, -/obj/item/stack/sheet/mineral/wood, -/obj/item/stack/sheet/mineral/wood, -/obj/item/stack/sheet/mineral/wood, -/obj/item/stack/sheet/mineral/wood, -/turf/open/floor/plating/asteroid, +/obj/item/grown/log, +/obj/item/grown/log, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "ax" = ( -/obj/item/twohanded/spear, -/turf/open/floor/plating/asteroid, -/area/ruin/unpowered) -"ay" = ( -/obj/item/grown/log, -/obj/item/grown/log, -/turf/open/floor/plating/asteroid, -/area/ruin/unpowered) -"az" = ( /obj/structure/table/wood, /obj/item/storage/bag/plants/portaseeder, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, +/area/ruin/unpowered) +"ay" = ( +/obj/item/flashlight/lamp/bananalamp, +/turf/open/floor/plating/asteroid/spacehermit, +/area/ruin/unpowered) +"az" = ( +/obj/item/seeds/banana, +/obj/item/seeds/banana, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aA" = ( /turf/open/floor/plating/asteroid/airless, /area/ruin/unpowered) "aB" = ( -/obj/item/seeds/banana, -/obj/item/seeds/banana, -/turf/open/floor/plating/asteroid, -/area/ruin/unpowered) -"aC" = ( /obj/effect/mob_spawn/human/clown/corpse, /obj/structure/fans/tiny/invisible, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, +/area/ruin/unpowered) +"aC" = ( +/obj/item/mining_scanner, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aD" = ( -/obj/item/mining_scanner, -/turf/open/floor/plating/asteroid, +/obj/item/circuitboard/computer/rdconsole, +/obj/item/circuitboard/machine/protolathe/department/science, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aE" = ( /obj/machinery/status_display/shuttle, @@ -153,16 +145,16 @@ /area/ruin/unpowered) "aF" = ( /obj/item/pickaxe/emergency, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aG" = ( /obj/structure/sink/puddle, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aH" = ( /obj/structure/table/wood, /obj/item/storage/firstaid/regular, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aI" = ( /obj/effect/mob_spawn/human/corpse/assistant, @@ -171,17 +163,17 @@ "aJ" = ( /obj/structure/table/wood, /obj/item/storage/bag/ore, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aK" = ( /obj/structure/reagent_dispensers/fueltank, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aL" = ( /obj/structure/table/wood, /obj/item/storage/toolbox/mechanical, /obj/item/clothing/head/welding, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aM" = ( /mob/living/simple_animal/hostile/carp, @@ -189,7 +181,7 @@ /area/template_noop) "aN" = ( /obj/structure/mineral_door/sandstone, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aO" = ( /mob/living/simple_animal/hostile/carp, @@ -197,17 +189,17 @@ /area/ruin/unpowered) "aP" = ( /obj/item/stack/ore/gold, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aQ" = ( /obj/item/stack/ore/gold, /obj/item/stack/ore/gold, /obj/item/stack/ore/gold, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aR" = ( /obj/item/stack/ore/iron, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aS" = ( /turf/closed/wall/mineral/iron, @@ -215,12 +207,12 @@ "aT" = ( /obj/item/stack/ore/iron, /obj/item/stack/ore/iron, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aU" = ( /obj/item/flashlight/lamp, /obj/structure/table/wood, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "aV" = ( /obj/item/clothing/suit/space/orange, @@ -255,92 +247,100 @@ /turf/closed/wall/mineral/titanium, /area/ruin/unpowered) "bc" = ( -/obj/item/circuitboard/machine/circuit_imprinter, -/turf/open/floor/plating/asteroid, +/obj/structure/table/wood, +/obj/item/paper/crumpled{ + info = "

So this is it, I guess.. Always thought I'd go out doing something stupid, but.. not like this.
Should anyone find this note, tell Jeremy Clarke he can kiss my ass. Launching the pod unprepared like that..
Well. Now that I'm keeling over here, I guess I should write how I came to be the last one standing here.
Four of us took this pod.. only three of us woke up though.. fourth pod is locked from the inside, we couldn't figure out how to get it open.
Me and the clown set up a farm s'we can survive at least a while.. though, he didn't seem to understand that man cannot live on banana alone.
Had a bunch of circuits on him for some reason when he eventually passed, whispered to me something about a chem dispenser.. I.. don't know what he meant by that. I was never good with machinary. Maybe he wanted space lube?
The assistant that we gave the space suit to go out and find help never came back.. I'm betting he found salvation and left us behind.. never can trust those grubby greytiders." + }, +/obj/item/pen, +/obj/item/bluespace_thread{ + pixel_x = -1; + pixel_y = 1 + }, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "bd" = ( -/obj/item/grown/bananapeel, -/turf/open/floor/plating/asteroid, +/obj/item/circuitboard/machine/circuit_imprinter, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "be" = ( +/obj/item/grown/bananapeel, +/turf/open/floor/plating/asteroid/spacehermit, +/area/ruin/unpowered) +"bf" = ( /obj/effect/mob_spawn/human/corpse/cargo_tech, /obj/structure/fans/tiny/invisible, /obj/effect/decal/cleanable/vomit, -/turf/open/floor/plating/asteroid, -/area/ruin/unpowered) -"bf" = ( -/obj/item/clothing/glasses/meson, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "bg" = ( -/obj/item/stock_parts/matter_bin, -/turf/open/floor/plating/asteroid, +/obj/item/clothing/glasses/meson, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "bh" = ( /obj/item/circuitboard/machine/autolathe, /turf/open/floor/mineral/titanium, /area/ruin/powered) "bi" = ( -/obj/item/stock_parts/manipulator, -/turf/open/floor/plating/asteroid, +/obj/item/stock_parts/matter_bin, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "bj" = ( -/obj/item/holosign_creator/atmos, -/turf/open/floor/plating/asteroid, +/obj/item/stock_parts/manipulator, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "bk" = ( /obj/structure/holosign/barrier/atmos, /turf/open/floor/plating/asteroid, /area/ruin/unpowered) "bl" = ( +/obj/item/holosign_creator/atmos, +/turf/open/floor/plating/asteroid/spacehermit, +/area/ruin/unpowered) +"bm" = ( /obj/machinery/power/floodlight, /obj/structure/cable{ icon_state = "0-2" }, -/turf/open/floor/plating/asteroid, -/area/ruin/unpowered) -"bm" = ( -/obj/structure/cable{ - icon_state = "1-4" - }, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "bn" = ( /obj/structure/cable{ - icon_state = "2-8" + icon_state = "1-4" }, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "bo" = ( /obj/structure/cable{ - icon_state = "1-2" + icon_state = "2-8" }, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "bp" = ( -/obj/item/stack/ore/iron, -/obj/item/stack/ore/iron, -/obj/item/stack/ore/iron, -/turf/open/floor/plating/asteroid, +/obj/structure/cable{ + icon_state = "1-2" + }, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "bq" = ( +/obj/item/stack/ore/iron, +/obj/item/stack/ore/iron, +/obj/item/stack/ore/iron, +/turf/open/floor/plating/asteroid/spacehermit, +/area/ruin/unpowered) +"br" = ( /obj/structure/cable, /obj/machinery/power/port_gen/pacman{ active = 1; anchored = 1; sheets = 10 }, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) -"br" = ( +"bs" = ( /obj/item/stack/ore/iron, /obj/item/stack/sheet/mineral/plasma, /obj/item/stack/sheet/mineral/plasma, -/turf/open/floor/plating/asteroid, -/area/ruin/unpowered) -"bs" = ( -/obj/item/flashlight/lamp/bananalamp, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/spacehermit, /area/ruin/unpowered) "bt" = ( /obj/structure/fluff/empty_cryostasis_sleeper, @@ -349,6 +349,9 @@ }, /turf/open/floor/mineral/titanium, /area/ruin/powered) +"bu" = ( +/turf/closed/mineral/random/spacehermit, +/area/ruin/unpowered) (1,1,1) = {" aa @@ -486,11 +489,11 @@ ab ab ab ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu ab ab ab @@ -532,20 +535,20 @@ ab ab ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ab ab ab @@ -581,25 +584,25 @@ aA aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ab ab aA @@ -632,27 +635,27 @@ ab aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ab ab aA @@ -678,34 +681,34 @@ aa aA ab ab +bu +bu ab ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ab ab aA @@ -729,36 +732,36 @@ aa aa aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ae af ae -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu ab ab aA @@ -781,37 +784,37 @@ aa aa aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu af aq af -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu ab ab aA @@ -833,38 +836,38 @@ aa aA aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ae ae ao ae ae -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu ab aA aa @@ -886,37 +889,37 @@ aA aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu af an an an af -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu ab ab aA @@ -938,24 +941,24 @@ aA aA aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ae ae bh @@ -963,13 +966,13 @@ an an ae aZ -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu ab aA aa @@ -990,24 +993,24 @@ aA aA aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu af aY ao @@ -1015,13 +1018,13 @@ ao ao ak ba -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu ab aA aa @@ -1042,24 +1045,24 @@ aA aA aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu al aj ap @@ -1067,13 +1070,13 @@ an as aj aE -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu ab aA aa @@ -1094,24 +1097,24 @@ aA aA aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu af bt ao @@ -1119,13 +1122,13 @@ ao ao bt ba -ac -ab -ab -ab -ab -ab -ab +ah +bu +bu +bu +bu +bu +bu ab aA aa @@ -1146,23 +1149,23 @@ aA aA ab ab -ab -ab -ab -ab -ab -ab -ab -ah -az -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +ad +ax +bu +bu +bu +bu +bu +bu +bu +bu aR ae ae @@ -1171,13 +1174,13 @@ an an ae aZ -ac -ab -ab -ab -ab -ab -ab +ah +bu +bu +bu +bu +bu +bu ab aA aM @@ -1197,24 +1200,24 @@ aa aA aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ac -ac -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +ah +ah +bu +bu +bu +bu +bu +bu +bu +bu aR ag ag @@ -1223,13 +1226,13 @@ ar af at bb -ac -ab -ab -ab -ab -ab -ab +ah +bu +bu +bu +bu +bu +bu ab aA aa @@ -1249,39 +1252,39 @@ aa aA aA ab -ab -ab -ab -ab -ab -ab -ab -ad +bu +bu +bu +bu +bu +bu +bu ac -ac -ac -ac -ab +ah +ah +ah +ah +bu aH aJ aL -ab -ab +bu +bu aT -ac -ac -ac -ac -ac -ac -ac +ah +ah +ah +ah +ah +ah +ah aR -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu ab aA aa @@ -1301,39 +1304,39 @@ aA aA ab ab -ab -ab -ab -ab -ab -ab -ab -ad -au +bu +bu +bu +bu +bu +bu +bu ac -aB am -ac -ac -ac -ac -ab -ab -ac -ac -ac -ac -ac -ac -ac -ac -bg -ab -ab -ab -ab -ab -ab +ah +az +aD +ah +ah +ah +ah +bu +bu +ah +ah +ah +ah +ah +ah +ah +ah +bi +bu +bu +bu +bu +bu +bu ab aA aa @@ -1352,40 +1355,40 @@ aa aA aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ad -aw -bs -aC +bu +bu +bu +bu +bu +bu +bu +bu ac -ac -ac -ac -ac -ab -ab -ab -ac -ac -ac -ac -ac -ac -ac -ab -ab -ab -ab -ab -ab -ab +au +ay +aB +ah +ah +ah +ah +ah +bu +bu +bu +ah +ah +ah +ah +ah +ah +ah +bu +bu +bu +bu +bu +bu +bu ab aA aa @@ -1404,40 +1407,40 @@ aa aA aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ad -ax +bu +bu +bu +bu +bu +bu +bu +bu ac -aD +av +ah +aC aF -ac -ac -ac -ab -ab -ab +ah +ah +ah +bu +bu +bu aU -bd -ac -ac -bl +be +ah +ah bm -ac -ac -ab -ab -ab -ab -ab -ab -ab +bn +ah +ah +bu +bu +bu +bu +bu +bu +bu ab aA aa @@ -1456,39 +1459,39 @@ aa aA aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ad -ay -ac -ac +bu +bu +bu +bu +bu +bu +bu +bu ac +aw +ah +ah +ah aG -ac -ac -ab -ab -ab -av -be -ac -ac -ac -bn +ah +ah +bu +bu +bu +bc +bf +ah +ah +ah bo -bo -bq -ab -ab -ab -ab -ab +bp +bp +br +bu +bu +bu +bu +bu ab ab aA @@ -1508,39 +1511,39 @@ aa aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ac -ac -ac -ac -ac -ac -ac -ab -ab -ab -bc -bf -ac -ac -ac -ac -ac -ac -br -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +ah +ah +ah +ah +ah +ah +ah +bu +bu +bu +bd +bg +ah +ah +ah +ah +ah +ah +bs +bu +bu +bu +bu +bu ab aA aa @@ -1559,39 +1562,39 @@ aa aA aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ac -ab -ac -ac -ac -ac -ac -ab -ab -ac -ac -ac -ac -ac -ac -ac -ac -ac -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +ah +bu +ah +ah +ah +ah +ah +bu +bu +ah +ah +ah +ah +ah +ah +ah +ah +ah +bu +bu +bu +bu ab ab aA @@ -1611,39 +1614,39 @@ aa aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ac -ac -ac -ac -ac +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +ah +ah +ah +ah +ah aN -ac -ac -ac -ac +ah +ah +ah +ah aR -ac -ac -ac -ac -ab -ab -ab -ab -ab +ah +ah +ah +ah +bu +bu +bu +bu +bu ab aA aa @@ -1662,39 +1665,39 @@ aa aa aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ac -ac -ac -ab -ab -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +ah +ah +ah +bu +bu +ah +ah +ah +ah +ah +ah +ah +ah +ah +ah +ah +bu +bu ab ab aa @@ -1715,38 +1718,38 @@ aa aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu aK -ab -ab -ac -ac -ac -ac -ac -ac -ac -bp -ac -ac +bu +bu +ah +ah +ah +ah +ah +ah +ah +bq +ah +ah aR -ab -ab +bu +bu ab aA aa @@ -1767,38 +1770,38 @@ aa aa aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu aP -ac -ac -ac -ac -ac -ac -ac -ac -ac +ah +ah +ah +ah +ah +ah +ah +ah +ah aR -ab -ab +bu +bu ab aA aa @@ -1819,38 +1822,38 @@ aa aa aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu aQ aP -ac -ac +ah +ah aR -ac -ac -ac -ac -ab -ac -ab -ab +ah +ah +ah +ah +bu +ah +bu +bu ab aA aa @@ -1871,37 +1874,37 @@ aa aa aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ac -ac -ac -ac +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +ah +ah +ah +ah aP -ac -bg -ab -ab -ab +ah +bi +bu +bu +bu ab ab aa @@ -1925,35 +1928,35 @@ aA ab ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ac -ac -ac -ac -ac -ac +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +ah +ah +ah +ah +ah +ah aS -ab -ab -ab +bu +bu +bu ab aA aa @@ -1977,35 +1980,35 @@ aa aA aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -bg -ac -ac -ac -ac -ac +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bi +ah +ah +ah +ah +ah aS -ab -ab -ab +bu +bu +bu ab aA aa @@ -2030,34 +2033,34 @@ aa aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ac -ac -bj -ac -ac -ac +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +ah +ah +bl +ah +ah +ah aS -ab -ab -ab +bu +bu +bu ab aA aa @@ -2083,32 +2086,32 @@ aa aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ab aS -bi -ac -ac -ac +bj +ah +ah +ah aS aS -ab -ab +bu +bu ab ab aA @@ -2135,32 +2138,32 @@ aM aa aA ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ab ab ab aS -ac -ac -ac -ac +ah +ah +ah +ah aS aS -ab -ab +bu +bu ab aA aa @@ -2188,18 +2191,18 @@ aa aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ab ab aA @@ -2241,17 +2244,17 @@ aA aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ab aA aA @@ -2294,16 +2297,16 @@ aA aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu +bu ab aA aA @@ -2347,15 +2350,15 @@ aA aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu +bu ab aA aa @@ -2400,14 +2403,14 @@ aA aA ab ab -ab -ab -ab -ab -ab -ab -ab -ab +bu +bu +bu +bu +bu +bu +bu +bu ab ab ab diff --git a/_maps/map_files/LayeniaStation/LayeniaStation.dmm b/_maps/map_files/LayeniaStation/LayeniaStation.dmm index 006d7aedd..d75f5d7f9 100644 --- a/_maps/map_files/LayeniaStation/LayeniaStation.dmm +++ b/_maps/map_files/LayeniaStation/LayeniaStation.dmm @@ -9396,6 +9396,12 @@ dir = 1 }, /area/science/misc_lab) +"bbY" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1{ + name = "scrubbers pipe" + }, +/turf/closed/wall, +/area/hydroponics) "bcb" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -10909,8 +10915,8 @@ /obj/structure/cable{ icon_state = "1-4" }, -/turf/open/floor/plating, -/area/maintenance/fore/secondary) +/turf/closed/wall, +/area/hydroponics) "bwq" = ( /obj/effect/spawner/lootdrop/maintenance{ lootcount = 2; @@ -18305,6 +18311,23 @@ }, /turf/open/floor/plasteel/dark, /area/engine/engine_smes) +"dou" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/loading_area{ + dir = 1; + icon_state = "drain"; + name = "drain" + }, +/obj/structure/table/glass, +/turf/open/floor/plasteel{ + icon_state = "floor_plate" + }, +/area/crew_quarters/fitness/pool) "dov" = ( /obj/structure/cable{ icon_state = "0-8" @@ -28883,20 +28906,27 @@ /turf/closed/wall/r_wall, /area/maintenance/port/aft) "gkz" = ( -/obj/structure/closet/secure_closet/personal, -/obj/item/toy/poolnoodle/blue, -/obj/item/toy/poolnoodle/red, -/obj/item/toy/poolnoodle/yellow, +/obj/structure/table, +/obj/item/reagent_containers/rag/towel{ + pixel_x = -2; + pixel_y = 18 + }, +/obj/item/reagent_containers/rag/towel{ + pixel_y = 22 + }, +/obj/item/reagent_containers/rag/towel{ + pixel_y = 3 + }, +/obj/item/reagent_containers/rag/towel{ + pixel_x = -1; + pixel_y = 7 + }, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, /obj/effect/turf_decal/tile/neutral{ dir = 8 }, -/obj/machinery/light{ - dir = 8; - pixel_x = -7 - }, /obj/effect/turf_decal/loading_area{ dir = 8; icon_state = "drain"; @@ -29661,6 +29691,22 @@ icon_state = "floor_trim" }, /area/engine/engineering) +"gsf" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/loading_area{ + dir = 1; + icon_state = "drain"; + name = "drain" + }, +/turf/open/floor/plasteel{ + icon_state = "floor_plate" + }, +/area/crew_quarters/fitness/pool) "gsh" = ( /obj/effect/turf_decal/loading_area{ dir = 1; @@ -30915,6 +30961,17 @@ }, /turf/open/floor/plating, /area/maintenance/aft) +"gHS" = ( +/obj/machinery/light{ + dir = 1; + pixel_y = 16 + }, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer3{ + dir = 4; + name = "air vent" + }, +/turf/open/floor/grass, +/area/hydroponics) "gIv" = ( /obj/effect/turf_decal/loading_area{ dir = 8; @@ -31724,8 +31781,7 @@ dir = 4 }, /obj/effect/turf_decal/tile/neutral, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1{ - dir = 4; +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden/layer1{ name = "scrubbers pipe" }, /turf/open/floor/plasteel/dark, @@ -31743,18 +31799,21 @@ /area/medical/sleeper) "gTx" = ( /obj/effect/turf_decal/tile/neutral{ - dir = 1 + dir = 4 }, /obj/effect/turf_decal/tile/neutral{ - dir = 8 + dir = 1 }, /obj/effect/turf_decal/loading_area{ - dir = 8; + dir = 1; icon_state = "drain"; name = "drain" }, +/obj/item/reagent_containers/rag/towel{ + pixel_x = 3; + pixel_y = 9 + }, /turf/open/floor/plasteel{ - dir = 4; icon_state = "floor_plate" }, /area/crew_quarters/fitness/pool) @@ -34165,6 +34224,19 @@ dir = 8; pixel_x = 24 }, +/obj/structure/table, +/obj/item/watertank, +/obj/item/reagent_containers/spray/plantbgone{ + pixel_x = 13; + pixel_y = 5 + }, +/obj/item/reagent_containers/spray/plantbgone{ + pixel_y = 3 + }, +/obj/item/reagent_containers/spray/plantbgone{ + pixel_x = 8; + pixel_y = 8 + }, /turf/open/floor/plasteel/dark, /area/hydroponics) "hDl" = ( @@ -39990,30 +40062,10 @@ /turf/open/floor/plasteel/dark, /area/chapel/main) "iWa" = ( -/obj/structure/table, -/obj/item/reagent_containers/spray/plantbgone{ - pixel_y = 3 - }, -/obj/item/reagent_containers/spray/plantbgone{ - pixel_x = 8; - pixel_y = 8 - }, -/obj/item/reagent_containers/spray/plantbgone{ - pixel_x = 13; - pixel_y = 5 - }, -/obj/item/watertank, -/obj/effect/turf_decal/tile/neutral{ +/obj/structure/window/reinforced{ dir = 4 }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, +/turf/open/floor/grass, /area/hydroponics) "iWe" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ @@ -43172,6 +43224,14 @@ }, /turf/open/floor/plating, /area/chapel/main) +"jRW" = ( +/obj/machinery/door/airlock/maintenance{ + dir = 1; + name = "Hydroponics Maintenance"; + req_access_txt = "35" + }, +/turf/open/floor/plasteel/dark, +/area/hydroponics) "jSj" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer3{ dir = 8; @@ -44607,13 +44667,14 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1{ name = "scrubbers pipe" }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer3{ - name = "air supply pipe" - }, /obj/structure/disposalpipe/segment, /obj/structure/cable{ icon_state = "1-2" }, +/obj/machinery/atmospherics/pipe/manifold/supply/hidden/layer3{ + dir = 4; + name = "air supply pipe" + }, /turf/open/floor/plasteel/dark, /area/hydroponics) "knF" = ( @@ -48493,10 +48554,13 @@ /turf/open/floor/plasteel/dark, /area/crew_quarters/fitness) "lvD" = ( -/obj/structure/kitchenspike, /obj/machinery/camera{ c_tag = "Kitchen Cold Room" }, +/obj/machinery/light{ + dir = 1; + pixel_y = 16 + }, /turf/open/floor/plasteel/showroomfloor, /area/crew_quarters/kitchen) "lwc" = ( @@ -49885,7 +49949,6 @@ /area/maintenance/fore) "lSg" = ( /obj/structure/table, -/obj/item/book/manual/hydroponics_pod_people, /obj/item/paper/guides/jobs/hydroponics, /obj/item/reagent_containers/glass/bottle/mutagen, /obj/item/reagent_containers/glass/bottle/mutagen, @@ -49905,6 +49968,9 @@ dir = 8; name = "air vent" }, +/obj/machinery/reagentgrinder{ + pixel_y = 8 + }, /turf/open/floor/plasteel/dark, /area/hydroponics) "lSI" = ( @@ -53966,21 +54032,6 @@ }, /area/science/misc_lab) "mUr" = ( -/obj/structure/table, -/obj/item/reagent_containers/rag/towel{ - pixel_x = -2; - pixel_y = 18 - }, -/obj/item/reagent_containers/rag/towel{ - pixel_y = 22 - }, -/obj/item/reagent_containers/rag/towel{ - pixel_y = 3 - }, -/obj/item/reagent_containers/rag/towel{ - pixel_x = -1; - pixel_y = 7 - }, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -53992,6 +54043,7 @@ icon_state = "drain"; name = "drain" }, +/obj/structure/jacuzzi, /turf/open/floor/plasteel{ dir = 4; icon_state = "floor_plate" @@ -54696,6 +54748,9 @@ }, /turf/open/floor/plasteel, /area/engine/break_room) +"nfw" = ( +/turf/open/floor/grass, +/area/hydroponics) "nfB" = ( /obj/effect/turf_decal/tile/green{ dir = 1 @@ -66155,11 +66210,6 @@ }, /area/bridge) "pYk" = ( -/obj/machinery/door/airlock/maintenance{ - dir = 4; - name = "Hydroponics Maintenance"; - req_access_txt = "35" - }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1{ dir = 4; name = "scrubbers pipe" @@ -66171,8 +66221,8 @@ /obj/structure/cable{ icon_state = "4-8" }, -/turf/open/floor/plasteel/dark, -/area/maintenance/fore/secondary) +/turf/closed/wall, +/area/hydroponics) "pYn" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -66983,26 +67033,17 @@ }, /area/medical/virology) "qlS" = ( -/obj/structure/closet/crate/hydroponics, -/obj/item/shovel/spade, -/obj/item/wrench, -/obj/item/reagent_containers/glass/bucket, -/obj/item/wirecutters, -/obj/item/screwdriver, -/obj/item/crowbar, -/obj/effect/turf_decal/loading_area{ - icon_state = "drain"; - name = "drain" +/obj/machinery/atmospherics/pipe/simple/supply/hidden/layer3{ + dir = 8; + name = "air supply pipe" }, -/obj/effect/turf_decal/loading_area{ +/obj/machinery/door/window/northleft{ dir = 4; - icon_state = "drain"; - name = "drain" + icon_state = "right"; + name = "Chicken Coop"; + req_access_txt = "35" }, -/obj/machinery/light_switch{ - pixel_x = -23 - }, -/turf/open/floor/plasteel, +/turf/open/floor/grass, /area/hydroponics) "qlU" = ( /obj/structure/sign/logo/kinaris{ @@ -67059,7 +67100,6 @@ }, /area/science/research) "qmq" = ( -/obj/machinery/hydroponics/constructable, /obj/effect/turf_decal/tile/green{ dir = 4 }, @@ -67567,21 +67607,11 @@ /turf/open/floor/plasteel/dark, /area/tcommsat/computer) "qsc" = ( -/obj/structure/table, -/obj/machinery/reagentgrinder{ - pixel_y = 8 - }, -/obj/effect/turf_decal/tile/neutral{ +/obj/structure/window/reinforced{ dir = 4 }, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, +/obj/item/reagent_containers/food/snacks/grown/wheat, +/turf/open/floor/grass, /area/hydroponics) "qsj" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer1{ @@ -69747,10 +69777,16 @@ }, /area/engine/break_room) "qRW" = ( -/obj/item/toy/beach_ball/holoball{ - name = "baskingball" +/obj/structure/closet/crate/wooden, +/obj/item/stack/sheet/mineral/wood, +/obj/item/stack/sheet/mineral/wood, +/obj/item/stack/sheet/mineral/wood, +/obj/item/stack/sheet/mineral/wood, +/obj/item/paper/guides/jobs/hydroponics{ + info = "Chickens love their wheat, if you feed it to them, they will be encouraged to lay eggs early. But make sure they have somewhere to nest, or they wont lay! Make sure they have soft grass, or hay to walk on, otherwise they will be unhappy. If a chickens unhappy, then you are unhappy."; + name = "paper- 'Chicken Keeping!'" }, -/turf/open/floor/plating, +/turf/open/floor/grass, /area/hydroponics) "qSg" = ( /obj/effect/turf_decal/loading_area{ @@ -71944,6 +71980,9 @@ /obj/effect/turf_decal/tile/green{ dir = 4 }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1{ + name = "scrubbers pipe" + }, /turf/open/floor/plasteel/dark/side, /area/hydroponics) "rrS" = ( @@ -85468,6 +85507,32 @@ /obj/structure/railing, /turf/open/floor/plating/asteroid/layenia, /area/layenia) +"uHP" = ( +/obj/structure/table, +/obj/item/reagent_containers/rag/towel{ + pixel_x = 2; + pixel_y = 5 + }, +/obj/item/reagent_containers/rag/towel{ + pixel_x = 3; + pixel_y = 9 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/loading_area{ + dir = 8; + icon_state = "drain"; + name = "drain" + }, +/turf/open/floor/plasteel, +/area/crew_quarters/fitness/pool) "uId" = ( /obj/structure/sign/logo/kinaris{ icon_state = "kinaris_sign3"; @@ -88438,7 +88503,6 @@ /turf/open/floor/plasteel/dark, /area/quartermaster/miningdock) "vuZ" = ( -/obj/machinery/hydroponics/constructable, /obj/effect/turf_decal/tile/green{ dir = 4 }, @@ -90447,6 +90511,12 @@ }, /turf/open/floor/plasteel/dark, /area/hallway/primary/port) +"vTc" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer1{ + name = "air scrubber" + }, +/turf/open/floor/grass, +/area/hydroponics) "vTh" = ( /obj/effect/turf_decal/loading_area{ dir = 1; @@ -91737,15 +91807,6 @@ /turf/open/floor/plasteel/white, /area/science/circuit) "wkf" = ( -/obj/structure/table, -/obj/item/reagent_containers/rag/towel{ - pixel_x = 2; - pixel_y = 5 - }, -/obj/item/reagent_containers/rag/towel{ - pixel_x = 3; - pixel_y = 9 - }, /obj/effect/turf_decal/tile/neutral{ dir = 1 }, @@ -91765,8 +91826,9 @@ icon_state = "drain"; name = "drain" }, -/obj/machinery/newscaster{ - pixel_y = 32 +/obj/machinery/light{ + dir = 1; + pixel_y = 16 }, /turf/open/floor/plasteel, /area/crew_quarters/fitness/pool) @@ -95052,15 +95114,22 @@ /turf/open/floor/plasteel, /area/engine/atmos) "xee" = ( +/obj/structure/closet/crate/hydroponics, +/obj/item/shovel/spade, +/obj/item/wrench, +/obj/item/reagent_containers/glass/bucket, +/obj/item/wirecutters, +/obj/item/screwdriver, +/obj/item/crowbar, +/obj/effect/turf_decal/loading_area{ + icon_state = "drain"; + name = "drain" + }, /obj/effect/turf_decal/loading_area{ dir = 4; icon_state = "drain"; name = "drain" }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1{ - dir = 4; - name = "scrubbers pipe" - }, /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer3{ dir = 8; name = "air supply pipe" @@ -95068,6 +95137,11 @@ /obj/structure/cable{ icon_state = "4-8" }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1{ + dir = 4; + name = "scrubbers pipe" + }, +/obj/structure/window/reinforced, /turf/open/floor/plasteel, /area/hydroponics) "xej" = ( @@ -95973,9 +96047,12 @@ /turf/open/floor/plasteel/dark, /area/arcade) "xqk" = ( -/obj/machinery/space_heater, -/turf/open/floor/plating, -/area/maintenance/fore/secondary) +/mob/living/simple_animal/chicken{ + force_gender = male; + name = "rooster" + }, +/turf/open/floor/grass, +/area/hydroponics) "xql" = ( /obj/effect/turf_decal/tile/neutral, /obj/effect/turf_decal/tile/neutral{ @@ -97809,6 +97886,19 @@ planetary_atmos = 1 }, /area/layenia/cloudlayer) +"xNj" = ( +/mob/living/simple_animal/chicken{ + force_gender = female; + name = "chicken" + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1{ + name = "scrubbers pipe" + }, +/obj/machinery/light{ + pixel_y = -1 + }, +/turf/open/floor/grass, +/area/hydroponics) "xNp" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden/layer3{ dir = 8; @@ -113886,10 +113976,10 @@ wkf mUr rbJ vrd -nBK +uHP gkz nBK -gTx +nBK cnB wwE pfI @@ -114139,7 +114229,7 @@ tBc yfm wcK yeA -sks +gTx xDB xDB sPO @@ -114396,7 +114486,7 @@ acP tEk ylM yeA -nLu +dou xDB kMH fLo @@ -114653,7 +114743,7 @@ xej gTW gTW yeA -rfR +gsf xDB sXB nss @@ -126488,11 +126578,11 @@ tti aoE tti ksw -cHs -cHs -cHs -cHs -cHs +vxc +vxc +vxc +vxc +vxc cHs cHs cHs @@ -126747,7 +126837,7 @@ pWH rDw bwp xqk -vxc +nfw qRW vxc rrL @@ -127003,10 +127093,10 @@ aNI pre vxc pYk -vxc -vxc -vxc -vxc +gHS +vTc +xNj +bbY rrR gTc uJs @@ -127770,7 +127860,7 @@ kXX oog eLb aJe -vxc +jRW qmq hDa lSg diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index c0bf99606..fb51e5930 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -10955,9 +10955,6 @@ /turf/open/floor/plasteel, /area/tdome/arena) "kjW" = ( -/obj/item/twohanded/required/kirbyplants{ - icon_state = "plant-22" - }, /obj/effect/turf_decal/tile{ color = "#630000"; dir = 1 @@ -10973,6 +10970,7 @@ /obj/effect/turf_decal/tile{ color = "#630000" }, +/obj/structure/jacuzzi, /turf/open/floor/mineral/basaltstone_floor, /area/centcom/ferry) "kke" = ( diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index 9a0664933..fdaf578cd 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -64,7 +64,7 @@ //DNA - Because fuck you and your magic numbers being all over the codebase. #define DNA_BLOCK_SIZE 3 -#define DNA_UNI_IDENTITY_BLOCKS 14 +#define DNA_UNI_IDENTITY_BLOCKS 20 #define DNA_HAIR_COLOR_BLOCK 1 #define DNA_FACIAL_HAIR_COLOR_BLOCK 2 #define DNA_SKIN_TONE_BLOCK 3 @@ -80,6 +80,14 @@ #define DNA_MUTANTMARKING_BLOCK 13 #define DNA_TAUR_BLOCK 14 +// hyper edit +#define DNA_COSMETIC_HEAD_BLOCK 15 +#define DNA_COSMETIC_CHEST_BLOCK 16 +#define DNA_COSMETIC_L_ARM_BLOCK 17 +#define DNA_COSMETIC_R_ARM_BLOCK 18 +#define DNA_COSMETIC_L_LEG_BLOCK 19 +#define DNA_COSMETIC_R_LEG_BLOCK 20 + #define DNA_SEQUENCE_LENGTH 4 #define DNA_MUTATION_BLOCKS 8 #define DNA_UNIQUE_ENZYMES_LEN 32 @@ -118,6 +126,9 @@ #define MARKINGS 17 #define WINGCOLOR 18 +#define NOMOUTH 19 + + //organ slots #define ORGAN_SLOT_BRAIN "brain" #define ORGAN_SLOT_APPENDIX "appendix" diff --git a/code/__DEFINES/hyper_arousal.dm b/code/__DEFINES/hyper_arousal.dm new file mode 100644 index 000000000..a0450b138 --- /dev/null +++ b/code/__DEFINES/hyper_arousal.dm @@ -0,0 +1,13 @@ +#define BELLY_MIN_SIZE 0 +#define BELLY_MAX_SIZE 8 + +#define BELLY_STRAIN_SIZE 3 // for flavor text +#define BELLY_STRETCH_SIZE 6 // for flavor text + +#define BUTT_MIN_SIZE 0 +#define BUTT_MAX_SIZE_SELECTABLE 5 +#define BUTT_MAX_SIZE 8 + +#define GENITALS_HIDDEN "hidden" +#define GENITALS_CLOTHES "clothes" +#define GENITALS_VISIBLE "visible" diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index c91c0fda4..363d4c38b 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -60,6 +60,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define isbrain(A) (istype(A, /mob/living/brain)) +#define isnoncarbon(A) (isliving(A) && !iscarbon(A)) + //Carbon mobs #define iscarbon(A) (istype(A, /mob/living/carbon)) diff --git a/code/__DEFINES/melee.dm b/code/__DEFINES/melee.dm index cb4a43321..e0eace8db 100644 --- a/code/__DEFINES/melee.dm +++ b/code/__DEFINES/melee.dm @@ -7,4 +7,5 @@ #define MARTIALART_MUSHPUNCH "mushroom punch" #define MARTIALART_KRAVMAGA "krav maga" #define MARTIALART_CQC "CQC" -#define MARTIALART_PLASMAFIST "plasma fist" \ No newline at end of file +#define MARTIALART_PLASMAFIST "plasma fist" +#define MARTIALART_KNS_CQC "KNS CQC" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index d45280be4..43f7dffb4 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -151,6 +151,7 @@ //Nutrition levels for humans #define NUTRITION_LEVEL_FAT 600 #define NUTRITION_LEVEL_FULL 550 +#define NUTRITION_LEVEL_ALMOST_FULL 535 #define NUTRITION_LEVEL_WELL_FED 450 #define NUTRITION_LEVEL_FED 350 #define NUTRITION_LEVEL_HUNGRY 250 diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm index 80356bec2..9502db55f 100644 --- a/code/__DEFINES/say.dm +++ b/code/__DEFINES/say.dm @@ -58,6 +58,7 @@ #define SPAN_COMMAND "command_headset" #define SPAN_CLOWN "clown" #define SPAN_SINGING "singing" +#define SPAN_SMALL "small" //bitflag #defines for return value of the radio() proc. #define ITALICS 1 diff --git a/code/__HELPERS/_hyper_helpers.dm b/code/__HELPERS/_hyper_helpers.dm new file mode 100644 index 000000000..5e23e5857 --- /dev/null +++ b/code/__HELPERS/_hyper_helpers.dm @@ -0,0 +1,10 @@ +GLOBAL_LIST_EMPTY(cosmetic_heads) +GLOBAL_LIST_EMPTY(cosmetic_chests) +GLOBAL_LIST_EMPTY(cosmetic_arms) +GLOBAL_LIST_EMPTY(cosmetic_legs) + +/proc/init_cosmetic_parts(datum/cosmetic_part/type, list/global_list) + for(var/subtype in subtypesof(type)) + var/datum/cosmetic_part/part = new subtype() + global_list[part.id] = part + return global_list \ No newline at end of file diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 9b5ac9395..62c6cdfbe 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -54,6 +54,14 @@ init_sprite_accessory_subtypes(/datum/sprite_accessory/vagina, GLOB.vagina_shapes_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/breasts, GLOB.breasts_shapes_list) + + // hyper: cosmetic parts + init_cosmetic_parts(/datum/cosmetic_part/head, GLOB.cosmetic_heads) + init_cosmetic_parts(/datum/cosmetic_part/chest, GLOB.cosmetic_chests) + init_cosmetic_parts(/datum/cosmetic_part/arms, GLOB.cosmetic_arms) + init_cosmetic_parts(/datum/cosmetic_part/legs, GLOB.cosmetic_legs) + // end cosmetic parts + GLOB.breasts_size_list = list ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o") //We need the list to choose from initialized, but it's no longer a sprite_accessory thing. GLOB.genital_fluids_list = list ("Milk", "Water", "Semen", "Femcum", "Honey") GLOB.gentlemans_organ_names = list("phallus", "willy", "dick", "prick", "member", "tool", "gentleman's organ", "cock", "wang", "knob", "dong", "joystick", "pecker", "johnson", "weenie", "tadger", "schlong", "thirsty ferret", "baloney pony", "schlanger") diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index ed0434f2f..ae5638d48 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -136,15 +136,27 @@ var/datum/sprite_accessory/S = instance if(!S.ckeys_allowed) snowflake_mam_snouts_list[S.name] = mspath + var/list/snowflake_ipc_antenna_list = list() for(var/mspath in GLOB.ipc_antennas_list) - var/datum/sprite_accessory/mam_snouts/instance = GLOB.ipc_antennas_list[mspath] + var/datum/sprite_accessory/antenna/instance = GLOB.ipc_antennas_list[mspath] if(istype(instance, /datum/sprite_accessory)) var/datum/sprite_accessory/S = instance if(intendedspecies && S.recommended_species && !S.recommended_species.Find(intendedspecies)) continue if(!S.ckeys_allowed) snowflake_ipc_antenna_list[S.name] = mspath + + var/list/snowflake_ipc_screen_list = list() + for(var/mspath in GLOB.ipc_screens_list) + var/datum/sprite_accessory/screen/instance = GLOB.ipc_screens_list[mspath] + if(istype(instance, /datum/sprite_accessory)) + var/datum/sprite_accessory/S = instance + if(intendedspecies && S.recommended_species && !S.recommended_species.Find(intendedspecies)) + continue + if(!S.ckeys_allowed) + snowflake_ipc_screen_list[S.name] = mspath + var/color1 = random_short_color() var/color2 = random_short_color() var/color3 = random_short_color() @@ -233,11 +245,17 @@ "womb_cum_mult" = CUM_RATE_MULT, "womb_efficiency" = CUM_EFFICIENCY, "womb_fluid" = "femcum", - "ipc_screen" = snowflake_ipc_antenna_list ? pick(snowflake_ipc_antenna_list) : "None", - "ipc_antenna" = "None", + "ipc_screen" = snowflake_ipc_screen_list ? pick(snowflake_ipc_screen_list) : "None", + "ipc_antenna" = snowflake_ipc_antenna_list ? pick(snowflake_ipc_antenna_list) : "None", "flavor_text" = "", "silicon_flavor_text" = "", - "ooc_text" = "")) + "ooc_text" = "", + "cosmetic_head" = GLOB.cosmetic_heads["default"], + "cosmetic_chest" = GLOB.cosmetic_chests["default"], + "cosmetic_l_arm" = GLOB.cosmetic_arms["default"], + "cosmetic_r_arm" = GLOB.cosmetic_arms["default"], + "cosmetic_l_leg" = GLOB.cosmetic_legs["default"], + "cosmetic_r_leg" = GLOB.cosmetic_legs["default"])) /proc/random_hair_style(gender) switch(gender) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 3ceb1547a..1ceef52c5 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1109,7 +1109,7 @@ B --><-- A /proc/get_random_station_turf() return safepick(get_area_turfs(pick(GLOB.the_station_areas))) - + /proc/get_safe_random_station_turf() for (var/i in 1 to 5) var/list/L = get_area_turfs(pick(GLOB.the_station_areas)) @@ -1273,6 +1273,14 @@ GLOBAL_REAL_VAR(list/stack_trace_storage) animate(src, transform = M, time = halftime, easing = ELASTIC_EASING) animate(src, transform = OM, time = halftime, easing = ELASTIC_EASING) +/atom/proc/do_twist(targetangle = 45, timer = 20) + var/matrix/OM = matrix(transform) + var/matrix/M = matrix(transform) + var/halftime = timer * 0.5 + M.Turn(pick(-targetangle, targetangle)) + animate(src, transform = M, time = halftime, easing = ELASTIC_EASING) + animate(src, transform = OM, time = halftime, easing = QUAD_EASING) + /atom/proc/do_squish(squishx = 1.2, squishy = 0.6, timer = 20) var/matrix/OM = matrix(transform) var/matrix/M = matrix(transform) @@ -1526,7 +1534,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) blocked |= typesof(/obj/item/reagent_containers/food/drinks/prospacillin, /obj/item/reagent_containers/food/drinks/diminicillin ) - + return pick(subtypesof(/obj/item/reagent_containers/food/drinks) - blocked) //For these two procs refs MUST be ref = TRUE format like typecaches! diff --git a/code/datums/dna.dm b/code/datums/dna.dm index ae9ac3826..f28b66435 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -132,6 +132,12 @@ if(!GLOB.taur_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/taur, GLOB.taur_list) L[DNA_TAUR_BLOCK] = construct_block(GLOB.taur_list.Find(features["taur"]), GLOB.taur_list.len) + L[DNA_COSMETIC_HEAD_BLOCK] = construct_block(GLOB.cosmetic_heads.Find(features["cosmetic_head"]), GLOB.cosmetic_heads.len) + L[DNA_COSMETIC_CHEST_BLOCK] = construct_block(GLOB.cosmetic_chests.Find(features["cosmetic_chest"]), GLOB.cosmetic_chests.len) + L[DNA_COSMETIC_L_ARM_BLOCK] = construct_block(GLOB.cosmetic_arms.Find(features["cosmetic_l_arm"]), GLOB.cosmetic_arms.len) + L[DNA_COSMETIC_R_ARM_BLOCK] = construct_block(GLOB.cosmetic_arms.Find(features["cosmetic_r_arm"]), GLOB.cosmetic_arms.len) + L[DNA_COSMETIC_L_LEG_BLOCK] = construct_block(GLOB.cosmetic_legs.Find(features["cosmetic_l_leg"]), GLOB.cosmetic_legs.len) + L[DNA_COSMETIC_R_LEG_BLOCK] = construct_block(GLOB.cosmetic_legs.Find(features["cosmetic_r_leg"]), GLOB.cosmetic_legs.len) for(var/i=1, i<=DNA_UNI_IDENTITY_BLOCKS, i++) if(L[i]) @@ -227,6 +233,18 @@ construct_block(GLOB.mam_body_markings_list.Find(features["mam_body_markings"]), GLOB.mam_body_markings_list.len) if(DNA_TAUR_BLOCK) construct_block(GLOB.taur_list.Find(features["taur"]), GLOB.taur_list.len) + if(DNA_COSMETIC_HEAD_BLOCK) + construct_block(GLOB.cosmetic_heads.Find(features["cosmetic_head"]), GLOB.cosmetic_heads.len) + if(DNA_COSMETIC_CHEST_BLOCK) + construct_block(GLOB.cosmetic_chests.Find(features["cosmetic_chest"]), GLOB.cosmetic_chests.len) + if(DNA_COSMETIC_L_ARM_BLOCK) + construct_block(GLOB.cosmetic_arms.Find(features["cosmetic_l_arm"]), GLOB.cosmetic_arms.len) + if(DNA_COSMETIC_R_ARM_BLOCK) + construct_block(GLOB.cosmetic_arms.Find(features["cosmetic_r_arm"]), GLOB.cosmetic_arms.len) + if(DNA_COSMETIC_L_LEG_BLOCK) + construct_block(GLOB.cosmetic_legs.Find(features["cosmetic_l_leg"]), GLOB.cosmetic_legs.len) + if(DNA_COSMETIC_R_LEG_BLOCK) + construct_block(GLOB.cosmetic_legs.Find(features["cosmetic_r_leg"]), GLOB.cosmetic_legs.len) //Please use add_mutation or activate_mutation instead /datum/dna/proc/force_give(datum/mutation/human/HM) diff --git a/code/datums/martial/kinaris.dm b/code/datums/martial/kinaris.dm new file mode 100644 index 000000000..fed965802 --- /dev/null +++ b/code/datums/martial/kinaris.dm @@ -0,0 +1,116 @@ +#define KNS_KICK_COMBO "DG" +#define KNS_RESTRAIN_COMBO "GG" + +/datum/martial_art/kinaris + name = "KNS CQC" + id = MARTIALART_KNS_CQC + help_verb = /mob/living/carbon/human/proc/KNS_CQC_help + block_chance = 75 + +/datum/martial_art/kinaris/proc/drop_restraining() + restraining = FALSE + +/datum/martial_art/kinaris/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return FALSE + if(findtext(streak,KNS_KICK_COMBO)) + streak = "" + Kick(A,D) + return TRUE + if(findtext(streak,KNS_RESTRAIN_COMBO)) + streak = "" + Restrain(A,D) + return TRUE + return FALSE + +/datum/martial_art/kinaris/proc/Kick(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return FALSE + if(!D.stat || !D.IsKnockdown()) + D.visible_message("[A] kicks [D] back!", \ + "[A] kicks you back!") + playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1) + var/atom/throw_target = get_edge_target_turf(D, A.dir) + D.throw_at(throw_target, 1, 14, A) + D.apply_damage(10, BRUTE) + log_combat(A, D, "kicked (KNS CQC)") + if(D.IsKnockdown() && !D.stat) + D.visible_message("[A] kicks [D]'s head, knocking [D.p_them()] out!", \ + "[A] kicks your head, knocking you out!") + playsound(get_turf(A), 'sound/weapons/genhit1.ogg', 50, 1, -1) + D.SetSleeping(300) + D.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15, 150) + return TRUE + +/datum/martial_art/kinaris/proc/Restrain(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(restraining) + return + if(!can_use(A)) + return FALSE + if(!D.stat) + D.visible_message("[A] locks [D] into a restraining position!", \ + "[A] locks you into a restraining position!") + D.adjustStaminaLoss(20) + D.Stun(100) + restraining = TRUE + addtimer(CALLBACK(src, .proc/drop_restraining), 50, TIMER_UNIQUE) + return TRUE + +/datum/martial_art/kinaris/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return FALSE + add_to_streak("G",D) + if(check_streak(A,D)) + return TRUE + if(A.grab_state >= GRAB_AGGRESSIVE) + D.grabbedby(A, 1) + else + A.start_pulling(D, 1) + if(A.pulling) + D.stop_pulling() + log_combat(A, D, "grabbed", addition="aggressively") + A.grab_state = GRAB_AGGRESSIVE //Instant aggressive grab + return TRUE + +/datum/martial_art/kinaris/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + if(!can_use(A)) + return FALSE + add_to_streak("D",D) + var/obj/item/I = null + if(check_streak(A,D)) + return TRUE + if(prob(65)) + if(!D.stat || !D.IsKnockdown() || !restraining) + I = D.get_active_held_item() + D.visible_message("[A] strikes [D]'s jaw with their hand!", \ + "[A] strikes your jaw, disorienting you!") + playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1) + if(I && D.temporarilyRemoveItemFromInventory(I)) + A.put_in_hands(I) + D.Jitter(2) + D.apply_damage(5, BRUTE) + else + D.visible_message("[A] attempted to disarm [D]!", \ + "[A] attempted to disarm [D]!") + playsound(D, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + log_combat(A, D, "disarmed (KNS CQC)", "[I ? " grabbing \the [I]" : ""]") + if(restraining && A.pulling == D) + D.visible_message("[A] puts [D] into a chokehold!", \ + "[A] puts you into a chokehold!") + D.SetSleeping(400) + restraining = FALSE + if(A.grab_state < GRAB_NECK) + A.grab_state = GRAB_NECK + else + restraining = FALSE + return FALSE + return TRUE + +/mob/living/carbon/human/proc/KNS_CQC_help() + set name = "Remember The Basics" + set desc = "You try to remember some of the basics of training." + set category = "KNS CQC" + to_chat(usr, "You try to remember some of your training, undertaken when entering Kinaris employ.") + + to_chat(usr, "CQC Kick: Disarm Grab. Knocks opponent away. Knocks out stunned or knocked down opponents.") + to_chat(usr, "Restrain: Grab Grab. Locks opponents into a restraining position, disarm to knock them out with a choke hold.") diff --git a/code/game/objects/items/implants/implant_kinaris.dm b/code/game/objects/items/implants/implant_kinaris.dm new file mode 100644 index 000000000..3a2039ab8 --- /dev/null +++ b/code/game/objects/items/implants/implant_kinaris.dm @@ -0,0 +1,38 @@ +/obj/item/implant/kinaris + name = "KNS ENH implant" + desc = "Enables enhanced reflexes, permitting the user to deploy specialised training." + icon = 'icons/obj/implants.dmi' + icon_state ="adrenal" + activated = 1 + var/datum/martial_art/kinaris/style = new + +/obj/item/implant/kinaris/get_data() + var/dat = {"Implant Specifications:
+ Name: KNS ENH Implant
+ Life: Roughly twelve hours after death of host
+ Implant Details:
+ Function: Forces certain sections of the brain and body into a state of higher function, permitting the user to deploy specialised training."} + return dat + +/obj/item/implant/kinaris/activate() + . = ..() + var/mob/living/carbon/human/H = imp_in + if(!ishuman(H)) + return + if(!H.mind) + return + if(H.mind.has_martialart(MARTIALART_KNS_CQC)) + style.remove(H) + to_chat(H, "You disable your reflex implant.") + else + style.teach(H,1) + to_chat(H, "You feel your body burn briefly as you enable your reflex implant.") + +/obj/item/implanter/kinaris + name = "implanter (KNS ENH)" + imp_type = /obj/item/implant/kinaris + +/obj/item/implantcase/kinaris + name = "implant case - 'KNS ENH'" + desc = "A glass case containing an implant that can enhance the body of the user." + imp_type = /obj/item/implant/kinaris diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 60c7de146..f8d822431 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -242,6 +242,7 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \ new/datum/stack_recipe("display case chassis", /obj/structure/displaycase_chassis, 5, one_per_turf = TRUE, on_floor = TRUE), \ new/datum/stack_recipe("loom", /obj/structure/loom, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \ new/datum/stack_recipe("apiary", /obj/structure/beebox, 40, time = 50),\ + new/datum/stack_recipe("nestbox", /obj/structure/nestbox, 4, time = 30, one_per_turf = TRUE, on_floor = TRUE),\ null, \ new/datum/stack_recipe("picture frame", /obj/item/wallframe/picture, 1, time = 10),\ new/datum/stack_recipe("mortar", /obj/item/reagent_containers/glass/mortar, 3), \ diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 2368bb7a1..e50c0ccec 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -613,12 +613,11 @@ if(prob(5)) name = "drone (760) action figure" desc = "An action figure modeled after 'drone (760)', it seems to be a bit dusty." + + /* || A Deck of Cards for playing various games of chance || */ - - - /obj/item/toy/cards resistance_flags = FLAMMABLE max_integrity = 50 @@ -636,7 +635,8 @@ playsound(src, 'sound/items/cardshuffle.ogg', 50, 1) return BRUTELOSS -/obj/item/toy/cards/proc/apply_card_vars(obj/item/toy/cards/newobj, obj/item/toy/cards/sourceobj) // Applies variables for supporting multiple types of card deck +/obj/item/toy/cards/proc/apply_card_vars(obj/item/toy/cards/newobj, obj/item/toy/cards/sourceobj) +// Applies variables for supporting multiple types of card deck if(!istype(sourceobj)) return @@ -658,53 +658,24 @@ /obj/item/toy/cards/deck/proc/populate_deck() icon_state = "deck_[deckstyle]_full" - for(var/i in 2 to 10) - cards += "[i] of Hearts" - cards += "[i] of Spades" - cards += "[i] of Clubs" - cards += "[i] of Diamonds" - cards += "King of Hearts" - cards += "King of Spades" - cards += "King of Clubs" - cards += "King of Diamonds" - cards += "Queen of Hearts" - cards += "Queen of Spades" - cards += "Queen of Clubs" - cards += "Queen of Diamonds" - cards += "Jack of Hearts" - cards += "Jack of Spades" - cards += "Jack of Clubs" - cards += "Jack of Diamonds" - cards += "Ace of Hearts" - cards += "Ace of Spades" - cards += "Ace of Clubs" - cards += "Ace of Diamonds" + var/_suits = list("Hearts", "Spades", "Clubs", "Diamonds") + var/_faces = list("Ace", "King", "Queen", "Jack") + for(var/suit in _suits) + for(var/i in 2 to 10) + cards += list(list( + "name" = "[i] of [suit]", + "icon_state" = "[i]of[lowertext(suit)]", + "rotation" = null, + "face_up" = null + )) + for(var/i in _faces) + cards += list(list( + "name" = "[i] of [suit]", + "icon_state" = "[lowertext(i)]of[lowertext(suit)]", + "rotation" = null, + "face_up" = null + )) -//ATTACK HAND IGNORING PARENT RETURN VALUE -//ATTACK HAND NOT CALLING PARENT -/obj/item/toy/cards/deck/attack_hand(mob/user) - draw_card(user) - -/obj/item/toy/cards/deck/proc/draw_card(mob/user) - if(user.lying) - return - var/choice = null - if(cards.len == 0) - to_chat(user, "There are no more cards to draw!") - return - var/obj/item/toy/cards/singlecard/H = new/obj/item/toy/cards/singlecard(user.loc) - if(holo) - holo.spawned += H // track them leaving the holodeck - choice = cards[1] - H.cardname = choice - H.parentdeck = src - var/O = src - H.apply_card_vars(H,O) - src.cards.Cut(1,2) //Removes the top card from the list - H.pickup(user) - user.put_in_hands(H) - user.visible_message("[user] draws a card from the deck.", "You draw a card from the deck.") - update_icon() /obj/item/toy/cards/deck/update_icon() if(cards.len > original_size/2) @@ -716,60 +687,6 @@ else if(cards.len == 0) icon_state = "deck_[deckstyle]_empty" -/obj/item/toy/cards/deck/attack_self(mob/user) - if(cooldown < world.time - 50) - cards = shuffle(cards) - playsound(src, 'sound/items/cardshuffle.ogg', 50, 1) - user.visible_message("[user] shuffles the deck.", "You shuffle the deck.") - cooldown = world.time - -/obj/item/toy/cards/deck/attackby(obj/item/I, mob/living/user, params) - if(istype(I, /obj/item/toy/cards/singlecard)) - var/obj/item/toy/cards/singlecard/SC = I - if(SC.parentdeck == src) - if(!user.temporarilyRemoveItemFromInventory(SC)) - to_chat(user, "The card is stuck to your hand, you can't add it to the deck!") - return - cards += SC.cardname - user.visible_message("[user] adds a card to the bottom of the deck.","You add the card to the bottom of the deck.") - qdel(SC) - else - to_chat(user, "You can't mix cards from other decks!") - update_icon() - else if(istype(I, /obj/item/toy/cards/cardhand)) - var/obj/item/toy/cards/cardhand/CH = I - if(CH.parentdeck == src) - if(!user.temporarilyRemoveItemFromInventory(CH)) - to_chat(user, "The hand of cards is stuck to your hand, you can't add it to the deck!") - return - cards += CH.currenthand - user.visible_message("[user] puts [user.p_their()] hand of cards in the deck.", "You put the hand of cards in the deck.") - qdel(CH) - else - to_chat(user, "You can't mix cards from other decks!") - update_icon() - else - return ..() - -/obj/item/toy/cards/deck/MouseDrop(atom/over_object) - . = ..() - var/mob/living/M = usr - if(!istype(M) || usr.incapacitated() || usr.lying) - return - if(Adjacent(usr)) - if(over_object == M && loc != M) - M.put_in_hands(src) - to_chat(usr, "You pick up the deck.") - - else if(istype(over_object, /obj/screen/inventory/hand)) - var/obj/screen/inventory/hand/H = over_object - if(M.putItemFromInventoryInHandIfPossible(src, H.held_index)) - to_chat(usr, "You pick up the deck.") - - else - to_chat(usr, "You can't reach it from here!") - - /obj/item/toy/cards/cardhand name = "hand of cards" @@ -780,59 +697,9 @@ var/list/currenthand = list() var/choice = null -/obj/item/toy/cards/cardhand/attack_self(mob/user) - var/list/handradial = list() - interact(user) - - for(var/t in currenthand) - handradial[t] = image(icon = src.icon, icon_state = "sc_[t]_[deckstyle]") - - if(usr.stat || !ishuman(usr)) - return - var/mob/living/carbon/human/cardUser = usr - var/O = src - var/choice = show_radial_menu(usr,src, handradial, custom_check = CALLBACK(src, .proc/check_menu, user), radius = 36, require_near = TRUE) - if(!choice) - return FALSE - var/obj/item/toy/cards/singlecard/C = new/obj/item/toy/cards/singlecard(cardUser.loc) - currenthand -= choice - handradial -= choice - C.parentdeck = parentdeck - C.cardname = choice - C.apply_card_vars(C,O) - C.pickup(cardUser) - cardUser.put_in_hands(C) - cardUser.visible_message("[cardUser] draws a card from [cardUser.p_their()] hand.", "You take the [C.cardname] from your hand.") - - interact(cardUser) - update_sprite() - if(length(currenthand) == 1) - var/obj/item/toy/cards/singlecard/N = new/obj/item/toy/cards/singlecard(loc) - N.parentdeck = parentdeck - N.cardname = currenthand[1] - N.apply_card_vars(N,O) - qdel(src) - N.pickup(cardUser) - cardUser.put_in_hands(N) - to_chat(cardUser, "You also take [currenthand[1]] and hold it.") - -/obj/item/toy/cards/cardhand/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) - if(istype(C)) - if(C.parentdeck == src.parentdeck) - src.currenthand += C.cardname - user.visible_message("[user] adds a card to [user.p_their()] hand.", "You add the [C.cardname] to your hand.") - qdel(C) - interact(user) - update_sprite(src) - else - to_chat(user, "You can't mix cards from other decks!") - else - return ..() - /obj/item/toy/cards/cardhand/apply_card_vars(obj/item/toy/cards/newobj,obj/item/toy/cards/sourceobj) ..() newobj.deckstyle = sourceobj.deckstyle - update_sprite() newobj.card_hitsound = sourceobj.card_hitsound newobj.card_force = sourceobj.card_force newobj.card_throwforce = sourceobj.card_throwforce @@ -840,6 +707,7 @@ newobj.card_throw_range = sourceobj.card_throw_range newobj.card_attack_verb = sourceobj.card_attack_verb newobj.resistance_flags = sourceobj.resistance_flags + update_icon() /** * check_menu: Checks if we are allowed to interact with a radial menu @@ -857,13 +725,23 @@ /** * This proc updates the sprite for when you create a hand of cards */ -/obj/item/toy/cards/cardhand/proc/update_sprite() +/obj/item/toy/cards/cardhand/update_icon() cut_overlays() var/overlay_cards = currenthand.len if(overlay_cards) var/k = overlay_cards == 2 ? 1 : overlay_cards - 2 for(var/i = k; i <= overlay_cards; i++) - var/card_overlay = image(icon=src.icon,icon_state="sc_[currenthand[i]]_[deckstyle]",pixel_x=(1-i+k)*3,pixel_y=(1-i+k)*3) + var/_card = currenthand[i] + var/_angle = _card["rotation"] || rotation + var/matrix/rot_matrix = matrix() + rot_matrix.Turn(GetAngle(_angle)) + var/image/card_overlay = image( + icon = src.icon, + icon_state = "sc_[_card["icon_state"] || _card["name"]]_[deckstyle]", + pixel_x = (1 - i + k)*3, + pixel_y = (1 - i + k)*3, + ) + card_overlay.transform = rot_matrix add_overlay(card_overlay) /obj/item/toy/cards/singlecard @@ -872,8 +750,7 @@ icon = 'icons/obj/toy.dmi' icon_state = "singlecard_down_nanotrasen" w_class = WEIGHT_CLASS_TINY - var/cardname = null - var/flipped = 0 + var/card = null pixel_x = -5 @@ -882,7 +759,9 @@ if(ishuman(user)) var/mob/living/carbon/human/cardUser = user if(cardUser.is_holding(src)) - cardUser.visible_message("[cardUser] checks [cardUser.p_their()] card.", "The card reads: [cardname].") + cardUser.visible_message( + "[cardUser] checks [cardUser.p_their()] card.", + "The card reads: [card["name"]]. [card["desc"] || ""]") else . += "You need to have the card in your hand to check it!" @@ -892,65 +771,11 @@ set src in range(1) if(!ishuman(usr) || !usr.canUseTopic(src, BE_CLOSE)) return - if(!flipped) - src.flipped = 1 - if (cardname) - src.icon_state = "sc_[cardname]_[deckstyle]" - src.name = src.cardname - else - src.icon_state = "sc_Ace of Spades_[deckstyle]" - src.name = "What Card" - src.pixel_x = 5 - else if(flipped) - src.flipped = 0 - src.icon_state = "singlecard_down_[deckstyle]" - src.name = "card" - src.pixel_x = -5 - -/obj/item/toy/cards/singlecard/attackby(obj/item/I, mob/living/user, params) - if(istype(I, /obj/item/toy/cards/singlecard/)) - var/obj/item/toy/cards/singlecard/C = I - if(C.parentdeck == src.parentdeck) - var/obj/item/toy/cards/cardhand/H = new/obj/item/toy/cards/cardhand(user.loc) - H.currenthand += C.cardname - H.currenthand += src.cardname - H.parentdeck = C.parentdeck - H.apply_card_vars(H,C) - to_chat(user, "You combine the [C.cardname] and the [src.cardname] into a hand.") - qdel(C) - qdel(src) - H.pickup(user) - user.put_in_active_hand(H) - else - to_chat(user, "You can't mix cards from other decks!") - - if(istype(I, /obj/item/toy/cards/cardhand/)) - var/obj/item/toy/cards/cardhand/H = I - if(H.parentdeck == parentdeck) - H.currenthand += cardname - user.visible_message("[user] adds a card to [user.p_their()] hand.", "You add the [cardname] to your hand.") - qdel(src) - H.interact(user) - if(H.currenthand.len > 4) - H.icon_state = "[deckstyle]_hand5" - else if(H.currenthand.len > 3) - H.icon_state = "[deckstyle]_hand4" - else if(H.currenthand.len > 2) - H.icon_state = "[deckstyle]_hand3" - else - to_chat(user, "You can't mix cards from other decks!") - else - return ..() - -/obj/item/toy/cards/singlecard/attack_self(mob/user) - if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) - return - Flip() + FlipCard() /obj/item/toy/cards/singlecard/apply_card_vars(obj/item/toy/cards/singlecard/newobj,obj/item/toy/cards/sourceobj) ..() newobj.deckstyle = sourceobj.deckstyle - newobj.icon_state = "singlecard_down_[deckstyle]" // Without this the card is invisible until flipped. It's an ugly hack, but it works. newobj.card_hitsound = sourceobj.card_hitsound newobj.hitsound = newobj.card_hitsound newobj.card_force = sourceobj.card_force @@ -963,6 +788,7 @@ newobj.throw_range = newobj.card_throw_range newobj.card_attack_verb = sourceobj.card_attack_verb newobj.attack_verb = newobj.card_attack_verb + newobj.update_icon() /* || Syndicate playing cards, for pretending you're Gambit and playing poker for the nuke disk. || diff --git a/code/game/turfs/simulated/floor/plating/asteroid.dm b/code/game/turfs/simulated/floor/plating/asteroid.dm index 2c6adffa8..78520bbfd 100644 --- a/code/game/turfs/simulated/floor/plating/asteroid.dm +++ b/code/game/turfs/simulated/floor/plating/asteroid.dm @@ -123,8 +123,9 @@ planetary_atmos = TRUE baseturfs = /turf/open/lava/smooth/lava_land_surface - - +/turf/open/floor/plating/asteroid/spacehermit + initial_gas_mix = OPENTURF_DEFAULT_ATMOS + baseturfs = /turf/open/floor/plating/asteroid/spacehermit /turf/open/floor/plating/asteroid/airless initial_gas_mix = AIRLESS_ATMOS diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm index 58c72cf90..53f9874f4 100644 --- a/code/game/turfs/simulated/minerals.dm +++ b/code/game/turfs/simulated/minerals.dm @@ -221,6 +221,22 @@ /turf/closed/mineral/iron/volcanic = 95) +//For spacehermit stuff + + +/turf/closed/mineral/random/spacehermit + icon_state = "rock" + turf_type = /turf/open/floor/plating/asteroid/spacehermit + baseturfs = /turf/open/floor/plating/asteroid/spacehermit + initial_gas_mix = OPENTURF_DEFAULT_ATMOS + defer_change = 1 + mineralChance = 6 + mineralSpawnChanceList = list( + /turf/closed/mineral/uranium = 2, /turf/closed/mineral/diamond = 1, /turf/closed/mineral/gold = 4, /turf/closed/mineral/titanium = 4, + /turf/closed/mineral/silver = 6, /turf/closed/mineral/plasma = 15, /turf/closed/mineral/iron = 40, + /turf/closed/mineral/gibtonite = 2, /turf/closed/mineral/bscrystal = 1) + +//end of spacehermit stuff /turf/closed/mineral/iron mineralType = /obj/item/stack/ore/iron diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 822d72abe..b8d8a1879 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -108,7 +108,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/roleplayroles = FALSE //for the roleplay roles var/importantroles = FALSE //for things that define as important. - var/datum/species/pref_species = new /datum/species/human() //Mutant race var/list/features = list("mcolor" = "FFF", "tail_lizard" = "Smooth", @@ -196,7 +195,14 @@ GLOBAL_LIST_EMPTY(preferences_datums) "flavor_text" = "", "silicon_flavor_text" = "", "ooc_text" = "", - "front_genitals_over_hair" = FALSE + "front_genitals_over_hair" = FALSE, + "cosmetic_head" = /datum/cosmetic_part/head/default, + "cosmetic_chest" = /datum/cosmetic_part/chest/default, + "cosmetic_l_arm" = /datum/cosmetic_part/arms/default, + "cosmetic_r_arm" = /datum/cosmetic_part/arms/default, + "cosmetic_l_leg" = /datum/cosmetic_part/legs/default, + "cosmetic_r_leg" = /datum/cosmetic_part/legs/default, + "cosmetic_markings" = FALSE ) /// Security record note section @@ -439,7 +445,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Custom Species Name:[custom_species ? custom_species : "None"]
" dat += "Random Body
" dat += "Always Random Body:[be_random_body ? "Yes" : "No"]
" - dat += "
Cycle background:[bgstate]
" + dat += "
Select background:[bgstate]
" dat += "Render front genitals over hair:[features["front_genitals_over_hair"] ? "Yes" : "No"]
" var/use_skintones = pref_species.use_skintones @@ -824,8 +830,55 @@ GLOBAL_LIST_EMPTY(preferences_datums) mutant_category = 0 dat += "" - dat += "" + + // HYPER EDIT: Cosmetic body parts + + var/datum/cosmetic_part/cosmetic_head = features["cosmetic_head"] + var/datum/cosmetic_part/cosmetic_chest = features["cosmetic_chest"] + var/datum/cosmetic_part/cosmetic_arms = features["cosmetic_l_arm"] + var/datum/cosmetic_part/cosmetic_legs = features["cosmetic_l_leg"] + + dat += "
" + dat += "

Cosmetic Parts

" + dat += "Use markings on cosmetic parts? " + dat += "[features["cosmetic_markings"] ? "Yes" : "No"]" + dat += "
" + dat += "
" + dat += "

Head

" + dat += "There are no valid alt heads for this species!") + var/new_cosmetic_part + var/question = "Choose your character's alt head style:" + new_cosmetic_part = input(user, question, "Character Preference") as null|anything in selectable_parts + if(new_cosmetic_part) + features["cosmetic_head"] = GLOB.cosmetic_heads[selectable_parts[new_cosmetic_part]] + update_preview_icon() + + if("cosmetic_chest") + var/list/selectable_parts = list() + for(var/path in GLOB.cosmetic_chests) + var/datum/cosmetic_part/possible_part = GLOB.cosmetic_chests[path] + var/list/supported_species = possible_part.supported_species + if(!show_mismatched_markings && supported_species && !supported_species.Find(pref_species.id)) + continue + selectable_parts[possible_part.name] = path + if(!selectable_parts.len) + to_chat(user, "There are no valid alt chests for this species!") + var/new_cosmetic_part + var/question = "Choose your character's alt chest style:" + new_cosmetic_part = input(user, question, "Character Preference") as null|anything in selectable_parts + if(new_cosmetic_part) + features["cosmetic_chest"] = GLOB.cosmetic_chests[selectable_parts[new_cosmetic_part]] + update_preview_icon() + + // currently symmetrical + if("cosmetic_arms") + var/list/selectable_parts = list() + for(var/path in GLOB.cosmetic_arms) + var/datum/cosmetic_part/possible_part = GLOB.cosmetic_arms[path] + var/list/supported_species = possible_part.supported_species + if(!show_mismatched_markings && supported_species && !supported_species.Find(pref_species.id)) + continue + selectable_parts[possible_part.name] = path + if(!selectable_parts.len) + to_chat(user, "There are no valid alt arms for this species!") + var/new_cosmetic_part + var/question = "Choose your character's alt arms style:" + new_cosmetic_part = input(user, question, "Character Preference") as null|anything in selectable_parts + if(new_cosmetic_part) + features["cosmetic_l_arm"] = GLOB.cosmetic_arms[selectable_parts[new_cosmetic_part]] + features["cosmetic_r_arm"] = GLOB.cosmetic_arms[selectable_parts[new_cosmetic_part]] + update_preview_icon() + + if("cosmetic_legs") + var/list/selectable_parts = list() + for(var/path in GLOB.cosmetic_legs) + var/datum/cosmetic_part/possible_part = GLOB.cosmetic_legs[path] + var/list/supported_species = possible_part.supported_species + if(!show_mismatched_markings && supported_species && !supported_species.Find(pref_species.id)) + continue + selectable_parts[possible_part.name] = path + if(!selectable_parts.len) + to_chat(user, "There are no valid alt legs for this species!") + var/new_cosmetic_part + var/question = "Choose your character's alt legs style:" + new_cosmetic_part = input(user, question, "Character Preference") as null|anything in selectable_parts + if(new_cosmetic_part) + features["cosmetic_l_leg"] = GLOB.cosmetic_legs[selectable_parts[new_cosmetic_part]] + features["cosmetic_r_leg"] = GLOB.cosmetic_legs[selectable_parts[new_cosmetic_part]] + update_preview_icon() + + // End hyper edit + //Genital code if("cock_color") var/new_cockcolor = input(user, "Penis color:", "Character Preference") as color|null @@ -2430,14 +2560,15 @@ GLOBAL_LIST_EMPTY(preferences_datums) to_chat(user,"Invalid color. Your color is not bright enough.") if("belly_size") - var/new_bellysize = input(user, "Belly size :\n(1-3)", "Character Preference") as num|null - if(new_bellysize) - features["belly_size"] = clamp(new_bellysize, 1, 3) + var/new_bellysize = input(user, "Belly size :\n([BELLY_MIN_SIZE]-[BELLY_MAX_SIZE])", "Character Preference") as num|null + if(new_bellysize != null) + features["belly_size"] = clamp(new_bellysize, BELLY_MIN_SIZE, BELLY_MAX_SIZE) if("butt_size") - var/new_buttsize = input(user, "Butt size :\n(0-5)", "Character Preference") as num|null - if(new_buttsize) - features["butt_size"] = clamp(new_buttsize, 0, 5) + var/new_buttsize = input(user, "Butt size :\n([BUTT_MIN_SIZE]-[BUTT_MAX_SIZE_SELECTABLE])", "Character Preference") as num|null + if(new_buttsize != null) + features["butt_size"] = clamp(new_buttsize, BUTT_MIN_SIZE, BUTT_MAX_SIZE_SELECTABLE) + //Restricted to 5 in menu, because we have chems to make them big IC, like with breasts and what not. if("vag_shape") var/new_shape @@ -2572,7 +2703,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(features["has_belly"] == FALSE) features["hide_belly"] = FALSE features["inflatable_belly"] = FALSE - features["belly_size"] = 1 + features["belly_size"] = 0 if("inflatable_belly") features["inflatable_belly"] = !features["inflatable_belly"] @@ -2687,6 +2818,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("all") be_random_body = !be_random_body + if("cosmetic_markings") + features["cosmetic_markings"] = !features["cosmetic_markings"] + if("hear_midis") toggles ^= SOUND_MIDI diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 80fab69e6..a77f504f8 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -576,6 +576,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car all_quirks -= V cit_character_pref_load(S) + hyper_character_pref_load(S) return 1 @@ -671,6 +672,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["all_quirks"] , all_quirks) cit_character_pref_save(S) + hyper_character_pref_save(S) return 1 diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index 8b2a34a08..c675bce3e 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -438,7 +438,7 @@ resistance_flags = NONE armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) clothing_flags = BLOCK_GAS_SMOKE_EFFECT | ALLOWINTERNALS - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|HIDEFACIALHAIR + flags_inv = HIDEEYES|HIDEFACE|HIDEFACIALHAIR gas_transfer_coefficient = 0.01 permeability_coefficient = 0.01 flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index 107350fca..5aeb76da3 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -3,7 +3,7 @@ desc = "A face-covering mask that can be connected to an air supply. While good for concealing your identity, it isn't good for blocking gas flow." //More accurate icon_state = "gas_alt" clothing_flags = BLOCK_GAS_SMOKE_EFFECT | ALLOWINTERNALS - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|HIDEFACIALHAIR|HIDESNOUT + flags_inv = HIDEEYES|HIDEFACE|HIDEFACIALHAIR|HIDESNOUT w_class = WEIGHT_CLASS_NORMAL item_state = "gas_alt" gas_transfer_coefficient = 0.01 diff --git a/code/modules/clothing/masks/hailer.dm b/code/modules/clothing/masks/hailer.dm index 9c100208d..5d53688cd 100644 --- a/code/modules/clothing/masks/hailer.dm +++ b/code/modules/clothing/masks/hailer.dm @@ -27,7 +27,7 @@ icon_state = "swat" item_state = "swat" aggressiveness = 3 - flags_inv = HIDEFACIALHAIR|HIDEFACE|HIDEEYES|HIDEEARS|HIDEHAIR + flags_inv = HIDEFACIALHAIR|HIDEFACE|HIDEEYES|HIDEHAIR visor_flags_inv = 0 /obj/item/clothing/mask/gas/sechailer/cyborg diff --git a/code/modules/games/cas.dm b/code/modules/games/cas.dm index 4fbb931b3..5911e26aa 100644 --- a/code/modules/games/cas.dm +++ b/code/modules/games/cas.dm @@ -2,39 +2,36 @@ // This is a parody of Cards Against Humanity (https://en.wikipedia.org/wiki/Cards_Against_Humanity) // which is licensed under CC BY-NC-SA 2.0, the full text of which can be found at the following URL: // https://creativecommons.org/licenses/by-nc-sa/2.0/legalcode -// Original code by Zuhayr, Polaris Station, ported with modifications -/datum/playingcard - var/name = "playing card" - var/card_icon = "card_back" - var/suit - var/number +// Original code by Zuhayr, Polaris Station, ported with modifications +// April 2022: modified by sarcoph for hyperstation /obj/item/toy/cards/deck/cas name = "\improper CAS deck (white)" desc = "A deck for the game Cards Against Spess, still popular after all these centuries. Warning: may include traces of broken fourth wall. This is the white deck." icon = 'icons/obj/toy.dmi' icon_state = "deck_caswhite_full" - deckstyle = "caswhite" - var/card_face = "cas_white" + deckstyle = "cas" + var/card_face = "white" var/blanks = 25 var/decksize = 150 var/card_text_file = "strings/cas_white.txt" - var/list/allcards = list() /obj/item/toy/cards/deck/cas/black name = "\improper CAS deck (black)" desc = "A deck for the game Cards Against Spess, still popular after all these centuries. Warning: may include traces of broken fourth wall. This is the black deck." icon_state = "deck_casblack_full" - deckstyle = "casblack" - card_face = "cas_black" + card_face = "black" blanks = 0 decksize = 50 card_text_file = "strings/cas_black.txt" /obj/item/toy/cards/deck/cas/populate_deck() - var/static/list/cards_against_space = list("cas_white" = world.file2list("strings/cas_white.txt"),"cas_black" = world.file2list("strings/cas_black.txt")) - allcards = cards_against_space[card_face] - var/list/possiblecards = allcards.Copy() + var/static/list/cards_against_space = list( + "white" = world.file2list("strings/cas_white.txt"), + "black" = world.file2list("strings/cas_black.txt") + ) + var/list/_cards = cards_against_space[card_face] + var/list/possiblecards = _cards.Copy() if(possiblecards.len < decksize) // sanity check decksize = (possiblecards.len - 1) var/list/randomcards = list() @@ -42,105 +39,74 @@ randomcards += pick_n_take(possiblecards) for(var/x in 1 to randomcards.len) var/cardtext = randomcards[x] - var/datum/playingcard/P - P = new() - P.name = "[cardtext]" - P.card_icon = "[src.card_face]" - cards += P + cards += list(list( + "name" = cardtext, + "icon_state" = src.card_face, + "rotation" = null, + "face_up" = null + )) if(!blanks) return for(var/x in 1 to blanks) - var/datum/playingcard/P - P = new() - P.name = "Blank Card" - P.card_icon = "cas_white" - cards += P - shuffle_inplace(cards) // distribute blank cards throughout deck + cards += list(list( + "name" = "Blank Card", + "icon_state" = "white", + "rotation" = null, + "face_up" = null + )) -/obj/item/toy/cards/deck/cas/draw_card(mob/user) - if(user.lying) - return - if(cards.len == 0) - to_chat(user, "There are no more cards to draw!") - return - var/obj/item/toy/cards/singlecard/cas/H = new/obj/item/toy/cards/singlecard/cas(user.loc) - var/datum/playingcard/choice = cards[1] - if (choice.name == "Blank Card") - H.blank = 1 - H.name = choice.name - H.buffertext = choice.name - H.icon_state = choice.card_icon - H.card_face = choice.card_icon - H.parentdeck = src - src.cards -= choice - H.pickup(user) - user.put_in_hands(H) - user.visible_message("[user] draws a card from the deck.", "You draw a card from the deck.") - update_icon() - -/obj/item/toy/cards/deck/cas/attackby(obj/item/I, mob/living/user, params) - if(istype(I, /obj/item/toy/cards/singlecard/cas)) - var/obj/item/toy/cards/singlecard/cas/SC = I - if(!user.temporarilyRemoveItemFromInventory(SC)) - to_chat(user, "The card is stuck to your hand, you can't add it to the deck!") - return - var/datum/playingcard/RC // replace null datum for the re-added card - RC = new() - RC.name = "[SC.name]" - RC.card_icon = SC.card_face - cards += RC - user.visible_message("[user] adds a card to the bottom of the deck.","You add the card to the bottom of the deck.") - qdel(SC) +/obj/item/toy/cards/deck/cas/DrawOneCard(list/card_indices) + var/obj/item/toy/cards/singlecard/cas/S = new/obj/item/toy/cards/singlecard/cas(usr.loc) + var/_card = cards[card_indices[1]] + _card["rotation"] = rotation + _card["face_up"] = face_up + if(_card["name"] == "Blank Card") + S.blank = TRUE + S.card = _card + S.card_face = card_face + S.rotation = _card["rotation"] + S.face_up = _card["face_up"] + S.parentdeck = src + S.apply_card_vars(S,src) + cards -= list(_card) update_icon() + return S /obj/item/toy/cards/deck/cas/update_icon() if(cards.len < 26) - icon_state = "deck_[deckstyle]_low" + icon_state = "deck_[deckstyle][card_face]_low" + + + +// ----------- SINGLE CARD ----------- /obj/item/toy/cards/singlecard/cas name = "CAS card" desc = "A CAS card." - icon_state = "cas_white" - flipped = 0 - var/card_face = "cas_white" + icon_state = "sc_cas_white" + var/card_face = "white" var/blank = 0 var/buffertext = "A funny bit of text." /obj/item/toy/cards/singlecard/cas/examine(mob/user) . = ..() - if (flipped) + if (!face_up) . += "The card is face down." else if (blank) . += "The card is blank. Write on it with a pen." else . += "The card reads: [name]" - . += "Alt-click to flip it." - -/obj/item/toy/cards/singlecard/cas/Flip() - set name = "Flip Card" - set category = "Object" - set src in range(1) - if(!ishuman(usr) || !usr.canUseTopic(src, BE_CLOSE)) - return - if(!flipped) - name = "CAS card" - else if(flipped) - name = buffertext - flipped = !flipped - update_icon() - -/obj/item/toy/cards/singlecard/cas/AltClick(mob/living/user) - . = ..() - if(!ishuman(user) || !user.canUseTopic(src, BE_CLOSE)) - return - Flip() - return TRUE /obj/item/toy/cards/singlecard/cas/update_icon() - if(flipped) - icon_state = "[card_face]_flipped" + var/matrix/rot_matrix = matrix() + rot_matrix.Turn(GetAngle(rotation)) + transform = rot_matrix + if(face_up) + icon_state = "sc_[deckstyle]_[card_face]" + name = card["name"] else - icon_state = "[card_face]" + icon_state = "sc_[deckstyle]_[card_face]_flipped" + name = "CAS card" /obj/item/toy/cards/singlecard/cas/attackby(obj/item/I, mob/living/user, params) if(istype(I, /obj/item/pen)) diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm index 1565ab437..f568378c1 100644 --- a/code/modules/jobs/job_types/captain.dm +++ b/code/modules/jobs/job_types/captain.dm @@ -65,7 +65,7 @@ Captain satchel = /obj/item/storage/backpack/satchel/cap duffelbag = /obj/item/storage/backpack/duffelbag/captain - implants = list(/obj/item/implant/mindshield) + implants = list(/obj/item/implant/mindshield, /obj/item/implant/kinaris) accessory = /obj/item/clothing/accessory/medal/gold/captain chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/captain) diff --git a/code/modules/jobs/job_types/security.dm b/code/modules/jobs/job_types/security.dm index 0b18fd650..c199641ff 100644 --- a/code/modules/jobs/job_types/security.dm +++ b/code/modules/jobs/job_types/security.dm @@ -62,7 +62,7 @@ Head of Security duffelbag = /obj/item/storage/backpack/duffelbag/sec box = /obj/item/storage/box/security - implants = list(/obj/item/implant/mindshield) + implants = list(/obj/item/implant/mindshield, /obj/item/implant/kinaris) chameleon_extras = list(/obj/item/gun/energy/e_gun/hos, /obj/item/stamp/hos) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 168b31410..c436e5808 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -487,6 +487,9 @@ if(HAS_TRAIT(src, TRAIT_NOHUNGER)) return 1 + if(!has_mouth()) + return 1 + if(nutrition < 100 && !blood) if(message) visible_message("[src] dry heaves!", \ diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 92ad4fa08..1765e6873 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -5,30 +5,24 @@ icon_state = "caucasian_m" appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE + /mob/living/carbon/human/Initialize() add_verb(src, /mob/living/proc/mob_sleep) add_verb(src, /mob/living/proc/lay_down) time_initialized = world.time - //initialize limbs first create_bodyparts() - //initialize dna. for spawned humans; overwritten by other code create_dna(src) randomize_human(src) dna.initialize_dna() - if(dna.species) set_species(dna.species.type) - //initialise organs create_internal_organs() //most of it is done in set_species now, this is only for parent call physiology = new() - AddComponent(/datum/component/personal_crafting) - . = ..() - RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, /mob/living/carbon/human/clean_blood) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index a036dbb7f..287dd4d29 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -318,15 +318,24 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/mob/living/carbon/human/H = C if(NOGENITALS in H.dna.species.species_traits) H.give_genitals(TRUE) //call the clean up proc to delete anything on the mob then return. - // EDIT ENDS + if(NOMOUTH in species_traits) + for(var/obj/item/bodypart/head/head in C.bodyparts) + head.mouth = FALSE + + /datum/species/proc/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load) if(C.dna.species.exotic_bloodtype) if(!new_species.exotic_bloodtype) C.dna.blood_type = random_blood_type() else C.dna.blood_type = new_species.exotic_bloodtype + + if(NOMOUTH in species_traits) + for(var/obj/item/bodypart/head/head in C.bodyparts) + head.mouth = TRUE + if(DIGITIGRADE in species_traits) C.Digitigrade_Leg_Swap(TRUE) for(var/X in inherent_traits) @@ -502,6 +511,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/list/standing = list() var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD) + H.handle_cosmetic_parts() if(HD && !(HAS_TRAIT(H, TRAIT_HUSK))) // lipstick @@ -594,6 +604,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) //CITADEL EDIT - Do not forget to add this to relevent_layers list just above too! H.remove_overlay(BODY_TAUR_LAYER) //END EDIT + H.handle_cosmetic_parts() if(!mutant_bodyparts) return @@ -678,21 +689,17 @@ GLOBAL_LIST_EMPTY(roundstart_races) if("mam_tail" in mutant_bodyparts) if(H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR) || (!H.dna.features["taur"] == "None")) bodyparts_to_add -= "mam_tail" - if("mam_waggingtail" in mutant_bodyparts) if(H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR) || (!H.dna.features["taur"] == "None")) bodyparts_to_add -= "mam_waggingtail" else if ("mam_tail" in mutant_bodyparts) bodyparts_to_add -= "mam_waggingtail" - if("mam_ears" in mutant_bodyparts) if(!H.dna.features["mam_ears"] || H.dna.features["mam_ears"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEARS)) || !HD || HD.status == BODYPART_ROBOTIC) bodyparts_to_add -= "mam_ears" - if("mam_snouts" in mutant_bodyparts) //Take a closer look at that snout! if((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || HD.status == BODYPART_ROBOTIC) bodyparts_to_add -= "mam_snouts" - if("taur" in mutant_bodyparts) if(!H.dna.features["taur"] || H.dna.features["taur"] == "None" || (H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR))) bodyparts_to_add -= "taur" @@ -1647,8 +1654,8 @@ GLOBAL_LIST_EMPTY(roundstart_races) ) if (target.canbearoused) target.adjustArousalLoss(5) - if (target.getArousalLoss() >= 100 && ishuman(target) && HAS_TRAIT(target, TRAIT_MASO) && target.has_dna()) - target.mob_climax(forced_climax=TRUE) + if (HAS_TRAIT(target, TRAIT_MASO)) + target.mob_climax_instant() if (!HAS_TRAIT(target, TRAIT_NYMPHO)) stop_wagging_tail(target) @@ -2005,8 +2012,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) H.update_damage_overlays() if(HAS_TRAIT(H, TRAIT_MASO)) H.adjustArousalLoss(damage * brutemod * H.physiology.brute_mod) - if (H.getArousalLoss() >= 100 && ishuman(H) && H.has_dna()) - H.mob_climax(forced_climax=TRUE) + H.mob_climax_instant() else//no bodypart, we deal damage with a more general method. H.adjustBruteLoss(damage * hit_percent * brutemod * H.physiology.brute_mod) diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 4c224a515..6b68f3b8a 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -690,6 +690,8 @@ generate/load female uniform sprites matching all previously decided variables . += "-[BP.digitigrade_type]" if(BP.dmg_overlay_type) . += "-[BP.dmg_overlay_type]" + if(BP.cosmetic_icon) + . += "-cosm[BP.cosmetic_icon]" if(BP.body_markings) . += "-[BP.body_markings]" else diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index ca11c14d0..fc3c25c15 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -77,8 +77,7 @@ var/mob/living/carbon/human/B = src if(HAS_TRAIT(B, TRAIT_CHOKE_SLUT)) B.adjustArousalLoss(7) - if (B.getArousalLoss() >= 100 && ishuman(B) && B.has_dna()) - B.mob_climax(forced_climax=TRUE) + B.mob_climax_instant() else SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation) else diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index 2e66809e5..4ebe01313 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -181,147 +181,6 @@ else ..() -/mob/living/simple_animal/chick - name = "\improper chick" - desc = "Adorable! They make such a racket though." - icon_state = "chick" - icon_living = "chick" - icon_dead = "chick_dead" - icon_gib = "chick_gib" - gender = FEMALE - mob_biotypes = MOB_ORGANIC|MOB_BEAST - speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") - speak_emote = list("cheeps") - emote_hear = list("cheeps.") - emote_see = list("pecks at the ground.","flaps its tiny wings.") - density = FALSE - speak_chance = 2 - turns_per_move = 2 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/chicken = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - attacktext = "kicks" - health = 3 - maxHealth = 3 - ventcrawler = VENTCRAWLER_ALWAYS - var/amount_grown = 0 - pass_flags = PASSTABLE | PASSGRILLE | PASSMOB - mob_size = MOB_SIZE_TINY - gold_core_spawnable = FRIENDLY_SPAWN - - do_footstep = TRUE - -/mob/living/simple_animal/chick/Initialize() - . = ..() - pixel_x = rand(-6, 6) - pixel_y = rand(0, 10) - -/mob/living/simple_animal/chick/Life() - . =..() - if(!.) - return - if(!stat && !ckey) - amount_grown += rand(1,2) - if(amount_grown >= 100) - new /mob/living/simple_animal/chicken(src.loc) - qdel(src) - -/mob/living/simple_animal/chick/holo/Life() - ..() - amount_grown = 0 - -/mob/living/simple_animal/chicken - name = "\improper chicken" - desc = "Hopefully the eggs are good this season." - gender = FEMALE - mob_biotypes = MOB_ORGANIC|MOB_BEAST - icon_state = "chicken_brown" - icon_living = "chicken_brown" - icon_dead = "chicken_brown_dead" - speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") - speak_emote = list("clucks","croons") - emote_hear = list("clucks.") - emote_see = list("pecks at the ground.","flaps its wings viciously.") - density = FALSE - speak_chance = 2 - turns_per_move = 3 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/chicken = 2) - var/egg_type = /obj/item/reagent_containers/food/snacks/egg - var/food_type = /obj/item/reagent_containers/food/snacks/grown/wheat - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - attacktext = "kicks" - health = 15 - maxHealth = 15 - ventcrawler = VENTCRAWLER_ALWAYS - var/eggsleft = 0 - var/eggsFertile = TRUE - var/body_color - var/icon_prefix = "chicken" - pass_flags = PASSTABLE | PASSMOB - mob_size = MOB_SIZE_SMALL - var/list/feedMessages = list("It clucks happily.","It clucks happily.") - var/list/layMessage = EGG_LAYING_MESSAGES - var/list/validColors = list("brown","black","white") - gold_core_spawnable = FRIENDLY_SPAWN - var/static/chicken_count = 0 - - do_footstep = TRUE - -/mob/living/simple_animal/chicken/Initialize() - . = ..() - if(!body_color) - body_color = pick(validColors) - icon_state = "[icon_prefix]_[body_color]" - icon_living = "[icon_prefix]_[body_color]" - icon_dead = "[icon_prefix]_[body_color]_dead" - pixel_x = rand(-6, 6) - pixel_y = rand(0, 10) - ++chicken_count - -/mob/living/simple_animal/chicken/Destroy() - --chicken_count - return ..() - -/mob/living/simple_animal/chicken/attackby(obj/item/O, mob/user, params) - if(istype(O, food_type)) //feedin' dem chickens - if(!stat && eggsleft < 8) - var/feedmsg = "[user] feeds [O] to [name]! [pick(feedMessages)]" - user.visible_message(feedmsg) - qdel(O) - eggsleft += rand(1, 4) - else - to_chat(user, "[name] doesn't seem hungry!") - else - ..() - -/mob/living/simple_animal/chicken/Life() - . =..() - if(!.) - return - if((!stat && prob(3) && eggsleft > 0) && egg_type) - visible_message("[src] [pick(layMessage)]") - eggsleft-- - var/obj/item/E = new egg_type(get_turf(src)) - E.pixel_x = rand(-6,6) - E.pixel_y = rand(-6,6) - if(eggsFertile) - if(chicken_count < MAX_CHICKENS && prob(25)) - START_PROCESSING(SSobj, E) - -/obj/item/reagent_containers/food/snacks/egg/var/amount_grown = 0 -/obj/item/reagent_containers/food/snacks/egg/process() - if(isturf(loc)) - amount_grown += rand(1,2) - if(amount_grown >= 100) - visible_message("[src] hatches with a quiet cracking sound.") - new /mob/living/simple_animal/chick(get_turf(src)) - STOP_PROCESSING(SSobj, src) - qdel(src) - else - STOP_PROCESSING(SSobj, src) // Space kiwis, ergo quite a copypasta of chickens. diff --git a/code/modules/mob/living/simple_animal/gremlin/gremlin.dm b/code/modules/mob/living/simple_animal/gremlin/gremlin.dm index 0d154c2c2..b4694b6f0 100644 --- a/code/modules/mob/living/simple_animal/gremlin/gremlin.dm +++ b/code/modules/mob/living/simple_animal/gremlin/gremlin.dm @@ -73,7 +73,7 @@ GLOBAL_LIST(bad_gremlin_items) var/datum/job/captain/C = new /datum/job/captain access_card.access = C.get_access() if(!body_color) - body_color = pick(list("orange","blue","purple", "green")) + body_color = pick(list("orange","blue","purple", "green", "crystal")) AddElement(/datum/element/mob_holder, "gremlin_[body_color]") icon_state = "gremlin_[body_color]" icon_living = "gremlin_[body_color]" diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index b291a295c..ac9a37eb2 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -104,6 +104,8 @@ var/icon_update_needed = FALSE var/obj/machinery/computer/apc_control/remote_control = null + var/obj/effect/light/lighteffect //light effect + /obj/machinery/power/apc/unlocked locked = FALSE @@ -185,6 +187,8 @@ addtimer(CALLBACK(src, .proc/update), 5) /obj/machinery/power/apc/Destroy() + if(lighteffect) + lighteffect.Del() GLOB.apcs_list -= src if(malfai && operating) @@ -320,6 +324,13 @@ // And now, separately for cleanness, the lighting changing if(update_state & UPSTATE_ALLGOOD) + if(!lighteffect)//make the light + lighteffect = new/obj/effect/light + lighteffect.loc = src.loc + lighteffect.alpha = 50 + lighteffect.pixel_x = pixel_x + lighteffect.pixel_y = pixel_y + switch(charging) if(APC_NOT_CHARGING) light_color = LIGHT_COLOR_RED @@ -328,6 +339,7 @@ if(APC_FULLY_CHARGED) light_color = LIGHT_COLOR_GREEN set_light(lon_range) + lighteffect.color = light_color else if(update_state & UPSTATE_BLUESCREEN) light_color = LIGHT_COLOR_BLUE set_light(lon_range) @@ -336,6 +348,10 @@ icon_update_needed = FALSE +/obj/machinery/power/apc/Move() + if(lighteffect) + lighteffect.loc = src.loc //move the light overlay + /obj/machinery/power/apc/proc/check_updates() var/last_update_state = update_state var/last_update_overlay = update_overlay diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 39072d5b4..0db3695fc 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -223,6 +223,9 @@ var/bulb_emergency_pow_mul = 0.75 // the multiplier for determining the light's power in emergency mode var/bulb_emergency_pow_min = 0.5 // the minimum value for the light's power in emergency mode + var/obj/effect/light/lighteffect //light effect + + /obj/machinery/light/broken status = LIGHT_BROKEN icon_state = "tube-broken" @@ -242,6 +245,8 @@ icon_state = "bulb-broken" /obj/machinery/light/Move() + if(lighteffect) + lighteffect.loc = src.loc if(status != LIGHT_BROKEN) break_light_tube(1) return ..() @@ -285,6 +290,8 @@ /obj/machinery/light/Destroy() var/area/A = get_area(src) + if(lighteffect) + lighteffect.Del() if(A) on = FALSE // A.update_lights() @@ -292,24 +299,35 @@ return ..() /obj/machinery/light/update_icon() + if(!lighteffect) //dont have a light bloom, make it. + lighteffect = new/obj/effect/light/large + lighteffect.loc = src.loc cut_overlays() switch(status) // set icon_states if(LIGHT_OK) var/area/A = get_area(src) if(emergency_mode || (A && A.fire)) icon_state = "[base_state]_emergency" + lighteffect.alpha = 0 else icon_state = "[base_state]" if(on) + lighteffect.alpha = CLAMP(light_power*35, 5, 100) + lighteffect.color = light_color var/mutable_appearance/glowybit = mutable_appearance(overlayicon, base_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE) glowybit.alpha = CLAMP(light_power*250, 30, 200) add_overlay(glowybit) if(LIGHT_EMPTY) icon_state = "[base_state]-empty" + lighteffect.alpha = 0 if(LIGHT_BURNED) icon_state = "[base_state]-burned" + lighteffect.alpha = 0 if(LIGHT_BROKEN) icon_state = "[base_state]-broken" + lighteffect.alpha = 0 + if(!on) + lighteffect.alpha = 0 return // update the icon_state and luminosity of the light depending on its state diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 63bd0bc52..65b86570c 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -197,7 +197,10 @@ L.visible_message("[L] is hit by \a [src][organ_hit_text]!", \ "[L] is hit by \a [src][organ_hit_text]!", null, COMBAT_MESSAGE_RANGE) if(candink && def_zone == BODY_ZONE_HEAD) - playsound(src, 'sound/weapons/dink.ogg', 30, 1) + if(prob(2)) + playsound(src, 'hyperstation/sound/weapons/dinkalternate.ogg', 30, 0) + else + playsound(src, 'sound/weapons/dink.ogg', 30, 1) L.on_hit(src) var/reagent_note diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm index de4a4b684..2cb562cff 100644 --- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -1001,3 +1001,5 @@ glass_desc = "A Summer time drink that can be frozen and eaten or Drinked from a glass!" glass_name = "Orange Creamsicle" hydration = 4 + + diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 6d93a5e13..150c9db70 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -65,6 +65,12 @@ var/who = (isnull(user) || eater == user) ? "your" : "[eater.p_their()]" to_chat(user, "You have to remove [who] [covered] first!") return 0 + if(!eater.has_mouth()) + if(eater == user) + to_chat(eater, "You have no mouth, and cannot eat.") + else + to_chat(user, "You can't feed [eater], because they have no mouth!") + return 0 return 1 /obj/item/reagent_containers/ex_act() diff --git a/code/modules/ruins/objects_and_mobs/sin_ruins.dm b/code/modules/ruins/objects_and_mobs/sin_ruins.dm index 18e90b3a6..5fba1aa4b 100644 --- a/code/modules/ruins/objects_and_mobs/sin_ruins.dm +++ b/code/modules/ruins/objects_and_mobs/sin_ruins.dm @@ -282,16 +282,16 @@ . = ..() if(.) return - if(chalice_taken == 1) + if(chalice_taken) to_chat(user, "You hear a voice in your head... \"My chalice has already been taken, dear. I cannot give you another...\"") return if(user.getArousalLoss() < 100) to_chat(user, "You hear a voice in your head... \"You are not horny enough to receive my blessing, dear~\"") return - if (ishuman(user) && user.has_dna()) - user.mob_climax(forced_climax=TRUE) - to_chat(user, "You hear a voice in your head... \"You are worth of my blessing dear~\"") - to_chat(user, "You feel overpowering pleasure surge through your entire body.") - var/A = new /obj/item/reagent_containers/chalice/lust - user.put_in_hands(A) - chalice_taken = 1 \ No newline at end of file + var/mob/living/carbon/user_carbon = user + user_carbon.mob_climax_instant() + to_chat(user, "You hear a voice in your head... \"You are worthy of my blessing, dear~\"") + to_chat(user, "You feel overpowering pleasure surge through your entire body.") + var/A = new /obj/item/reagent_containers/chalice/lust + user.put_in_hands(A) + chalice_taken = TRUE \ No newline at end of file diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm index 7f3b477bc..b0208f9db 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/bodyparts.dm @@ -44,6 +44,13 @@ var/skin_tone = "" var/body_gender = "" var/species_id = "" + /** + * HYPER: when getting the appearance of a limb, the game will check + * for `cosmetic_icon` first. if there is none, it will default back + * to `species_id`. this allows us to use custom body part types for + * each limb in character customization. + */ + var/datum/cosmetic_part/cosmetic_icon var/color_src var/base_bp_icon //Overrides the icon being used for this limb. This is mainly for downstreams, implemented and maintained as a favor in return for implementing synths. And also because should_draw_* for icon overrides was pretty messy. You're welcome. var/should_draw_gender = FALSE @@ -195,7 +202,7 @@ burn_dam += burn if(status == BODYPART_ORGANIC) //pain is only applied to organic organs, because nerves. - pain_dam += (brute+burn)*1.2 //add the total damage applied to the limb as pain damage, build pain quicker, because sudden pain is more.. painful. + pain_dam += (brute+burn)*1.05 //add the total damage applied to the limb as pain damage, build pain quicker, because sudden pain is more.. painful. //We've dealt the physical damages, if there's room lets apply the stamina damage. var/current_damage = get_damage(TRUE) //This time around, count stamina loss too. @@ -391,12 +398,22 @@ else body_markings = "plain" auxmarking = "plain" - markings_color = list(colorlist) - else body_markings = null auxmarking = null + if(cosmetic_icon && is_organic_limb()) + if(cosmetic_icon.icon) + base_bp_icon = cosmetic_icon.icon + use_digitigrade = cosmetic_icon.support_digitigrade ? use_digitigrade : NOT_DIGITIGRADE + color_src = cosmetic_icon.color_src ? cosmetic_icon.color_src : MUTCOLORS + if(!H.dna.features["cosmetic_markings"]) + body_markings = null + auxmarking = null + + if((MUTCOLORS in S.species_traits) || (MUTCOLORS_PARTSONLY in S.species_traits)) + markings_color = list(colorlist) + if(!dropping_limb && H.dna.check_mutation(HULK)) mutation_color = "00aa00" else @@ -436,6 +453,11 @@ var/image_dir = 0 var/icon_gender = (body_gender == FEMALE) ? "f" : "m" //gender of the icon, if applicable + var/is_husk = species_id == "husk" + + /// HYPER: allow for custom limb icons in character customization + var/has_cosmetic_state = !isnull(cosmetic_icon) && !isnull(cosmetic_icon.icon_state) + var/limb_style = has_cosmetic_state ? cosmetic_icon.icon_state : species_id if(dropped) image_dir = SOUTH @@ -464,7 +486,7 @@ if(animal_origin) if(is_organic_limb()) limb.icon = 'icons/mob/animal_parts.dmi' - if(species_id == "husk") + if(is_husk) limb.icon_state = "[animal_origin]_husk_[body_zone]" else limb.icon_state = "[animal_origin]_[body_zone]" @@ -479,21 +501,21 @@ if(is_organic_limb()) limb.icon = base_bp_icon || 'icons/mob/human_parts.dmi' if(should_draw_gender) - limb.icon_state = "[species_id]_[body_zone]_[icon_gender]" + limb.icon_state = "[limb_style]_[body_zone]_[icon_gender]" else if (use_digitigrade) if(base_bp_icon == DEFAULT_BODYPART_ICON_ORGANIC) //Compatibility hack for the current iconset. limb.icon_state = "digitigrade_[use_digitigrade]_[body_zone]" else - limb.icon_state = "[species_id]_digitigrade_[use_digitigrade]_[body_zone]" + limb.icon_state = "[limb_style]_digitigrade_[use_digitigrade]_[body_zone]" else - limb.icon_state = "[species_id]_[body_zone]" + limb.icon_state = "[limb_style]_[body_zone]" // Body markings if(body_markings) - if(species_id == "husk") + if(is_husk) marking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[body_zone]", -MARKING_LAYER, image_dir) - else if(species_id == "husk" && use_digitigrade) + else if(is_husk && use_digitigrade) marking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_digitigrade_[use_digitigrade]_[body_zone]", -MARKING_LAYER, image_dir) else if(!use_digitigrade) @@ -508,10 +530,10 @@ // Citadel End if(aux_zone) - aux = image(limb.icon, "[species_id]_[aux_zone]", -aux_layer, image_dir) + aux = image(limb.icon, "[limb_style]_[aux_zone]", -aux_layer, image_dir) . += aux if(body_markings) - if(species_id == "husk") + if(is_husk) auxmarking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[aux_zone]", -aux_layer, image_dir) else auxmarking = image(body_markings_icon, "[body_markings]_[aux_zone]", -aux_layer, image_dir) @@ -528,16 +550,16 @@ aux = image(limb.icon, "[aux_zone]", -aux_layer, image_dir) . += aux if(!isnull(auxmarking)) - if(species_id == "husk") + if(is_husk) auxmarking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[aux_zone]", -aux_layer, image_dir) else auxmarking = image(body_markings_icon, "[body_markings]_[aux_zone]", -aux_layer, image_dir) . += auxmarking if(!isnull(body_markings)) - if(species_id == "husk") + if(is_husk) marking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_[body_zone]", -MARKING_LAYER, image_dir) - else if(species_id == "husk" && use_digitigrade) + else if(is_husk && use_digitigrade) marking = image('modular_citadel/icons/mob/markings_notmammals.dmi', "husk_digitigrade_[use_digitigrade]_[body_zone]", -MARKING_LAYER, image_dir) else if(!use_digitigrade) @@ -550,20 +572,27 @@ . += marking return - if(color_src) //TODO - add color matrix support for base species limbs - var/draw_color = mutation_color || species_color || (skin_tone && skintone2hex(skin_tone)) - if(draw_color) - limb.color = "#[draw_color]" + if(!color_src) + return + switch(color_src) + if(MUTCOLORS) + var/draw_color = mutation_color || species_color || (skin_tone && skintone2hex(skin_tone)) + if(draw_color) + limb.color = "#[draw_color]" + if(aux_zone) + aux.color = "#[draw_color]" + if(MATRIXED) + limb.color = list(markings_color) if(aux_zone) - aux.color = "#[draw_color]" - if(!isnull(auxmarking)) - auxmarking.color = list(markings_color) + aux.color = list(markings_color) - if(!isnull(body_markings)) - if(species_id == "husk") - marking.color = "#141414" - else - marking.color = list(markings_color) + if(!isnull(auxmarking)) + auxmarking.color = list(markings_color) + if(!isnull(body_markings)) + if(is_husk) + marking.color = "#141414" + else + marking.color = list(markings_color) /obj/item/bodypart/deconstruct(disassembled = TRUE) diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index a71cc199c..45f791b3c 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -34,6 +34,9 @@ var/lip_color = "white" //If the head is a special sprite var/custom_head + + // if head has mouth; may depend on species + var/mouth = TRUE /obj/item/bodypart/head/can_dismember(obj/item/I) if(!((owner.stat == DEAD) || owner.InFullCritical())) diff --git a/code/modules/surgery/organs/stomach.dm b/code/modules/surgery/organs/stomach.dm index afa991ca7..1c45d5df6 100644 --- a/code/modules/surgery/organs/stomach.dm +++ b/code/modules/surgery/organs/stomach.dm @@ -120,6 +120,27 @@ return owner.vomit(stun = FALSE) -/obj/item/organ/stomach/ipc - name = "ipc stomach" + +/obj/item/organ/stomach/synthliz + name = "synthliz stomach" icon_state = "stomach-ipc" + +// ipc power cell from oracle +/obj/item/organ/stomach/cell + name = "micro-cell" + icon_state = "microcell" + w_class = WEIGHT_CLASS_NORMAL + zone = BODY_ZONE_CHEST + slot = ORGAN_SLOT_STOMACH + status = ORGAN_ROBOTIC + attack_verb = list("assault and battery'd") + desc = "A micro-cell, for IPC use only. Do not swallow." + +/obj/item/organ/stomach/cell/emp_act(severity) + switch(severity) + if(1) + owner.nutrition = 50 + to_chat(owner, "Alert: Heavy EMP Detected. Rebooting power cell to prevent damage.") + if(2) + owner.nutrition = 250 + to_chat(owner, "Alert: EMP Detected. Cycling battery.") \ No newline at end of file diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index b2d19c586..7c6fe05ca 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -581,7 +581,7 @@ for(var/V in listeners) var/mob/living/carbon/human/H = V if(H.canbearoused && H.has_dna() && HAS_TRAIT(H, TRAIT_NYMPHO)) // probably a redundant check but for good measure - H.mob_climax(forced_climax=TRUE) + H.mob_climax_instant() */ else if((findtext(message, family_friendly_words))) cooldown = COOLDOWN_MEME @@ -1123,7 +1123,7 @@ if(E.phase > 1) if(HAS_TRAIT(H, TRAIT_NYMPHO) && H.canbearoused && E.lewd) // probably a redundant check but for good measure addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "Your [E.enthrallGender] pushes you over the limit, overwhelming your body with pleasure."), 5) - H.mob_climax(forced_climax=TRUE) + H.mob_climax_instant() H.SetStun(20) H.setArousalLoss(H.min_arousal) E.resistanceTally = 0 //makes resistance 0, but resets arousal, resistance buildup is faster unaroused (massively so). diff --git a/code/modules/vending/autodrobe.dm b/code/modules/vending/autodrobe.dm index e53d8a471..2dbaf5394 100644 --- a/code/modules/vending/autodrobe.dm +++ b/code/modules/vending/autodrobe.dm @@ -111,7 +111,8 @@ /obj/item/clothing/ears/headphones = 2, /obj/item/clothing/head/wig/random = 3, /obj/item/clothing/suit/ran = 2, - /obj/item/clothing/head/ran = 2) + /obj/item/clothing/head/ran = 2, + /obj/item/clothing/under/vaultsuit = 3) contraband = list(/obj/item/clothing/suit/judgerobe = 1, /obj/item/clothing/head/powdered_wig = 1, /obj/item/gun/magic/wand = 2, diff --git a/code/modules/vending/clothesmate.dm b/code/modules/vending/clothesmate.dm index 5ceb898cf..0a3044a6f 100644 --- a/code/modules/vending/clothesmate.dm +++ b/code/modules/vending/clothesmate.dm @@ -111,6 +111,7 @@ /obj/item/clothing/suit/toggle/jacket_white = 3, /obj/item/clothing/suit/jacket/letterman_red = 3, /obj/item/clothing/suit/fluffyhalfcrop = 3, + /obj/item/clothing/under/officesexy = 3, /obj/item/clothing/ears/headphones = 10, /obj/item/clothing/suit/apron/purple_bartender = 4, /obj/item/clothing/under/rank/bartender/purple = 4, @@ -133,6 +134,7 @@ /obj/item/clothing/head/cowboyhat/pink = 3, /obj/item/clothing/shoes/cowboyboots = 3, /obj/item/clothing/shoes/cowboyboots/black = 3, + /obj/item/clothing/under/raccveralls = 3, /obj/item/clothing/under/polychromic/vsweater = 3) contraband = list(/obj/item/clothing/under/syndicate/tacticool = 3, /obj/item/clothing/under/syndicate/tacticool/skirt = 3, diff --git a/code/modules/vending/megaseed.dm b/code/modules/vending/megaseed.dm index e8e243c7f..5b87fd02d 100644 --- a/code/modules/vending/megaseed.dm +++ b/code/modules/vending/megaseed.dm @@ -46,7 +46,8 @@ /obj/item/seeds/tower = 3, /obj/item/seeds/watermelon = 3, /obj/item/seeds/wheat = 3, - /obj/item/seeds/whitebeet = 3) + /obj/item/seeds/whitebeet = 3, + /obj/item/seeds/kalyna = 3) //Hyperstation addition contraband=list(/obj/item/seeds/amanita = 2, /obj/item/seeds/glowshroom = 2, diff --git a/hyperstation/code/datums/elements/spooky.dm b/hyperstation/code/datums/elements/spooky.dm new file mode 100644 index 000000000..793f0f555 --- /dev/null +++ b/hyperstation/code/datums/elements/spooky.dm @@ -0,0 +1,38 @@ +//A spooky element, this can be added to pretty much anything, take your pick. It is effectively harmless however. +//target.AddElement(/datum/element/spooky) for example. + +/datum/element/spooky + element_flags = ELEMENT_DETACH + var/datum/C + +/datum/element/spooky/Attach(datum/target) + . = ..() + /* + if(!iscarbon(target)) + return ELEMENT_INCOMPATIBLE //keeping this uncommented would make it so it only works with carbons, which is not really needed unless one adds procs that require the user to be a carbon in the future. + */ + C = target + START_PROCESSING(SSobj, src) + +/datum/element/spooky/Detach(datum/source, force) + . = ..() + STOP_PROCESSING(SSobj, src) + +/datum/element/spooky/process() + var/spookythings = rand(120) + switch(spookythings) + if(119 to 120) //Plays a spooky ambient sound as long as one hasn't been played in a while. + var/sound = pick(SPOOKY) + for(var/mob/living/carbon/L in view(6, C)) + if(L.client && !L.client.played) + SEND_SOUND(L, sound(sound, repeat = 0, wait = 0, volume = 35, channel = CHANNEL_AMBIENCE)) + L.client.played = TRUE + addtimer(CALLBACK(L.client, /client/proc/ResetAmbiencePlayed), 600) + if(65 to 70) //Lights flicker. + var/obj/machinery/light/L = locate(/obj/machinery/light) in view(4, C) + if(L) + L.flicker() + if(1 to 5) //Very small chance to cause an unburnt tile to change to its burnt sprite. + var/turf/open/floor/T = locate(/turf/open/floor) in view(4, C) + if(T && prob(5) && !isplatingturf(T) && !istype(T, /turf/open/floor/engine/cult) && !T.burnt) + T.burn_tile() diff --git a/hyperstation/code/effects/lighting.dm b/hyperstation/code/effects/lighting.dm new file mode 100644 index 000000000..02e07bfcd --- /dev/null +++ b/hyperstation/code/effects/lighting.dm @@ -0,0 +1,18 @@ +/obj/effect/light/ + name = "bloom" + icon = 'hyperstation/icons/effects/lighting.dmi' + icon_state = "light" + plane = ABOVE_LIGHTING_PLANE + blend_mode = BLEND_ADD + mouse_opacity = 0 + anchored = TRUE //no more flying around. + +/obj/effect/light/medium + icon = 'hyperstation/icons/effects/lightingmed.dmi' + pixel_x = -16 + pixel_y = -16 + +/obj/effect/light/large + icon = 'hyperstation/icons/effects/lightinglarge.dmi' + pixel_x = -48 + pixel_y = -48 \ No newline at end of file diff --git a/hyperstation/code/game/objects/items/cards.dm b/hyperstation/code/game/objects/items/cards.dm new file mode 100644 index 000000000..a2c0c1e1b --- /dev/null +++ b/hyperstation/code/game/objects/items/cards.dm @@ -0,0 +1,464 @@ +/* +* cards rework; uses TGUI and allows in-depth manipulation of cards, such as +* angles, flip states, drawing a number of cards at once, peeking, etc. +* +* sarcoph march 2022 +*/ + +#define CARD_ROTATION_UP "upright" +#define CARD_ROTATION_SIDE "sideways" +#define CARD_ROTATION_DOWN "reversed" + +// ================================= GENERAL CARDS ================================= + +/obj/item/toy/cards + var/face_up = FALSE + var/rotation = CARD_ROTATION_UP + var/merge_rank = 0 + +/obj/item/toy/cards/proc/GetAngle(_dir) + var/_angle = 0 + switch(_dir) + if(CARD_ROTATION_UP) + _angle = 0 + if(CARD_ROTATION_SIDE) + _angle = 90 + if(CARD_ROTATION_DOWN) + _angle = 180 + return _angle + +/obj/item/toy/cards/proc/GetNextAngle(angle) + var/list/_rotations = list(CARD_ROTATION_UP, CARD_ROTATION_SIDE, CARD_ROTATION_DOWN) + var/list/_indexof = _rotations.Find(rotation) + return _rotations[_indexof%3 + 1] + + +/obj/item/toy/cards/proc/RotateCards(angle) + if(angle) + rotation = angle + else + rotation = GetNextAngle(angle) + update_icon() + +/obj/item/toy/cards/proc/FlipCards(side) + if(side != null) face_up = side + else face_up = !face_up + update_icon() + + +/** + * Handles functionality for merging different types of cards. + * + * Arguments: + * * target - The "greater" card item that this object is going to merge into. + * * user - The mob that is performing this merge. + * + * Returns: + * * TRUE/FALSE: Whether or not this logic is considered "processed" - i.e., a merge + * was actually attempted. + */ +/obj/item/toy/cards/proc/MergeInto(obj/item/toy/cards/target, mob/living/user) + if(target.parentdeck != src && src.parentdeck != target && src.parentdeck != target.parentdeck) + to_chat(user, "You can't mix cards from other decks!") + return TRUE + if(!user.temporarilyRemoveItemFromInventory(src)) + to_chat(user, "\The [src] is stuck to your hand, you can't add it to \the [target]!") + return TRUE + return FALSE + // unimplemented + +/** + * Announces card(s) being added to a deck, and then deletes the card(s). + */ +/obj/item/toy/cards/proc/FinishMergingCards(obj/item/toy/cards/target, mob/living/user) + return FALSE // unimplemented + +/obj/item/toy/cards/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/toy/cards)) + var/obj/item/toy/cards/C = I + var/obj/item/toy/cards/greater = merge_rank > C.merge_rank ? src : C + var/obj/item/toy/cards/lesser = greater == src ? C : src + if(lesser.MergeInto(greater, user)) return + else + return ..() + +// ================================= DECK OF CARDS ================================= + +/obj/item/toy/cards/deck + var/peeking = FALSE + var/dealing = FALSE + merge_rank = 3 + +/** + * Randomizes positions of all cards in a deck, plays a nice sound, + * and announces this to the `user`'s surroundings. There is a small + * cooldown. + * + * Arguments: + * * user - The `mob` shuffling this deck of cards. + */ +/obj/item/toy/cards/deck/proc/ShuffleCards(mob/user) + if(cooldown < world.time - 5 SECONDS) + cards = shuffle(cards) + playsound(src, 'sound/items/cardshuffle.ogg', 50, 1) + user.visible_message("[user] shuffles the deck.", "You shuffle the deck.") + cooldown = world.time + +/** + * Draws cards into a new hand from a list of indices, turning those new + * cards into a hand. + * + * Arguments: + * * card_indices - The `list` of card indices to remove from `cards`. + * + * Returns: + * * /obj/item/toy/cards/cardhand - A new hand containing the removed cards. + * OR + * * /obj/item/toy/cards/singlecard - A single card, if there is only one index. + */ +/obj/item/toy/cards/deck/proc/DrawCards(list/card_indices) + if(card_indices.len == 1) return DrawOneCard(card_indices) + var/obj/item/toy/cards/cardhand/H = new/obj/item/toy/cards/cardhand(usr.loc) + var/list/cards_to_remove = list() + for(var/C in card_indices) + var/card_to_add = cards[C] + card_to_add["rotation"] = rotation + card_to_add["face_up"] = face_up + H.currenthand += list(card_to_add) + cards_to_remove += list(card_to_add) + cards -= cards_to_remove + H.parentdeck = src + H.apply_card_vars(H,src) + update_icon() + return H + +/obj/item/toy/cards/deck/proc/DrawOneCard(list/card_indices) + var/obj/item/toy/cards/singlecard/S = new/obj/item/toy/cards/singlecard(usr.loc) + var/_card = cards[card_indices[1]] + _card["rotation"] = rotation + _card["face_up"] = face_up + S.card = _card + S.rotation = _card["rotation"] + S.face_up = _card["face_up"] + S.parentdeck = src + S.apply_card_vars(S,src) + cards -= list(_card) + return S + +/obj/item/toy/cards/deck/FinishMergingCards(obj/item/toy/cards/target, mob/living/user) + var/message = "[user] adds \the [target] to the bottom of \the [src]." + var/self_message = "You add \the [target] to the bottom of \the [src]." + user.visible_message(message, self_message) + qdel(target) + update_icon() + +/obj/item/toy/cards/deck/examine(mob/user) + . = ..() + . += "Alt-Click to quick-draw a card." + +/obj/item/toy/cards/deck/AltClick(mob/user) + . = ..() + if(!ishuman(usr) || !usr.canUseTopic(src, BE_CLOSE)) + return + var/obj/item/toy/cards/drawn = DrawCards(list(1)) + drawn.pickup(user) + user.put_in_hands(drawn) + to_chat(user, "You draw \a [drawn] from \the [src].") + + +// =================== TGUI stuff =================== + +/obj/item/toy/cards/deck/ui_interact(mob/user, ui_key, datum/tgui/ui, force_open, datum/tgui/master_ui, datum/tgui_state/state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "CardsDeck", name, 300, 400, master_ui, state) + ui.open() + +/obj/item/toy/cards/deck/ui_act(action, params) + if(..()) + return + var/list/targets = list(usr, src) + switch(action) + if("deal") + var/n_cards = text2num(params["count"]) + var/n_hands = text2num(params["hands"]) + if(dealing) return FALSE + if(!n_cards || !n_hands) return FALSE + if(n_cards * n_hands > cards.len) + to_chat(usr, "You can't deal more cards than there are in the deck!") + return FALSE + dealing = TRUE + to_chat(usr, "You get ready to deal cards...") + if(do_after_mob(usr, targets, 1 SECONDS, progress=TRUE)) + visible_message("[usr] begins dealing cards.") + var/list/holder = list() + var/broken = FALSE + for(var/C = 1, C <= n_cards, C++) holder += C // card indices placeholder + for(var/H = 1, H <= n_hands, H++) + if(do_after_mob(usr, targets, 0.5 SECONDS, progress=TRUE) && dealing) + var/obj/item/toy/cards/hand = DrawCards(holder) + step(hand, GLOB.alldirs[H]) + else + broken = TRUE + break + if(broken) + visible_message("[usr] stops in the middle of dealing cards!") + dealing = FALSE + if("draw") + if(!params["cards"]) return FALSE + var/list/P = splittext(params["cards"], ",") + var/list/P_cards = list() + for(var/T in P) + P_cards += text2num(T) + 1 + var/obj/item/toy/cards/H = DrawCards(P_cards) + H.pickup(usr) + usr.put_in_hands(H) + if("flip") + var/P_side = params["side"] + var/flip_side = null + if(P_side) + flip_side = P_side == "face_down" ? FALSE : P_side == "face_up" ? TRUE : null + FlipCards(flip_side) + return TRUE + if("peek") + visible_message("[usr] is peeking in \the [name]!") + if(do_after_mob(usr, targets, 3 SECONDS, progress=TRUE)) + to_chat(usr, "You peek into \the [name].") + peeking = TRUE + if("rotate") + RotateCards(params["angle"]) + return TRUE + if("shuffle") + peeking = FALSE + ShuffleCards(usr) + update_icon() + return TRUE + +/obj/item/toy/cards/deck/ui_data(mob/user) + var/list/data = list() + data["face_up"] = face_up + data["rotation"] = rotation + data["cards"] = cards + data["name"] = name + data["peeking"] = peeking + return data + +/obj/item/toy/cards/deck/ui_static_data(mob/user) + var/list/data = list() + data["possible_rotations"] = list(CARD_ROTATION_UP, CARD_ROTATION_SIDE, CARD_ROTATION_DOWN) + return data + +/obj/item/toy/cards/deck/ui_close() + dealing = FALSE + peeking = FALSE + + +// ================================= HAND OF CARDS ================================= + +/obj/item/toy/cards/cardhand + merge_rank = 2 + face_up = TRUE + +/obj/item/toy/cards/cardhand/proc/QuickAnnounce(mob/living/user) + if(!user.is_holding(src)) + to_chat(user, "You need to be holding \the [src] to show it!") + return + if(user.stat || user.restrained()) + return + var/list/temp_cards = list() + var/facedown = 0 + for(var/C in currenthand) + var/_flipped = C["face_up"] || face_up + var/_angle = C["rotation"] || rotation + if(_flipped) + if(is_all_same_direction()) + temp_cards += "\a [C["name"]]" + else + temp_cards += "\a [_angle] [C["name"]]" + else + facedown++ + if(facedown > 0) + temp_cards += "[facedown] unrevealed card\s" + visible_message("[user] shows [user.p_their()] hand: [english_list(temp_cards)].") + +/obj/item/toy/cards/cardhand/proc/is_all_same_direction() + var/compare_orient = currenthand[1]["rotation"] + for(var/list/C in currenthand) + var/_rotation = C["rotation"] || rotation + if(_rotation != compare_orient) return FALSE + return TRUE + +/obj/item/toy/cards/cardhand/proc/DrawOneCard(list/card_indices) + var/obj/item/toy/cards/singlecard/S = new/obj/item/toy/cards/singlecard(usr.loc) + var/_card = currenthand[card_indices[1]] + S.card = _card + S.rotation = _card["rotation"] + S.face_up = _card["face_up"] + S.parentdeck = src.parentdeck + S.apply_card_vars(S,src) + currenthand -= list(_card) + S.update_icon() + update_icon() + return S + +/obj/item/toy/cards/cardhand/MergeInto(obj/item/toy/cards/target, mob/living/user) + if(..()) + return TRUE + if(istype(target, /obj/item/toy/cards/deck)) + var/obj/item/toy/cards/deck/C = target + for(var/_card = 1, _card < currenthand.len, _card++) + currenthand[_card]["rotation"] = null + currenthand[_card]["face_up"] = null + C.cards += currenthand + C.FinishMergingCards(src, user) + else if(istype(target, /obj/item/toy/cards/cardhand)) + if(do_after_mob(user, list(user,src,target), 0.5 SECONDS)) + var/obj/item/toy/cards/cardhand/C = target + C.currenthand += currenthand + C.FinishMergingCards(src, user) + else + return FALSE + return TRUE + +/obj/item/toy/cards/cardhand/FinishMergingCards(obj/item/toy/cards/target, mob/living/user) + user.visible_message("[user] combines \the [target] into [user.p_their()] hand.",\ + "You combine \the [target] into the [src].") + qdel(target) + update_icon() + +/obj/item/toy/cards/cardhand/examine(mob/user) + . = ..() + . += "Alt-Click to quick-announce your deck." + +/obj/item/toy/cards/cardhand/AltClick(mob/user) + . = ..() + QuickAnnounce(user) + + +// =================== TGUI stuff =================== + +/obj/item/toy/cards/cardhand/ui_interact(mob/user, ui_key, datum/tgui/ui, force_open, datum/tgui/master_ui, datum/tgui_state/state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "CardsHand", name, 300, 400, master_ui, state) + ui.open() + +/obj/item/toy/cards/cardhand/ui_act(action, params) + if(..()) + return + switch(action) + if("draw") + if(!params["cards"]) return FALSE + var/list/P = splittext(params["cards"], ",") + var/list/P_cards = list() + for(var/T in P) + P_cards += text2num(T) + 1 + var/obj/item/toy/cards/H = DrawOneCard(P_cards) + H.pickup(usr) + usr.put_in_hands(H) + if("flip") + if(!params["card"]) return FALSE + var/card = currenthand[text2num(params["card"]) + 1] + card["face_up"] = !card["face_up"] + if("rotate") + if(!params["card"]) return FALSE + var/card = currenthand[text2num(params["card"]) + 1] + if(params["angle"]) + card["rotation"] = params["angle"] + update_icon() + return TRUE + +/obj/item/toy/cards/cardhand/ui_data(mob/user) + var/list/data = list() + data["cards"] = currenthand + data["name"] = name + return data + +/obj/item/toy/cards/cardhand/ui_static_data(mob/user) + var/list/data = list() + data["possible_rotations"] = list(CARD_ROTATION_UP, CARD_ROTATION_SIDE, CARD_ROTATION_DOWN) + return data + + +// ================================= SINGLE CARDS ================================= + +/obj/item/toy/cards/singlecard + merge_rank = 1 + +/obj/item/toy/cards/singlecard/MergeInto(obj/item/toy/cards/target, mob/living/user) + if(..()) + return TRUE + if(istype(target, /obj/item/toy/cards/deck)) + var/obj/item/toy/cards/deck/C = target + card["rotation"] = null + card["face_up"] = null + C.cards += list(card) + C.FinishMergingCards(src, user) + else if(istype(target, /obj/item/toy/cards/cardhand)) + var/obj/item/toy/cards/cardhand/C = target + C.currenthand += list(card) + C.FinishMergingCards(src, user) + else if(istype(target, /obj/item/toy/cards/singlecard)) + var/obj/item/toy/cards/singlecard/C = target + var/obj/item/toy/cards/cardhand/H = new/obj/item/toy/cards/cardhand(user.loc) + H.currenthand += list(C.card) + H.currenthand += list(src.card) + H.parentdeck = C.parentdeck + H.apply_card_vars(H,C) + H.pickup(user) + user.put_in_hands(H) + C.FinishMergingCards(src, user) + else + return FALSE + return TRUE + +/obj/item/toy/cards/singlecard/proc/FlipCard(mob/user) + if(!ishuman(usr) || !usr.canUseTopic(src, BE_CLOSE)) + return + FlipCards() + to_chat(user, "You flip \the [src] [(face_up ? "face-up" : "face-down")].") + +/obj/item/toy/cards/singlecard/proc/RotateCard(mob/user, var/rotation_angle) + if(!ishuman(usr) || !usr.canUseTopic(src, BE_CLOSE)) + return + to_chat(user, "You turn \the [src] to \a [rotation_angle] position.") + RotateCards(rotation_angle) + + +/obj/item/toy/cards/singlecard/FinishMergingCards(obj/item/toy/cards/singlecard/target, mob/living/user) + to_chat(user, "You combine the [target.card["name"]] and the [src.card["name"]] into a hand.") + qdel(target) + qdel(src) + update_icon() + +/obj/item/toy/cards/singlecard/examine(mob/user) + . = ..() + . += "Click to flip. Alt-Click to rotate." + +/obj/item/toy/cards/singlecard/attack_self(mob/user) + . = ..() + FlipCard(user) + +/obj/item/toy/cards/singlecard/AltClick(mob/user) + . = ..() + RotateCard(user, GetNextAngle(rotation)) + +/obj/item/toy/cards/singlecard/update_icon() + . = ..() + var/matrix/rot_matrix = matrix() + rot_matrix.Turn(GetAngle(rotation)) + transform = rot_matrix + + var/rotation_name = (rotation == CARD_ROTATION_UP ? "" : rotation + " ") + if(face_up) + if(card) + src.icon_state = "sc_[card["icon_state"] || card["name"]]_[deckstyle]" + src.name = rotation_name + src.card["name"] + else + src.icon_state = "sc_aceofspades_[deckstyle]" + src.name = "What Card" + src.pixel_x = 5 + else + src.icon_state = "singlecard_down_[deckstyle]" + src.name = rotation_name + "card" + src.pixel_x = -5 \ No newline at end of file diff --git a/hyperstation/code/game/objects/items/lovedice.dm b/hyperstation/code/game/objects/items/lovedice.dm new file mode 100644 index 000000000..2048fce55 --- /dev/null +++ b/hyperstation/code/game/objects/items/lovedice.dm @@ -0,0 +1,92 @@ +/* +lover's dice: based off a really funny yakuza gif i saw on tumblr years ago +these give suggestions for sex acts to perform. it's stupid but fun. + +sarcoph mar 2022 +*/ + +// dice bag + +/obj/item/storage/pill_bottle/lovedice + name = "bag of love dice" + desc = "Contains all the intimate ideas you'll ever need. A game that everyone wins!" + icon = 'hyperstation/icons/obj/toy.dmi' + icon_state = "lovedicebag" + price = 1 + +/obj/item/storage/pill_bottle/lovedice/Initialize() + . = ..() + new /obj/item/dice/lover/d6_gesture(src) + new /obj/item/dice/lover/d6_location(src) + new /obj/item/dice/lover/d6_action(src) + new /obj/item/dice/lover/d6_bodypart(src) + + +// dice + +/obj/item/dice/lover + desc = "A die with six sides to inspire some bedroom action." + icon = 'hyperstation/icons/obj/toy.dmi' + sides = 6 + +/obj/item/dice/lover/update_icon() + return // override the dice proc for this, there is no overlay + +/obj/item/dice/lover/examine(mob/user) + . = ..() // again, no overlays + . += "The top reads [result]." + + +// actual dice + +/obj/item/dice/lover/d6_gesture + name = "lover's d6 (v1)" + desc = "A die with six sides to inspire some bedroom action. This one has intimate gestures." + icon_state = "loved6_1" + special_faces = list( + "Let's hug", + "Let's kiss", + "Let's play", + "Let's fuck", // it actually says "let's do it" on the irl ones lol + "Let's wrestle", + "Let's ?" + ) + +/obj/item/dice/lover/d6_location + name = "lover's d6 (v2)" + desc = "A die with six sides to inspire some bedroom action. This one has different locations." + icon_state = "loved6_2" + special_faces = list( + "On a chair", + "On the bed", + "On the floor", + "In the closet", + "In the bathtub", + "In the ?" + ) + +/obj/item/dice/lover/d6_action + name = "lover's d6 (v3)" + desc = "A die with six sides to inspire some bedroom action. This one has different actions." + icon_state = "loved6_3" + special_faces = list( + "Caress my", + "Kiss my", + "Grab my", + "Rub my", + "Tickle my", + "Surprise!" + ) + +/obj/item/dice/lover/d6_bodypart + name = "lover's d6 (v4)" + desc = "A die with six sides to inspire some bedroom action. This one has body parts." + icon_state = "loved6_4" + special_faces = list( + "Back", + "Chest", + "Face", + "Ass", + "Genitals", + "Surprise!" + ) \ No newline at end of file diff --git a/hyperstation/code/game/objects/items/stacks/tiles/tile_types.dm b/hyperstation/code/game/objects/items/stacks/tiles/tile_types.dm new file mode 100644 index 000000000..310dea667 --- /dev/null +++ b/hyperstation/code/game/objects/items/stacks/tiles/tile_types.dm @@ -0,0 +1,28 @@ +/turf/open/floor/ + var/farm_quality = 0 //all tiles have a farm quality, which is for farm animals to enjoy. 0 = worse 100 = best! + +//Grass quality +/turf/open/floor/grass + farm_quality = 50 + +/turf/open/floor/grass/fairy + farm_quality = 70 + +//Hay +/obj/item/stack/tile/hay + name = "hay bedding tile" + singular_name = "hay bedding tile" + desc = "A patch of bundled hay." + icon = 'hyperstation/icons/obj/tiles.dmi' + icon_state = "tile_hay" + item_state = "tile_hay" + turf_type = /turf/open/floor/grass/hay + resistance_flags = FLAMMABLE + +/turf/open/floor/grass/hay //like grass + name = "hay bedding" + icon = 'hyperstation/icons/obj/hydroponics/harvest.dmi' + desc = "A patch of bundled hay, to help keep animals happy." + icon_state = "hay" + farm_quality = 100 //the best for farming! + floor_tile = /obj/item/stack/tile/hay diff --git a/hyperstation/code/game/objects/railings.dm b/hyperstation/code/game/objects/railings.dm index 561765f68..2c01a1721 100644 --- a/hyperstation/code/game/objects/railings.dm +++ b/hyperstation/code/game/objects/railings.dm @@ -151,15 +151,18 @@ usr.visible_message("[user] starts climbing onto \the [src]!") - if(!do_after(user, 20)) + if(!do_after(user, 15, src)) return if(get_turf(user) == get_turf(src)) + usr.dir = get_dir(usr.loc, get_step(src, src.dir))//turn and face railing usr.forceMove(get_step(src, src.dir)) else + usr.dir = get_dir(usr.loc, loc)//turn and face railing usr.forceMove(get_turf(src)) usr.visible_message("[user] climbed over \the [src]!") + usr.do_twist(targetangle = 45, timer = 8) /obj/structure/railing/handrail name = "handrail" diff --git a/hyperstation/code/modules/arousal/arousalhud.dm b/hyperstation/code/modules/arousal/arousalhud.dm index ff7725ddb..318edefd0 100644 --- a/hyperstation/code/modules/arousal/arousalhud.dm +++ b/hyperstation/code/modules/arousal/arousalhud.dm @@ -11,7 +11,15 @@ for(var/obj/item/organ/genital/G in U.internal_organs) if(!G.nochange) if(!G.dontlist) - dat += "[G.mode == "hidden" ? "[G.name] (Hidden)" : (G.mode == "clothes" ? "[G.name] (Hidden by Clothes)" : (G.mode == "visable" ? "[G.name] (Visable)" : "[G.name] (Visable)"))]
" + var/genital_visibility = "[G.name] (Visible)" + switch(G.mode) + if(GENITALS_HIDDEN) + genital_visibility = "[G.name] (Hidden)" + if(GENITALS_CLOTHES) + genital_visibility = "[G.name] (Hidden by Clothes)" + if(GENITALS_VISIBLE) + genital_visibility = "[G.name] (Visible)" + dat += "[genital_visibility]
" dat += {"
Contexual Options

"} var/obj/item/organ/genital/penis/P = user.getorganslot("penis") @@ -45,20 +53,17 @@ if(Belly.inflatable) dat += "Deflate belly" dat += "(Shrink your belly down a size)
" - - - if(user.pulling) + + if(user.pulling && !isnoncarbon(user.pulling)) // do not fuck animals dat += "Climax over [user.pulling]" //you can cum on objects if you really want... dat += "(Orgasm over a person or object.)
" - if(isliving(user.pulling)) - if(iscarbon(user.pulling)) - dat += "Climax with [user.pulling]" - dat += {"(Orgasm with another person.)
"} - - var/mob/living/carbon/human/H = user.pulling - if(H.breedable && P && H) - dat += "Impregnate [U.pulling] ([clamp(U.impregchance,0,100)]%)" - dat += "(Climax inside another person, and attempt to knock them up.)
" + if(iscarbon(user.pulling)) + dat += "Climax with [user.pulling]" + dat += {"(Orgasm with another person.)
"} + var/mob/living/carbon/human/H = user.pulling + if(H && H.breedable && P) + dat += "Impregnate [U.pulling] ([clamp(U.impregchance,0,100)]%)" + dat += "(Climax inside another person, and attempt to knock them up.)
" else dat += "Climax over" dat += "(Requires a partner)
" @@ -81,11 +86,9 @@ dat += "Old Menu" dat += "Toggle Undergarments " dat += "
" - var/datum/browser/popup = new(user, "arousal", "Arousal Panel") popup.set_content(dat) popup.set_title_image(user.browse_rsc_icon(icon, icon_state), 500,600) - popup.open() @@ -140,7 +143,7 @@ T.toggle_visibility(picked_visibility) if(href_list["masturbate"]) - if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal. + if (H.isPercentAroused(33)) //requires 33% arousal. H.solomasturbate() return else @@ -148,7 +151,7 @@ return if(href_list["container"]) - if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal. + if (H.isPercentAroused(33)) //requires 33% arousal. H.cumcontainer() return else @@ -156,7 +159,7 @@ return if(href_list["clothesplosion"]) - if (H.arousalloss >= (H.max_arousal / 100) * 33) //Requires 33% arousal. + if (H.isPercentAroused(33)) //Requires 33% arousal. H.clothesplosion() return else @@ -164,7 +167,7 @@ return if(href_list["climaxself"]) - if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal. + if (H.isPercentAroused(33)) //requires 33% arousal. H.climaxself() return else @@ -172,7 +175,7 @@ return if(href_list["climax"]) - if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal. + if (H.isPercentAroused(33)) //requires 33% arousal. H.climaxalone(FALSE) return else @@ -180,7 +183,7 @@ return if(href_list["climaxover"]) - if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal. + if (H.isPercentAroused(33)) //requires 33% arousal. H.climaxover(usr.pulling) return else @@ -188,7 +191,7 @@ return if(href_list["climaxwith"]) - if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal. + if (H.isPercentAroused(33)) //requires 33% arousal. H.climaxwith(usr.pulling) return else @@ -196,7 +199,7 @@ return if(href_list["impreg"]) - if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal. + if (H.isPercentAroused(33)) //requires 33% arousal. H.impregwith(usr.pulling) return else @@ -211,13 +214,7 @@ return if(href_list["shrink_belly"]) - var/obj/item/organ/genital/belly/E = usr.getorganslot("belly") - if(E.size > 0) - to_chat(usr, "You feel your belly diminish.") - E.size -= 1 - H.update_genitals() - else - to_chat(usr, "Your belly is already at the minimum size! ") + H.shrink_belly(1) if(href_list["removecondom"]) H.menuremovecondom() @@ -337,7 +334,7 @@ obj/screen/arousal/proc/kiss() return //We got hands, let's pick an organ var/obj/item/organ/genital/picked_organ - picked_organ = pick_masturbate_genitals() + picked_organ = pick_climax_genitals(masturbation=TRUE, title="Masturbation") if(picked_organ) src << browse(null, "window=arousal") //closes the window mob_masturbate(picked_organ) @@ -371,55 +368,31 @@ obj/screen/arousal/proc/kiss() to_chat(src, "You cannot climax without choosing genitals.") return -/mob/living/carbon/human/proc/climaxwith(mob/living/T) - - var/mob/living/carbon/human/L = pick_partner() +/mob/living/carbon/human/proc/climaxwith(mob/living/carbon/human/partner) + if(!partner) + to_chat(src, "You cannot do this alone.") + return var/obj/item/organ/genital/picked_organ picked_organ = pick_climax_genitals() - if(picked_organ) - var/mob/living/partner = L - if(partner) - src << browse(null, "window=arousal") //alls fine, we can close the window now. - var/obj/item/organ/genital/penis/P = picked_organ - var/spillage = "No" //default to no, just incase player has items on to prevent climax - if(!P.condom == 1&&!P.sounding == 1) //you cant climax with a condom on or sounding in. - 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, FALSE, FALSE) - else - mob_climax_partner(picked_organ, partner, FALSE, FALSE, FALSE) - else - to_chat(src, "You cannot do this alone.") - return - else //They either lack organs that can masturbate, or they didn't pick one. + if(!picked_organ) to_chat(src, "You cannot climax without choosing genitals.") return + src << browse(null, "window=arousal") // close arousal window + mob_climax_inside_spillage(picked_organ, partner, impreg=FALSE) - -/mob/living/carbon/human/proc/climaxover(mob/living/T) - - var/mob/living/carbon/human/L = T +/mob/living/carbon/human/proc/climaxover(atom/partner) var/obj/item/organ/genital/picked_organ picked_organ = pick_climax_genitals() - if(picked_organ) - src << browse(null, "window=arousal") //alls fine, we can close the window now. - var/mob/living/partner = L - if(partner) - var/obj/item/organ/genital/penis/P = picked_organ - if(P.condom == 1) - to_chat(src, "You cannot do this action with a condom on.") - return - if(P.sounding == 1) - to_chat(src, "You cannot do this action with a sounding in.") - return - mob_climax_partner(picked_organ, partner, FALSE, FALSE, TRUE) - else - to_chat(src, "You cannot do this alone.") - return - else //They either lack organs that can masturbate, or they didn't pick one. + if(!picked_organ) to_chat(src, "You cannot climax without choosing genitals.") return + src << browse(null, "window=arousal") //alls fine, we can close the window now. + if(!partner || isnoncarbon(partner)) + to_chat(src, "You cannot do this alone.") + return + mob_climax_cover(picked_organ, partner) + /mob/living/carbon/human/proc/clothesplosion() if(usr.restrained(TRUE)) @@ -434,39 +407,29 @@ obj/screen/arousal/proc/kiss() H.visible_message("[H] explodes out of their clothes!'") - -/mob/living/carbon/human/proc/impregwith(mob/living/T) - - var/mob/living/carbon/human/L = pick_partner() - var/obj/item/organ/genital/picked_organ - picked_organ = src.getorganslot("penis") //Impregnation must be done with a penis. - if(picked_organ) - var/mob/living/partner = L - if(partner) - if(!partner.breedable)//check if impregable. - to_chat(src, "Your partner cannot be impregnated.")//some fuckary happening, you shouldnt even get to this point tbh. - return - var/obj/item/organ/genital/penis/P = picked_organ - //you cant impreg with a condom on or sounding in. - if(P.condom == 1) - to_chat(src, "You cannot do this action with a condom on.") - return - if(P.sounding == 1) - to_chat(src, "You cannot do this action with a sounding in.") - return - src << browse(null, "window=arousal") //alls fine, we can close the window now. - //Keeping this for messy fun - 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, TRUE, FALSE) - else - mob_climax_partner(picked_organ, partner, FALSE, TRUE, FALSE) - else - to_chat(src, "You cannot do this alone.") - return - else //no penis :( - to_chat(src, "You cannot impregnate without a penis.") +/mob/living/carbon/human/proc/impregwith(mob/living/carbon/T) + var/mob/living/carbon/human/partner = pick_partner() + var/obj/item/organ/genital/penis/picked_organ = src.getorganslot("penis") + // wow that is a lot of conditionals huh + if(!picked_organ) // no penis :( + to_chat(src, "You cannot impregnate someone without a penis.") return + if(!partner) + to_chat(src, "You cannot do this alone.") + return + if(!partner.breedable) //check if impregable. + to_chat(src, "Your partner cannot be impregnated.") + //some fuckery happening, you shouldnt even get to this point tbh. + return + if(picked_organ.condom) + to_chat(src, "You cannot impregnate someone with a condom on.") + return + if(picked_organ.sounding) + to_chat(src, "You cannot impregnate someone with a sounding rod in.") + return + src << browse(null, "window=arousal") //alls fine, we can close the window now. + mob_climax_inside_spillage(picked_organ, partner, impreg=TRUE) + /mob/living/carbon/human/proc/cumcontainer(mob/living/T) //We'll need hands and no restraints. @@ -506,6 +469,11 @@ obj/screen/arousal/proc/kiss() cum_splatter_icon.Blend(icon('hyperstation/icons/effects/cumoverlay.dmi', "cum_obj"), ICON_MULTIPLY) add_overlay(cum_splatter_icon) +/mob/living/carbon/add_cum_overlay(type="normal") + var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') + cumoverlay.icon_state = "cum_[type]" + add_overlay(cumoverlay) + /atom/proc/wash_cum() cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_normal")) cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_large")) @@ -529,7 +497,7 @@ obj/screen/arousal/proc/kiss() return //We got hands, let's pick an organ var/obj/item/organ/genital/picked_organ - picked_organ = pick_masturbate_genitals() + picked_organ = pick_climax_genitals(masturbation=TRUE, title="Masturbation") if(picked_organ) src << browse(null, "window=arousal") //closes the window mob_masturbate(picked_organ, cover = TRUE) diff --git a/hyperstation/code/modules/arousal/expansion.dm b/hyperstation/code/modules/arousal/expansion.dm new file mode 100644 index 000000000..5000c16d3 --- /dev/null +++ b/hyperstation/code/modules/arousal/expansion.dm @@ -0,0 +1,35 @@ +/mob/living/carbon/proc/expand_belly(expansion = 1) + var/obj/item/organ/genital/belly/_belly = getorganslot("belly") + if(!expansion || !_belly || !_belly.inflatable) + return + var/original_size = _belly.size + _belly.size = clamp(_belly.size + expansion, BELLY_MIN_SIZE, BELLY_MAX_SIZE) + if(_belly.size == original_size) + return + var/_verb = "expand" + switch(_belly.size) + if(BELLY_MAX_SIZE to INFINITY) + _verb = "stretch to its limit" + if(BELLY_STRETCH_SIZE to BELLY_MAX_SIZE) + _verb = "stretch" + if(BELLY_STRAIN_SIZE to BELLY_STRETCH_SIZE) + _verb = "strain" + to_chat(src, "You feel your belly [_verb].") + _belly.update() + + +/mob/living/carbon/proc/shrink_belly(shrinkage = 1) + var/obj/item/organ/genital/belly/_belly = getorganslot("belly") + if(!shrinkage ||!_belly || !_belly.inflatable) + return + var/original_size = _belly.size + _belly.size = clamp(_belly.size - shrinkage, BELLY_MIN_SIZE, BELLY_MAX_SIZE) + if(_belly.size == original_size) + return + var/_verb = "diminish" + var/_class = "userlove" + if(_belly.size == BELLY_MIN_SIZE) + _verb = "can't shrink anymore" + _class = "warning" + to_chat(src, "You feel your belly [_verb].") + _belly.update() \ No newline at end of file diff --git a/hyperstation/code/modules/arousal/organs/genitals.dm b/hyperstation/code/modules/arousal/organs/genitals.dm new file mode 100644 index 000000000..9da552021 --- /dev/null +++ b/hyperstation/code/modules/arousal/organs/genitals.dm @@ -0,0 +1,6 @@ +/obj/item/organ/genital/proc/get_fluid_source() + if(producing) + return src.reagents + if(linked_organ && linked_organ.producing) + return linked_organ.reagents + return null \ No newline at end of file diff --git a/hyperstation/code/modules/client/hyper_pref_savefile.dm b/hyperstation/code/modules/client/hyper_pref_savefile.dm new file mode 100644 index 000000000..a0a71216b --- /dev/null +++ b/hyperstation/code/modules/client/hyper_pref_savefile.dm @@ -0,0 +1,36 @@ +/datum/preferences/proc/hyper_character_pref_load(savefile/S) + S["feature_cosmetic_head"] >> features["cosmetic_head"] + S["feature_cosmetic_chest"] >> features["cosmetic_chest"] + S["feature_cosmetic_l_arm"] >> features["cosmetic_l_arm"] + S["feature_cosmetic_r_arm"] >> features["cosmetic_r_arm"] + S["feature_cosmetic_l_leg"] >> features["cosmetic_l_leg"] + S["feature_cosmetic_r_leg"] >> features["cosmetic_r_leg"] + S["cosmetic_markings"] >> features["cosmetic_markings"] + var/cosmetic_head = sanitize_inlist(features["cosmetic_head"], GLOB.cosmetic_heads, "default") + var/cosmetic_chest = sanitize_inlist(features["cosmetic_chest"], GLOB.cosmetic_chests, "default") + var/cosmetic_l_arm = sanitize_inlist(features["cosmetic_l_arm"], GLOB.cosmetic_arms, "default") + var/cosmetic_r_arm = sanitize_inlist(features["cosmetic_r_arm"], GLOB.cosmetic_arms, "default") + var/cosmetic_l_leg = sanitize_inlist(features["cosmetic_l_leg"], GLOB.cosmetic_legs, "default") + var/cosmetic_r_leg = sanitize_inlist(features["cosmetic_r_leg"], GLOB.cosmetic_legs, "default") + features["cosmetic_head"] = GLOB.cosmetic_heads[cosmetic_head] + features["cosmetic_chest"] = GLOB.cosmetic_chests[cosmetic_chest] + features["cosmetic_l_arm"] = GLOB.cosmetic_arms[cosmetic_l_arm] + features["cosmetic_r_arm"] = GLOB.cosmetic_arms[cosmetic_r_arm] + features["cosmetic_l_leg"] = GLOB.cosmetic_legs[cosmetic_l_leg] + features["cosmetic_r_leg"] = GLOB.cosmetic_legs[cosmetic_r_leg] + + +/datum/preferences/proc/hyper_character_pref_save(savefile/S) + var/datum/cosmetic_part/head = features["cosmetic_head"] + var/datum/cosmetic_part/chest = features["cosmetic_chest"] + var/datum/cosmetic_part/l_arm = features["cosmetic_l_arm"] + var/datum/cosmetic_part/r_arm = features["cosmetic_r_arm"] + var/datum/cosmetic_part/l_leg = features["cosmetic_l_leg"] + var/datum/cosmetic_part/r_leg = features["cosmetic_r_leg"] + WRITE_FILE(S["feature_cosmetic_head"], head.id) + WRITE_FILE(S["feature_cosmetic_chest"], chest.id) + WRITE_FILE(S["feature_cosmetic_l_arm"], l_arm.id) + WRITE_FILE(S["feature_cosmetic_r_arm"], r_arm.id) + WRITE_FILE(S["feature_cosmetic_l_leg"], l_leg.id) + WRITE_FILE(S["feature_cosmetic_r_leg"], r_leg.id) + WRITE_FILE(S["cosmetic_markings"], features["cosmetic_markings"]) \ No newline at end of file diff --git a/hyperstation/code/modules/client/loadout/mask.dm b/hyperstation/code/modules/client/loadout/mask.dm new file mode 100644 index 000000000..0406a96f7 --- /dev/null +++ b/hyperstation/code/modules/client/loadout/mask.dm @@ -0,0 +1,5 @@ +/datum/gear/gasmask + name = "gas mask" + category = SLOT_WEAR_MASK + path = /obj/item/clothing/mask/gas + cost = 3 \ No newline at end of file diff --git a/hyperstation/code/modules/food_and_drinks/recipes/drinks_recipes.dm b/hyperstation/code/modules/food_and_drinks/recipes/drinks_recipes.dm index 05fedabf7..83e49f04f 100644 --- a/hyperstation/code/modules/food_and_drinks/recipes/drinks_recipes.dm +++ b/hyperstation/code/modules/food_and_drinks/recipes/drinks_recipes.dm @@ -3,3 +3,9 @@ id = /datum/reagent/consumable/pilk results = list(/datum/reagent/consumable/pilk = 2) required_reagents = list(/datum/reagent/consumable/milk = 1, /datum/reagent/consumable/space_cola = 1) + +/datum/chemical_reaction/javelin + name = "Javelin" + id = /datum/reagent/consumable/ethanol/javelin + results = list(/datum/reagent/consumable/ethanol/javelin = 5) + required_reagents = list(/datum/reagent/consumable/kalynajuice = 1, /datum/reagent/consumable/ethanol/whiskey = 2, /datum/reagent/consumable/ethanol/vodka = 2) diff --git a/hyperstation/code/modules/hydroponics/grown/kalyna.dm b/hyperstation/code/modules/hydroponics/grown/kalyna.dm new file mode 100644 index 000000000..55a681ab4 --- /dev/null +++ b/hyperstation/code/modules/hydroponics/grown/kalyna.dm @@ -0,0 +1,40 @@ +// Kalyna Berries +/obj/item/seeds/kalyna + name = "pack of kalyna berry seeds" + desc = "Seeds that grow into Kalyna plants. Take that Red Kalyna and rise it up." + icon = 'hyperstation/icons/obj/hydroponics/seeds.dmi' + icon_state = "seed-kalyna" + species = "kalyna" + plantname = "Kalyna Shrub Tree" + product = /obj/item/reagent_containers/food/snacks/grown/kalyna + lifespan = 20 + maturation = 5 + production = 5 + growthstages = 5 + yield = 4 + growing_icon = 'hyperstation/icons/obj/hydroponics/growing_fruits.dmi' + icon_grow = "kalyna-grow" + icon_dead = "kalyna-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + reagents_add = list(/datum/reagent/consumable/nutriment/vitamin = 0.04, /datum/reagent/consumable/nutriment = 0.1) + +/obj/item/reagent_containers/food/snacks/grown/kalyna + seed = /obj/item/seeds/kalyna + name = "branch of kalyna" + desc = "Red berries, attached to a branch. Just looking at it makes you feel an aura of unity." + icon = 'hyperstation/icons/obj/hydroponics/harvest.dmi' + icon_state = "kalynaberries" + gender = PLURAL + filling_color = "#FF0000" + bitesize_mod = 2 + foodtype = FRUIT + juice_results = list(/datum/reagent/consumable/kalynajuice = 1) + tastes = list("sweet cranberries" = 1) + distill_reagent = /datum/reagent/consumable/ethanol/gin + +//For those of you wondering, yes, this is very much an addition in support of Ukraine. I decided to make these after a distant Ukranian relative of mine sent me Go_A's 'Kalyna' song. +//Our future as a free world could very well depend on the heroism of the people of glorious Ukraine. We owe our freedom to them. +//I know that this game should be for escapism, but there's only so much we should escape from. When people's lives are being upturned-- Or taken by a brutal invasive reigime, perhaps then it's when we should take a stand. +//And for those that oppose the freedom of the people of Ukraine, Russia, and Belarus; Prykhyl'nyk Putina, idy na khuy. And show some basic empathy for once, it's not that hard. +//Glory to Ukraine, Glory to the Heroes! +// - Arctaisia, Hyperstation developer and spriter, 23/04/2022 #StandWithUkraine diff --git a/hyperstation/code/modules/mob/dead/new_player/cosmetic_parts.dm b/hyperstation/code/modules/mob/dead/new_player/cosmetic_parts.dm new file mode 100644 index 000000000..a08db8726 --- /dev/null +++ b/hyperstation/code/modules/mob/dead/new_player/cosmetic_parts.dm @@ -0,0 +1,154 @@ +/* +COSMETIC PARTS + +this system allows you to change the _base_ appearance of a limb independent +of species or markings. this, for example, allows us to create "hybrids" like a +mammal with avian legs. + +keep in mind that this does not change the species of a leg! in the above example, +the mammal's bird-legs are still mammal legs. this matters in some places, like +body part transplants; mis-matched species will cause extra damage if the surgery +fails and the part is rejected. so you can't attach mammal-bird legs to avians +safely. +*/ + +/datum/cosmetic_part + /** A unique string that is used to identify this part for save files. Allows name changes without breaking saves */ + var/id + /** The name of the cosmetic part. This shows up in the preferences dropdown. */ + var/name + var/icon = 'hyperstation/icons/mob/char_parts.dmi' + var/icon_state + /** How colors are determined for this part. MUTCOLORS for base color, MATRIXED to allow multiple channels. */ + var/color_src = MUTCOLORS + /** Whether or not this cosmetic part has an alternate form for digitigrade legs. */ + var/support_digitigrade = TRUE + /** Species IDs that support this cosmetic part. Bypassed with "show mismatched markings" option. */ + var/list/supported_species + +/datum/cosmetic_part/head +/datum/cosmetic_part/chest +/datum/cosmetic_part/arms +/datum/cosmetic_part/legs + +// HEADS +// ========================================= + +/datum/cosmetic_part/head/default + id = "default" + name = "default" + icon = null + +/datum/cosmetic_part/head/ipc_round + id = "ipc_round" + name = "round ipc head" + icon_state = "ipc_round" + supported_species = list("ipc") + + +// CHESTS +// ========================================= + +/datum/cosmetic_part/chest/default + id = "default" + name = "default" + icon = null + +/datum/cosmetic_part/chest/ipc_sleek + id = "ipc_sleek" + name = "sleek ipc chest" + icon_state = "ipc_sleek" + supported_species = list("ipc") + color_src = MATRIXED + +/datum/cosmetic_part/chest/ipc_jointed + id = "ipc_jointed" + name = "jointed ipc chest" + icon_state = "ipc_jointed" + supported_species = list("ipc") + color_src = MATRIXED + + +// ARMS +// ========================================= + +/datum/cosmetic_part/arms/default + id = "default" + name = "default" + icon = null + +/datum/cosmetic_part/arms/avian_alt + id = "avian_alt" + name = "avian claws" + icon_state = "avian_alt" + supported_species = list("mammal", "avian", "aquatic", "insect", "xeno", "synthliz") + +/datum/cosmetic_part/arms/insect + id = "insect" + name = "insect arms" + icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi' + icon_state = "insect" + supported_species = list("mammal", "avian", "aquatic", "insect", "xeno", "synthliz") + +/datum/cosmetic_part/arms/ipc_sleek + id = "ipc_sleek" + name = "sleek ipc arms" + icon_state = "ipc_sleek" + supported_species = list("ipc") + color_src = MATRIXED + +/datum/cosmetic_part/arms/ipc_jointed + id = "ipc_jointed" + name = "jointed ipc arms" + icon_state = "ipc_jointed" + supported_species = list("ipc") + color_src = MATRIXED + + +// LEGS +// ========================================= + +/datum/cosmetic_part/legs/default + id = "default" + name = "default" + icon = null + +/datum/cosmetic_part/legs/avian + id = "avian" + name = "avian legs" + icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi' + icon_state = "avian" + supported_species = list("mammal", "avian", "aquatic", "insect", "xeno", "synthliz") + support_digitigrade = FALSE + +/datum/cosmetic_part/legs/mammal + id = "mammal" + name = "mammal legs" + icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi' + icon_state = "mammal" + supported_species = list("mammal", "avian", "aquatic", "insect", "xeno", "synthliz") + support_digitigrade = TRUE + +/datum/cosmetic_part/legs/insect + id = "insect" + name = "insect legs" + icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi' + icon_state = "insect" + supported_species = list("mammal", "avian", "aquatic", "insect", "xeno", "synthliz") + support_digitigrade = TRUE + +/datum/cosmetic_part/legs/ipc_sleek + id = "ipc_sleek" + name = "sleek ipc legs" + icon_state = "ipc_sleek" + supported_species = list("ipc") + color_src = MATRIXED + support_digitigrade = FALSE + +/datum/cosmetic_part/legs/ipc_jointed + id = "ipc_jointed" + name = "jointed ipc legs" + icon_state = "ipc_jointed" + supported_species = list("ipc") + color_src = MATRIXED + support_digitigrade = FALSE \ No newline at end of file diff --git a/hyperstation/code/modules/mob/dead/new_player/sprite_accessories.dm b/hyperstation/code/modules/mob/dead/new_player/sprite_accessories.dm index 7c42ec3fa..34dc50426 100644 --- a/hyperstation/code/modules/mob/dead/new_player/sprite_accessories.dm +++ b/hyperstation/code/modules/mob/dead/new_player/sprite_accessories.dm @@ -437,6 +437,12 @@ from modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm: name = "RadDog (Hyper)" icon_state = "chemlight" icon = 'hyperstation/icons/mob/char_markings.dmi' + +//racc do a code maybe it won't explode +/datum/sprite_accessory/mam_body_markings/raccalt + name = "RaccAlt (Hyper)" + icon_state = "raccalt" + icon = 'hyperstation/icons/mob/char_markings.dmi' /* TAUR BODIES @@ -469,6 +475,92 @@ from modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm: taur_mode = PAW_TAURIC ckeys_allowed = list("chemlight") + +/* +IPCS +========================================================== +*/ + +/* +from modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm: + +/datum/sprite_accessory/antenna + icon = 'modular_citadel/icons/mob/ipc_antennas.dmi' + color_src = MUTCOLORS3 + recommended_species = list("ipc") + +/datum/sprite_accessory/screen + icon = 'modular_citadel/icons/mob/ipc_screens.dmi' + color_src = null +*/ + +/datum/sprite_accessory/antenna/idog_down + name = "Dog, down (Hyper)" + icon_state = "idog_down" + color_src = MATRIXED + +/datum/sprite_accessory/antenna/idog_up + name = "Dog, up (Hyper)" + icon_state = "idog_up" + color_src = MATRIXED + +/datum/sprite_accessory/antenna/headset + name = "Headphones (Hyper)" + icon_state = "headset" + color_src = MATRIXED + +/datum/sprite_accessory/antenna/aquatic + name = "Fins (Hyper)" + icon_state = "aquatic" + color_src = MATRIXED + +/datum/sprite_accessory/screen/ghost + name = "Ghost (Hyper)" + icon_state = "ghost" + +/datum/sprite_accessory/screen/heartbeat + name = "Heartbeat (Hyper)" + icon_state = "heartbeat" + +/datum/sprite_accessory/screen/ocean + name = "Ocean (Hyper)" + icon_state = "ocean" + +/datum/sprite_accessory/antenna/catears + name = "Pointy Ears (Hyper)" + icon_state = "catears" + color_src = MATRIXED + +/datum/sprite_accessory/screen/valley + name = "Valley (Hyper)" + icon_state = "valley" + +/datum/sprite_accessory/screen/visor + name = "Black Visor (Hyper)" + icon = 'hyperstation/icons/mob/ipc_screen_visor.dmi' + icon_state = "visor_black" + +/datum/sprite_accessory/screen/visor/blue + name = "Blue Visor (Hyper)" + icon_state = "visor_blue" + +/datum/sprite_accessory/screen/visor/green + name = "Green Visor (Hyper)" + icon_state = "visor_green" + +/datum/sprite_accessory/screen/visor/red + name = "Red Visor (Hyper)" + icon_state = "visor_red" + +/datum/sprite_accessory/screen/visor/white + name = "White Visor (Hyper)" + icon_state = "visor_white" + +/datum/sprite_accessory/screen/visor/yellow + name = "Yellow Visor (Hyper)" + icon_state = "visor_yellow" + + /* HAIRSTYLES ========================================================== diff --git a/hyperstation/code/modules/mob/living/carbon/carbon.dm b/hyperstation/code/modules/mob/living/carbon/carbon.dm index 27d4e3283..3af2e9447 100644 --- a/hyperstation/code/modules/mob/living/carbon/carbon.dm +++ b/hyperstation/code/modules/mob/living/carbon/carbon.dm @@ -13,6 +13,11 @@ /mob/living/carbon/handle_status_effects() ..() handle_pain() + +/mob/living/carbon/has_mouth() + var/obj/item/bodypart/head/head = get_bodypart(BODY_ZONE_HEAD) + if(head && head.mouth) + return TRUE /mob/living/carbon/proc/handle_pain() var/pain_amount = 0 @@ -97,7 +102,7 @@ stuttering += 3 //stutter words //if they are asleep, this wont trigger. - if (total_pain > 110 && stat == 0) //taking 77 all damage at once from full health, will put you into shock and kill you. This cant be achived with chip damage (or fist fights), because youll die before you reach this pain level. + if (total_pain > 120 && stat == 0) //taking 130 all damage at once from full health, will put you into shock and kill you. This cant be achived with chip damage (or fist fights), because youll die before you reach this pain level. if(prob(50)) emote("scream")//scream to_chat(src, "You give into the pain...") diff --git a/hyperstation/code/modules/mob/living/carbon/human/cosmetic_part_handler.dm b/hyperstation/code/modules/mob/living/carbon/human/cosmetic_part_handler.dm new file mode 100644 index 000000000..25b93526f --- /dev/null +++ b/hyperstation/code/modules/mob/living/carbon/human/cosmetic_part_handler.dm @@ -0,0 +1,17 @@ +/obj/item/bodypart/proc/apply_cosmetic(datum/cosmetic_part/part) + if(!is_organic_limb() || animal_origin || !part) + return + cosmetic_icon = part + + +/mob/living/carbon/human/proc/handle_cosmetic_parts() + var/features = dna.features + var/list/body_zones = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + for(var/zone in body_zones) + var/datum/cosmetic_part/cosmetic_pref = features["cosmetic_" + zone] + if(!istype(cosmetic_pref, /datum/cosmetic_part)) + continue + var/datum/cosmetic_part/part = cosmetic_pref + var/obj/item/bodypart/body_part = get_bodypart(zone) + if(body_part && part) + body_part.apply_cosmetic(part) \ No newline at end of file diff --git a/hyperstation/code/modules/mob/living/carbon/human/species_types/ipc.dm b/hyperstation/code/modules/mob/living/carbon/human/species_types/ipc.dm new file mode 100644 index 000000000..b3e22f9ef --- /dev/null +++ b/hyperstation/code/modules/mob/living/carbon/human/species_types/ipc.dm @@ -0,0 +1,58 @@ +// APC powercord: oracle port + +/obj/item/apc_powercord + name = "power cord" + desc = "An internal power cord hooked up to a battery. Useful if you run on electricity. Not so much otherwise." + icon = 'icons/obj/power.dmi' + icon_state = "wire1" + + +/obj/item/apc_powercord/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + if(!istype(target, /obj/machinery/power/apc) || !ishuman(user) || !proximity_flag) + return ..() + user.changeNext_move(CLICK_CD_MELEE) + var/mob/living/carbon/human/H = user + var/obj/item/organ/stomach/cell/battery = H.getorganslot(ORGAN_SLOT_STOMACH) + if(!battery) + to_chat(H, "You try to siphon energy from \the [target], but your power cell is gone!") + return + + if(istype(H) && H.nutrition >= NUTRITION_LEVEL_ALMOST_FULL) + to_chat(user, "You are already fully charged!") + return + + if(istype(target, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = target + if(A.cell && A.cell.charge > A.cell.maxcharge*0.25) + powerdraw_loop(A, H) + return + else + to_chat(user, "There is not enough charge to draw from that APC.") + return + + +/obj/item/apc_powercord/proc/powerdraw_loop(obj/machinery/power/apc/A, mob/living/carbon/human/H) + H.visible_message("[H] inserts a power connector into the [A].", "You begin to draw power from the [A].") + while(do_after(H, 10, target = A)) + if(!istype(A)) + return + if(loc != H) + to_chat(H, "You must keep your connector out while charging!") + break + if(A.cell.charge == 0) + to_chat(H, "The [A] doesn't have enough charge to spare.") + break + A.charging = 1 + if(A.cell.charge >= 500) + H.nutrition += 50 + A.cell.charge -= 250 + to_chat(H, "You siphon off some of the stored charge for your own use.") + else + H.nutrition += A.cell.charge/10 + A.cell.charge = 0 + to_chat(H, "You siphon off as much as the [A] can spare.") + break + if(H.nutrition > NUTRITION_LEVEL_WELL_FED) + to_chat(H, "You are now fully charged.") + break + H.visible_message("[H] unplugs from the [A].", "You unplug from the [A].") \ No newline at end of file diff --git a/hyperstation/code/modules/mob/living/simple_animal/friendly/chicken_types.dm b/hyperstation/code/modules/mob/living/simple_animal/friendly/chicken_types.dm new file mode 100644 index 000000000..e69de29bb diff --git a/hyperstation/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/hyperstation/code/modules/mob/living/simple_animal/friendly/farm_animals.dm new file mode 100644 index 000000000..3d12f301b --- /dev/null +++ b/hyperstation/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -0,0 +1,246 @@ +/mob/living/simple_animal/ + var/happiness = 50 //how happy they are. + + + + ///////////// +//////////////// CHICKEN ///////////////// + ///////////// + +/mob/living/simple_animal/chick + icon = 'hyperstation/icons/mob/chickens.dmi' + name = "\improper chick" + desc = "Adorable! They make such a racket though." + icon_state = "chick" + icon_living = "chick" + icon_dead = "chick_dead" + icon_gib = "chick_gib" + gender = FEMALE + mob_biotypes = MOB_ORGANIC|MOB_BEAST + speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") + speak_emote = list("cheeps") + emote_hear = list("cheeps.") + emote_see = list("pecks at the ground.","flaps its tiny wings.") + density = FALSE + speak_chance = 2 + turns_per_move = 2 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/chicken = 1) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + attacktext = "kicks" + health = 3 + maxHealth = 3 + ventcrawler = VENTCRAWLER_ALWAYS + var/amount_grown = 0 + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + mob_size = MOB_SIZE_TINY + gold_core_spawnable = FRIENDLY_SPAWN + + do_footstep = TRUE + +/mob/living/simple_animal/chick/Initialize() + . = ..() + pixel_x = rand(-6, 6) + pixel_y = rand(0, 10) + +/mob/living/simple_animal/chick/Life() + . =..() + if(!.) + return + if(!stat && !ckey) + amount_grown += rand(1,2) + if(amount_grown >= 100) + new /mob/living/simple_animal/chicken(src.loc) + qdel(src) + +/mob/living/simple_animal/chick/holo/Life() + ..() + amount_grown = 0 + +/mob/living/simple_animal/chicken + icon = 'hyperstation/icons/mob/chickens.dmi' + name = "\improper chicken" + desc = "Hopefully the eggs are good this season." + gender = FEMALE + mob_biotypes = MOB_ORGANIC|MOB_BEAST + icon_state = "chicken_brown" + icon_living = "chicken_brown" + icon_dead = "chicken_brown_dead" + speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") + speak_emote = list("clucks","croons") + emote_hear = list("clucks.") + emote_see = list("pecks at the ground.","flaps its wings viciously.") + density = FALSE + speak_chance = 2 + turns_per_move = 3 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/chicken = 2) + var/egg_type = /obj/item/reagent_containers/food/snacks/egg + var/food_type = /obj/item/reagent_containers/food/snacks/grown/wheat + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + attacktext = "kicks" + health = 15 + maxHealth = 15 + ventcrawler = VENTCRAWLER_ALWAYS + var/eggsleft = 0 + var/eggsFertile = FALSE + var/body_color + var/icon_prefix = "chicken" + pass_flags = PASSTABLE | PASSMOB + mob_size = MOB_SIZE_SMALL + var/list/feedMessages = list("It clucks happily.","It clucks happily.") + var/list/layMessage = EGG_LAYING_MESSAGES + var/list/validColors = list("brown","black","white") + gold_core_spawnable = FRIENDLY_SPAWN + var/static/chicken_count = 0 + + var/partner //for the sex. + var/egglay_timer = 0 + var/obj/structure/nestbox/nest_target + + var/last_egg + var/force_gender //for map loading + + + do_footstep = TRUE + +/mob/living/simple_animal/chicken/Initialize() + . = ..() + if((prob(30) && !force_gender) || force_gender == "male") //30% of a male. or setup if already male. + gender = MALE + name = "rooster" + + if(!body_color) + body_color = pick(validColors) + icon_state = "[icon_prefix]_[body_color]" + icon_living = "[icon_prefix]_[body_color]" + icon_dead = "[icon_prefix]_[body_color]_dead" + pixel_x = rand(-6, 6) + pixel_y = rand(0, 10) + ++chicken_count + +/mob/living/simple_animal/chicken/Destroy() + --chicken_count + return ..() + +/mob/living/simple_animal/chicken/attackby(obj/item/O, mob/user, params) + if(istype(O, food_type)) //feedin' dem chickens + if(!stat && eggsleft < 2) + var/feedmsg = "[user] feeds [O] to [name]! [pick(feedMessages)]" + user.visible_message(feedmsg) + qdel(O) + eggsleft += 1 + egglay_timer = 0 + else + to_chat(user, "[name] doesn't seem hungry!") + else + ..() + + +/mob/living/simple_animal/chicken/Life() + . = ..() + if(!.) + return + if (istype(get_turf(src), /turf/open/floor)) + var/turf/open/floor/turfon = get_turf(src) + if (turfon.farm_quality > happiness) + happiness ++ + if (prob(5)) + visible_message("[src] pecks happily at the ground.") //CLUCK CLUCK CLUCK + else + happiness -- + else + happiness -- + + happiness = clamp(happiness,0,100) //clamp + + + if (!(gender == FEMALE)) //only females lay eggs and do the rest of the code. + return + + + //Breeding. + if (gender == FEMALE && eggsFertile == 0 && eggsleft > 0) + for(var/mob/living/simple_animal/chicken/C in view(2,src)) //look for a male near them, or on them. + if(C) + if(C.gender == MALE) //rooster + eggsFertile = 1 //they had sex, just go with it. + partner = C //you know who the partner is. + //EEGGGG TIME + + //after 10mins, lay a egg regardless. + if(world.time > (last_egg+600 SECONDS) && !eggsleft) + if(prob(15)) //just to offset + eggsleft ++ + last_egg = world.time + + + if((!stat && eggsleft > 0) && egg_type) + egglay_timer ++ + else + egglay_timer = 0 + + + //chance to lay egg on their own. + + + //look for nest! + + //We have found a nest, override movement + if(nest_target) //if alive and we have a target. + stop_automated_movement = 1 + walk_to(src,nest_target,0,8) + else + stop_automated_movement = 0 + walk_to(src,0) //reset walk_to + + if (egglay_timer > 10 && !stat) //time to lay a egg and not dead. + if((!stat && eggsleft > 0) && egg_type) + if (!nest_target) + for(var/obj/structure/nestbox/N in view(3,src)) // look for a eggbox if you dont have one already + nest_target = N + break + + //We are at the nest we have chosen. + if (nest_target) + for(var/obj/structure/nestbox/B in get_turf(src)) + if((prob(25) && eggsleft > 0) && egg_type) + visible_message("[src] [pick(layMessage)]") + eggsleft-- + var/obj/item/E = new egg_type(get_turf(src)) + E.pixel_x = rand(-6,6) + E.pixel_y = rand(-6,6) + egglay_timer = 0 //set timer + nest_target = null //layed the egg, time to move on + if(eggsFertile && partner) + if(chicken_count < MAX_CHICKENS && prob(happiness)) + START_PROCESSING(SSobj, E) + eggsFertile = 0 //youve layed your fertile egg. + E.name = "fertile egg" + partner = null + last_egg = world.time + break + + +/obj/item/reagent_containers/food/snacks/egg/var/amount_grown = 0 +/obj/item/reagent_containers/food/snacks/egg/process() + if(isturf(loc)) + for(var/obj/structure/nestbox/B in get_turf(src)) //can only hatch in a nestbox or incubator + amount_grown += rand(1,2) + if(amount_grown >= 100) + visible_message("[src] hatches with a quiet cracking sound.") + new /mob/living/simple_animal/chick(get_turf(src)) + STOP_PROCESSING(SSobj, src) + qdel(src) + break + else + STOP_PROCESSING(SSobj, src) + + +/mob/living/simple_animal/chicken/examine() + . = ..() + . += "this one is [gender]." + if(happiness<20) + . += "It looks stressed." diff --git a/hyperstation/code/modules/mob/mob_helpers.dm b/hyperstation/code/modules/mob/mob_helpers.dm index 00f9c8631..0a47f7b0a 100644 --- a/hyperstation/code/modules/mob/mob_helpers.dm +++ b/hyperstation/code/modules/mob/mob_helpers.dm @@ -27,3 +27,7 @@ mob/proc/checkloadappearance() else to_chat(H, "You either took too long or chose not to change. Alrighty. Remember, you have 90 seconds from spawn to get to a mirror and still do it if you wish.") return + +// oracle port +/mob/proc/has_mouth() + return FALSE \ No newline at end of file diff --git a/hyperstation/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/hyperstation/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm new file mode 100644 index 000000000..76805f825 --- /dev/null +++ b/hyperstation/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -0,0 +1,11 @@ +//Hope you're not a Russian T-90 +/datum/reagent/consumable/ethanol/javelin + name = "Javelin Cocktail" + boozepwr = 80 + color = "#F54F10" + quality = DRINK_FANTASTIC + taste_description = "explosive fireballs" + glass_icon_state = "javelin" + shot_glass_icon_state = "javelin_warhead" + glass_name = "Javelin Cocktail" + glass_desc = "A man-portable, delicious glass of justice." diff --git a/hyperstation/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/hyperstation/code/modules/reagents/chemistry/reagents/drink_reagents.dm index 3656de158..3c088d34f 100644 --- a/hyperstation/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/hyperstation/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -21,3 +21,13 @@ if(holder.has_reagent(/datum/reagent/consumable/capsaicin)) holder.remove_reagent(/datum/reagent/consumable/capsaicin, 1) ..() + +/datum/reagent/consumable/kalynajuice + name = "Red Kalyna Juice" + description = "Juice from Kalyna plants." + color = "#E90501" // rgb: 233, 5, 1 + taste_description = "sweet cranberries" + glass_icon_state = "kalyna" + glass_name = "glass of red kalyna juice" + glass_desc = "A vibrantly red juice!" + hydration = 4 diff --git a/hyperstation/code/modules/research/designs/medical_designs.dm b/hyperstation/code/modules/research/designs/medical_designs.dm new file mode 100644 index 000000000..825bcd2a2 --- /dev/null +++ b/hyperstation/code/modules/research/designs/medical_designs.dm @@ -0,0 +1,69 @@ +/datum/design/ipc_liver + name = "IPC Reagent Processor" + id = "ipc_liver" + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_GLASS = 1000) + build_path = /obj/item/organ/liver/ipc + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + construction_time = 100 + +/datum/design/ipc_eyes + name = "IPC Eyes" + id = "ipc_eyes" + build_type = PROTOLATHE + materials = list(MAT_METAL = 1000, MAT_GLASS = 2000) + build_path = /obj/item/organ/eyes/ipc + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + construction_time = 100 + +/datum/design/ipc_tongue + name = "Positronic Voicebox" + id = "ipc_tongue" + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_GLASS = 1000) + build_path = /obj/item/organ/tongue/robot/ipc + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + construction_time = 100 + +/datum/design/ipc_stomach + name = "Micro-cell" + id = "ipc_stomach" + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_GLASS = 1000, MAT_PLASMA = 200) + build_path = /obj/item/organ/stomach/cell + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + construction_time = 100 + +/datum/design/synthliz_stomach + name = "Synthetic Lizardperson Stomach" + id = "synthliz_stomach" + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_GLASS = 1000) + build_path = /obj/item/organ/stomach/synthliz + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + construction_time = 100 + +/datum/design/power_cord + name = "IPC Power Cord" + id = "power_cord" + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_GLASS = 1000) + build_path = /obj/item/organ/cyberimp/arm/power_cord + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + construction_time = 100 + + +/datum/techweb_node/ipc_organs + id = "ipc_organs" + display_name = "IPC Parts" + description = "We have the technology to replace him." + prereq_ids = list("cyber_organs","robotics") + design_ids = list("ipc_liver", "ipc_eyes", "ipc_tongue", "ipc_stomach", "synthliz_stomach", "power_cord") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1500) + export_price = 5000 diff --git a/hyperstation/code/modules/resize/resizing.dm b/hyperstation/code/modules/resize/resizing.dm index 137723add..71ad20f9f 100644 --- a/hyperstation/code/modules/resize/resizing.dm +++ b/hyperstation/code/modules/resize/resizing.dm @@ -12,6 +12,7 @@ var/const/RESIZE_MICRO = 0.25 /mob/living var/size_multiplier = 1 //multiplier for the mob's icon size atm var/previous_size = 1 + var/small_speech = FALSE //Cyanosis - Action that resizes the sprite for the client but nobody else. Say goodbye to attacking yourself when someone's above you lmao var/datum/action/sizecode_resize/small_sprite @@ -237,8 +238,14 @@ mob/living/get_effective_size() //Proc for changing mob_size to be grabbed for item weight classes /mob/living/proc/update_mobsize(var/mob/living/tmob) + if(small_speech == TRUE) //if they have small speech reset it. + small_speech = FALSE + UnregisterSignal(src, COMSIG_MOB_SAY) + if(size_multiplier <= 0.50) mob_size = 0 + RegisterSignal(src, COMSIG_MOB_SAY, .proc/handle_small_speech) + small_speech = TRUE if(size_multiplier < 1) mob_size = 1 if(size_multiplier == 1) @@ -246,6 +253,9 @@ mob/living/get_effective_size() if(size_multiplier > 1) mob_size = 3 +/mob/living/proc/handle_small_speech(owner, list/speech_args) //for making peoples text small + speech_args[SPEECH_SPANS] |= SPAN_SMALL + //Proc for instantly grabbing valid size difference. Code optimizations soon(TM) /* /mob/living/proc/sizeinteractioncheck(var/mob/living/tmob) diff --git a/hyperstation/code/modules/surgery/organs/augments_arms.dm b/hyperstation/code/modules/surgery/organs/augments_arms.dm index 8430fcac5..933b584eb 100644 --- a/hyperstation/code/modules/surgery/organs/augments_arms.dm +++ b/hyperstation/code/modules/surgery/organs/augments_arms.dm @@ -23,3 +23,11 @@ w_class = WEIGHT_CLASS_BULKY sharpness = IS_SHARP_ACCURATE attack_verb = list("slashed", "cut") + + +// oracle port +/obj/item/organ/cyberimp/arm/power_cord + name = "power cord implant" + desc = "An internal power cord hooked up to a battery. Useful if you run on volts." + contents = newlist(/obj/item/apc_powercord) + zone = "l_arm" \ No newline at end of file diff --git a/hyperstation/code/obj/condom.dm b/hyperstation/code/obj/condom.dm index 727ff4739..2c3122fe2 100644 --- a/hyperstation/code/obj/condom.dm +++ b/hyperstation/code/obj/condom.dm @@ -115,7 +115,6 @@ obj/item/condom/update_icon() return if(!P.condom) return - var/obj/item/condom/filled/C = new P.linked_organ.reagents.trans_to(C, P.linked_organ.reagents.total_volume) C.loc = loc diff --git a/hyperstation/code/obj/farming/farming.dm b/hyperstation/code/obj/farming/farming.dm new file mode 100644 index 000000000..ad4671774 --- /dev/null +++ b/hyperstation/code/obj/farming/farming.dm @@ -0,0 +1,30 @@ +/obj/item/reagent_containers/food/snacks/grown/wheat + var/stacktype = /obj/item/stack/tile/hay + var/tile_coefficient = 0.02 // same as grass + +/obj/item/reagent_containers/food/snacks/grown/wheat/attack_self(mob/user) + to_chat(user, "You prepare the hay bedding.") + var/grassAmt = 1 + round(seed.potency * tile_coefficient) // The grass we're holding + for(var/obj/item/reagent_containers/food/snacks/grown/wheat/G in user.loc) // The grass on the floor + if(G.type != type) + continue + grassAmt += 1 + round(G.seed.potency * tile_coefficient) + qdel(G) + new stacktype(user.drop_location(), grassAmt) + qdel(src) + + +/obj/structure/nestbox + name = "nest box" + icon = 'hyperstation/icons/obj/hydroponics/farming.dmi' + icon_state = "nestbox" + desc = "A little nest box, for collecting eggs" + density = FALSE + anchored = TRUE + +/obj/structure/nestbox/wrench_act(mob/living/user, obj/item/I) + user.visible_message("[user] disassembles [src].", + "You start to disassemble [src]...", "You hear clanking and banging noises.") + if(I.use_tool(src, user, 20, volume=50)) + new /obj/item/stack/sheet/mineral/wood (loc, 4) + qdel(src) \ No newline at end of file diff --git a/hyperstation/code/obj/fleshlight.dm b/hyperstation/code/obj/fleshlight.dm index 125c7ec34..421cfb8f6 100644 --- a/hyperstation/code/obj/fleshlight.dm +++ b/hyperstation/code/obj/fleshlight.dm @@ -45,11 +45,9 @@ inuse = 1 if(!(C == user)) //if we are targeting someone else. C.visible_message("[user] is trying to use [src] on [C]'s penis.", "[user] is trying to use [src] on your penis.") - if(!do_mob(user, C, 3 SECONDS)) //3 second delay inuse = 0 return - //checked if not used on yourself, if not, carry on. playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) //slapping sound inuse = 0 @@ -57,27 +55,22 @@ C.visible_message("[user] pumps [src] on [C]'s penis.", "[user] pumps [src] up and down on your penis.") else user.visible_message("[user] pumps [src] on their penis.", "You pump the fleshlight on your penis.") - if(prob(30)) //30% chance to make them moan. C.emote("moan") - C.do_jitter_animation() C.adjustArousalLoss(20) //make the target more aroused. - if (C.getArousalLoss() >= 100 && ishuman(C) && C.has_dna()) - C.mob_climax(forced_climax=TRUE) //make them cum if they are over the edge. - + if(C.can_orgasm() && ishuman(C)) + var/mob/living/carbon/human/user_human = C + user_human.mob_climax_outside(P, mb_time=0) // instant, because we already did a warmup return - else to_chat(user, "You don't see anywhere to use this on.") - inuse = 0 ..() //Hyperstation 13 portal fleshlight //kinky! - /obj/item/portallight name = "portal fleshlight" desc = "A silver love(TM) fleshlight, used to stimulate someones penis, with bluespace tech that allows lovers to hump at a distance." @@ -95,7 +88,6 @@ var/paired = 0 var/obj/item/portalunderwear var/useable = FALSE - var/option = "" /obj/item/portallight/examine(mob/user) . = ..() @@ -104,136 +96,109 @@ else . += "The device is paired, and awaiting input. " -/obj/item/portallight/attack(mob/living/carbon/C, mob/living/user) //use portallight! nearly the same as the fleshlight apart from you have a buddy! - var/obj/item/organ/genital/penis/P = C.getorganslot("penis") - +/obj/item/portallight/attack(mob/living/carbon/user, mob/living/holder) if(inuse) //just to stop stacking and causing people to cum instantly return - if(!useable) - to_chat(user, "It seems the device has failed or your partner is not wearing their device.") - - if(C == user)//if your using it on yourself, more options! otherwise, just fuck. + if(!UpdateUsability()) + to_chat(holder, "\The [src] appears to be currently unusable.") + return + var/option = "Fuck" + if(user == holder) option = input(usr, "Choose action", "Portal Fleshlight", "Fuck") in list("Fuck", "Lick", "Touch") - else - option = "Fuck" - - var/obj/item/organ/genital/G - if(istype(portalunderwear.loc, /obj/item/organ/genital)) //Sanity check. Without this it will runtime error. - G = portalunderwear.loc - if(!G) + var/obj/item/organ/genital/target_genital = portalunderwear.loc + var/mob/living/carbon/human/target = target_genital.owner + var/obj/item/organ/genital/penis/P = user.getorganslot("penis") + if(option == "Fuck" && !P.is_exposed()) + to_chat(holder, "You don't see anywhere to use this on.") return - var/mob/living/carbon/human/M = G.owner - - if(option == "Fuck"&&!P.is_exposed()) //we are trying to fuck with no penis! - to_chat(user, "You don't see anywhere to use this on.") - return - else //other options dont need checks - inuse = 1 - if(!(C == user)) //if we are targeting someone else. - C.visible_message("[user] is trying to use [src] on [C]'s penis.", "[user] is trying to use [src] on your penis.") - - if(!do_mob(user, C, 3 SECONDS)) //3 second delay - inuse = 0 - return - - //checked if not used on yourself, if not, carry on. - if(option == "Fuck") - playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) //slapping sound for fuck. - - inuse = 0 - if(!(C == user)) - C.visible_message("[user] pumps [src] on [C]'s penis.", "[user] pumps [src] up and down on your penis.") - else - if(option == "Fuck") - user.visible_message("[user] pumps [src] on their penis.", "You pump the fleshlight on your penis.") - if(option == "Lick") - user.visible_message("[user] licks into [src].", "You lick into [src].") - if(option == "Touch") - user.visible_message("[user] touches softly against [src].", "You touch softly on [src].") - - - if(prob(30)) //30% chance to make your partner moan. - M.emote("moan") - - if(option == "Fuck")// normal fuck - to_chat(M, "You feel a [P.length] inch, [P.shape] shaped penis pumping through the portal into your [G.name].")//message your partner, and kinky! - if(prob(30)) //30% chance to make them moan. - C.emote("moan") - if(prob(30)) //30% chance to make your partner moan. - M.emote("moan") - C.adjustArousalLoss(20) - M.adjustArousalLoss(20) - M.do_jitter_animation() //make your partner shake too! - - if (M.getArousalLoss() >= 100 && ishuman(M) && prob(5))//Why not have a probability to cum when someone's getting nailed with max arousal?~ - if(G.is_exposed()) //Oh yea, if vagina is not exposed, the climax will not cause a spill - M.mob_climax_outside(G, spillage = TRUE) - else - M.mob_climax_outside(G, spillage = FALSE) - - if (C.getArousalLoss() >= 100 && ishuman(C) && C.has_dna()) - var/mob/living/carbon/human/O = C - - if( (P.condom == 1) || (P.sounding == 1)) //If coundomed and/or sounded, do not fire impreg chance - O.mob_climax_partner(P, M, FALSE, FALSE, FALSE, TRUE) - else //Else, fire impreg chance - if(G.name == "vagina") //no more spontaneous impregnations through the butt! - O.mob_climax_partner(P, M, FALSE, TRUE, FALSE, TRUE) - else - O.mob_climax_partner(P, M, FALSE, FALSE, FALSE, TRUE) - - if(option == "Lick") - to_chat(M, "You feel a tongue lick you through the portal against your [G.name].") - M.adjustArousalLoss(10) - if(option == "Touch") - to_chat(M, "You feel someone touching your [G.name] through the portal.") - M.adjustArousalLoss(5) - + /* + WARMUP START - prevents spam/instant climax + */ + inuse = TRUE + if(user != holder) + user.visible_message("[holder] is trying to use [src] on [user]'s penis.",\ + "[holder] is trying to use [src] on your penis.") + if(!do_mob(holder, user, 3 SECONDS)) + inuse = FALSE return + inuse = FALSE + /* + WARMUP END - proceed with the action + */ + switch(option) + if("Fuck") + playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) + if(user == holder) + holder.visible_message("[holder] pumps [src] on their penis.", + "You pump the fleshlight on your penis.") + else + user.visible_message("[holder] pumps [src] on [user]'s penis.",\ + "[holder] pumps [src] up and down on your penis.") + to_chat(target, "You feel a [P.length] inch, [P.shape] shaped penis pumping through the portal into your [target_genital.name].") + if(prob(30)) + user.emote("moan") + user.adjustArousalLoss(20) + target.adjustArousalLoss(20) + target.do_jitter_animation() + if(target.can_orgasm() && prob(5)) + target.mob_climax_outside(target_genital, spillage=target_genital.is_exposed()) + if(user.can_orgasm()) + var/mob/living/carbon/human/O = user + var/impreg_chance = target_genital.name == "vagina" && !P.condom && !P.sounding + if(O.mob_climax_in_partner(P, target, spillage=FALSE, remote=TRUE) && impreg_chance) + target.impregnate(by=O) + if("Lick") + holder.visible_message("[holder] licks into [src].",\ + "You lick into [src].") + to_chat(target, "You feel a tongue lick you through the portal against your [target_genital.name].") + target.adjustArousalLoss(10) + if("Touch") + holder.visible_message("[holder] touches softly against [src].",\ + "You touch softly on [src].") + to_chat(target, "You feel someone touching your [target_genital.name] through the portal.") + target.adjustArousalLoss(5) + if(prob(30)) + target.emote("moan") ..() /obj/item/portallight/proc/updatesleeve() //get their looks and vagina colour! - cut_overlays()//remove current overlays - - var/obj/item/organ/genital/G - if(istype(portalunderwear.loc, /obj/item/organ/genital)) //Sanity check. Without this it will runtime. - G = portalunderwear.loc - if(!G) - useable = FALSE + cut_overlays() + if(!UpdateUsability()) return + var/obj/item/organ/genital/G = portalunderwear.loc + var/mob/living/carbon/human/H = G.owner // H is verified by UpdateUsability() + var/sleeve_species = "normal" + switch(H.dna.species.name) + if("Lizardperson") + sleeve_species = "lizard" + if("Slimeperson") + sleeve_species = "slime" + if("Avian") + sleeve_species = "avian" + sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_[sleeve_species]") + sleeve.color = "#" + H.dna.features["mcolor"] + add_overlay(sleeve) + if(G.name == "vagina") + organ = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_vag") + organ.color = portalunderwear.loc.color + if(G.name == "anus") + organ = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_anus") + organ.color = "#" + H.dna.features["mcolor"] + add_overlay(organ) - var/mob/living/carbon/human/H = G.owner +/obj/item/portallight/proc/UpdateUsability() + var/useable = FALSE + if(portalunderwear && istype(portalunderwear.loc, /obj/item/organ/genital)) + var/obj/item/organ/genital/loc_genitals = portalunderwear.loc + if(loc_genitals.owner && ishuman(loc_genitals.owner)) + useable = TRUE + src.useable = useable + return useable - if(H) //if the portal panties are on someone. - sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_normal") - if(H.dna.species.name == "Lizardperson") // lizard nerd - sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_lizard") - - if(H.dna.species.name == "Slimeperson") // slime nerd - sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_slime") - - if(H.dna.species.name == "Avian") // bird nerd - sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_avian") - - sleeve.color = "#" + H.dna.features["mcolor"] - add_overlay(sleeve) - - if(G.name == "vagina") - organ = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_vag") - organ.color = portalunderwear.loc.color - if(G.name == "anus") - organ = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_anus") - organ.color = "#" + H.dna.features["mcolor"] - - useable = TRUE - add_overlay(organ) - else - useable = FALSE //Hyperstation 13 portal underwear //can be attached to vagina or anus, just like the vibrator, still requires pairing with the portallight - /obj/item/portalpanties name = "portal panties" desc = "A silver love(TM) pair of portal underwear, with bluespace tech allows lovers to hump at a distance. Needs to be paired with a portal fleshlight before use." @@ -252,7 +217,6 @@ else . += "The device is paired, and awaiting attachment. " - /obj/item/portalpanties/attackby(obj/item/I, mob/living/user) //pairing if(istype(I, /obj/item/portallight)) var/obj/item/portallight/P = I @@ -268,11 +232,9 @@ ..() //just allows people to hit it with other objects, if they so wished. /obj/item/portalpanties/attack(mob/living/carbon/C, mob/living/user) - if(!portallight) //we arent paired yet! noobie trap, let them know. to_chat(user, "[src] can only be attached once paired with a portal fleshlight.") return - var/obj/item/organ/genital/picked_organ var/mob/living/carbon/human/S = user var/mob/living/carbon/human/T = C @@ -282,21 +244,17 @@ "[user] is trying to put [src] on you!") if(!do_mob(user, C, 3 SECONDS))//warn them and have a delay of 5 seconds to apply. return - if((picked_organ.name == "vagina")||(picked_organ.name == "anus")) //only fits on a vagina or anus - src.shapetype = picked_organ.name if(!picked_organ.equipment) to_chat(user, "You wrap [src] around [T]'s [picked_organ.name].") else to_chat(user, "They already have [picked_organ.equipment.name] there.") return - if(!user.transferItemToLoc(src, picked_organ)) //check if you can put it in return src.attached = TRUE picked_organ.equipment = src - var/obj/item/portallight/P = portallight //now we need to send what they look like, but saddly if the person changes colour for what ever reason, it wont update. but dont tell people shh. if(P) //just to make sure @@ -312,6 +270,7 @@ var/obj/item/portallight/P = portallight P.updatesleeve() + /obj/item/storage/box/portallight name = "Portal Fleshlight and Underwear" icon = 'hyperstation/icons/obj/fleshlight.dmi' diff --git a/hyperstation/code/obj/jacuzzi.dm b/hyperstation/code/obj/jacuzzi.dm new file mode 100644 index 000000000..b2699f3bb --- /dev/null +++ b/hyperstation/code/obj/jacuzzi.dm @@ -0,0 +1,44 @@ +/obj/structure/jacuzzi + name = "jacuzzi" + icon = 'hyperstation/icons/obj/jacuzzi.dmi' + icon_state = "tub" + desc = "A luxurious pool, but with bubbles!" + var/filled = TRUE + density = FALSE + var/mutable_appearance/waterlower + var/mutable_appearance/water + var/mutable_appearance/top + var/mutable_appearance/mist + anchored = TRUE + +//Dont move, it goes in walls and is shit. +///obj/structure/jacuzzi/attackby(obj/item/W, mob/user, params) +// if(istype(W, /obj/item/wrench)) +// W.play_tool_sound(src) +// anchored = !anchored + +/obj/structure/jacuzzi/Initialize() + . = ..() + top = mutable_appearance('hyperstation/icons/obj/jacuzzi.dmi', "tub_top") + top.layer = 5 + add_overlay(top) + +/obj/structure/jacuzzi/attack_hand(mob/user) + . = ..() + filled = !filled + if(!filled) + cut_overlay(waterlower) + cut_overlay(water) + cut_overlay(mist) + else + waterlower = mutable_appearance('hyperstation/icons/obj/jacuzzi.dmi', "water") + water = mutable_appearance('hyperstation/icons/obj/jacuzzi.dmi', "over_water") + mist = mutable_appearance('hyperstation/icons/obj/jacuzzi.dmi', "mist") + waterlower.layer = 3 + water.layer = 10 + mist.layer = 11 + mist.pixel_y = 5 + add_overlay(waterlower) + add_overlay(water) + add_overlay(mist) + diff --git a/hyperstation/code/obj/kinkyclothes.dm b/hyperstation/code/obj/kinkyclothes.dm index 1e74de37c..3a5e44318 100644 --- a/hyperstation/code/obj/kinkyclothes.dm +++ b/hyperstation/code/obj/kinkyclothes.dm @@ -129,3 +129,72 @@ obj/item/clothing/neck/stole/black max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT heat_protection = CHEST|GROIN|ARMS armor = list("melee" = 60, "bullet" = 80, "laser" = 80, "energy" = 90, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50) + +/obj/item/clothing/under/raccveralls + name = "form fitting overalls" + desc = "A tight form fitting pair of overalls." + icon = 'hyperstation/icons/obj/clothing/uniforms.dmi' + icon_state = "raccveralls" + alternate_worn_icon = 'hyperstation/icons/mobs/uniforms.dmi' + item_state = "raccveralls" + can_adjust = FALSE + +/obj/item/clothing/under/officesexy + name = "Revealing office uniform" + desc = "A sexy office uniform, that has a low cropped front to show off some chest, or bra. And a tall dress that covers the stomach, complete with a set of buttons." + icon = 'hyperstation/icons/obj/clothing/uniforms.dmi' + icon_state = "office_revealing" + alternate_worn_icon = 'hyperstation/icons/mobs/uniforms.dmi' + item_state = "office_revealing" + can_adjust = FALSE + mutantrace_variation = NO_MUTANTRACE_VARIATION + +/obj/item/clothing/under/vaultsuit + name = "vault suit" + desc = "A tight form fitting Vault-Tec standard issue Vault Jumpsuit! Snazzy!" + icon = 'hyperstation/icons/obj/clothing/uniforms.dmi' + icon_state = "vaultsuit" + alternate_worn_icon = 'hyperstation/icons/mobs/uniforms.dmi' + item_state = "b_suit" + can_adjust = FALSE + var/firstpickup = TRUE + var/pickupsound = TRUE + +/obj/item/clothing/under/vaultsuit/no_sound + pickupsound = FALSE + +/obj/item/clothing/under/vaultsuit/equipped(mob/user, slot) + . = ..() + if(!pickupsound) + return + if(!ishuman(user)) + return + if(slot == SLOT_W_UNIFORM) + if(!firstpickup) + SEND_SOUND(user, sound('hyperstation/sound/effects/vaultsuit/FalloutEXPUp.ogg', volume = 50)) + else + firstpickup = FALSE + SEND_SOUND(user, sound('hyperstation/sound/effects/vaultsuit/FalloutLevelUp.ogg', volume = 50)) + SEND_SOUND(user, sound('hyperstation/sound/effects/vaultsuit/InkSpotsSting.ogg', volume = 60)) + return + +/obj/item/clothing/suit/tunnelfox + name = "tunnel fox jacket" + desc = "Tunnel Foxes Rule!" + icon = 'hyperstation/icons/obj/clothing/suits.dmi' + icon_state = "tunnelfox" + alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi' + item_state = "tunnelfox" + body_parts_covered = CHEST|LEGS|ARMS + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + +/obj/item/clothing/suit/tunnelfox_t + name = "opened tunnel fox jacket" + desc = "Tunnel Foxes Rule!" + icon = 'hyperstation/icons/obj/clothing/suits.dmi' + icon_state = "tunnelfox_t" + alternate_worn_icon = 'hyperstation/icons/mobs/suits.dmi' + item_state = "tunnelfox_t" + body_parts_covered = CHEST|LEGS|ARMS + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + diff --git a/hyperstation/code/obj/pregnancytester.dm b/hyperstation/code/obj/pregnancytester.dm index 974fda3ab..d08610271 100644 --- a/hyperstation/code/obj/pregnancytester.dm +++ b/hyperstation/code/obj/pregnancytester.dm @@ -8,7 +8,6 @@ var/results = "null" w_class = WEIGHT_CLASS_TINY - /obj/item/pregnancytest/attack_self(mob/user) if(QDELETED(src)) return @@ -16,28 +15,26 @@ return if(isAI(user)) return - if(user.stat > 0)//unconscious or dead + if(user.stat > CONSCIOUS)//unconscious or dead return - if(status == 1) - return //Already been used once, pregnancy tests only work once. - test(user) - -/obj/item/pregnancytest/proc/force(mob/living/user) - //Force it negative - icon_state = "negative" - name = "[results] pregnancy test" - status = 1 - to_chat(user, "You use the pregnancy test, the display reads negative!") - - -/obj/item/pregnancytest/proc/test(mob/living/user) + Test(user) +/obj/item/pregnancytest/proc/Test(mob/living/user) + if(status) + return + var/_results = FALSE var/obj/item/organ/genital/womb/W = user.getorganslot("womb") - if(W.pregnant == 1) - results = "positive" - icon_state = "positive" - name = "[results] pregnancy test" - status = 1 - to_chat(user, "You use the pregnancy test, the display reads positive!") - else - force(user) \ No newline at end of file + if(!W) + return // no reason to waste the single-use on them + if(W.pregnant) + _results = TRUE + UpdateResult(user, _results) + +/obj/item/pregnancytest/proc/UpdateResult(mob/living/user, results) + var/result_text = results ? "positive" : "negative" + src.results = result_text + icon_state = result_text + name = "[results] preganancy test" + status = TRUE + if(user) + to_chat(user, "You use the pregnancy test, the display reads [results]!") \ No newline at end of file diff --git a/hyperstation/code/obj/vibrator.dm b/hyperstation/code/obj/vibrator.dm index 51f495945..40e1446e9 100644 --- a/hyperstation/code/obj/vibrator.dm +++ b/hyperstation/code/obj/vibrator.dm @@ -132,14 +132,13 @@ Code: if(2) //med, can make you cum to_chat(U, "You feel intense pleasure surge through your [G.name]") U.do_jitter_animation() - if (U.getArousalLoss() >= 100 && ishuman(U) && U.has_dna()) - U.mob_climax(forced_climax=TRUE) + U.mob_climax_instant() if(3) //high, makes you stun to_chat(U, "You feel overpowering pleasure surge through your [G.name]") U.Jitter(3) U.Stun(30) - if (U.getArousalLoss() >= 100 && ishuman(U) && U.has_dna()) - U.mob_climax(forced_climax=TRUE) + if (U.can_orgasm()) + U.mob_climax_instant() if(prob(50)) U.emote("moan") diff --git a/hyperstation/icons/effects/lighting.dmi b/hyperstation/icons/effects/lighting.dmi new file mode 100644 index 000000000..3f59b47d2 Binary files /dev/null and b/hyperstation/icons/effects/lighting.dmi differ diff --git a/hyperstation/icons/effects/lightinglarge.dmi b/hyperstation/icons/effects/lightinglarge.dmi new file mode 100644 index 000000000..fb1eeebc8 Binary files /dev/null and b/hyperstation/icons/effects/lightinglarge.dmi differ diff --git a/hyperstation/icons/effects/lightingmed.dmi b/hyperstation/icons/effects/lightingmed.dmi new file mode 100644 index 000000000..0b87ce7dc Binary files /dev/null and b/hyperstation/icons/effects/lightingmed.dmi differ diff --git a/hyperstation/icons/mob/char_markings.dmi b/hyperstation/icons/mob/char_markings.dmi index 96702915b..e311b1d0f 100644 Binary files a/hyperstation/icons/mob/char_markings.dmi and b/hyperstation/icons/mob/char_markings.dmi differ diff --git a/hyperstation/icons/mob/char_parts.dmi b/hyperstation/icons/mob/char_parts.dmi new file mode 100644 index 000000000..4bcab58af Binary files /dev/null and b/hyperstation/icons/mob/char_parts.dmi differ diff --git a/hyperstation/icons/mob/chickens.dmi b/hyperstation/icons/mob/chickens.dmi new file mode 100644 index 000000000..70fbbdda0 Binary files /dev/null and b/hyperstation/icons/mob/chickens.dmi differ diff --git a/hyperstation/icons/mob/clothes/extra.dmi b/hyperstation/icons/mob/clothes/extra.dmi index 1dd717c37..bd28845a8 100644 Binary files a/hyperstation/icons/mob/clothes/extra.dmi and b/hyperstation/icons/mob/clothes/extra.dmi differ diff --git a/hyperstation/icons/mob/ipc_screen_visor.dmi b/hyperstation/icons/mob/ipc_screen_visor.dmi new file mode 100644 index 000000000..1bf897228 Binary files /dev/null and b/hyperstation/icons/mob/ipc_screen_visor.dmi differ diff --git a/hyperstation/icons/mobs/uniforms.dmi b/hyperstation/icons/mobs/uniforms.dmi index 89868e8c4..4c545feea 100644 Binary files a/hyperstation/icons/mobs/uniforms.dmi and b/hyperstation/icons/mobs/uniforms.dmi differ diff --git a/hyperstation/icons/obj/clothing/suits.dmi b/hyperstation/icons/obj/clothing/suits.dmi index 9232ad123..00194e271 100644 Binary files a/hyperstation/icons/obj/clothing/suits.dmi and b/hyperstation/icons/obj/clothing/suits.dmi differ diff --git a/hyperstation/icons/obj/clothing/uniforms.dmi b/hyperstation/icons/obj/clothing/uniforms.dmi index 56e017c56..ec1fe01f9 100644 Binary files a/hyperstation/icons/obj/clothing/uniforms.dmi and b/hyperstation/icons/obj/clothing/uniforms.dmi differ diff --git a/hyperstation/icons/obj/genitals/belly.dmi b/hyperstation/icons/obj/genitals/belly.dmi index feac6b40c..af5d45f1c 100644 Binary files a/hyperstation/icons/obj/genitals/belly.dmi and b/hyperstation/icons/obj/genitals/belly.dmi differ diff --git a/hyperstation/icons/obj/genitals/butt.dmi b/hyperstation/icons/obj/genitals/butt.dmi index e4b8f5c6b..d5d8ee53e 100644 Binary files a/hyperstation/icons/obj/genitals/butt.dmi and b/hyperstation/icons/obj/genitals/butt.dmi differ diff --git a/hyperstation/icons/obj/hydroponics/farming.dmi b/hyperstation/icons/obj/hydroponics/farming.dmi new file mode 100644 index 000000000..1a478a052 Binary files /dev/null and b/hyperstation/icons/obj/hydroponics/farming.dmi differ diff --git a/hyperstation/icons/obj/hydroponics/growing_fruits.dmi b/hyperstation/icons/obj/hydroponics/growing_fruits.dmi new file mode 100644 index 000000000..691f93859 Binary files /dev/null and b/hyperstation/icons/obj/hydroponics/growing_fruits.dmi differ diff --git a/hyperstation/icons/obj/hydroponics/harvest.dmi b/hyperstation/icons/obj/hydroponics/harvest.dmi new file mode 100644 index 000000000..7ef398455 Binary files /dev/null and b/hyperstation/icons/obj/hydroponics/harvest.dmi differ diff --git a/hyperstation/icons/obj/hydroponics/seeds.dmi b/hyperstation/icons/obj/hydroponics/seeds.dmi new file mode 100644 index 000000000..89c8a1f87 Binary files /dev/null and b/hyperstation/icons/obj/hydroponics/seeds.dmi differ diff --git a/hyperstation/icons/obj/jacuzzi.dmi b/hyperstation/icons/obj/jacuzzi.dmi new file mode 100644 index 000000000..c87e39a6d Binary files /dev/null and b/hyperstation/icons/obj/jacuzzi.dmi differ diff --git a/hyperstation/icons/obj/tiles.dmi b/hyperstation/icons/obj/tiles.dmi new file mode 100644 index 000000000..cdcb8aec3 Binary files /dev/null and b/hyperstation/icons/obj/tiles.dmi differ diff --git a/hyperstation/icons/obj/toy.dmi b/hyperstation/icons/obj/toy.dmi new file mode 100644 index 000000000..98e2dabb4 Binary files /dev/null and b/hyperstation/icons/obj/toy.dmi differ diff --git a/hyperstation/sound/effects/vaultsuit/FalloutEXPUp.ogg b/hyperstation/sound/effects/vaultsuit/FalloutEXPUp.ogg new file mode 100644 index 000000000..8175df7dd Binary files /dev/null and b/hyperstation/sound/effects/vaultsuit/FalloutEXPUp.ogg differ diff --git a/hyperstation/sound/effects/vaultsuit/FalloutLevelUp.ogg b/hyperstation/sound/effects/vaultsuit/FalloutLevelUp.ogg new file mode 100644 index 000000000..c11c47674 Binary files /dev/null and b/hyperstation/sound/effects/vaultsuit/FalloutLevelUp.ogg differ diff --git a/hyperstation/sound/effects/vaultsuit/InkSpotsSting.ogg b/hyperstation/sound/effects/vaultsuit/InkSpotsSting.ogg new file mode 100644 index 000000000..4acde860c Binary files /dev/null and b/hyperstation/sound/effects/vaultsuit/InkSpotsSting.ogg differ diff --git a/hyperstation/sound/weapons/dinkalternate.ogg b/hyperstation/sound/weapons/dinkalternate.ogg new file mode 100644 index 000000000..e7d00f8b6 Binary files /dev/null and b/hyperstation/sound/weapons/dinkalternate.ogg differ diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi index 806511877..2d5b7140e 100644 Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi index 8e2b11443..d98f9dad9 100644 Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index 8fe60a952..0b00d73a4 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/mob/uniform.dmi b/icons/mob/uniform.dmi index 1360328ba..91538bd3d 100644 Binary files a/icons/mob/uniform.dmi and b/icons/mob/uniform.dmi differ diff --git a/icons/obj/drinks.dmi b/icons/obj/drinks.dmi index 4ba28ee99..250fb561a 100644 Binary files a/icons/obj/drinks.dmi and b/icons/obj/drinks.dmi differ diff --git a/icons/obj/hydroponics/harvest.dmi b/icons/obj/hydroponics/harvest.dmi index 368b0e5c0..7b83313a7 100644 Binary files a/icons/obj/hydroponics/harvest.dmi and b/icons/obj/hydroponics/harvest.dmi differ diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index b0e6ab7bf..3cf1c4bc1 100644 Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ diff --git a/icons/obj/tiles.dmi b/icons/obj/tiles.dmi index 59e8fdec0..65defc25f 100644 Binary files a/icons/obj/tiles.dmi and b/icons/obj/tiles.dmi differ diff --git a/icons/obj/toy.dmi b/icons/obj/toy.dmi index 333c8ca0d..403cba517 100644 Binary files a/icons/obj/toy.dmi and b/icons/obj/toy.dmi differ diff --git a/modular_citadel/code/datums/status_effects/chems.dm b/modular_citadel/code/datums/status_effects/chems.dm index 92aa3b21b..433c20135 100644 --- a/modular_citadel/code/datums/status_effects/chems.dm +++ b/modular_citadel/code/datums/status_effects/chems.dm @@ -620,7 +620,7 @@ else if (lowertext(customTriggers[trigger]) == "cum")//aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa if (HAS_TRAIT(C, TRAIT_NYMPHO) && lewd) if (C.getArousalLoss() > 80) - C.mob_climax(forced_climax=TRUE) + C.mob_climax_instant() C.SetStun(10)//We got your stun effects in somewhere, Kev. else C.adjustArousalLoss(10) diff --git a/modular_citadel/code/game/machinery/vending.dm b/modular_citadel/code/game/machinery/vending.dm index efbaa9f95..9f1157c99 100644 --- a/modular_citadel/code/game/machinery/vending.dm +++ b/modular_citadel/code/game/machinery/vending.dm @@ -70,6 +70,7 @@ /obj/item/electropack/vibrator = 2, /obj/item/fleshlight = 2, /obj/item/storage/box/portallight = 1, + /obj/item/storage/pill_bottle/lovedice = 5, ) contraband = list( /obj/item/clothing/under/gear_harness = 3, diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index c8d4ea191..8b8780b46 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -11,7 +11,6 @@ /mob/living/carbon/human canbearoused = TRUE - var/saved_underwear = ""//saves their underwear so it can be toggled later var/saved_undershirt = "" var/saved_socks = "" @@ -62,7 +61,6 @@ /mob/living/proc/handle_arousal() - /mob/living/carbon/handle_arousal() if(canbearoused && dna) var/datum/species/S @@ -110,8 +108,11 @@ if(updating_arousal) updatearousal() +/** + * despite the name of this, it actually returns a number between 0 and 100 + */ /mob/living/proc/getPercentAroused() - var/percentage = ((100 / max_arousal) * arousalloss) + var/percentage = ((arousalloss / max_arousal) * 100) return percentage /mob/living/proc/isPercentAroused(percentage)//returns true if the mob's arousal (measured in a percent of 100) is greater than the arg percentage. @@ -127,26 +128,25 @@ /mob/living/carbon/updatearousal() . = ..() - - for(var/obj/item/organ/genital/G in internal_organs) - if(istype(G)) + for(var/obj/item/organ/genital/genital in internal_organs) + if(istype(genital)) var/datum/sprite_accessory/S - switch(G.type) + switch(genital.type) if(/obj/item/organ/genital/penis) - S = GLOB.cock_shapes_list[G.shape] + S = GLOB.cock_shapes_list[genital.shape] if(/obj/item/organ/genital/testicles) - S = GLOB.balls_shapes_list[G.shape] + S = GLOB.balls_shapes_list[genital.shape] if(/obj/item/organ/genital/vagina) - S = GLOB.vagina_shapes_list[G.shape] + S = GLOB.vagina_shapes_list[genital.shape] if(/obj/item/organ/genital/breasts) - S = GLOB.breasts_shapes_list[G.shape] + S = GLOB.breasts_shapes_list[genital.shape] if(S?.alt_aroused) - G.aroused_state = isPercentAroused(G.aroused_amount) - if(getArousalLoss() >= ((max_arousal / 100) * 33)) - G.aroused_state = TRUE + genital.aroused_state = isPercentAroused(genital.aroused_amount) + if(getArousalLoss() >= isPercentAroused(33)) + genital.aroused_state = TRUE else - G.aroused_state = FALSE - G.update_appearance() + genital.aroused_state = FALSE + genital.update_appearance() @@ -166,41 +166,13 @@ 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/arousal_state = "arousal0" + if(stat != DEAD) + var/arousal_percent = getPercentAroused() + var/arousal_rounded = FLOOR(arousal_percent, 10) + arousal_state = "arousal[arousal_rounded]" + hud_used.arousal.icon_state = arousal_state + return TRUE /obj/screen/arousal name = "arousal" @@ -217,647 +189,654 @@ if(M.canbearoused) ui_interact(usr) -/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. +/** + * Checks if this mob can orgasm. Criteria includes: + * - Mob is human + * - Mob is capable of arousal + * - Mob has DNA + * - Mob is aroused past a certain threshold + * + * Arguments: + * - `arousal` - The minimum arousal needed to pass the check. Default is `100`. + * + * Returns: `TRUE`/`FALSE` - Whether or not the mob passes all of the above checks. + */ +/mob/living/proc/can_orgasm(arousal=100) + if(src.canbearoused && src.getArousalLoss() >= arousal && ishuman(src) && src.has_dna()) + return TRUE + return FALSE + + +/mob/living/proc/mob_climax() set name = "Masturbate" set category = "IC" if(canbearoused && !restrained() && !stat) 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 + if(isPercentAroused(33))//33% arousal or greater required src.visible_message("[src] starts masturbating!", \ - "You start masturbating.") + "You start masturbating.") if(do_after(src, 30, target = src)) src.visible_message("[src] relieves [p_them()]self!", \ - "You have relieved yourself.") + "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.") -//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, cover = FALSE, mb_time = 30) //Masturbation, keep it gender-neutral +/** + * Arguments: + * - `genital`: Genitals being used for this interaction. + * - `cover`: Whether or not the mob is climaxing over themselves. + * - `mb_time`: Warm-up time for this interaction. + */ +/mob/living/carbon/human/proc/mob_masturbate(obj/item/organ/genital/genital, cover=FALSE, mb_time=3 SECONDS) var/total_fluids = 0 - var/datum/reagents/fluid_source = null - var/condomed = 0 - var/sounded = 0 - - if(G.name == "penis")//if the select organ is a penis + var/datum/reagents/fluid_source = genital.get_fluid_source() + var/condomed = FALSE + var/sounded = FALSE + if(genital.name == "penis") var/obj/item/organ/genital/penis/P = src.getorganslot("penis") - if(P.condom) //if the penis is condomed - condomed = 1 - if(P.sounding) - sounded = 1 - 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 + condomed = P.condom + sounded = P.sounding + if(!fluid_source) + to_chat(src, "Your [genital.name] is unable to produce it's own fluids, it's missing the organs for it.") + return total_fluids = fluid_source.total_volume 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].", \ - "You start to [G.masturbation_verb] your [G.name].") - + src.visible_message("[src] starts to [genital.masturbation_verb] [p_their()] [genital.name].", \ + "You start to [genital.masturbation_verb] your [genital.name].", \ + "You start to [genital.masturbation_verb] your [genital.name].") if(do_after(src, mb_time, target = src)) if(total_fluids > 5 &&!condomed &&!sounded) fluid_source.reaction(src.loc, TOUCH, 1, 0) fluid_source.clear_reagents() - if(!condomed && !sounded && !cover) - 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.") - - if(!condomed &&!sounded && cover)//For when you want to make a mess of yourself. - fluid_source.trans_to(src, total_fluids*G.fluid_transfer_factor) - total_fluids -= total_fluids*G.fluid_transfer_factor - if(total_fluids > 80) // now thats a big cum! - if(isliving(src)) - var/mutable_appearance/cumoverlaylarge = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlaylarge.icon_state = "cum_large" - src.add_overlay(cumoverlaylarge) - - if(total_fluids > 5) - fluid_source.reaction(src.loc, TOUCH, 1, 0) - var/mob/living/carbon/human/H = src - if(H && G.name == "penis") - H.cumdrip_rate += rand(5,10) - fluid_source.clear_reagents() - src.visible_message("[src] climaxes over [p_their()] self, using [p_their()] [G.name]!", \ - "You orgasm over yourself, using your [G.name].", \ - "You have climaxed over something, using your [G.name].") - if(isliving(src)) - var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlay.icon_state = "cum_normal" - src.add_overlay(cumoverlay) - else - src.add_cum_overlay() - - if(condomed) //condomed - src.visible_message("[src] orgasms, climaxing into [p_their()] condom ", \ - "You cum into your condom.", \ - "You have relieved yourself.") - if(sounded) //sounded - src.visible_message("[src] orgasms, but the rod blocks anything from leaking out!", \ - "You cum with the rod inside.", \ - "You don't quite feel totally relieved.") - if(total_fluids > 0 &&condomed &&!sounded) - src.condomclimax() - - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(G.can_climax) - setArousalLoss(min_arousal) - - -/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30, spillage = TRUE) //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.", \ - "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(spillage) + if(!condomed && !sounded)//For when you want to make a mess of yourself. + if(cover) + fluid_source.trans_to(src, total_fluids*genital.fluid_transfer_factor) + total_fluids -= total_fluids*genital.fluid_transfer_factor + if(total_fluids > 80) // now thats a big cum! + src.add_cum_overlay("large") if(total_fluids > 5) fluid_source.reaction(src.loc, TOUCH, 1, 0) - + var/mob/living/carbon/human/H = src + if(H && genital.name == "penis") + H.cumdrip_rate += rand(5,10) fluid_source.clear_reagents() - src.visible_message("[src] orgasms[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""], with [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].") - else //Else from spillage check, also note subtle text change - src.visible_message("[src] orgasms with [p_their()] [G.name]!", \ - "You climax 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) - - -/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, mob/living/L, spillage = TRUE, impreg = FALSE,cover = FALSE,remote = FALSE, 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 - - if(mb_time && !remote) //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 something!") - if(remote) - src.visible_message("[src] is about to climax with someone!", \ - "You're about to climax with someone!", \ - "You're preparing to climax with something!") - - if(cover)//covering the partner in cum, this overrides other options. - 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 > 80) // now thats a big cum! - if(isliving(L)) - var/mutable_appearance/cumoverlaylarge = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlaylarge.icon_state = "cum_large" - L.add_overlay(cumoverlaylarge) - - if(total_fluids > 5) - fluid_source.reaction(L.loc, TOUCH, 1, 0) - var/mob/living/carbon/human/H = L - if(H) - H.cumdrip_rate += rand(5,10) - fluid_source.clear_reagents() - src.visible_message("[src] climaxes over [L], using [p_their()] [G.name]!", \ - "You orgasm over [L], using your [G.name].", \ - "You have climaxed over something, using your [G.name].") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - - if(isliving(L)) - var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlay.icon_state = "cum_normal" - L.add_overlay(cumoverlay) + src.visible_message("[src] climaxes over [p_their()] self, using [p_their()] [genital.name]!", \ + "You orgasm over yourself, using your [genital.name].", \ + "You have climaxed over something, using your [genital.name].") + src.add_cum_overlay() else - L.add_cum_overlay() - - setArousalLoss(min_arousal) - if(G.can_climax) + 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.") + if(condomed) //condomed + src.visible_message("[src] orgasms, climaxing into [p_their()] condom!", \ + "You cum into your condom.", \ + "You have relieved yourself.") + if(sounded) //sounded + src.visible_message("[src] orgasms, but the rod blocks anything from leaking out!", \ + "You cum with the rod inside.", \ + "You don't quite feel totally relieved.") + if(total_fluids > 0 && condomed && !sounded) + src.condomclimax() + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + if(genital.can_climax) setArousalLoss(min_arousal) - if(spillage && !cover) - if(do_after(src, mb_time, target = src)) - if(!in_range(src, L) && !remote) - return - fluid_source.trans_to(L, total_fluids*G.fluid_transfer_factor) - total_fluids -= total_fluids*G.fluid_transfer_factor + +/** + * Mob climaxes using provided genitals without a partner. This is in contrast with `mob_climax_instant()`, + * which does not have a warm-up time and uses all genitals at once to climax. + * + * Arguments: + * - `genital`: The selected genitals for this interaction. Determines fluid type. + * - `mb_time`: The warm-up time for this interaction. + * - `spillage`: Whether or not climaxing causes this mob to spill fluids on the floor. + */ +/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/genital, spillage=TRUE, mb_time=3 SECONDS) + var/total_fluids = 0 + var/datum/reagents/fluid_source = genital.get_fluid_source() + var/unable_to_come = FALSE + if(fluid_source) + total_fluids = fluid_source.total_volume + else + unable_to_come = TRUE + if(unable_to_come) + src.visible_message("[src] shudders, their [genital.name] unable to cum.", \ + "Your [genital.name] cannot cum, giving no relief.", \ + "Your [genital.name] cannot cum, giving no relief.") + return + 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(spillage) if(total_fluids > 5) - fluid_source.reaction(L.loc, TOUCH, 1, 0) - var/mob/living/carbon/human/H = L - if(H) - H.cumdrip_rate += rand(5,10) + fluid_source.reaction(src.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) + src.visible_message("[src] orgasms[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""], with [p_their()] [genital.name]!", \ + "You climax[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""] with your [genital.name].", \ + "You climax using your [genital.name].") + else + src.visible_message("[src] orgasms with [p_their()] [genital.name]!", \ + "You climax with your [genital.name].", \ + "You climax using your [genital.name].") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + if(genital.can_climax) + setArousalLoss(min_arousal) - if(G.can_climax) - setArousalLoss(min_arousal) +/** + * Climax over a partner with a selected organ. This is external and creates spills on the partner/floor, + * and does not have a chance to impregnate. For this interaction, the partner may be an object instead of + * a person (but it cannot be an animal). + * + * Arguments: + * - `genital`: The genital being used for this interaction. Determines fluid type and volume. + * - `partner`: The thing being used as a partner. Generally an object or a person. + * - `mb_time`: The warm-up time for this interaction. Default is 3 seconds + */ +/mob/living/carbon/human/proc/mob_climax_cover(obj/item/organ/genital/genital, atom/partner, mb_time=3 SECONDS) + if(isnoncarbon(partner)) // deny animals + return FALSE + if(istype(genital, /obj/item/organ/genital/penis)) + var/obj/item/organ/genital/penis/P = genital + if(P.condom) + to_chat(src, "You cannot do this action with a condom on.") + return + if(P.sounding) + to_chat(src, "You cannot do this action with a sounding in.") + return + var/total_fluids = 0 + var/datum/reagents/fluid_source = genital.get_fluid_source() + if(!fluid_source) + to_chat(src, "Your [genital.name] is unable to produce it's own fluids, it's missing the organs for it.") + return + total_fluids = fluid_source.total_volume + if(mb_time) + src.visible_message("[src] is about to climax with [partner]!", \ + "You're about to climax with [partner]!", \ + "You're preparing to climax with something!") + if(!do_after(src, mb_time, target = src)) + return + if(!in_range(src, partner)) + return + var/transferred_units = total_fluids * genital.fluid_transfer_factor + fluid_source.trans_to(partner, transferred_units) + total_fluids -= transferred_units + if(total_fluids > 5) + fluid_source.reaction(partner.loc, TOUCH, 1, 0) + fluid_source.clear_reagents() + if(ismob(partner)) + var/mob/living/carbon/partner_carbon = partner + if(partner_carbon) + var/cum_overlay = "normal" + if(total_fluids > 80) + cum_overlay = "large" + partner_carbon.add_cum_overlay(cum_overlay) + var/mob/living/carbon/human/partner_human = partner_carbon + if(partner_human) + partner_human.cumdrip_rate += rand(5,10) + else + partner.add_cum_overlay() + src.visible_message("[src] climaxes over [partner], using [p_their()] [genital.name]!", \ + "You orgasm over [partner], using your [genital.name].", \ + "You have climaxed over something, using your [genital.name].") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + setArousalLoss(min_arousal) + + +/** + * Climax inside a partner using a selected organ. + * + * Arguments: + * - `genital`: The genital being used for this interaction. Determines type/volume of fluids. + * - `partner`: The person that this mob is climaxing in. + * - `spillage`: Whether or not the fluids will spill on the ground. + * - `remote`: Whether or not this interaction is being triggered long-distance. + * - `mb_time`: Warm-up time for this interaction. + * + * Returns: `TRUE`/`FALSE` - whether or not this interaction succeeded. + */ +/mob/living/carbon/human/proc/mob_climax_in_partner(obj/item/organ/genital/genital, mob/living/carbon/partner, spillage=TRUE, remote=FALSE, mb_time=3 SECONDS) + if(isnoncarbon(partner)) // deny animals + return FALSE + var/total_fluids = 0 + var/datum/reagents/fluid_source = genital.get_fluid_source() + if(!fluid_source) + to_chat(src, "Your [genital.name] is unable to produce it's own fluids, it's missing the organs for it.") + return FALSE + total_fluids = fluid_source.total_volume + var/partner_text = partner + if(remote) + partner_text = "someone" + src.visible_message("[src] is about to climax with [partner_text]!", \ + "You're about to climax with [partner_text]!", \ + "You're preparing to climax with something!") + if(!do_after(src, mb_time, target = src)) + return FALSE + if(!remote && !in_range(src, partner)) + return FALSE + if(spillage) + fluid_source.trans_to(partner, total_fluids*genital.fluid_transfer_factor) + total_fluids -= total_fluids*genital.fluid_transfer_factor + if(total_fluids > 5) + fluid_source.reaction(partner.loc, TOUCH, 1, 0) + var/mob/living/carbon/human/H = partner + if(H) + H.cumdrip_rate += rand(5,10) + fluid_source.clear_reagents() + src.visible_message("[src] climaxes with [partner], overflowing and spilling, using [p_their()] [genital.name]!",\ + "You orgasm with [partner], spilling out of them, using your [genital.name].",\ + "You have climaxed with someone, spilling out of them, using your [genital.name].") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) else //knots, portal fleshlights, and other non-spilling orgasms - if(!cover) - if(!remote && !in_range(src, L)) - return - if(do_after(src, mb_time, target = src)) - - if(!spillage) //hyper inflation - var/obj/item/organ/genital/belly/B = L.getorganslot("belly") - if(B) - if(B.inflatable && total_fluids > 80) //requires a big cumshot to expand. - if(B.size < 3) - B.size += 1 - to_chat(L, "You feel your belly expand.") - else - to_chat(L, "You feel your belly strain.") - - var/obj/item/organ/genital/penis/P = G - if (P.condom)//condomed. - src.condomclimax() - else - fluid_source.trans_to(L, total_fluids) - total_fluids = 0 - if(!remote) - 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.") - else - src.visible_message("[src] climaxes with someone, using [p_their()] [G.name]!", \ - "You ejaculate with someone, using your [G.name].", \ - "You have climaxed inside someone, using your [G.name].") - to_chat(L, "You feel someone ejaculate inside you.") - - 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) - + var/can_inflate = TRUE + if(istype(genital, /obj/item/organ/genital/penis)) + var/obj/item/organ/genital/penis/P = genital + if(P.condom)//condomed. + can_inflate = FALSE + src.condomclimax() + if(can_inflate) + fluid_source.trans_to(partner, total_fluids) + if(!spillage && total_fluids > 80) // hyper - cum inflation, ONLY if not wearing a condom + partner.expand_belly(1) + src.visible_message("[src] climaxes with [partner_text], [p_their()] [genital.name] spilling nothing!", \ + "You ejaculate with [partner_text], your [genital.name] spilling nothing.", \ + "You have climaxed inside someone, your [genital.name] spilling nothing.") + if(remote) + to_chat(partner, "You feel someone ejaculate inside you.") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + if(genital.can_climax) + setArousalLoss(min_arousal) //Hyper - antag code + var/mob/living/carbon/partner_carbon = partner if(src.mind.special_role == ROLE_LEWD_TRAITOR) for(var/datum/objective/obj in src.mind.objectives) - if (L.mind == obj.target) - L.mind.sexed = TRUE //sexed + if (partner_carbon.mind == obj.target) + partner_carbon.mind.sexed = TRUE //sexed to_chat(src, "You feel deep satisfaction with yourself.") - - //Hyper - impreg - if(impreg) - //Role them odds, only people with the dicks can send the chance to the person with the settings enabled at the momment. - if(prob(L.impregchance)) - var/obj/item/organ/genital/womb/W = L.getorganslot("womb") - if(W) //check if they have a womb. - if (L.breedable == 1 && W.pregnant == 0) //Dont get pregnant again, if you are pregnant. - log_game("Debug: [L] has been impregnated by [src]") - to_chat(L, "You feel your hormones change, and a motherly instinct take over.") //leting them know magic has happened. - W.pregnant = 1 - if (HAS_TRAIT(L, TRAIT_HEAT)) - SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "heat", /datum/mood_event/heat) //well done you perv. - REMOVE_TRAIT(L, TRAIT_HEAT, ROUNDSTART_TRAIT) //take the heat away, you satisfied it! - - //Make breasts produce quicker. - var/obj/item/organ/genital/breasts/B = L.getorganslot("breasts") - if (B.fluid_mult < 0.5 && B) - B.fluid_mult = 0.5 + return TRUE -/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 +/** + * User chooses whether or not their climax will spill outside of their partner. + * + * Arguments: + * - `picked_organ`: The user's genital being used. + * - `partner`: This mob's partner for the interaction. + * - `impreg`: Whether or not this interaction has a chance to impregnate. + */ +/mob/living/carbon/human/proc/mob_climax_inside_spillage(obj/item/organ/genital/picked_organ, mob/living/carbon/partner, impreg=FALSE) + var/obj/item/organ/genital/penis/_penis = picked_organ + if(picked_organ.name != "penis" || _penis.sounding || _penis.condom) + mob_climax_in_partner(picked_organ, partner, spillage=FALSE) + return + var/_question = "Would your fluids spill outside?" + var/_title = "Choose overflowing option" + var/spillage = input(src, _question, _title, "Yes") as anything in list("Yes", "No") + if(mob_climax_in_partner(picked_organ, partner, spillage == "Yes") && impreg) + partner.impregnate(by=src) - if(G.name == "penis")//if the select organ is a penis - var/obj/item/organ/genital/penis/P = src.getorganslot("penis") - if(P.condom) //if the penis is condomed - to_chat(src, "You cannot fill containers when there is a condom over your [G.name].") - return - if(P.sounding) //if the penis is sounded - to_chat(src, "You cannot fill containers when there is a rod inside your [G.name].") - return - 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 - +/** + * Attempt to climax into a container it, filling it with whatever fluids are + * associated to the selected genitals. + * + * Arguments: + * - `genital`: The genital being used for climax. + * - `container`: The container being targeted. + * - `mb_time`: The warm-up time for this interaction. + */ +/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/genital, obj/item/reagent_containers/container, mb_time=3 SECONDS) 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.") + var/total_fluids = 0 + var/datum/reagents/fluid_source = genital.get_fluid_source() + if(!fluid_source) + to_chat(src, "Your [genital.name] is unable to produce it's own fluids, it's missing the organs for it.") + return + total_fluids = fluid_source.total_volume + if(genital.name == "penis") + var/obj/item/organ/genital/penis/P = src.getorganslot("penis") + if(P.condom) + to_chat(src, "You cannot fill containers when there is a condom over your [genital.name].") + return + if(P.sounding) + to_chat(src, "You cannot fill containers when there is a rod inside your [genital.name].") + return + src.visible_message("[src] starts to [genital.masturbation_verb] their [genital.name] over [container].", \ + "You start to [genital.masturbation_verb] your [genital.name] over [container].", \ + "You start to [genital.masturbation_verb] your [genital.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] and fill [container] with a total of [total_fluids]u's.", \ - "You have relieved some pressure.") + src.visible_message("[src] uses [p_their()] [genital.name] to fill [container]!", \ + "You used your [genital.name] and fill [container] with a total of [total_fluids]u's.", \ + "You have relieved some pressure.") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) container.add_cum_overlay() //your aim is bad... - if(G.can_climax) + if(genital.can_climax) setArousalLoss(min_arousal) -/mob/living/carbon/human/proc/pick_masturbate_genitals() +/** + * Prompts the user to select one of their target's genitals to interact with. + * The target must have their genitals visible to be selectable. + * + * Returns: `null`|`obj/item/organ/genital`: The selected genital. + */ +/mob/living/carbon/human/proc/target_genitals(mob/living/carbon/human/T) var/obj/item/organ/genital/ret_organ var/list/genitals_list = 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 - if(!G.dontlist) - genitals_list += G - if(genitals_list.len) - ret_organ = input(src, "with what?", "Masturbate", null) as null|obj in genitals_list - return ret_organ - return null //error stuff - -/mob/living/carbon/human/proc/target_genitals(mob/living/carbon/human/T) //used for targeting others - var/obj/item/organ/genital/ret_organ - var/list/genitals_list = list() - var/list/worn_stuff = get_equipped_items() - - for(var/obj/item/organ/genital/G in T.internal_organs) - if(G.is_exposed(worn_stuff)) //Nude or through_clothing - if(!G.dontlist) - genitals_list += G + for(var/obj/item/organ/genital/genital in T.internal_organs) + if(genital.is_exposed() && !genital.dontlist) + genitals_list += genital if(genitals_list.len) ret_organ = input(src, "", "Genitals", null) as null|obj in genitals_list return ret_organ return null //error stuff -/mob/living/carbon/human/proc/pick_climax_genitals() + +/** + * Prompts the user to select genitals. The genitals must be exposed to be selectable. + * + * Arguments: + * - `masturbation`: Whether or not this is being used for masturbation interactions. + * - `title`: The title of the prompt window. + * + * Returns: `null`|`obj/item/organ/genital`: The selected genital. + */ +/mob/living/carbon/human/proc/pick_climax_genitals(masturbation=FALSE, title="Climax") var/obj/item/organ/genital/ret_organ var/list/genitals_list = 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 - if(!G.dontlist) - genitals_list += G + for(var/obj/item/organ/genital/genital in internal_organs) + if(genital.can_climax && genital.is_exposed() && !genital.dontlist) + if(!masturbation || genital.can_masturbate_with) + genitals_list += genital if(genitals_list.len) - ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list + ret_organ = input(src, "with what?", title, null) as null|obj in genitals_list return ret_organ return null //error stuff -/mob/living/carbon/human/proc/pick_partner_overide() //used for cumming on people without genitals exposed - var/list/partners = list() - if(src.pulling) - partners += src.pulling //Yes, even objects for now - if(src.pulledby) - partners += src.pulledby - //Now we got both of them, let's check if they're proper - for(var/I in partners) - if(isliving(I)) - else - partners -= I //No fucking objects - //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 -/mob/living/carbon/human/proc/pick_partner() - var/list/partners = list() - if(src.pulling) - partners += src.pulling //Yes, even objects for now - if(src.pulledby) - partners += src.pulledby - //Now we got both of them, let's check if they're proper +/** + * Prompts the user to select a mob that they are either pulling or being pulled by. + * + * Arguments: + * - `needs_exposed`: Whether or not the partner must have exposed genitals. + * Returns: `null`|`mob/living/carbon`: The selected mob. + */ +/mob/living/carbon/human/proc/pick_partner(needs_exposed=TRUE) + var/list/partners = list(src.pulling, src.pulledby) 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 - //NOW the list should only contain correct partners + if(!iscarbon(I)) + partners -= I + continue + if(!needs_exposed) + continue + var/mob/living/carbon/C = I + if(!C.exposed_genitals.len && !C.is_groin_exposed() && !C.is_chest_exposed()) + partners -= I if(!partners.len) - return null //No one left. - return input(src, "With whom?", "Sexual partner", null) in partners //pick one, default to null + return null + return input(src, "With whom?", "Sexual partner", null) as null|mob in partners + +/** + * Prompts the user to provide a container (or food item) based on what's in their hands. + * + * Returns: `null`|`obj/item/reagent_containers` - The selected container. + */ /mob/living/carbon/human/proc/pick_climax_container() var/obj/item/reagent_containers/SC = null 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 - if(containers_list.len) - SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list + 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. + return null -//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 - if(stat == DEAD) //corpses can't cum +/** + * The old arousal menu. This shows a BYOND-style input selection menu that + * emulates the same functionality as the newer one. + */ +/mob/living/carbon/human/mob_climax() + if(!canbearoused || !has_dna()) return if(mb_cd_timer > world.time) - 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!") + 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) - 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() - - 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(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!") + if(stat == DEAD) + to_chat(src, "You can't do that while dead!") + return + if(stat == UNCONSCIOUS) + to_chat(src, "You must be conscious to do that!") + return + if(getArousalLoss() < 33) + to_chat(src, "You aren't aroused enough for that!") + return + var/list/choices = list("Masturbate", "Climax alone", "Climax with partner","Climax over partner", "Fill container", "Remove condom", "Remove sounding rod") + var/choice = input(src, "Select sexual activity", "Sexual activity:") as null|anything in choices + switch(choice) + if("Remove sounding rod") + 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 + var/obj/item/organ/genital/penis/P = src.getorganslot("penis") + if(!P.sounding) + to_chat(src, "You don't have a rod inside!") + return + if(P.sounding) + to_chat(src, "You pull the rod off from the tip of your penis!") + src.removesounding() + return 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","Climax over partner", "Fill container", "Remove condom", "Remove sounding rod") + if("Remove condom") + 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 + var/obj/item/organ/genital/penis/P = src.getorganslot("penis") + if(!P.condom) + to_chat(src, "You don't have a condom on!") + return + if(P.condom) + to_chat(src, "You tug the condom off the end of your penis!") + src.removecondom() + return + return - switch(choice) - if("Remove sounding rod") - 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 - var/obj/item/organ/genital/penis/P = src.getorganslot("penis") - if(!P.sounding) - to_chat(src, "You don't have a rod inside!") - return - if(P.sounding) - to_chat(src, "You pull the rod off from the tip of your penis!") - src.removesounding() - return + 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_climax_genitals(masturbation=TRUE, title="Masturbation") + 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 - if("Remove condom") - 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 - var/obj/item/organ/genital/penis/P = src.getorganslot("penis") - if(!P.condom) - to_chat(src, "You don't have a condom on!") - return - if(P.condom) - to_chat(src, "You tug the condom off the end of your penis!") - src.removecondom() - 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("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 + 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) + to_chat(src, "You cannot climax without choosing genitals.") + return + var/mob/living/carbon/partner = pick_partner() //Get someone + if(!partner) + to_chat(src, "You cannot do this alone.") + return + var/obj/item/organ/genital/penis/P = picked_organ + var/impreg = "No" + if(partner.breedable && picked_organ.name == "penis" && !P.condom && !P.sounding) + var/impreg_question = "Would this action carry the risk of pregnancy?" + var/impreg_title = "Choose a option" + impreg = input(src, impreg_question, impreg_title, "Yes") as anything in list("Yes", "No") + mob_climax_inside_spillage(picked_organ, partner, impreg == "Yes") - 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 over partner") + var/obj/item/organ/genital/picked_organ + picked_organ = pick_climax_genitals() + if(picked_organ) + var/mob/living/carbon/partner = pick_partner(needs_exposed=FALSE) //Get your partner, clothed or not. + if(partner) + mob_climax_cover(picked_organ, partner) + else + to_chat(src, "You cannot do this alone.") - 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/obj/item/organ/genital/penis/P = picked_organ - if(partner.breedable == 1 && picked_organ.name == "penis"&&!P.condom == 1&&!P.sounding == 1) - var/impreg = input(src, "Would this action carry the risk of pregnancy?", "Choose a option", "Yes") as anything in list("Yes", "No") - if(impreg == "Yes") //If we are impregging - 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, TRUE, FALSE) - else - mob_climax_partner(picked_organ, partner, FALSE, TRUE, FALSE) - else - 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, FALSE, FALSE) - else - mob_climax_partner(picked_organ, partner, FALSE, FALSE, FALSE) //Wow, im trash at coding, I need to find a better way of coding this, ill rewrite it later.-quote - return + 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. - else //If we arent impregging - 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, FALSE, FALSE) - else - mob_climax_partner(picked_organ, partner, FALSE, FALSE, 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 over partner") - var/obj/item/organ/genital/picked_organ - picked_organ = pick_climax_genitals() - if(picked_organ) - var/mob/living/partner = pick_partner_overide() //Get your partner, clothed or not. - if(partner) - mob_climax_partner(picked_organ, partner, FALSE, FALSE, TRUE) - else - to_chat(src, "You cannot do this alone.") +/** + * Causes a mob to climax on the spot. There is no warmup time, nor a + * menu for selecting genitals. Instead, the mob will climax using all + * climaxable genitals, either onto their location (floor) or on whatever + * is pulling/being pulled by them. + */ +/mob/living/carbon/proc/mob_climax_instant(arousal=100) + if(stat == DEAD) //corpses can't cum + to_chat(src, "You can't do that while dead!") + return + if(!can_orgasm(arousal)) + return + var/mob/living/carbon/human/user_human = src + for(var/obj/item/organ/O in internal_organs) + if(!istype(O, /obj/item/organ/genital)) + continue + var/obj/item/organ/genital/current_genital = O + if(!current_genital.can_climax) + continue // no wombs or testicles + var/mob/living/carbon/partner + var/check_target + if(current_genital.is_exposed()) + if(src.pulling) + if(iscarbon(src.pulling)) + check_target = src.pulling + else if(src.pulledby) + if(iscarbon(src.pulledby)) + check_target = src.pulledby + if(check_target) + var/mob/living/carbon/C = check_target + if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) + partner = C + if(partner) + user_human.mob_climax_in_partner(current_genital, partner, mb_time=0) + continue + user_human.mob_climax_outside(current_genital, mb_time=0) + return - 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. +/** + * Rolls a chance for the mob to get pregnant, assuming they are capable of doing so. + * + * Arguments: + * - `by` - The mob that is impregnating this one. + */ +/mob/living/carbon/proc/impregnate(mob/living/carbon/by) + var/obj/item/organ/genital/womb/W = getorganslot("womb") + if(!W || W.pregnant || !breedable) + return + if(!prob(impregchance)) + return + W.pregnant = TRUE + log_game("DEBUG: [src] has been impregnated by [by]") + to_chat(src, "You feel your hormones change, and a motherly instinct take over.") + if(HAS_TRAIT(src, TRAIT_HEAT)) + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "heat", /datum/mood_event/heat) + REMOVE_TRAIT(src, TRAIT_HEAT, ROUNDSTART_TRAIT) + var/obj/item/organ/genital/breasts/B = src.getorganslot("breasts") + if (B && B.fluid_mult < 0.5) + B.fluid_mult = 0.5 \ No newline at end of file diff --git a/modular_citadel/code/modules/arousal/organs/anus.dm b/modular_citadel/code/modules/arousal/organs/anus.dm index 11a31bc68..73cedd56d 100644 --- a/modular_citadel/code/modules/arousal/organs/anus.dm +++ b/modular_citadel/code/modules/arousal/organs/anus.dm @@ -36,6 +36,12 @@ size_name = "hefty" if(5) size_name = "godly" + if(6) + size_name = "gigantic" + if(7) + size_name = "unfathomably large" + if(8) + size_name = "absolute dumptruck" else size_name = "nonexistant" diff --git a/modular_citadel/code/modules/arousal/organs/genitals.dm b/modular_citadel/code/modules/arousal/organs/genitals.dm index ac28f594a..9edea910a 100644 --- a/modular_citadel/code/modules/arousal/organs/genitals.dm +++ b/modular_citadel/code/modules/arousal/organs/genitals.dm @@ -78,22 +78,21 @@ if("Always visible") through_clothes = TRUE hidden = FALSE - mode = "visible" + mode = GENITALS_VISIBLE if(!(src in owner.exposed_genitals)) owner.exposed_genitals += src if("Hidden by clothes") through_clothes = FALSE hidden = FALSE - mode = "clothes" + mode = GENITALS_CLOTHES if(src in owner.exposed_genitals) owner.exposed_genitals -= src if("Always hidden") through_clothes = FALSE hidden = TRUE - mode = "hidden" + mode = GENITALS_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() @@ -229,7 +228,7 @@ if(!getorganslot("belly")) var/obj/item/organ/genital/belly/B = new if(dna.features["belly_size"]) - B.size = dna.features["belly_size"]-1 + B.size = dna.features["belly_size"] B.Insert(src) if(B) if(dna.species.use_skintones && dna.features["genitals_use_skintone"]) @@ -478,32 +477,39 @@ genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]_[aroused_state]_[layertext]" colourcode = S.color_src - if(G.slot == "belly") //we have a different size system + if(G.slot == "belly") + size = round(size) genital_overlay.icon = 'hyperstation/icons/obj/genitals/belly.dmi' - genital_overlay.icon_state = "belly_[size]" + genital_overlay.icon_state = "belly_[size]_OTHER" colourcode = "belly_color" - //sizecheck added to prevent rendering blank icons - if(G.slot == "anus" && G.size > 0) //we have a different size system + //creates directional layering by rendering twice. + genital_overlay_directional.icon = 'hyperstation/icons/obj/genitals/belly.dmi' //Added directionals for larger bellies! + genital_overlay_directional.icon_state = "belly_[size]_NORTH" + genital_overlay_directional.layer = -GENITALS_BEHIND_LAYER + if(G.slot == "anus") + size = round(size) genital_overlay.icon = 'hyperstation/icons/obj/genitals/butt.dmi' - genital_overlay.icon_state = "butt_[round(size)]_OTHER" + genital_overlay.icon_state = "butt_[size]_OTHER" genital_overlay.layer = -ID_LAYER //in front of suit, behind bellies. //creates directional layering by rendering twice. North has higher layer priority to occlude hands. genital_overlay_directional.icon = 'hyperstation/icons/obj/genitals/butt.dmi' - genital_overlay_directional.icon_state = "butt_[round(size)]_NORTH" + genital_overlay_directional.icon_state = "butt_[size]_NORTH" genital_overlay_directional.layer = -NECK_LAYER colourcode = "butt_color" - if(use_skintones) //butts are forced a colour, either skin tones, or main colour. how ever, mutants use a darker version, because of their body tone. + if(use_skintones && H.dna.features["genitals_use_skintone"]) + //butts are forced a colour, either skin tones, or main colour. + //how ever, mutants use a darker version, because of their body tone. genital_overlay.color = "#[skintone2hex(H.skin_tone)]" - genital_overlay.icon_state = "butt_[round(size)]_OTHER" - genital_overlay_directional.icon_state = "butt_[round(size)]_NORTH" + genital_overlay.icon_state = "butt_[size]_OTHER" + genital_overlay_directional.icon_state = "butt_[size]_NORTH" else genital_overlay.color = "#[H.dna.features["mcolor"]]" - genital_overlay.icon_state = "butt_[round(size)]_OTHER_m" - genital_overlay_directional.icon_state = "butt_[round(size)]_NORTH_m" + genital_overlay.icon_state = "butt_[size]_OTHER_m" + genital_overlay_directional.icon_state = "butt_[size]_NORTH_m" if(S.center) @@ -514,21 +520,10 @@ if (colourtint) genital_overlay.color = "#[colourtint]" else - switch(colourcode) - if("cock_color") - genital_overlay.color = "#[H.dna.features["cock_color"]]" - if (colourtint) - genital_overlay.color = "#[colourtint]" - 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("belly_color") - genital_overlay.color = "#[H.dna.features["belly_color"]]" - if("butt_color") - genital_overlay.color = "#[H.dna.features["butt_color"]]" + if(colourcode != MUTCOLORS) + genital_overlay.color = "#[H.dna.features[colourcode]]" + if(colourcode == "cock_color" && colourtint) + genital_overlay.color = "#[colourtint]" standing += genital_overlay @@ -542,7 +537,7 @@ if (colourtint) genital_overlay_directional.color = "#[colourtint]" else - genital_overlay_directional.color = "#[H.dna.features["butt_color"]]" + genital_overlay_directional.color = "#[H.dna.features[colourcode]]" standing += genital_overlay_directional diff --git a/modular_citadel/code/modules/client/loadout/uniform.dm b/modular_citadel/code/modules/client/loadout/uniform.dm index ac65dfe85..fcc6846c2 100644 --- a/modular_citadel/code/modules/client/loadout/uniform.dm +++ b/modular_citadel/code/modules/client/loadout/uniform.dm @@ -523,4 +523,22 @@ name = "Tunic" category = SLOT_W_UNIFORM path = /obj/item/clothing/under/tunic + cost = 1 + +/datum/gear/overalls + name = "form fitting overalls" + category = SLOT_W_UNIFORM + path = /obj/item/clothing/under/raccveralls + cost = 1 + +/datum/gear/sexyoffice + name = "Revealing office uniform" + category = SLOT_W_UNIFORM + path = /obj/item/clothing/under/officesexy + cost = 1 + +/datum/gear/vaultsuit + name = "vault jumpsuit" + category = SLOT_W_UNIFORM + path = /obj/item/clothing/under/vaultsuit cost = 1 \ No newline at end of file diff --git a/modular_citadel/code/modules/client/preferences_savefile.dm b/modular_citadel/code/modules/client/preferences_savefile.dm index fae7e9e0d..3067e680a 100644 --- a/modular_citadel/code/modules/client/preferences_savefile.dm +++ b/modular_citadel/code/modules/client/preferences_savefile.dm @@ -2,9 +2,9 @@ //ipcs S["feature_ipc_screen"] >> features["ipc_screen"] S["feature_ipc_antenna"] >> features["ipc_antenna"] - features["ipc_screen"] = sanitize_inlist(features["ipc_screen"], GLOB.ipc_screens_list) features["ipc_antenna"] = sanitize_inlist(features["ipc_antenna"], GLOB.ipc_antennas_list) + //Citadel features["flavor_text"] = sanitize_text(features["flavor_text"], initial(features["flavor_text"])) features["ooc_text"] = sanitize_text(features["ooc_text"], initial(features["ooc_text"])) @@ -35,6 +35,7 @@ LAZYADD(chosen_gear, path) gear_points -= initial(path.cost) + /datum/preferences/proc/cit_character_pref_save(savefile/S) //ipcs WRITE_FILE(S["feature_ipc_screen"], features["ipc_screen"]) diff --git a/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm b/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm index 547c1a576..e3eab59c5 100644 --- a/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm +++ b/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm @@ -25,12 +25,12 @@ if(ismob(M) && M.canbearoused && arousal_gain != 0) var/orgasm = FALSE if(arousal_gain > 0) - if(M.getArousalLoss() >= 100 && ishuman(M) && M.has_dna()) + if(M.can_orgasm()) var/mob/living/carbon/human/H = M var/orgasm_message = pick("A sharp pulse of electricity pushes you to orgasm!", "You feel a jolt of electricity force you into orgasm!") H.visible_message("\The [assembly] electrodes shock [H]!", "[orgasm_message]") playsound(src, "sound/effects/light_flicker.ogg", 30, 1) - H.mob_climax(forced_climax=TRUE) + H.mob_climax_instant() orgasm = TRUE else M.adjustArousalLoss(arousal_gain) diff --git a/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm b/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm index c2f6f2e58..5002d4f52 100644 --- a/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm +++ b/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm @@ -2121,7 +2121,7 @@ datum/sprite_accessory/ears/human/gret /datum/sprite_accessory/antenna icon = 'modular_citadel/icons/mob/ipc_antennas.dmi' - color_src = MUTCOLORS2 + color_src = MUTCOLORS3 recommended_species = list("ipc") /datum/sprite_accessory/antenna/none diff --git a/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm b/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm index 2aa99b95a..fe0e28949 100644 --- a/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm +++ b/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm @@ -261,7 +261,7 @@ mutant_heart = /obj/item/organ/heart/ipc mutantlungs = /obj/item/organ/lungs/ipc mutantliver = /obj/item/organ/liver/ipc - mutantstomach = /obj/item/organ/stomach/ipc + mutantstomach = /obj/item/organ/stomach/synthliz mutanteyes = /obj/item/organ/eyes/ipc exotic_bloodtype = "SY" diff --git a/modular_citadel/code/modules/mob/living/carbon/human/species_types/ipc.dm b/modular_citadel/code/modules/mob/living/carbon/human/species_types/ipc.dm index f1f7c07a3..684ae707c 100644 --- a/modular_citadel/code/modules/mob/living/carbon/human/species_types/ipc.dm +++ b/modular_citadel/code/modules/mob/living/carbon/human/species_types/ipc.dm @@ -6,7 +6,7 @@ icon_limbs = DEFAULT_BODYPART_ICON_CITADEL blacklisted = 0 sexes = 0 - species_traits = list(MUTCOLORS,NOEYES,NOTRANSSTING) + species_traits = list(MUTCOLORS,NOEYES,NOTRANSSTING,NOMOUTH) inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID mutant_bodyparts = list("ipc_screen", "ipc_antenna") default_features = list("ipc_screen" = "Blank", "ipc_antenna" = "None") @@ -16,8 +16,9 @@ mutant_heart = /obj/item/organ/heart/ipc mutantlungs = /obj/item/organ/lungs/ipc mutantliver = /obj/item/organ/liver/ipc - mutantstomach = /obj/item/organ/stomach/ipc + mutantstomach = /obj/item/organ/stomach/cell mutanteyes = /obj/item/organ/eyes/ipc + mutant_organs = list(/obj/item/organ/cyberimp/arm/power_cord) // oracle port exotic_bloodtype = "HF" diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/enlargement.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/enlargement.dm index e0d8e8417..95f58780c 100644 --- a/modular_citadel/code/modules/reagents/chemistry/reagents/enlargement.dm +++ b/modular_citadel/code/modules/reagents/chemistry/reagents/enlargement.dm @@ -412,7 +412,7 @@ B = nB //If they have & opt in, increase size. if(H.client?.prefs.cit_toggles & ASS_ENLARGEMENT) - if(B.size < 5) //just make sure you dont break sprites + if(B.size < BUTT_MAX_SIZE) //just make sure you dont break sprites B.size = B.size + 0.05 B.update() ..() diff --git a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm index 324b44db3..30c528cc0 100644 --- a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm +++ b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm @@ -197,7 +197,7 @@ /datum/reagent/drug/aphrodisiacplus/overdose_process(mob/living/M) if(M && M.canbearoused && !(M.client?.prefs.cit_toggles & NO_APHRO) && prob(33)) - if(prob(5) && M.getArousalLoss() >= 100 && ishuman(M) && M.has_dna()) + if(prob(5) && M.can_orgasm()) if(prob(5)) //Less spam to_chat(M, "Your libido is going haywire!") if(M.min_arousal < 50) diff --git a/modular_citadel/icons/mob/ipc_antennas.dmi b/modular_citadel/icons/mob/ipc_antennas.dmi index 9f20aa442..77c4930ef 100644 Binary files a/modular_citadel/icons/mob/ipc_antennas.dmi and b/modular_citadel/icons/mob/ipc_antennas.dmi differ diff --git a/modular_citadel/icons/mob/ipc_screens.dmi b/modular_citadel/icons/mob/ipc_screens.dmi index c7d44da5a..938cfe555 100644 Binary files a/modular_citadel/icons/mob/ipc_screens.dmi and b/modular_citadel/icons/mob/ipc_screens.dmi differ diff --git a/modular_citadel/icons/mob/mutant_bodyparts.dmi b/modular_citadel/icons/mob/mutant_bodyparts.dmi index b4fad43ba..7b9b87f50 100644 Binary files a/modular_citadel/icons/mob/mutant_bodyparts.dmi and b/modular_citadel/icons/mob/mutant_bodyparts.dmi differ diff --git a/modular_citadel/icons/mob/suit_digi.dmi b/modular_citadel/icons/mob/suit_digi.dmi index 8a14afa11..48a956a9f 100644 Binary files a/modular_citadel/icons/mob/suit_digi.dmi and b/modular_citadel/icons/mob/suit_digi.dmi differ diff --git a/modular_citadel/icons/mob/uniform_digi.dmi b/modular_citadel/icons/mob/uniform_digi.dmi index ddc536dd1..221688ec0 100644 Binary files a/modular_citadel/icons/mob/uniform_digi.dmi and b/modular_citadel/icons/mob/uniform_digi.dmi differ diff --git a/modular_citadel/icons/obj/genitals/breasts_onmob.dmi b/modular_citadel/icons/obj/genitals/breasts_onmob.dmi index cd960abbc..e8573ec54 100644 Binary files a/modular_citadel/icons/obj/genitals/breasts_onmob.dmi and b/modular_citadel/icons/obj/genitals/breasts_onmob.dmi differ diff --git a/modular_citadel/icons/obj/genitals/hud.dmi b/modular_citadel/icons/obj/genitals/hud.dmi index bf2adcb1f..e090dbd84 100644 Binary files a/modular_citadel/icons/obj/genitals/hud.dmi and b/modular_citadel/icons/obj/genitals/hud.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 968494d99..3d8ea6179 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -50,6 +50,7 @@ #include "code\__DEFINES\food.dm" #include "code\__DEFINES\footsteps.dm" #include "code\__DEFINES\hud.dm" +#include "code\__DEFINES\hyper_arousal.dm" #include "code\__DEFINES\integrated_electronics.dm" #include "code\__DEFINES\interaction_flags.dm" #include "code\__DEFINES\inventory.dm" @@ -118,6 +119,7 @@ #include "code\__DEFINES\wires.dm" #include "code\__DEFINES\dcs\signals.dm" #include "code\__HELPERS\_cit_helpers.dm" +#include "code\__HELPERS\_hyper_helpers.dm" #include "code\__HELPERS\_lists.dm" #include "code\__HELPERS\_logging.dm" #include "code\__HELPERS\_string_lists.dm" @@ -508,6 +510,7 @@ #include "code\datums\looping_sounds\weather.dm" #include "code\datums\martial\boxing.dm" #include "code\datums\martial\cqc.dm" +#include "code\datums\martial\kinaris.dm" #include "code\datums\martial\krav_maga.dm" #include "code\datums\martial\mushpunch.dm" #include "code\datums\martial\plasma_fist.dm" @@ -981,6 +984,7 @@ #include "code\game\objects\items\implants\implant_exile.dm" #include "code\game\objects\items\implants\implant_explosive.dm" #include "code\game\objects\items\implants\implant_freedom.dm" +#include "code\game\objects\items\implants\implant_kinaris.dm" #include "code\game\objects\items\implants\implant_krav_maga.dm" #include "code\game\objects\items\implants\implant_mindshield.dm" #include "code\game\objects\items\implants\implant_misc.dm" @@ -3097,16 +3101,21 @@ #include "hyperstation\code\datums\components\crafting\bounties.dm" #include "hyperstation\code\datums\components\crafting\recipes.dm" #include "hyperstation\code\datums\elements\holder_micro.dm" +#include "hyperstation\code\datums\elements\spooky.dm" #include "hyperstation\code\datums\mood_events\events.dm" #include "hyperstation\code\datums\ruins\lavaland.dm" #include "hyperstation\code\datums\traits\good.dm" #include "hyperstation\code\datums\traits\neutral.dm" #include "hyperstation\code\datums\weather\lithium_aurora.dm" #include "hyperstation\code\datums\weather\oxygen_rain.dm" +#include "hyperstation\code\effects\lighting.dm" #include "hyperstation\code\game\machinery\lore_terminal.dm" #include "hyperstation\code\game\objects\railings.dm" +#include "hyperstation\code\game\objects\items\cards.dm" #include "hyperstation\code\game\objects\items\coconutcannon.dm" #include "hyperstation\code\game\objects\items\cosmetics.dm" +#include "hyperstation\code\game\objects\items\lovedice.dm" +#include "hyperstation\code\game\objects\items\stacks\tiles\tile_types.dm" #include "hyperstation\code\game\objects\items\storage\big_bag.dm" #include "hyperstation\code\game\objects\structures\bench.dm" #include "hyperstation\code\game\objects\structures\ghost_role_spawners.dm" @@ -3138,6 +3147,8 @@ #include "hyperstation\code\modules\antagonists\werewolf\werewolf.dm" #include "hyperstation\code\modules\arousal\arousalhud.dm" #include "hyperstation\code\modules\arousal\creampie.dm" +#include "hyperstation\code\modules\arousal\expansion.dm" +#include "hyperstation\code\modules\arousal\organs\genitals.dm" #include "hyperstation\code\modules\busy_space\air_traffic.dm" #include "hyperstation\code\modules\busy_space\atc_controller.dm" #include "hyperstation\code\modules\busy_space\loremaster.dm" @@ -3148,6 +3159,8 @@ #include "hyperstation\code\modules\cargo\sweatshop\metal.dm" #include "hyperstation\code\modules\cargo\sweatshop\sweatshop.dm" #include "hyperstation\code\modules\cargo\sweatshop\wooden.dm" +#include "hyperstation\code\modules\client\hyper_pref_savefile.dm" +#include "hyperstation\code\modules\client\loadout\mask.dm" #include "hyperstation\code\modules\client\loadout\glasses.dm" #include "hyperstation\code\modules\client\loadout\gloves.dm" #include "hyperstation\code\modules\client\loadout\neck.dm" @@ -3164,26 +3177,34 @@ #include "hyperstation\code\modules\events\crystalline_reentry.dm" #include "hyperstation\code\modules\events\crystalloid_entities.dm" #include "hyperstation\code\modules\food_and_drinks\recipes\drinks_recipes.dm" +#include "hyperstation\code\modules\hydroponics\grown\kalyna.dm" #include "hyperstation\code\modules\integrated_electronics\input.dm" #include "hyperstation\code\modules\jobs\job_types\roleplay.dm" #include "hyperstation\code\modules\mining\shelters.dm" #include "hyperstation\code\modules\mining\equipment\survival_pod.dm" #include "hyperstation\code\modules\mob\mob_helpers.dm" +#include "hyperstation\code\modules\mob\dead\new_player\cosmetic_parts.dm" #include "hyperstation\code\modules\mob\dead\new_player\sprite_accessories.dm" #include "hyperstation\code\modules\mob\living\status_indicators.dm" #include "hyperstation\code\modules\mob\living\carbon\carbon.dm" +#include "hyperstation\code\modules\mob\living\carbon\human\cosmetic_part_handler.dm" +#include "hyperstation\code\modules\mob\living\carbon\human\species_types\ipc.dm" #include "hyperstation\code\modules\mob\living\silicon\robot\robot.dm" #include "hyperstation\code\modules\mob\living\silicon\robot\robot_modules.dm" +#include "hyperstation\code\modules\mob\living\simple_animal\friendly\chicken_types.dm" +#include "hyperstation\code\modules\mob\living\simple_animal\friendly\farm_animals.dm" #include "hyperstation\code\modules\patreon\patreon.dm" #include "hyperstation\code\modules\power\reactor\fluffed.dm" #include "hyperstation\code\modules\power\reactor\fuel_rods.dm" #include "hyperstation\code\modules\power\reactor\rbmk.dm" #include "hyperstation\code\modules\power\reactor\reactor_cargo.dm" +#include "hyperstation\code\modules\reagents\chemistry\reagents\alcohol_reagents.dm" #include "hyperstation\code\modules\reagents\chemistry\reagents\drink_reagents.dm" #include "hyperstation\code\modules\reagents\chemistry\reagents\food_reagents.dm" #include "hyperstation\code\modules\reagents\chemistry\reagents\hydroponics_reactions.dm" #include "hyperstation\code\modules\reagents\chemistry\reagents\medicine_reagents.dm" #include "hyperstation\code\modules\reagents\chemistry\recipes\medicine.dm" +#include "hyperstation\code\modules\research\designs\medical_designs.dm" #include "hyperstation\code\modules\resize\resize_action.dm" #include "hyperstation\code\modules\resize\resizing.dm" #include "hyperstation\code\modules\resize\sizechems.dm" @@ -3197,6 +3218,7 @@ #include "hyperstation\code\obj\economy.dm" #include "hyperstation\code\obj\fleshlight.dm" #include "hyperstation\code\obj\fluff.dm" +#include "hyperstation\code\obj\jacuzzi.dm" #include "hyperstation\code\obj\kinkyclothes.dm" #include "hyperstation\code\obj\leash.dm" #include "hyperstation\code\obj\lunaritems.dm" @@ -3210,6 +3232,7 @@ #include "hyperstation\code\obj\sounding.dm" #include "hyperstation\code\obj\stargate_clothing.dm" #include "hyperstation\code\obj\vibrator.dm" +#include "hyperstation\code\obj\farming\farming.dm" #include "hyperstation\code\obj\items\lorebooks.dm" #include "hyperstation\code\obj\SYSuit\abilities.dm" #include "hyperstation\code\obj\SYSuit\main.dm" diff --git a/tgui/packages/tgui/interfaces/Cards/CardStatus.js b/tgui/packages/tgui/interfaces/Cards/CardStatus.js new file mode 100644 index 000000000..4885a0412 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Cards/CardStatus.js @@ -0,0 +1,27 @@ +import { useBackend } from '../../backend'; +import { Box, Section } from '../../components'; + +export const CardStatus = (props, context) => { + const { data } = useBackend(context); + const { face_up, rotation, cards, name } = data; + const { noRotation = false, notOrdered = false, children } = props; + + return ( +
+ + This {name} contains {cards.length} cards. + + {!noRotation && ( + + The deck is {rotation} and turned {face_up ? "face-up" : "face-down"}. + + )} + {!notOrdered & !!face_up && cards.length > 0 && ( + + You can see a {cards[cards.length - 1].name} on top. + + ) || null} + {...children} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Cards/CardsControlList.js b/tgui/packages/tgui/interfaces/Cards/CardsControlList.js new file mode 100644 index 000000000..3713cda75 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Cards/CardsControlList.js @@ -0,0 +1,82 @@ +import { useBackend, useLocalState } from '../../backend'; +import { Box, Button, Dropdown, LabeledList, NumberInput } from '../../components'; + +export const CardsControlList = (props, context) => { + const { data, act } = useBackend(context); + const { face_up, rotation, cards, possible_rotations } = data; + const { flip, rotate, draw, deal, shuffle } = props; + + const [drawCount, setDrawCount] = useLocalState(context, 'drawCount', 1); + const [dealCardsCount, setDealCardsCount] = useLocalState(context, 'dealCards', 1); + const [dealHandsCount, setDealHandsCount] = useLocalState(context, 'dealHands', 1); + const [selectedCards, setSelectedCards] = useLocalState(context, 'selectedCards', []); + + return ( + + {!!flip && ( + +