diff --git a/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm b/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm index d01e4f8da1..37a89ba2ff 100644 --- a/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm +++ b/_maps/RandomRuins/AnywhereRuins/golem_ship.dmm @@ -18,9 +18,7 @@ /obj/item/mining_scanner, /obj/item/flashlight/lantern, /obj/item/card/id/mining, -/obj/item/gps/mining{ - tracking = 0 - }, +/obj/item/gps/mining, /turf/open/floor/plating, /area/ruin/powered/golem_ship) "d" = ( @@ -34,9 +32,7 @@ /obj/item/mining_scanner, /obj/item/flashlight/lantern, /obj/item/card/id/mining, -/obj/item/gps/mining{ - tracking = 0 - }, +/obj/item/gps/mining, /turf/open/floor/plating, /area/ruin/powered/golem_ship) "e" = ( @@ -156,9 +152,7 @@ "x" = ( /obj/structure/table/wood, /obj/machinery/reagentgrinder, -/obj/item/gps/mining{ - tracking = 0 - }, +/obj/item/gps/mining, /turf/open/floor/mineral/titanium/purple, /area/ruin/powered/golem_ship) "z" = ( diff --git a/_maps/RandomRuins/SpaceRuins/arcade.dmm b/_maps/RandomRuins/SpaceRuins/arcade.dmm index 74bdc9a7b8..c68d3b224e 100644 --- a/_maps/RandomRuins/SpaceRuins/arcade.dmm +++ b/_maps/RandomRuins/SpaceRuins/arcade.dmm @@ -3,7 +3,7 @@ /turf/template_noop, /area/template_noop) "b" = ( -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/airless, /area/ruin/powered) "c" = ( /turf/closed/mineral/random/high_chance, @@ -135,7 +135,7 @@ /area/ruin/powered) "F" = ( /obj/structure/closet/crate/trashcart, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/airless, /area/ruin/powered) "G" = ( /obj/structure/chair/sofa/right, diff --git a/_maps/RandomRuins/SpaceRuins/augmentationfacility.dmm b/_maps/RandomRuins/SpaceRuins/augmentationfacility.dmm index e23beb3407..2a25a3b6e7 100644 --- a/_maps/RandomRuins/SpaceRuins/augmentationfacility.dmm +++ b/_maps/RandomRuins/SpaceRuins/augmentationfacility.dmm @@ -3,7 +3,7 @@ /turf/template_noop, /area/template_noop) "b" = ( -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/airless, /area/ruin/powered) "c" = ( /turf/closed/mineral/random/high_chance, @@ -176,8 +176,8 @@ /obj/structure/table, /obj/effect/decal/cleanable/dirt, /obj/item/autosurgeon{ - name = "rusted autosurgeon"; desc = "A device that automatically inserts an implant or organ into the user without the hassle of extensive surgery. It has a slot to insert an organ of implant. But this rusted version looks like it could only manage one implant...."; + name = "rusted autosurgeon"; uses = 1 }, /turf/open/floor/plasteel, @@ -205,7 +205,7 @@ /obj/machinery/atmospherics/components/unary/outlet_injector/on/layer1{ dir = 4 }, -/turf/open/floor/plating/asteroid, +/turf/open/floor/plating/asteroid/airless, /area/ruin/powered) "K" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer1{ diff --git a/_maps/RandomRuins/SpaceRuinsStation/roid6.dmm b/_maps/RandomRuins/SpaceRuinsStation/roid6.dmm index 56967b0c37..1bc497d4f9 100644 --- a/_maps/RandomRuins/SpaceRuinsStation/roid6.dmm +++ b/_maps/RandomRuins/SpaceRuinsStation/roid6.dmm @@ -80,7 +80,7 @@ /obj/item/shard{ icon_state = "medium" }, -/turf/open/floor/plating/airless{ +/turf/open/floor/plating{ icon_state = "platingdmg2" }, /area/ruin/space/has_grav) diff --git a/_maps/RandomZLevels/VR/murderdome.dmm b/_maps/RandomZLevels/VR/murderdome.dmm index 740f9fd09f..5b5cbbfcfe 100644 --- a/_maps/RandomZLevels/VR/murderdome.dmm +++ b/_maps/RandomZLevels/VR/murderdome.dmm @@ -232,7 +232,9 @@ /area/awaymission/vr/murderdome) "H" = ( /obj/machinery/telecomms/allinone, -/turf/open/indestructible, +/turf/open/indestructible{ + initial_gas_mix = "TEMP=2.7" + }, /area/awaymission/vr/murderdome) "R" = ( /obj/effect/spawner/structure/window/reinforced/indestructable, diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index f9ff2a594c..264fdb1058 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -4879,6 +4879,10 @@ dir = 4 }, /obj/machinery/holopad, +/obj/machinery/door/poddoor/shutters/preopen{ + id = "hopqueue"; + name = "HoP Queue Shutters" + }, /turf/open/floor/plasteel, /area/hallway/primary/central) "aiW" = ( @@ -6467,6 +6471,12 @@ }, /turf/open/floor/plasteel/white, /area/science/xenobiology) +"alX" = ( +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/turf/open/floor/plasteel/white, +/area/medical/chemistry) "alY" = ( /obj/structure/cable{ icon_state = "1-2" @@ -8069,6 +8079,13 @@ }, /turf/open/floor/plasteel/showroomfloor, /area/security/warden) +"apI" = ( +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/obj/structure/closet/secure_closet/medical1, +/turf/open/floor/plasteel/white, +/area/medical/medbay/central) "apJ" = ( /turf/closed/wall, /area/construction/mining/aux_base) @@ -9381,6 +9398,13 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/construction/mining/aux_base) +"asG" = ( +/obj/structure/disposalpipe/trunk{ + dir = 1 + }, +/obj/machinery/disposal/bin, +/turf/open/floor/plasteel/white, +/area/medical/medbay/central) "asH" = ( /obj/structure/closet/toolcloset, /obj/effect/turf_decal/tile/yellow{ @@ -9424,6 +9448,35 @@ }, /turf/open/floor/plating, /area/maintenance/port/fore) +"asL" = ( +/obj/machinery/light{ + dir = 8 + }, +/obj/machinery/firealarm{ + dir = 4; + pixel_x = -24 + }, +/obj/item/reagent_containers/spray/cleaner, +/obj/item/crowbar, +/obj/item/clothing/neck/stethoscope, +/obj/item/storage/box/gloves{ + pixel_x = 3; + pixel_y = 4 + }, +/obj/item/storage/box/masks, +/obj/structure/table, +/turf/open/floor/plasteel/white, +/area/medical/sleeper) +"asM" = ( +/obj/structure/table, +/obj/item/crowbar, +/obj/item/clothing/neck/stethoscope, +/obj/item/reagent_containers/spray/cleaner, +/obj/structure/sign/warning/nosmoking{ + pixel_y = 30 + }, +/turf/open/floor/plasteel/dark, +/area/medical/sleeper) "asN" = ( /obj/machinery/airalarm{ dir = 4; @@ -9453,12 +9506,66 @@ /obj/structure/closet/firecloset, /turf/open/floor/plating, /area/maintenance/fore) +"asT" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/medical/medbay/central) +"asU" = ( +/obj/structure/sign/warning/nosmoking{ + pixel_y = 30 + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/obj/machinery/door/airlock/medical/glass{ + id_tag = "MedbayFoyer"; + name = "Medbay"; + req_access_txt = "5" + }, +/turf/open/floor/plasteel/white, +/area/medical/medbay/central) +"asV" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/medical/medbay/central) "asW" = ( /obj/machinery/light/small{ dir = 8 }, /turf/open/floor/plasteel/grimy, /area/security/detectives_office) +"asX" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/obj/machinery/door/airlock/medical/glass{ + id_tag = "MedbayFoyer"; + name = "Medbay"; + req_access_txt = "5" + }, +/turf/open/floor/plasteel/white, +/area/medical/medbay/central) +"asY" = ( +/obj/structure/sign/warning/nosmoking{ + pixel_y = 30 + }, +/turf/open/floor/plasteel, +/area/hallway/primary/central) "ata" = ( /turf/open/floor/wood, /area/lawoffice) @@ -24467,12 +24574,6 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/closed/wall/r_wall, /area/engine/gravity_generator) -"bgP" = ( -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/turf/open/floor/plasteel/white, -/area/medical/chemistry) "bgQ" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 8 @@ -28210,12 +28311,6 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel/white, /area/medical/medbay/central) -"bpy" = ( -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/turf/open/floor/plasteel/white, -/area/medical/medbay/central) "bpz" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 8 @@ -28704,13 +28799,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/plasteel, /area/hallway/primary/central) -"bqM" = ( -/obj/effect/spawner/structure/window/reinforced, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, -/turf/open/floor/plating, -/area/medical/medbay/central) "bqN" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 5 @@ -28743,22 +28831,6 @@ }, /turf/open/floor/plasteel/white, /area/medical/medbay/central) -"bqR" = ( -/obj/structure/table, -/obj/item/crowbar, -/obj/item/clothing/neck/stethoscope, -/obj/item/reagent_containers/spray/cleaner, -/obj/structure/sign/warning/nosmoking{ - pixel_y = 30 - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 - }, -/turf/open/floor/plasteel/white, -/area/medical/medbay/central) "bqS" = ( /obj/structure/cable{ icon_state = "4-8" @@ -29261,13 +29333,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/crew_quarters/heads/hop) -"brV" = ( -/obj/effect/spawner/structure/window/reinforced, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 4 - }, -/turf/open/floor/plating, -/area/medical/medbay/central) "brW" = ( /obj/structure/cable{ icon_state = "4-8" @@ -29275,18 +29340,6 @@ /obj/machinery/vending/cart, /turf/open/floor/plasteel, /area/crew_quarters/heads/hop) -"brX" = ( -/obj/structure/table, -/obj/item/storage/box/masks, -/obj/item/storage/box/gloves{ - pixel_x = 3; - pixel_y = 4 - }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 4 - }, -/turf/open/floor/plasteel/white, -/area/medical/medbay/central) "brY" = ( /obj/structure/disposalpipe/segment{ dir = 6 @@ -31211,23 +31264,10 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, /area/medical/medbay/central) -"bwy" = ( -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/turf/open/floor/plasteel/white, -/area/medical/medbay/central) "bwz" = ( /obj/structure/disposalpipe/segment, /turf/open/floor/plasteel/white, /area/medical/medbay/central) -"bwA" = ( -/obj/machinery/disposal/bin, -/obj/structure/disposalpipe/trunk{ - dir = 8 - }, -/turf/open/floor/plasteel/white, -/area/medical/medbay/central) "bwB" = ( /obj/structure/chair, /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ @@ -32967,17 +33007,6 @@ }, /turf/open/floor/plating, /area/maintenance/port/aft) -"bAp" = ( -/obj/structure/closet/secure_closet/medical1, -/obj/machinery/light{ - dir = 8 - }, -/obj/machinery/firealarm{ - dir = 4; - pixel_x = -24 - }, -/turf/open/floor/plasteel/white, -/area/medical/sleeper) "bAq" = ( /obj/machinery/sleeper{ dir = 8 @@ -55994,6 +56023,10 @@ }, /turf/open/floor/plasteel, /area/crew_quarters/theatre) +"kEm" = ( +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/plating, +/area/maintenance/port/aft) "kEY" = ( /obj/effect/landmark/stationroom/box/engine, /turf/open/space/basic, @@ -56556,7 +56589,6 @@ }, /area/maintenance/bar) "mqZ" = ( -/obj/structure/reagent_dispensers/keg/aphro/strong, /obj/item/reagent_containers/glass/beaker, /turf/open/floor/plating, /area/maintenance/bar) @@ -58122,6 +58154,10 @@ /obj/effect/decal/cleanable/blood/old, /turf/open/floor/plating, /area/maintenance/fore) +"rtU" = ( +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/starboard/fore) "ruo" = ( /obj/structure/cable{ icon_state = "4-8" @@ -85306,7 +85342,7 @@ bVI bTA xgk bHE -bHE +kEm bHE bHE bHE @@ -92212,7 +92248,7 @@ btJ buV bws bqH -aJq +asY aJq byW bCv @@ -94263,8 +94299,8 @@ bfF bfF bfF bfF -bqM -brV +asT +asV bof bwv bvj @@ -94520,8 +94556,8 @@ bmF bob bnB bfF -bqR -brX +asU +asX bof bwx bvj @@ -95028,7 +95064,7 @@ aYV bfF bhc bip -bgP +alX bjL bkL bmT @@ -95294,7 +95330,7 @@ bpF bqS brY bwz -bwy +asG bvj bza bxb @@ -95550,10 +95586,10 @@ bpt bfF bqV bEe +bhh bBL -bwA bvj -bAl +asM bAl bvh bzS @@ -96068,7 +96104,7 @@ bvh ajv bxN bze -bAp +asL bvh bCG bBd @@ -98116,7 +98152,7 @@ bjX blp bmO bhi -bpy +apI bwz brg btZ @@ -104251,7 +104287,7 @@ anf anf alP alP -anf +rtU alP alP alP diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 61ecaddddc..1b7b9c7414 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -126013,7 +126013,6 @@ /turf/open/floor/plating, /area/chapel/office) "ijB" = ( -/obj/structure/reagent_dispensers/keg/aphro/strong, /obj/item/reagent_containers/glass/beaker, /turf/open/floor/plating, /area/crew_quarters/abandoned_gambling_den) @@ -126385,6 +126384,13 @@ }, /turf/open/space, /area/engine/atmos) +"kqo" = ( +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/port/fore) "ktK" = ( /obj/effect/turf_decal/tile/neutral{ dir = 4 @@ -127043,6 +127049,10 @@ dir = 1 }, /area/science/circuit) +"pok" = ( +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/theatre/abandoned) "poI" = ( /obj/structure/bed, /obj/item/tank/internals/anesthetic, @@ -164028,7 +164038,7 @@ aof aky aky aky -arD +kqo asZ aui avF @@ -178787,7 +178797,7 @@ dCX dEj dFq dGJ -dGJ +pok dJy dGJ dGJ diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index fbe5b954ff..e848fd87d1 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -78498,6 +78498,7 @@ icon_state = "4-8" }, /obj/machinery/atmospherics/pipe/manifold/supply/hidden, +/mob/living/simple_animal/opossum/poppy, /turf/open/floor/plating{ icon_state = "platingdmg3" }, @@ -84902,6 +84903,11 @@ /obj/machinery/light, /turf/open/floor/plating/airless, /area/hallway/secondary/entry) +"jGN" = ( +/obj/effect/decal/cleanable/dirt, +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/starboard/aft) "jHJ" = ( /obj/machinery/atmospherics/pipe/simple/general/visible{ dir = 9 @@ -125028,7 +125034,7 @@ bAP bCf bhQ bhQ -bAM +jGN bGH bOc bHZ diff --git a/_maps/map_files/LambdaStation/lambda.dmm b/_maps/map_files/LambdaStation/lambda.dmm index 4d2d8cb624..47d8dd9cb4 100644 --- a/_maps/map_files/LambdaStation/lambda.dmm +++ b/_maps/map_files/LambdaStation/lambda.dmm @@ -44383,7 +44383,6 @@ /turf/open/floor/plasteel/white, /area/hallway/primary/aft) "bFB" = ( -/obj/structure/reagent_dispensers/keg/aphro/strong, /obj/effect/decal/cleanable/dirt, /turf/open/floor/wood{ icon_state = "wood-broken5" @@ -80286,6 +80285,11 @@ }, /turf/open/space/basic, /area/engine/engineering) +"eCO" = ( +/obj/effect/decal/cleanable/dirt, +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/plasteel/dark, +/area/maintenance/starboard/aft) "eDP" = ( /obj/effect/turf_decal/trimline/blue/filled/corner{ dir = 8 @@ -82039,6 +82043,10 @@ }, /turf/open/floor/plasteel/cafeteria, /area/crew_quarters/kitchen) +"vDO" = ( +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/fore) "vDW" = ( /obj/structure/cable{ icon_state = "1-2" @@ -107579,7 +107587,7 @@ aaA abY ayy acw -acw +vDO acw acw aeG @@ -135198,7 +135206,7 @@ bPp ccJ clR bQh -clN +eCO bPp bQh bYx diff --git a/_maps/map_files/Mafia/mafia_ayylmao.dmm b/_maps/map_files/Mafia/mafia_ayylmao.dmm new file mode 100644 index 0000000000..5d6f2aec5f --- /dev/null +++ b/_maps/map_files/Mafia/mafia_ayylmao.dmm @@ -0,0 +1,768 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"b" = ( +/turf/closed/indestructible{ + desc = "A wall with alien alloy plating."; + icon = 'icons/turf/walls/abductor_wall.dmi'; + icon_state = "abductor"; + name = "alien wall"; + smooth = 5 + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/fakespace, +/area/mafia) +"j" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien22" + }, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/structure/closet/abductor, +/turf/open/floor/plating/abductor, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/obj/structure/bed/abductor, +/turf/open/floor/plating/abductor, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/obj/structure/bed/abductor, +/turf/open/floor/plating/abductor2, +/area/mafia) +"o" = ( +/obj/structure/closet/abductor, +/turf/open/floor/plating/abductor2, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/closed/indestructible/fakeglass, +/area/mafia) +"q" = ( +/turf/open/floor/plating/abductor, +/area/mafia) +"r" = ( +/turf/open/floor/plating/abductor2, +/area/mafia) +"t" = ( +/turf/open/floor/light/colour_cycle/dancefloor_a, +/area/mafia) +"u" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/turf/open/floor/light/colour_cycle/dancefloor_b, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"x" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/obj/effect/spawner/structure/window/plastitanium, +/turf/open/floor/plating/abductor2, +/area/mafia) +"z" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/fakespace, +/area/mafia) +"B" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien2" + }, +/area/mafia) +"D" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien15" + }, +/area/mafia) +"E" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien7" + }, +/area/mafia) +"F" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien12" + }, +/area/mafia) +"G" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien17" + }, +/area/mafia) +"H" = ( +/turf/open/space/basic, +/area/mafia) +"I" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien24" + }, +/area/mafia) +"J" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien18" + }, +/area/mafia) +"K" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien11" + }, +/area/mafia) +"L" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien14" + }, +/area/mafia) +"M" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien4" + }, +/area/mafia) +"N" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien21" + }, +/area/mafia) +"P" = ( +/turf/closed/indestructible/abductor, +/area/mafia) +"Q" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien5" + }, +/area/mafia) +"S" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien3" + }, +/area/mafia) +"U" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien23" + }, +/area/mafia) +"V" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien13" + }, +/area/mafia) +"W" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien20" + }, +/area/mafia) +"Y" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien8" + }, +/area/mafia) +"Z" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien10" + }, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(3,1,1) = {" +a +b +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +b +a +"} +(4,1,1) = {" +a +b +i +i +g +z +i +i +i +i +i +i +i +i +i +H +i +z +k +i +i +b +a +"} +(5,1,1) = {" +a +b +i +i +z +i +i +i +W +L +F +F +F +Z +P +i +i +i +h +i +i +b +a +"} +(6,1,1) = {" +a +b +i +i +i +W +L +Z +G +l +q +b +r +o +E +L +Z +P +i +i +i +b +a +"} +(7,1,1) = {" +a +b +i +i +i +N +o +r +p +m +q +x +r +n +x +q +l +B +i +i +i +b +a +"} +(8,1,1) = {" +a +b +i +i +i +U +n +r +b +b +q +b +r +b +b +q +m +M +i +i +i +b +a +"} +(9,1,1) = {" +a +b +i +i +W +G +x +r +r +x +q +x +r +x +q +q +x +E +P +i +i +b +a +"} +(10,1,1) = {" +a +b +i +i +N +l +x +b +x +x +x +x +x +x +x +b +x +o +B +i +i +b +a +"} +(11,1,1) = {" +a +b +i +i +j +m +q +q +q +x +t +t +t +x +r +r +r +n +S +i +i +b +a +"} +(12,1,1) = {" +a +b +i +i +j +b +x +b +x +x +t +u +t +x +x +b +x +b +S +i +i +b +a +"} +(13,1,1) = {" +a +b +i +i +j +n +r +r +r +x +t +t +t +x +q +q +q +m +S +i +i +b +a +"} +(14,1,1) = {" +a +b +i +i +U +o +x +b +x +x +x +x +x +x +x +b +x +l +M +i +i +b +a +"} +(15,1,1) = {" +a +b +i +i +I +J +x +q +q +x +r +x +q +x +r +r +x +Y +Q +i +i +b +a +"} +(16,1,1) = {" +a +b +i +i +i +N +m +q +b +b +r +b +q +b +b +r +n +B +i +i +i +b +a +"} +(17,1,1) = {" +a +b +i +i +i +U +l +q +x +n +r +x +q +m +x +r +o +M +i +i +i +b +a +"} +(18,1,1) = {" +a +b +i +i +i +I +D +K +J +o +r +b +q +l +Y +D +K +Q +i +i +i +b +a +"} +(19,1,1) = {" +a +b +i +i +z +i +i +i +I +D +V +V +V +K +Q +i +i +i +z +i +i +b +a +"} +(20,1,1) = {" +a +b +i +i +k +z +i +i +i +i +i +i +i +i +i +i +i +z +v +i +i +b +a +"} +(21,1,1) = {" +a +b +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +b +a +"} +(22,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_ball.dmm b/_maps/map_files/Mafia/mafia_ball.dmm new file mode 100644 index 0000000000..6ca955726b --- /dev/null +++ b/_maps/map_files/Mafia/mafia_ball.dmm @@ -0,0 +1,675 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"c" = ( +/turf/closed/wall/rust, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/obj/machinery/door/airlock/maintenance_hatch{ + max_integrity = 99999; + name = "Maintenance" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plating, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/structure/closet/cabinet{ + anchored = 1 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/dark, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel, +/area/mafia) +"o" = ( +/obj/structure/closet/cabinet{ + anchored = 1 + }, +/turf/open/floor/plasteel, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/closed/indestructible/fakeglass, +/area/mafia) +"q" = ( +/turf/open/floor/plasteel/dark, +/area/mafia) +"r" = ( +/turf/open/floor/plasteel, +/area/mafia) +"s" = ( +/turf/open/floor/fakespace, +/area/mafia) +"t" = ( +/turf/open/floor/plasteel/solarpanel, +/area/mafia) +"u" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/turf/open/floor/plasteel/solarpanel, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(3,1,1) = {" +a +a +a +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +a +a +a +"} +(4,1,1) = {" +a +a +a +c +g +h +d +d +d +d +d +e +d +d +d +d +d +h +k +c +a +a +a +"} +(5,1,1) = {" +a +a +a +c +h +d +d +e +a +a +j +a +j +a +a +d +d +i +h +c +a +a +a +"} +(6,1,1) = {" +a +a +a +c +i +a +a +j +a +l +q +a +r +o +a +j +a +a +d +c +a +a +a +"} +(7,1,1) = {" +a +a +c +c +d +a +o +r +p +m +q +p +r +n +p +q +l +a +d +c +c +a +a +"} +(8,1,1) = {" +a +a +c +d +d +a +n +r +a +a +q +a +r +a +a +q +m +a +d +d +c +a +a +"} +(9,1,1) = {" +a +a +c +d +a +a +p +r +r +p +q +p +r +p +q +q +p +a +a +d +c +a +a +"} +(10,1,1) = {" +a +a +c +d +a +l +p +a +p +p +p +p +p +p +p +a +p +o +a +d +c +a +a +"} +(11,1,1) = {" +a +a +c +d +j +m +q +q +q +p +s +t +s +p +r +r +r +n +j +e +c +a +a +"} +(12,1,1) = {" +a +a +c +e +a +a +p +a +p +p +t +u +t +p +p +a +p +a +a +d +c +a +a +"} +(13,1,1) = {" +a +a +c +d +j +n +r +r +r +p +s +t +s +p +q +q +q +m +j +d +c +a +a +"} +(14,1,1) = {" +a +a +c +d +a +o +p +a +p +p +p +p +p +p +p +a +p +l +a +i +c +a +a +"} +(15,1,1) = {" +a +a +c +d +a +a +p +q +q +p +r +p +q +p +r +r +p +a +a +d +c +a +a +"} +(16,1,1) = {" +a +a +c +f +d +a +m +q +a +a +r +a +q +a +a +r +n +a +d +d +c +a +a +"} +(17,1,1) = {" +a +a +c +c +d +a +l +q +p +n +r +p +q +m +p +r +o +a +d +c +c +a +a +"} +(18,1,1) = {" +a +a +a +c +d +a +a +j +a +o +r +a +q +l +a +j +a +a +d +c +a +a +a +"} +(19,1,1) = {" +a +a +a +c +h +i +d +d +a +a +j +a +j +a +a +d +e +d +h +c +a +a +a +"} +(20,1,1) = {" +a +a +a +c +k +h +d +d +e +d +d +d +d +d +d +d +d +h +v +c +a +a +a +"} +(21,1,1) = {" +a +a +a +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +a +a +a +"} +(22,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_gothic.dmm b/_maps/map_files/Mafia/mafia_gothic.dmm new file mode 100644 index 0000000000..a3c19fee85 --- /dev/null +++ b/_maps/map_files/Mafia/mafia_gothic.dmm @@ -0,0 +1,724 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"b" = ( +/turf/closed/wall/mineral/iron, +/area/mafia) +"c" = ( +/turf/closed/wall/rust, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/turf/closed/indestructible/fakedoor, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/structure/closet/crate/coffin, +/turf/open/floor/carpet/red, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/carpet/red, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/chapel{ + dir = 1 + }, +/area/mafia) +"o" = ( +/obj/structure/closet/crate/coffin, +/turf/open/floor/plasteel/chapel{ + dir = 4 + }, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/obj/effect/spawner/structure/window/plastitanium, +/turf/open/space/basic, +/area/mafia) +"q" = ( +/turf/open/floor/carpet/red, +/area/mafia) +"r" = ( +/turf/open/floor/plasteel/chapel{ + dir = 8 + }, +/area/mafia) +"t" = ( +/turf/open/floor/carpet/royalblack, +/area/mafia) +"u" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/turf/open/floor/carpet/royalblack, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"D" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/chapel{ + dir = 4 + }, +/area/mafia) +"E" = ( +/turf/open/floor/plasteel/chapel{ + dir = 4 + }, +/area/mafia) +"H" = ( +/obj/structure/closet/cabinet{ + anchored = 1 + }, +/turf/open/floor/carpet/red, +/area/mafia) +"J" = ( +/obj/structure/closet/crate/coffin, +/turf/open/floor/plasteel/chapel{ + dir = 1 + }, +/area/mafia) +"M" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/chapel{ + dir = 8 + }, +/area/mafia) +"N" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/chapel, +/area/mafia) +"O" = ( +/turf/open/floor/plasteel/chapel, +/area/mafia) +"P" = ( +/obj/structure/closet/crate/coffin, +/turf/open/floor/plasteel/chapel, +/area/mafia) +"Q" = ( +/obj/structure/closet/crate/coffin, +/turf/open/floor/plasteel/chapel{ + dir = 8 + }, +/area/mafia) +"X" = ( +/turf/open/floor/plasteel/chapel{ + dir = 1 + }, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(3,1,1) = {" +a +b +b +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +b +b +a +"} +(4,1,1) = {" +a +b +b +c +g +h +d +d +d +d +d +e +d +d +d +d +d +h +k +c +b +b +a +"} +(5,1,1) = {" +a +b +b +c +h +d +d +e +b +b +j +b +j +b +b +d +d +i +h +c +b +b +a +"} +(6,1,1) = {" +a +b +b +c +i +b +b +j +b +l +q +b +X +Q +b +j +b +b +d +c +b +b +a +"} +(7,1,1) = {" +a +b +c +c +d +b +J +r +p +m +q +p +E +N +p +q +l +b +d +c +c +b +a +"} +(8,1,1) = {" +a +b +c +d +d +b +D +O +b +b +q +b +r +b +b +q +m +b +d +d +c +b +a +"} +(9,1,1) = {" +a +b +c +d +b +b +p +X +r +p +q +p +O +p +q +q +p +b +b +d +c +b +a +"} +(10,1,1) = {" +a +b +c +d +b +l +p +b +p +p +p +p +p +p +p +b +p +Q +b +d +c +b +a +"} +(11,1,1) = {" +a +b +c +d +j +m +q +q +q +p +t +t +t +p +X +r +E +N +j +e +c +b +a +"} +(12,1,1) = {" +a +b +c +e +b +b +p +b +p +p +t +u +t +p +p +b +p +b +b +d +c +b +a +"} +(13,1,1) = {" +a +b +c +d +j +n +r +E +O +p +t +t +t +p +q +q +q +m +j +d +c +b +a +"} +(14,1,1) = {" +a +b +c +d +b +o +p +b +p +p +p +p +p +p +p +b +p +l +b +i +c +b +a +"} +(15,1,1) = {" +a +b +c +d +b +b +p +q +q +p +X +p +q +p +E +O +p +b +b +d +c +b +a +"} +(16,1,1) = {" +a +b +c +f +d +b +m +q +b +b +E +b +q +b +b +X +M +b +d +d +c +b +a +"} +(17,1,1) = {" +a +b +c +c +d +b +l +q +p +n +r +p +q +m +p +E +P +b +d +c +c +b +a +"} +(18,1,1) = {" +a +b +b +c +d +b +b +j +b +o +O +b +q +H +b +j +b +b +d +c +b +b +a +"} +(19,1,1) = {" +a +b +b +c +h +i +d +d +b +b +j +b +j +b +b +d +e +d +h +c +b +b +a +"} +(20,1,1) = {" +a +b +b +c +k +h +d +d +e +d +d +d +d +d +d +d +d +h +v +c +b +b +a +"} +(21,1,1) = {" +a +b +b +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +b +b +a +"} +(22,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_lavaland.dmm b/_maps/map_files/Mafia/mafia_lavaland.dmm new file mode 100644 index 0000000000..8c58fb9054 --- /dev/null +++ b/_maps/map_files/Mafia/mafia_lavaland.dmm @@ -0,0 +1,976 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"c" = ( +/turf/closed/wall/rust, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/obj/machinery/door/airlock/external{ + max_integrity = 99999; + name = "Maintenance" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plating, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/grass/fakebasalt, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/grass/fakebasalt, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 9 + }, +/obj/effect/turf_decal/trimline/brown/filled/corner, +/turf/open/floor/plasteel, +/area/mafia) +"o" = ( +/obj/effect/turf_decal/trimline/brown/filled/end{ + icon_state = "trimline_end_fill"; + dir = 4 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/closed/indestructible/fakeglass, +/area/mafia) +"q" = ( +/turf/open/floor/grass/fakebasalt, +/area/mafia) +"r" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 9 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"s" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 9 + }, +/turf/open/floor/plasteel, +/area/mafia) +"t" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"u" = ( +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red, +/turf/open/floor/plasteel/dark, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"w" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 1 + }, +/obj/effect/turf_decal/trimline/brown/filled/corner, +/turf/open/floor/plasteel, +/area/mafia) +"x" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 1 + }, +/obj/effect/turf_decal/trimline/brown/filled/line, +/turf/open/floor/plasteel, +/area/mafia) +"y" = ( +/obj/effect/turf_decal/trimline/brown/filled/end{ + icon_state = "trimline_end_fill"; + dir = 4 + }, +/turf/open/floor/plasteel, +/area/mafia) +"z" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 5 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"A" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 6 + }, +/turf/open/floor/plasteel, +/area/mafia) +"B" = ( +/obj/effect/turf_decal/trimline/brown/filled/end{ + icon_state = "trimline_end_fill"; + dir = 1 + }, +/turf/open/floor/plasteel, +/area/mafia) +"C" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 4 + }, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 8 + }, +/turf/open/floor/plasteel, +/area/mafia) +"D" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 10 + }, +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 4 + }, +/turf/open/floor/plasteel, +/area/mafia) +"E" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 1 + }, +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 8 + }, +/turf/open/floor/plasteel, +/area/mafia) +"F" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 5 + }, +/turf/open/floor/plasteel, +/area/mafia) +"G" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 10 + }, +/turf/open/floor/plasteel, +/area/mafia) +"H" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 10 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"I" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 6 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"J" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 6 + }, +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 1 + }, +/turf/open/floor/plasteel, +/area/mafia) +"K" = ( +/obj/effect/turf_decal/trimline/brown/filled/end{ + icon_state = "trimline_end_fill"; + dir = 8 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"L" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 5 + }, +/turf/open/floor/plasteel, +/area/mafia) +"M" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 10 + }, +/turf/open/floor/plasteel, +/area/mafia) +"N" = ( +/obj/effect/turf_decal/trimline/brown/filled/line, +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 4 + }, +/turf/open/floor/plasteel, +/area/mafia) +"O" = ( +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 8 + }, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 5 + }, +/turf/open/floor/plasteel, +/area/mafia) +"P" = ( +/obj/effect/turf_decal/trimline/brown/filled/end, +/turf/open/floor/plasteel, +/area/mafia) +"Q" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 9 + }, +/turf/open/floor/plasteel, +/area/mafia) +"R" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"S" = ( +/obj/effect/turf_decal/trimline/brown/filled/end{ + icon_state = "trimline_end_fill"; + dir = 8 + }, +/turf/open/floor/plasteel, +/area/mafia) +"T" = ( +/obj/effect/turf_decal/trimline/brown/filled/line, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 1 + }, +/turf/open/floor/plasteel, +/area/mafia) +"U" = ( +/obj/effect/turf_decal/trimline/brown/filled/line, +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 1 + }, +/turf/open/floor/plasteel, +/area/mafia) +"V" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 6 + }, +/turf/open/floor/plasteel, +/area/mafia) +"W" = ( +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"X" = ( +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"Y" = ( +/turf/closed/indestructible{ + icon = 'icons/turf/walls/reinforced_wall.dmi'; + icon_state = "r_wall"; + name = "reinforced wall" + }, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +a +"} +(3,1,1) = {" +a +Y +Y +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +Y +Y +a +"} +(4,1,1) = {" +a +Y +Y +c +g +h +d +d +d +d +d +e +d +d +d +d +d +h +k +c +Y +Y +a +"} +(5,1,1) = {" +a +Y +Y +c +h +d +d +e +Y +Y +j +Y +j +Y +Y +d +d +i +h +c +Y +Y +a +"} +(6,1,1) = {" +a +Y +Y +c +i +Y +Y +j +Y +l +q +Y +s +H +Y +j +Y +Y +d +c +Y +Y +a +"} +(7,1,1) = {" +a +Y +c +c +d +Y +r +M +p +m +q +p +w +A +p +q +l +Y +d +c +c +Y +a +"} +(8,1,1) = {" +a +Y +c +d +d +Y +L +N +Y +Y +q +Y +x +Y +Y +q +m +Y +d +d +c +Y +a +"} +(9,1,1) = {" +a +Y +c +d +Y +Y +p +O +P +p +q +p +y +p +q +q +p +Y +Y +d +c +Y +a +"} +(10,1,1) = {" +a +Y +c +d +Y +l +p +Y +p +p +p +p +p +p +p +Y +p +K +Y +d +c +Y +a +"} +(11,1,1) = {" +a +Y +c +d +j +m +q +q +q +p +q +u +q +p +B +C +C +J +j +e +c +Y +a +"} +(12,1,1) = {" +a +Y +c +e +Y +Y +p +Y +p +p +t +R +X +p +p +Y +p +Y +Y +d +c +Y +a +"} +(13,1,1) = {" +a +Y +c +d +j +n +C +C +P +p +q +W +q +p +q +q +q +m +j +d +c +Y +a +"} +(14,1,1) = {" +a +Y +c +d +Y +o +p +Y +p +p +p +p +p +p +p +Y +p +l +Y +i +c +Y +a +"} +(15,1,1) = {" +a +Y +c +d +Y +Y +p +q +q +p +S +p +q +p +B +D +p +Y +Y +d +c +Y +a +"} +(16,1,1) = {" +a +Y +c +f +d +Y +m +q +Y +Y +T +Y +q +Y +Y +E +G +Y +d +d +c +Y +a +"} +(17,1,1) = {" +a +Y +c +c +d +Y +l +q +p +Q +U +p +q +m +p +F +I +Y +d +c +c +Y +a +"} +(18,1,1) = {" +a +Y +Y +c +d +Y +Y +j +Y +z +V +Y +q +l +Y +j +Y +Y +d +c +Y +Y +a +"} +(19,1,1) = {" +a +Y +Y +c +h +i +d +d +Y +Y +j +Y +j +Y +Y +d +e +d +h +c +Y +Y +a +"} +(20,1,1) = {" +a +Y +Y +c +k +h +d +d +e +d +d +d +d +d +d +d +d +h +v +c +Y +Y +a +"} +(21,1,1) = {" +a +Y +Y +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +Y +Y +a +"} +(22,1,1) = {" +a +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_reebe.dmm b/_maps/map_files/Mafia/mafia_reebe.dmm new file mode 100644 index 0000000000..e82174ff93 --- /dev/null +++ b/_maps/map_files/Mafia/mafia_reebe.dmm @@ -0,0 +1,658 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"b" = ( +/turf/open/indestructible/reebe_void, +/area/mafia) +"c" = ( +/turf/open/indestructible/reebe_void/spawning/lattices, +/area/mafia) +"d" = ( +/obj/mafia_game_board, +/turf/open/indestructible/reebe_void/spawning/lattices, +/area/mafia) +"e" = ( +/turf/closed/wall/clockwork, +/area/mafia) +"f" = ( +/turf/open/floor/clockwork/reebe, +/area/mafia) +"g" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"h" = ( +/obj/structure/window/reinforced/clockwork/fulltile, +/obj/structure/grille/ratvar, +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"i" = ( +/obj/structure/bed, +/obj/item/bedsheet/orange, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"j" = ( +/obj/structure/chair/brass{ + dir = 4 + }, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"k" = ( +/obj/structure/chair/brass, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"l" = ( +/obj/structure/chair/brass{ + dir = 8 + }, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"m" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/obj/structure/chair/brass, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"n" = ( +/obj/structure/chair/brass{ + icon_state = "brass_chair"; + dir = 1 + }, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"o" = ( +/obj/item/toy/plush/plushvar, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"p" = ( +/obj/structure/window/reinforced/clockwork/fulltile, +/obj/structure/grille/ratvar, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"q" = ( +/obj/item/toy/plush/plushvar, +/turf/open/indestructible/reebe_void/spawning/lattices, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(3,1,1) = {" +a +b +b +c +c +c +b +b +b +b +b +b +b +b +b +b +b +c +c +c +b +b +a +"} +(4,1,1) = {" +a +b +b +c +d +c +b +b +b +b +b +b +b +b +b +b +b +q +d +c +b +b +a +"} +(5,1,1) = {" +a +b +b +c +c +c +c +b +e +e +p +e +p +e +e +b +b +c +c +c +b +b +a +"} +(6,1,1) = {" +a +b +b +b +b +p +p +e +e +i +f +e +f +i +e +e +p +p +c +b +b +b +a +"} +(7,1,1) = {" +a +b +b +b +b +p +i +f +h +g +f +h +f +g +h +f +i +p +b +b +b +b +a +"} +(8,1,1) = {" +a +b +b +b +b +e +g +f +e +e +f +e +f +e +e +f +g +e +b +b +b +b +a +"} +(9,1,1) = {" +a +b +b +b +e +e +h +f +j +h +j +h +j +h +j +f +h +e +e +b +b +b +a +"} +(10,1,1) = {" +a +b +b +b +e +i +h +e +h +h +h +h +h +h +h +e +h +i +e +b +b +b +a +"} +(11,1,1) = {" +a +b +b +b +p +g +f +f +k +h +f +f +o +h +n +f +f +g +p +b +b +b +a +"} +(12,1,1) = {" +a +b +b +b +e +e +h +e +h +h +f +m +f +h +h +e +h +e +e +b +b +b +a +"} +(13,1,1) = {" +a +b +b +b +p +g +f +f +k +h +f +f +f +h +n +f +f +g +p +b +b +b +a +"} +(14,1,1) = {" +a +b +b +b +e +i +h +e +h +h +h +h +h +h +h +e +h +i +e +b +b +b +a +"} +(15,1,1) = {" +a +b +b +b +e +e +h +f +l +h +l +h +l +h +l +f +h +e +e +b +b +b +a +"} +(16,1,1) = {" +a +b +b +b +b +e +g +f +e +e +f +e +f +e +e +f +g +e +b +b +b +b +a +"} +(17,1,1) = {" +a +b +b +b +b +p +i +f +h +g +f +h +f +g +h +f +i +p +b +b +b +b +a +"} +(18,1,1) = {" +a +b +b +b +b +p +p +e +e +f +i +e +i +f +e +e +p +p +c +b +b +b +a +"} +(19,1,1) = {" +a +b +b +c +c +c +c +b +e +e +p +e +p +e +e +b +b +c +c +c +b +b +a +"} +(20,1,1) = {" +a +b +b +c +d +c +b +b +b +b +b +b +b +b +b +b +b +c +d +c +b +b +a +"} +(21,1,1) = {" +a +b +b +c +c +c +b +b +b +b +b +b +b +b +b +b +b +c +c +c +b +b +a +"} +(22,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_snow.dmm b/_maps/map_files/Mafia/mafia_snow.dmm new file mode 100644 index 0000000000..a47440f00e --- /dev/null +++ b/_maps/map_files/Mafia/mafia_snow.dmm @@ -0,0 +1,755 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/obj/machinery/door/airlock/maintenance_hatch{ + max_integrity = 99999; + name = "Maintenance" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plating, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/item/bedsheet/brown, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/holofloor/wood, +/area/mafia) +"n" = ( +/obj/item/bedsheet/green, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"o" = ( +/obj/item/bedsheet/blue, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/closed/indestructible/fakeglass, +/area/mafia) +"q" = ( +/turf/open/floor/plasteel/dark, +/area/mafia) +"r" = ( +/turf/open/floor/plasteel, +/area/mafia) +"s" = ( +/turf/closed/wall/ice, +/area/mafia) +"t" = ( +/turf/open/floor/holofloor/snow, +/area/mafia) +"u" = ( +/obj/structure/statue/plasma/scientist{ + anchored = 1 + }, +/turf/open/floor/holofloor/snow, +/area/mafia) +"v" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/turf/open/floor/holofloor/snow, +/area/mafia) +"w" = ( +/turf/open/lava/plasma, +/area/mafia) +"x" = ( +/obj/machinery/door/airlock/external{ + max_integrity = 9999; + opacity = 0 + }, +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plasteel/dark, +/area/mafia) +"y" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/holofloor/snow, +/area/mafia) +"z" = ( +/obj/structure/lattice/catwalk, +/turf/open/lava/plasma, +/area/mafia) +"A" = ( +/obj/machinery/door/airlock/external{ + max_integrity = 9999 + }, +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plasteel, +/area/mafia) +"B" = ( +/obj/effect/landmark/mafia, +/obj/structure/lattice/catwalk, +/turf/open/lava/plasma, +/area/mafia) +"C" = ( +/obj/mafia_game_board, +/turf/open/floor/holofloor/snow, +/area/mafia) +"D" = ( +/obj/structure/closet/crate/science, +/obj/item/clothing/suit/hooded/wintercoat, +/obj/item/clothing/shoes/winterboots, +/turf/open/floor/holofloor/snow, +/area/mafia) +"E" = ( +/obj/structure/lattice/catwalk, +/obj/structure/closet/crate/critter, +/obj/item/clothing/suit/hooded/wintercoat/miner, +/obj/item/clothing/shoes/winterboots, +/turf/open/lava/plasma, +/area/mafia) +"F" = ( +/turf/open/floor/holofloor/wood, +/area/mafia) +"G" = ( +/obj/item/bedsheet/orange, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"H" = ( +/obj/item/bedsheet/black, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"I" = ( +/obj/item/bedsheet/red, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"J" = ( +/turf/open/floor/plating/snowed/smoothed, +/area/mafia) +"K" = ( +/turf/closed/indestructible/rock/snow, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +s +K +K +K +K +K +K +K +K +K +K +a +"} +(3,1,1) = {" +a +a +a +s +s +s +s +s +s +s +s +s +w +w +t +w +w +w +w +w +w +K +a +"} +(4,1,1) = {" +a +a +a +s +g +h +d +d +d +d +d +s +t +w +w +w +w +w +C +w +w +K +a +"} +(5,1,1) = {" +a +a +a +s +h +d +d +e +s +s +j +s +p +p +p +w +w +w +w +w +w +K +a +"} +(6,1,1) = {" +a +a +a +s +i +s +s +j +s +H +F +s +t +D +p +p +p +p +w +w +w +K +a +"} +(7,1,1) = {" +a +a +s +s +d +s +o +F +p +m +q +p +t +y +p +z +E +p +w +t +w +K +a +"} +(8,1,1) = {" +a +a +s +d +d +s +m +r +s +s +q +s +t +p +p +z +B +p +w +w +w +K +a +"} +(9,1,1) = {" +a +a +s +d +s +s +p +r +r +p +q +p +t +p +z +z +p +p +p +w +w +K +a +"} +(10,1,1) = {" +a +a +s +d +s +l +p +s +p +p +p +p +p +p +p +p +p +D +p +w +w +K +a +"} +(11,1,1) = {" +a +a +s +d +j +m +F +q +q +x +J +t +t +p +t +t +t +y +p +w +t +K +a +"} +(12,1,1) = {" +a +a +s +e +s +a +p +s +p +p +u +v +t +p +p +p +p +p +p +w +w +K +a +"} +(13,1,1) = {" +a +a +s +d +j +m +F +r +r +A +J +t +t +p +t +t +t +y +p +w +w +K +a +"} +(14,1,1) = {" +a +a +s +d +s +n +p +s +p +p +p +p +p +p +p +p +p +D +p +w +w +K +a +"} +(15,1,1) = {" +a +a +s +d +s +s +p +q +q +p +r +p +t +p +z +z +p +p +p +w +w +K +a +"} +(16,1,1) = {" +a +a +s +f +d +s +m +q +s +s +r +s +t +p +p +z +B +p +w +w +w +K +a +"} +(17,1,1) = {" +a +a +s +s +d +s +G +F +p +m +r +p +t +y +p +z +E +p +w +w +w +K +a +"} +(18,1,1) = {" +a +a +a +s +d +s +s +j +s +I +F +s +t +D +p +p +p +p +w +t +w +K +a +"} +(19,1,1) = {" +a +a +a +s +h +i +d +d +s +s +j +s +p +p +p +t +w +w +w +w +w +K +a +"} +(20,1,1) = {" +a +a +a +s +k +h +d +d +e +d +d +s +t +w +w +w +w +w +C +w +w +K +a +"} +(21,1,1) = {" +a +a +a +s +s +s +s +s +s +s +s +s +w +w +w +w +w +w +w +w +w +K +a +"} +(22,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +s +K +K +K +K +K +K +K +K +K +K +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_spiderclan.dmm b/_maps/map_files/Mafia/mafia_spiderclan.dmm new file mode 100644 index 0000000000..89771212b8 --- /dev/null +++ b/_maps/map_files/Mafia/mafia_spiderclan.dmm @@ -0,0 +1,690 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"b" = ( +/obj/structure/closet/cabinet{ + anchored = 1 + }, +/obj/item/reagent_containers/food/snacks/sashimi, +/turf/open/floor/carpet/black, +/area/mafia) +"c" = ( +/turf/closed/wall/rust, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/turf/closed/wall/mineral/wood{ + desc = "A door that goes nowhere. How kafkaesque."; + icon = 'icons/obj/doors/mineral_doors.dmi'; + icon_state = "paperframe"; + name = "Door" + }, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/wood, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/carpet/black, +/area/mafia) +"o" = ( +/obj/structure/closet/cabinet{ + anchored = 1 + }, +/obj/item/reagent_containers/food/snacks/sashimi, +/turf/open/floor/wood, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/closed/indestructible/fakeglass{ + icon = 'icons/obj/smooth_structures/paperframes.dmi'; + icon_state = "paper" + }, +/area/mafia) +"q" = ( +/turf/open/floor/wood, +/area/mafia) +"r" = ( +/turf/open/floor/carpet/black, +/area/mafia) +"t" = ( +/turf/open/floor/plating/beach/sand, +/area/mafia) +"u" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/turf/open/floor/plating/beach/sand, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"B" = ( +/obj/structure/showcase{ + density = 0; + desc = "Welp, only one way to recover your honour."; + icon = 'icons/obj/items_and_weapons.dmi'; + icon_state = "katana"; + name = "seppuku katana" + }, +/turf/open/floor/plating/beach/sand, +/area/mafia) +"S" = ( +/turf/closed/wall/mineral/wood, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +a +"} +(3,1,1) = {" +a +S +S +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +S +S +a +"} +(4,1,1) = {" +a +S +S +c +g +h +d +d +d +d +d +e +d +d +d +d +d +h +k +c +S +S +a +"} +(5,1,1) = {" +a +S +S +c +h +d +d +e +S +S +j +S +j +S +S +d +d +i +h +c +S +S +a +"} +(6,1,1) = {" +a +S +S +c +i +S +S +j +S +o +q +S +r +b +S +j +S +S +d +c +S +S +a +"} +(7,1,1) = {" +a +S +c +c +d +S +b +r +p +m +q +p +r +n +p +q +o +S +d +c +c +S +a +"} +(8,1,1) = {" +a +S +c +d +d +S +n +r +S +S +q +S +r +S +S +q +m +S +d +d +c +S +a +"} +(9,1,1) = {" +a +S +c +d +S +S +p +r +r +p +q +p +r +p +q +q +p +S +S +d +c +S +a +"} +(10,1,1) = {" +a +S +c +d +S +o +p +S +p +p +p +p +p +p +p +S +p +b +S +d +c +S +a +"} +(11,1,1) = {" +a +S +c +d +j +m +q +q +q +p +t +t +t +p +r +r +r +n +j +e +c +S +a +"} +(12,1,1) = {" +a +S +c +e +S +S +p +S +p +p +t +u +B +p +p +S +p +S +S +d +c +S +a +"} +(13,1,1) = {" +a +S +c +d +j +n +r +r +r +p +t +t +t +p +q +q +q +m +j +d +c +S +a +"} +(14,1,1) = {" +a +S +c +d +S +b +p +S +p +p +p +p +p +p +p +S +p +o +S +i +c +S +a +"} +(15,1,1) = {" +a +S +c +d +S +S +p +q +q +p +r +p +q +p +r +r +p +S +S +d +c +S +a +"} +(16,1,1) = {" +a +S +c +f +d +S +m +q +S +S +r +S +q +S +S +r +n +S +d +d +c +S +a +"} +(17,1,1) = {" +a +S +c +c +d +S +o +q +p +n +r +p +q +m +p +r +b +S +d +c +c +S +a +"} +(18,1,1) = {" +a +S +S +c +d +S +S +j +S +b +r +S +q +o +S +j +S +S +d +c +S +S +a +"} +(19,1,1) = {" +a +S +S +c +h +i +d +d +S +S +j +S +j +S +S +d +e +d +h +c +S +S +a +"} +(20,1,1) = {" +a +S +S +c +k +h +d +d +e +d +d +d +d +d +d +d +d +h +v +c +S +S +a +"} +(21,1,1) = {" +a +S +S +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +S +S +a +"} +(22,1,1) = {" +a +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_syndie.dmm b/_maps/map_files/Mafia/mafia_syndie.dmm new file mode 100644 index 0000000000..d56c3cc0c6 --- /dev/null +++ b/_maps/map_files/Mafia/mafia_syndie.dmm @@ -0,0 +1,869 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"b" = ( +/obj/structure/closet/syndicate{ + desc = "A storage closet for syndicate conflict resolution operatives."; + name = "red closet" + }, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool/skirt, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"c" = ( +/turf/closed/wall/rust, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/obj/machinery/door/airlock/maintenance_hatch{ + max_integrity = 99999; + name = "Maintenance" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plating, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/structure/closet/syndicate{ + desc = "A storage closet for syndicate conflict resolution operatives."; + name = "red closet" + }, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool/skirt, +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"m" = ( +/obj/structure/closet/syndicate{ + desc = "A storage closet for syndicate conflict resolution operatives."; + name = "red closet" + }, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool/skirt, +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"o" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/obj/effect/spawner/structure/window/plastitanium, +/turf/open/space/basic, +/area/mafia) +"q" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/chair/office{ + dir = 1; + name = "tactical swivel chair" + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"r" = ( +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"s" = ( +/turf/open/floor/mineral/plastitanium/red, +/area/mafia) +"t" = ( +/turf/open/floor/circuit/red, +/area/mafia) +"u" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/obj/structure/chair, +/turf/open/floor/circuit/red, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"w" = ( +/turf/closed/indestructible/syndicate, +/area/mafia) +"x" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/chair/office{ + dir = 4; + name = "tactical swivel chair" + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"y" = ( +/obj/structure/chair/office{ + dir = 2; + name = "tactical swivel chair" + }, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"z" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/chair/office{ + dir = 2; + name = "tactical swivel chair" + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"A" = ( +/obj/structure/chair/office{ + dir = 8; + name = "tactical swivel chair" + }, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"B" = ( +/obj/structure/chair/office{ + dir = 4; + name = "tactical swivel chair" + }, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"C" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/chair/office{ + dir = 8; + name = "tactical swivel chair" + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"E" = ( +/obj/structure/chair/office{ + dir = 1; + name = "tactical swivel chair" + }, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"F" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/closet/syndicate{ + desc = "A storage closet for syndicate conflict resolution operatives."; + name = "red closet" + }, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool/skirt, +/turf/open/floor/plasteel/dark, +/area/mafia) +"G" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/item/clothing/under/syndicate/tacticool, +/obj/structure/closet/syndicate{ + desc = "A storage closet for syndicate conflict resolution operatives."; + name = "red closet" + }, +/obj/item/clothing/under/syndicate/tacticool/skirt, +/turf/open/floor/plasteel/dark, +/area/mafia) +"H" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/dark, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +a +"} +(3,1,1) = {" +a +w +w +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +w +w +a +"} +(4,1,1) = {" +a +w +w +c +g +h +d +d +d +d +d +e +d +d +d +d +d +h +k +c +w +w +a +"} +(5,1,1) = {" +a +w +w +c +h +d +d +e +w +w +j +w +j +w +w +d +d +i +h +c +w +w +a +"} +(6,1,1) = {" +a +w +w +c +i +w +w +j +w +b +r +w +o +F +w +j +w +w +d +c +w +w +a +"} +(7,1,1) = {" +a +w +c +c +d +w +l +o +p +n +r +p +o +H +p +r +b +w +d +c +c +w +a +"} +(8,1,1) = {" +a +w +c +d +d +w +H +o +w +w +r +w +o +w +w +r +n +w +d +d +c +w +a +"} +(9,1,1) = {" +a +w +c +d +w +w +p +o +x +p +B +p +x +p +B +r +p +w +w +d +c +w +a +"} +(10,1,1) = {" +a +w +c +d +w +b +p +w +p +p +p +p +p +p +p +w +p +m +w +d +c +w +a +"} +(11,1,1) = {" +a +w +c +d +j +n +r +r +y +p +s +t +s +p +q +o +o +H +j +e +c +w +a +"} +(12,1,1) = {" +a +w +c +e +w +w +p +w +p +p +t +u +t +p +p +w +p +w +w +d +c +w +a +"} +(13,1,1) = {" +a +w +c +d +j +H +o +o +z +p +s +t +s +p +E +r +r +n +j +d +c +w +a +"} +(14,1,1) = {" +a +w +c +d +w +l +p +w +p +p +p +p +p +p +p +w +p +b +w +i +c +w +a +"} +(15,1,1) = {" +a +w +c +d +w +w +p +r +A +p +C +p +A +p +C +o +p +w +w +d +c +w +a +"} +(16,1,1) = {" +a +w +c +f +d +w +n +r +w +w +o +w +r +w +w +o +H +w +d +d +c +w +a +"} +(17,1,1) = {" +a +w +c +c +d +w +b +r +p +H +o +p +r +n +p +o +G +w +d +c +c +w +a +"} +(18,1,1) = {" +a +w +w +c +d +w +w +j +w +F +o +w +r +b +w +j +w +w +d +c +w +w +a +"} +(19,1,1) = {" +a +w +w +c +h +i +d +d +w +w +j +w +j +w +w +d +e +d +h +c +w +w +a +"} +(20,1,1) = {" +a +w +w +c +k +h +d +d +e +d +d +d +d +d +d +d +d +h +v +c +w +w +a +"} +(21,1,1) = {" +a +w +w +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +w +w +a +"} +(22,1,1) = {" +a +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 6415c984bf..16659f3e7a 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -10324,6 +10324,7 @@ /turf/open/floor/carpet, /area/crew_quarters/dorms) "atd" = ( +/mob/living/simple_animal/opossum, /turf/open/floor/plating{ icon_state = "panelscorched" }, @@ -82149,6 +82150,16 @@ /obj/structure/grille, /turf/open/floor/plating, /area/maintenance/port/aft) +"pbc" = ( +/obj/structure/light_construct/small{ + dir = 4 + }, +/obj/machinery/computer/security/telescreen/entertainment{ + pixel_x = 30 + }, +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/wood, +/area/maintenance/port/aft) "pbF" = ( /obj/machinery/portable_atmospherics/canister, /obj/machinery/atmospherics/components/unary/portables_connector/visible{ @@ -100175,7 +100186,7 @@ csT dux cxQ ckS -cmi +pbc cnk ckP cmi diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm index f3b1b2afb6..e07873089a 100644 --- a/_maps/map_files/OmegaStation/OmegaStation.dmm +++ b/_maps/map_files/OmegaStation/OmegaStation.dmm @@ -41988,6 +41988,16 @@ }, /turf/open/floor/plasteel, /area/hydroponics) +"saI" = ( +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/starboard) "scn" = ( /obj/structure/cable{ icon_state = "4-8" @@ -45205,6 +45215,10 @@ /obj/effect/landmark/carpspawn, /turf/open/space, /area/space/station_ruins) +"uXV" = ( +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/wood, +/area/maintenance/port/aft) "ver" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/visible{ dir = 10 @@ -79446,7 +79460,7 @@ aFM aOH aOT aPV -aRm +uXV aSI aRC aKm @@ -92526,7 +92540,7 @@ aSD aRz aRz aRz -sGY +saI aRz sIb bsD diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index f11815b537..e6f14badd9 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -334,8 +334,8 @@ /area/crew_quarters/fitness/pool) "aaM" = ( /turf/open/floor/plasteel/yellowsiding/corner{ - icon_state = "yellowcornersiding"; - dir = 8 + dir = 8; + icon_state = "yellowcornersiding" }, /area/crew_quarters/fitness/pool) "aaN" = ( @@ -354,8 +354,8 @@ /area/crew_quarters/fitness/pool) "aaP" = ( /turf/open/floor/plasteel/yellowsiding{ - icon_state = "yellowsiding"; - dir = 8 + dir = 8; + icon_state = "yellowsiding" }, /area/crew_quarters/fitness/pool) "aaQ" = ( @@ -433,8 +433,8 @@ /area/crew_quarters/fitness/pool) "aaW" = ( /turf/open/floor/plasteel/yellowsiding{ - icon_state = "yellowsiding"; - dir = 1 + dir = 1; + icon_state = "yellowsiding" }, /area/crew_quarters/fitness/pool) "aaX" = ( @@ -550,8 +550,8 @@ "abm" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel/yellowsiding{ - icon_state = "yellowsiding"; - dir = 4 + dir = 4; + icon_state = "yellowsiding" }, /area/crew_quarters/fitness/pool) "abn" = ( @@ -580,8 +580,8 @@ }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel/yellowsiding{ - icon_state = "yellowsiding"; - dir = 4 + dir = 4; + icon_state = "yellowsiding" }, /area/crew_quarters/fitness/pool) "abr" = ( @@ -595,8 +595,8 @@ "abt" = ( /obj/structure/pool/Rboard, /turf/open/floor/plasteel/yellowsiding{ - icon_state = "yellowsiding"; - dir = 8 + dir = 8; + icon_state = "yellowsiding" }, /area/crew_quarters/fitness/pool) "abu" = ( @@ -659,8 +659,8 @@ }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel/yellowsiding/corner{ - icon_state = "yellowcornersiding"; - dir = 4 + dir = 4; + icon_state = "yellowcornersiding" }, /area/crew_quarters/fitness/pool) "abB" = ( @@ -670,21 +670,21 @@ pixel_y = -27 }, /turf/open/floor/plasteel/yellowsiding{ - icon_state = "yellowsiding"; - dir = 1 + dir = 1; + icon_state = "yellowsiding" }, /area/crew_quarters/fitness/pool) "abC" = ( /obj/machinery/light, /turf/open/floor/plasteel/yellowsiding{ - icon_state = "yellowsiding"; - dir = 1 + dir = 1; + icon_state = "yellowsiding" }, /area/crew_quarters/fitness/pool) "abD" = ( /turf/open/floor/plasteel/yellowsiding/corner{ - icon_state = "yellowcornersiding"; - dir = 1 + dir = 1; + icon_state = "yellowcornersiding" }, /area/crew_quarters/fitness/pool) "abE" = ( @@ -6313,27 +6313,7 @@ "aoz" = ( /turf/closed/wall, /area/maintenance/fore) -"aoA" = ( -/obj/machinery/gateway{ - dir = 9 - }, -/obj/effect/turf_decal/bot_white/right, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, -/area/gateway) "aoB" = ( -/obj/machinery/gateway{ - dir = 1 - }, /obj/machinery/light{ dir = 1 }, @@ -6350,23 +6330,6 @@ }, /turf/open/floor/plasteel/dark, /area/gateway) -"aoC" = ( -/obj/machinery/gateway{ - dir = 5 - }, -/obj/effect/turf_decal/bot_white/left, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, -/area/gateway) "aoH" = ( /obj/structure/lattice, /obj/structure/grille, @@ -6692,44 +6655,10 @@ }, /turf/open/space, /area/space/nearstation) -"apq" = ( -/obj/machinery/gateway{ - dir = 8 - }, -/obj/effect/turf_decal/bot_white, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, -/area/gateway) "apr" = ( /obj/machinery/gateway/centerstation, /turf/open/floor/plasteel/dark, /area/gateway) -"aps" = ( -/obj/machinery/gateway{ - dir = 4 - }, -/obj/effect/turf_decal/bot_white, -/obj/effect/turf_decal/tile/neutral{ - dir = 1 - }, -/obj/effect/turf_decal/tile/neutral, -/obj/effect/turf_decal/tile/neutral{ - dir = 4 - }, -/obj/effect/turf_decal/tile/neutral{ - dir = 8 - }, -/turf/open/floor/plasteel/dark, -/area/gateway) "apt" = ( /obj/structure/chair{ dir = 4 @@ -6916,9 +6845,6 @@ /turf/closed/wall/r_wall, /area/ai_monitored/nuke_storage) "apU" = ( -/obj/machinery/gateway{ - dir = 10 - }, /obj/effect/turf_decal/bot_white/left, /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -6933,7 +6859,6 @@ /turf/open/floor/plasteel/dark, /area/gateway) "apV" = ( -/obj/machinery/gateway, /obj/effect/turf_decal/bot_white, /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -6948,9 +6873,6 @@ /turf/open/floor/plasteel/dark, /area/gateway) "apW" = ( -/obj/machinery/gateway{ - dir = 6 - }, /obj/effect/turf_decal/bot_white/right, /obj/effect/turf_decal/tile/neutral{ dir = 1 @@ -7876,32 +7798,7 @@ /turf/open/floor/plasteel, /area/gateway) "arY" = ( -/obj/structure/closet/crate/internals, -/obj/item/clothing/suit/hazardvest{ - desc = "A high-visibility lifejacket complete with whistle and slot for oxygen tanks."; - name = "emergency lifejacket" - }, -/obj/item/clothing/suit/hazardvest{ - desc = "A high-visibility lifejacket complete with whistle and slot for oxygen tanks."; - name = "emergency lifejacket" - }, -/obj/item/clothing/suit/hazardvest{ - desc = "A high-visibility lifejacket complete with whistle and slot for oxygen tanks."; - name = "emergency lifejacket" - }, -/obj/item/clothing/suit/hazardvest{ - desc = "A high-visibility lifejacket complete with whistle and slot for oxygen tanks."; - name = "emergency lifejacket" - }, -/obj/item/clothing/suit/hazardvest{ - desc = "A high-visibility lifejacket complete with whistle and slot for oxygen tanks."; - name = "emergency lifejacket" - }, -/obj/item/flashlight, -/obj/item/flashlight, -/obj/item/flashlight, -/obj/item/flashlight, -/obj/item/flashlight, +/obj/machinery/computer/gateway_control, /turf/open/floor/plasteel, /area/gateway) "arZ" = ( @@ -8904,6 +8801,32 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 4 }, +/obj/structure/closet/crate/internals, +/obj/item/clothing/suit/hazardvest{ + desc = "A high-visibility lifejacket complete with whistle and slot for oxygen tanks."; + name = "emergency lifejacket" + }, +/obj/item/clothing/suit/hazardvest{ + desc = "A high-visibility lifejacket complete with whistle and slot for oxygen tanks."; + name = "emergency lifejacket" + }, +/obj/item/clothing/suit/hazardvest{ + desc = "A high-visibility lifejacket complete with whistle and slot for oxygen tanks."; + name = "emergency lifejacket" + }, +/obj/item/clothing/suit/hazardvest{ + desc = "A high-visibility lifejacket complete with whistle and slot for oxygen tanks."; + name = "emergency lifejacket" + }, +/obj/item/clothing/suit/hazardvest{ + desc = "A high-visibility lifejacket complete with whistle and slot for oxygen tanks."; + name = "emergency lifejacket" + }, +/obj/item/flashlight, +/obj/item/flashlight, +/obj/item/flashlight, +/obj/item/flashlight, +/obj/item/flashlight, /turf/open/floor/plasteel, /area/gateway) "auh" = ( @@ -39042,7 +38965,6 @@ /area/chapel/dock) "bKe" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on, -/obj/structure/closet/emcloset, /turf/open/floor/plasteel/white{ heat_capacity = 1e+006 }, @@ -40077,12 +39999,6 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 8 }, -/obj/machinery/light/small{ - dir = 4 - }, -/obj/structure/chair{ - dir = 1 - }, /turf/open/floor/plasteel/white{ heat_capacity = 1e+006 }, @@ -47273,7 +47189,7 @@ "ceT" = ( /obj/effect/turf_decal/stripes/line, /obj/structure/closet/emcloset, -/turf/open/floor/plating/airless, +/turf/open/floor/plating, /area/engine/engineering) "ceU" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ @@ -50404,9 +50320,11 @@ /turf/open/floor/plasteel, /area/crew_quarters/heads/chief) "cqW" = ( -/obj/effect/spawner/structure/window/reinforced, -/turf/closed/mineral, -/area/chapel/asteroid/monastery) +/obj/structure/chair, +/turf/open/floor/plasteel/white{ + heat_capacity = 1e+006 + }, +/area/chapel/dock) "cqX" = ( /obj/structure/disposalpipe/segment, /obj/structure/lattice, @@ -52993,7 +52911,7 @@ /turf/open/floor/plasteel/dark, /area/security/main) "cCT" = ( -/obj/machinery/rnd/production/protolathe/department/cargo, +/obj/machinery/rnd/production/techfab/department/cargo, /turf/open/floor/plasteel, /area/quartermaster/storage) "cCU" = ( @@ -54128,6 +54046,20 @@ }, /turf/open/floor/plasteel, /area/construction/mining/aux_base) +"fgS" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/plasteel/dark, +/area/maintenance/department/engine) "fhM" = ( /obj/item/storage/secure/safe{ pixel_x = -22 @@ -55664,11 +55596,11 @@ /turf/open/floor/plating, /area/crew_quarters/dorms) "izF" = ( -/turf/open/floor/plating{ - initial_gas_mix = "o2=0.01;n2=0.01"; - luminosity = 2 +/obj/structure/closet/emcloset, +/turf/open/floor/plasteel/white{ + heat_capacity = 1e+006 }, -/area/maintenance/department/science) +/area/chapel/dock) "iAx" = ( /obj/structure/cable{ icon_state = "4-8" @@ -57738,10 +57670,7 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 6 }, -/turf/open/floor/plating{ - initial_gas_mix = "o2=0.01;n2=0.01"; - luminosity = 2 - }, +/turf/open/floor/plating, /area/maintenance/department/science) "nAs" = ( /obj/machinery/door/airlock/engineering/glass{ @@ -57904,7 +57833,7 @@ /obj/structure/disposalpipe/trunk{ dir = 8 }, -/turf/open/floor/plating, +/turf/open/floor/plating/airless, /area/space/nearstation) "nOY" = ( /obj/effect/turf_decal/stripes/line{ @@ -58007,10 +57936,7 @@ /obj/structure/table, /obj/item/paper_bin, /obj/item/pen, -/turf/open/floor/plating{ - initial_gas_mix = "o2=0.01;n2=0.01"; - luminosity = 2 - }, +/turf/open/floor/plating, /area/maintenance/department/science) "odM" = ( /obj/effect/landmark/barthpot, @@ -58733,6 +58659,10 @@ "pyw" = ( /turf/open/space/basic, /area/hallway/secondary/entry) +"pzF" = ( +/mob/living/simple_animal/opossum, +/turf/open/floor/wood, +/area/maintenance/department/crew_quarters/dorms) "pBD" = ( /obj/structure/cable{ icon_state = "0-8" @@ -59532,14 +59462,13 @@ /turf/open/floor/plating/airless, /area/space/nearstation) "ros" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ +/obj/machinery/light/small{ dir = 4 }, -/turf/open/floor/plating{ - initial_gas_mix = "o2=0.01;n2=0.01"; - luminosity = 2 +/turf/open/floor/plasteel/white{ + heat_capacity = 1e+006 }, -/area/maintenance/department/science) +/area/chapel/dock) "rrb" = ( /obj/structure/cable{ icon_state = "1-2" @@ -60138,7 +60067,6 @@ /turf/open/floor/plasteel/dark, /area/chapel/office) "tap" = ( -/obj/structure/reagent_dispensers/keg/aphro, /turf/open/floor/wood, /area/maintenance/department/crew_quarters/dorms) "taA" = ( @@ -60667,10 +60595,7 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 9 }, -/turf/open/floor/plating{ - initial_gas_mix = "o2=0.01;n2=0.01"; - luminosity = 2 - }, +/turf/open/floor/plating, /area/maintenance/department/science) "uos" = ( /obj/machinery/computer/camera_advanced/base_construction, @@ -61099,13 +61024,6 @@ /obj/structure/lattice, /turf/open/space/basic, /area/space/nearstation) -"vpz" = ( -/obj/structure/girder, -/turf/open/floor/plating{ - initial_gas_mix = "o2=0.01;n2=0.01"; - luminosity = 2 - }, -/area/maintenance/department/science) "vsk" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 @@ -61620,12 +61538,6 @@ }, /turf/open/floor/plating, /area/maintenance/department/crew_quarters/bar) -"wDe" = ( -/obj/effect/spawner/structure/window/reinforced, -/turf/open/floor/plasteel/white{ - heat_capacity = 1e+006 - }, -/area/chapel/dock) "wDl" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, /obj/effect/turf_decal/stripes/corner, @@ -62038,10 +61950,7 @@ /area/maintenance/department/science) "xsO" = ( /obj/item/ectoplasm, -/turf/open/floor/plating{ - initial_gas_mix = "o2=0.01;n2=0.01"; - luminosity = 2 - }, +/turf/open/floor/plating, /area/maintenance/department/science) "xuv" = ( /obj/item/broken_bottle, @@ -75866,8 +75775,8 @@ aaa bGI bNs cfN -cqW -cqW +cfN +cfN cfN cfN bZY @@ -76379,7 +76288,7 @@ aaa aaa bGI bNs -cqW +cfN bOw cfN bOw @@ -80477,7 +80386,7 @@ aaa aht aqG bGE -bKf +cqW bKf bMw bNy @@ -80734,8 +80643,8 @@ aht aht aqG bGE -bKf -bKf +izF +ros bMx bNz bHM @@ -80991,7 +80900,7 @@ aaa aaa aqG bGE -wDe +bGE bHM bHM bNA @@ -83563,7 +83472,7 @@ bHP bJb bJb bJb -bJb +fgS bJb bJb bPp @@ -97884,8 +97793,8 @@ aaa aaa aaa lcZ -aoA -apq +apW +apV apU aqQ arX @@ -98398,8 +98307,8 @@ aiT aiS aaa lcZ -aoC -aps +apU +apV apW aqS arZ @@ -99932,7 +99841,7 @@ akh alc alR amF -alb +pzF alb anm ajv @@ -107723,7 +107632,7 @@ aht bwm ikB iVJ -izF +lWy typ bwm aht @@ -108233,7 +108142,7 @@ bwm svN bIQ uek -izF +lWy bwm qnT lJr @@ -108488,9 +108397,9 @@ dMO lWy rxQ lWy -ros -izF -izF +bIQ +lWy +lWy mES lWy lWy @@ -108747,7 +108656,7 @@ bwm nzD uoj bwm -izF +lWy bwm dMI lWy @@ -109001,7 +108910,7 @@ bkF bwm bwm bwm -ros +bIQ bwm bwm bwm @@ -109258,8 +109167,8 @@ bkF qIO jXA bwm -ros -vpz +bIQ +hXt bwm aht bwm diff --git a/_maps/map_files/Snaxi/Snaxi.dmm b/_maps/map_files/Snaxi/Snaxi.dmm index 43e7915ca4..f7aca9baf2 100644 --- a/_maps/map_files/Snaxi/Snaxi.dmm +++ b/_maps/map_files/Snaxi/Snaxi.dmm @@ -18912,6 +18912,10 @@ }, /turf/open/floor/plasteel, /area/storage/primary) +"ihL" = ( +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/aft/secondary) "ihX" = ( /obj/structure/cable{ icon_state = "0-2" @@ -24053,6 +24057,10 @@ "luo" = ( /turf/open/floor/plasteel/white, /area/science/misc_lab) +"lwl" = ( +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/plating, +/area/engine/engineering) "lwH" = ( /obj/structure/cable{ icon_state = "1-4" @@ -83466,7 +83474,7 @@ tRe tRe tRe bWf -bVa +ihL kXG sqx owj @@ -98024,7 +98032,7 @@ xUL xUL dKF ijZ -ijZ +lwl bnU oJa cNE diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 5a14eb9041..a123ccab55 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -17249,6 +17249,10 @@ }, /turf/open/floor/mineral/titanium, /area/centcom/evac) +"Qc" = ( +/obj/effect/landmark/mafia_game_area, +/turf/open/space/basic, +/area/space) "Qe" = ( /turf/open/ai_visible, /area/ai_multicam_room) @@ -58911,7 +58915,7 @@ aa aa aa aa -aa +Qc aa aa aa diff --git a/_maps/shuttles/whiteship_box.dmm b/_maps/shuttles/whiteship_box.dmm index 0404cede34..3ac1d18ac4 100644 --- a/_maps/shuttles/whiteship_box.dmm +++ b/_maps/shuttles/whiteship_box.dmm @@ -2690,7 +2690,7 @@ /obj/structure/rack, /obj/item/storage/box/zipties, /obj/item/assembly/flash/handheld, -/obj/item/melee/classic_baton/telescopic, +/obj/item/melee/classic_baton, /obj/machinery/light/small/built{ dir = 8 }, diff --git a/code/__DEFINES/clockcult.dm b/code/__DEFINES/clockcult.dm index f597eb4ae7..5f4317060d 100644 --- a/code/__DEFINES/clockcult.dm +++ b/code/__DEFINES/clockcult.dm @@ -23,6 +23,7 @@ GLOBAL_VAR_INIT(script_scripture_unlocked, FALSE) //If script scripture is avail GLOBAL_VAR_INIT(application_scripture_unlocked, FALSE) //If application scripture is available GLOBAL_VAR_INIT(judgement_scripture_unlocked, FALSE) //If judgement scripture is available GLOBAL_LIST_EMPTY(all_scripture) //a list containing scripture instances; not used to track existing scripture +GLOBAL_LIST_EMPTY(all_clockwork_rites) //a list containing all clockwork rites. Filled the first time any cultist interacts with a sigil of rites. //Scripture tiers and requirements; peripherals should never be used #define SCRIPTURE_PERIPHERAL "Peripheral" diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 7e1a83486d..6900f36a33 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -209,6 +209,8 @@ #define COMSIG_MOB_SWAP_HANDS "mob_swap_hands" //from base of mob/swap_hand(): (obj/item) #define COMPONENT_BLOCK_SWAP 1 +#define COMSIG_PROCESS_BORGCHARGER_OCCUPANT "living_charge" + // /client signals #define COMSIG_MOB_CLIENT_LOGIN "mob_client_login" //sent when a mob/login() finishes: (client) #define COMSIG_MOB_CLIENT_LOGOUT "mob_client_logout" //sent when a mob/logout() starts: (client) @@ -452,6 +454,9 @@ #define COMPONENT_TWOHANDED_BLOCK_WIELD 1 #define COMSIG_TWOHANDED_UNWIELD "twohanded_unwield" //from base of datum/component/two_handed/proc/unwield(mob/living/carbon/user): (/mob/user) +// /datum/component/squeak signals +#define COMSIG_CROSS_SQUEAKED "cross_squeaked" // sent when a squeak component squeaks from crossing something, to delay anything else crossing that might squeak to prevent ear hurt. + // /datum/action signals #define COMSIG_ACTION_TRIGGER "action_trigger" //from base of datum/action/proc/Trigger(): (datum/action) #define COMPONENT_ACTION_BLOCK_TRIGGER 1 diff --git a/code/__DEFINES/exosuit_fabs.dm b/code/__DEFINES/exosuit_fabs.dm new file mode 100644 index 0000000000..d951a7bc28 --- /dev/null +++ b/code/__DEFINES/exosuit_fabs.dm @@ -0,0 +1,35 @@ +/// Module is compatible with Security Cyborg models +#define BORG_MODULE_SECURITY (1<<0) +/// Module is compatible with Miner Cyborg models +#define BORG_MODULE_MINER (1<<1) +/// Module is compatible with Janitor Cyborg models +#define BORG_MODULE_JANITOR (1<<2) +/// Module is compatible with Medical Cyborg models +#define BORG_MODULE_MEDICAL (1<<3) +/// Module is compatible with Engineering Cyborg models +#define BORG_MODULE_ENGINEERING (1<<4) + +/// Module is compatible with Ripley Exosuit models +#define EXOSUIT_MODULE_RIPLEY (1<<0) +/// Module is compatible with Odyseeus Exosuit models +#define EXOSUIT_MODULE_ODYSSEUS (1<<1) +/// Module is compatible with Clarke Exosuit models. Rebranded to firefighter because tg nerfed it to this. +#define EXOSUIT_MODULE_FIREFIGHTER (1<<2) +// #define EXOSUIT_MODULE_CLARKE (1<<2) +/// Module is compatible with Gygax Exosuit models +#define EXOSUIT_MODULE_GYGAX (1<<3) +/// Module is compatible with Durand Exosuit models +#define EXOSUIT_MODULE_DURAND (1<<4) +/// Module is compatible with H.O.N.K Exosuit models +#define EXOSUIT_MODULE_HONK (1<<5) +/// Module is compatible with Phazon Exosuit models +#define EXOSUIT_MODULE_PHAZON (1<<6) +/// Module is compatable with N models +#define EXOSUIT_MODULE_GYGAX_MED (1<<7) + +/// Module is compatible with "Working" Exosuit models - Ripley and Clarke +#define EXOSUIT_MODULE_WORKING EXOSUIT_MODULE_RIPLEY | EXOSUIT_MODULE_FIREFIGHTER // | EXOSUIT_MODULE_CLARKE +/// Module is compatible with "Combat" Exosuit models - Gygax, H.O.N.K, Durand and Phazon +#define EXOSUIT_MODULE_COMBAT EXOSUIT_MODULE_GYGAX | EXOSUIT_MODULE_HONK | EXOSUIT_MODULE_DURAND | EXOSUIT_MODULE_PHAZON +/// Module is compatible with "Medical" Exosuit modelsm - Odysseus +#define EXOSUIT_MODULE_MEDICAL EXOSUIT_MODULE_ODYSSEUS | EXOSUIT_MODULE_GYGAX_MED diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 3aa5a07480..d66521d945 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -74,6 +74,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define ismush(A) (is_species(A, /datum/species/mush)) #define isshadow(A) (is_species(A, /datum/species/shadow)) #define isskeleton(A) (is_species(A, /datum/species/skeleton)) +#define isethereal(A) (is_species(A, /datum/species/ethereal)) // Citadel specific species #define isipcperson(A) (is_species(A, /datum/species/ipc)) diff --git a/code/__DEFINES/layers_planes.dm b/code/__DEFINES/layers_planes.dm index 6f33bb1a0e..9188efacfd 100644 --- a/code/__DEFINES/layers_planes.dm +++ b/code/__DEFINES/layers_planes.dm @@ -131,6 +131,8 @@ #define LIGHTING_LAYER 15 #define LIGHTING_RENDER_TARGET "LIGHT_PLANE" +#define RAD_TEXT_LAYER 15.1 + #define ABOVE_LIGHTING_PLANE 16 #define ABOVE_LIGHTING_LAYER 16 #define ABOVE_LIGHTING_RENDER_TARGET "ABOVE_LIGHTING_PLANE" diff --git a/code/__DEFINES/loadout.dm b/code/__DEFINES/loadout.dm new file mode 100644 index 0000000000..94059c0880 --- /dev/null +++ b/code/__DEFINES/loadout.dm @@ -0,0 +1,56 @@ +//defines for loadout categories +//no category defines +#define LOADOUT_CATEGORY_NONE "ERROR" +#define LOADOUT_SUBCATEGORY_NONE "Miscellaneous" +#define LOADOUT_SUBCATEGORIES_NONE list("Miscellaneous") + +//backpack +#define LOADOUT_CATEGORY_BACKPACK "In backpack" +#define LOADOUT_SUBCATEGORY_BACKPACK_GENERAL "General" //basically anything that there's not enough of to have its own subcategory +#define LOADOUT_SUBCATEGORY_BACKPACK_TOYS "Toys" +//neck +#define LOADOUT_CATEGORY_NECK "Neck" +#define LOADOUT_SUBCATEGORY_NECK_GENERAL "General" +#define LOADOUT_SUBCATEGORY_NECK_TIE "Ties" +#define LOADOUT_SUBCATEGORY_NECK_SCARVES "Scarves" + +//mask +#define LOADOUT_CATEGORY_MASK "Mask" + +//hands +#define LOADOUT_CATEGORY_HANDS "Hands" + +//uniform +#define LOADOUT_CATEGORY_UNIFORM "Uniform" //there's so many types of uniform it's best to have lots of categories +#define LOADOUT_SUBCATEGORY_UNIFORM_GENERAL "General" +#define LOADOUT_SUBCATEGORY_UNIFORM_JOBS "Jobs" +#define LOADOUT_SUBCATEGORY_UNIFORM_SUITS "Suits" +#define LOADOUT_SUBCATEGORY_UNIFORM_SKIRTS "Skirts" +#define LOADOUT_SUBCATEGORY_UNIFORM_DRESSES "Dresses" +#define LOADOUT_SUBCATEGORY_UNIFORM_SWEATERS "Sweaters" +#define LOADOUT_SUBCATEGORY_UNIFORM_PANTS "Pants" +#define LOADOUT_SUBCATEGORY_UNIFORM_SHORTS "Shorts" + +//suit +#define LOADOUT_CATEGORY_SUIT "Suit" +#define LOADOUT_SUBCATEGORY_SUIT_GENERAL "General" +#define LOADOUT_SUBCATEGORY_SUIT_COATS "Coats" +#define LOADOUT_SUBCATEGORY_SUIT_JACKETS "Jackets" +#define LOADOUT_SUBCATEGORY_SUIT_JOBS "Jobs" + +//head +#define LOADOUT_CATEGORY_HEAD "Head" +#define LOADOUT_SUBCATEGORY_HEAD_GENERAL "General" +#define LOADOUT_SUBCATEGORY_HEAD_JOBS "Jobs" + +//shoes +#define LOADOUT_CATEGORY_SHOES "Shoes" + +//gloves +#define LOADOUT_CATEGORY_GLOVES "Gloves" + +//glasses +#define LOADOUT_CATEGORY_GLASSES "Glasses" + +//donator items +#define LOADOUT_CATEGORY_DONATOR "Donator" diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index d294c69972..927b661898 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -341,10 +341,11 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define COLOUR_PRIORITY_AMOUNT 4 //how many priority levels there are. //Endgame Results -#define NUKE_MISS_STATION 1 -#define NUKE_SYNDICATE_BASE 2 -#define STATION_DESTROYED_NUKE 3 -#define STATION_EVACUATED 4 +#define NUKE_NEAR_MISS 1 +#define NUKE_MISS_STATION 2 +#define NUKE_SYNDICATE_BASE 3 +#define STATION_DESTROYED_NUKE 4 +#define STATION_EVACUATED 5 #define BLOB_WIN 8 #define BLOB_NUKE 9 #define BLOB_DESTROYED 10 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 52657e9104..782095f883 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -176,6 +176,15 @@ #define DISGUST_LEVEL_VERYGROSS 50 #define DISGUST_LEVEL_GROSS 25 +//Charge levels for Ethereals +#define ETHEREAL_CHARGE_NONE 0 +#define ETHEREAL_CHARGE_LOWPOWER 20 +#define ETHEREAL_CHARGE_NORMAL 50 +#define ETHEREAL_CHARGE_ALMOSTFULL 75 +#define ETHEREAL_CHARGE_FULL 100 +#define ETHEREAL_CHARGE_OVERLOAD 125 +#define ETHEREAL_CHARGE_DANGEROUS 150 + //Slime evolution threshold. Controls how fast slimes can split/grow #define SLIME_EVOLUTION_THRESHOLD 10 @@ -284,6 +293,7 @@ #define DOOR_CRUSH_DAMAGE 15 //the amount of damage that airlocks deal when they crush you #define HUNGER_FACTOR 0.1 //factor at which mob nutrition decreases +#define ETHEREAL_CHARGE_FACTOR 0.08 //factor at which ethereal's charge decreases #define REAGENTS_METABOLISM 0.4 //How many units of reagent are consumed per tick, by default. #define REAGENTS_EFFECT_MULTIPLIER (REAGENTS_METABOLISM / 0.4) // By defining the effect multiplier this way, it'll exactly adjust all effects according to how they originally were with the 0.4 metabolism diff --git a/code/__DEFINES/pool.dm b/code/__DEFINES/pool.dm index 1e66109dc7..64bbd20b19 100644 --- a/code/__DEFINES/pool.dm +++ b/code/__DEFINES/pool.dm @@ -8,6 +8,5 @@ GLOBAL_LIST_INIT(blacklisted_pool_reagents, list( /datum/reagent/toxin/plasma, /datum/reagent/oxygen, /datum/reagent/nitrous_oxide, /datum/reagent/nitrogen, //gases /datum/reagent/fermi, //blanket fermichem ban sorry. this also covers mkultra, genital enlargers, etc etc. - /datum/reagent/drug/aphrodisiac, /datum/reagent/drug/anaphrodisiac, /datum/reagent/drug/aphrodisiacplus, /datum/reagent/drug/anaphrodisiacplus, //literally asking for prefbreaks /datum/reagent/consumable/femcum, /datum/reagent/consumable/semen //NO. )) diff --git a/code/__DEFINES/tgui.dm b/code/__DEFINES/tgui.dm index 5ba0096d1b..f5adeadade 100644 --- a/code/__DEFINES/tgui.dm +++ b/code/__DEFINES/tgui.dm @@ -1,4 +1,28 @@ -#define UI_INTERACTIVE 2 // Green/Interactive -#define UI_UPDATE 1 // Orange/Updates Only -#define UI_DISABLED 0 // Red/Disabled -#define UI_CLOSE -1 // Closed \ No newline at end of file +/// Green eye; fully interactive +#define UI_INTERACTIVE 2 +/// Orange eye; updates but is not interactive +#define UI_UPDATE 1 +/// Red eye; disabled, does not update +#define UI_DISABLED 0 +/// UI Should close +#define UI_CLOSE -1 + +/// Maximum number of windows that can be suspended/reused +#define TGUI_WINDOW_SOFT_LIMIT 5 +/// Maximum number of open windows +#define TGUI_WINDOW_HARD_LIMIT 9 + +/// Maximum ping timeout allowed to detect zombie windows +#define TGUI_PING_TIMEOUT 4 SECONDS + +/// Window does not exist +#define TGUI_WINDOW_CLOSED 0 +/// Window was just opened, but is still not ready to be sent data +#define TGUI_WINDOW_LOADING 1 +/// Window is free and ready to receive data +#define TGUI_WINDOW_READY 2 + +/// Get a window id based on the provided pool index +#define TGUI_WINDOW_ID(index) "tgui-window-[index]" +/// Get a pool index of the provided window id +#define TGUI_WINDOW_INDEX(window_id) text2num(copytext(window_id, 13)) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 3e5b27d376..e8f584cf75 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -178,6 +178,7 @@ #define TRAIT_FREERUNNING "freerunning" #define TRAIT_SKITTISH "skittish" #define TRAIT_POOR_AIM "poor_aim" +#define TRAIT_INSANE_AIM "insane_aim" //they don't miss. they never miss. it was all part of their immaculate plan. #define TRAIT_PROSOPAGNOSIA "prosopagnosia" #define TRAIT_DRUNK_HEALING "drunk_healing" #define TRAIT_TAGGER "tagger" @@ -202,7 +203,7 @@ #define TRAIT_NO_ALCOHOL "alcohol_intolerance" #define TRAIT_MUTATION_STASIS "mutation_stasis" //Prevents processed genetics mutations from processing. #define TRAIT_FAST_PUMP "fast_pump" -#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good.... +#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good... // mobility flag traits // IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it) diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index 733cbcfd8d..62e6c4daf9 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -201,9 +201,18 @@ WRITE_LOG(log, "Starting up round ID [GLOB.round_id].\n-------------------------") /* ui logging */ - -/proc/log_tgui(text) - WRITE_LOG(GLOB.tgui_log, text) +/proc/log_tgui(user_or_client, text) + var/entry = "" + if(!user_or_client) + entry += "no user" + else if(istype(user_or_client, /mob)) + var/mob/user = user_or_client + entry += "[user.ckey] (as [user])" + else if(istype(user_or_client, /client)) + var/client/client = user_or_client + entry += "[client.ckey]" + entry += ":\n[text]" + WRITE_LOG(GLOB.tgui_log, entry) /* Close open log handles. This should be called as late as possible, and no logging should hapen after. */ /proc/shutdown_logging() diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 0825e51ae4..a688d20451 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -85,9 +85,6 @@ GLOBAL_VAR_INIT(cmp_field, "name") /proc/cmp_advdisease_resistance_asc(datum/disease/advance/A, datum/disease/advance/B) return A.totalResistance() - B.totalResistance() -/proc/cmp_job_display_asc(datum/job/A, datum/job/B) - return A.display_order - B.display_order - /proc/cmp_uplink_items_dsc(datum/uplink_item/A, datum/uplink_item/B) return sorttext(initial(B.name), initial(A.name)) @@ -97,9 +94,6 @@ GLOBAL_VAR_INIT(cmp_field, "name") /proc/cmp_numbered_displays_name_dsc(datum/numbered_display/A, datum/numbered_display/B) return sorttext(B.sample_object.name, A.sample_object.name) -/proc/cmp_reagents_asc(datum/reagent/a, datum/reagent/b) - return sorttext(initial(b.name),initial(a.name)) - /proc/cmp_quirk_asc(datum/quirk/A, datum/quirk/B) var/a_sign = num2sign(initial(A.value) * -1) var/b_sign = num2sign(initial(B.value) * -1) @@ -133,3 +127,12 @@ GLOBAL_VAR_INIT(cmp_field, "name") return A.required_temp - B.required_temp //return coldest else return B.required_temp - A.required_temp //return hottest + +/proc/cmp_job_display_asc(datum/job/A, datum/job/B) + return A.display_order - B.display_order + +/proc/cmp_reagents_asc(datum/reagent/a, datum/reagent/b) + return sorttext(initial(b.name),initial(a.name)) + +/proc/cmp_typepaths_asc(A, B) + return sorttext("[B]","[A]") \ No newline at end of file diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index ca12567b59..c4964022e5 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -264,6 +264,13 @@ if(!findname(.)) break +/proc/random_unique_ethereal_name(attempts_to_find_unique_name=10) + for(var/i in 1 to attempts_to_find_unique_name) + . = capitalize(ethereal_name()) + + if(!findname(.)) + break + /proc/random_unique_moth_name(attempts_to_find_unique_name=10) for(var/i in 1 to attempts_to_find_unique_name) . = capitalize(pick(GLOB.moth_first)) + " " + capitalize(pick(GLOB.moth_last)) diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm index 8b699e3eee..e1848b21a2 100644 --- a/code/__HELPERS/names.dm +++ b/code/__HELPERS/names.dm @@ -4,6 +4,12 @@ else return "[pick(GLOB.lizard_names_female)]-[pick(GLOB.lizard_names_female)]" +/proc/ethereal_name() + var/tempname = "[pick(GLOB.ethereal_names)] [random_capital_letter()]" + if(prob(65)) + tempname += random_capital_letter() + return tempname + /proc/plasmaman_name() return "[pick(GLOB.plasmaman_names)] \Roman[rand(1,99)]" diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 9c71cfeb69..7c251edd88 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -113,6 +113,22 @@ else return trim(html_encode(name), max_length) +/** + * stripped_multiline_input but reflects to the user instead if it's too big and returns null. + */ +/proc/stripped_multiline_input_or_reflect(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/name = input(user, message, title, default) as message|null + if(isnull(name)) // Return null if canceled. + return null + if(length(name) > max_length) + to_chat(user, name) + to_chat(user, "^^^----- The preceeding message has been DISCARDED for being over the maximum length of [max_length]. It has NOT been sent! -----^^^") + return null + if(no_trim) + return copytext(html_encode(name), 1, max_length) + else + return trim(html_encode(name), max_length) + #define NO_CHARS_DETECTED 0 #define SPACES_DETECTED 1 #define SYMBOLS_DETECTED 2 @@ -760,6 +776,10 @@ GLOBAL_LIST_INIT(binary, list("0","1")) else return "[number]\th" + +/proc/random_capital_letter() + return uppertext(pick(GLOB.alphabet)) + /proc/unintelligize(message) var/regex/word_boundaries = regex(@"\b[\S]+\b", "g") var/prefix = message[1] @@ -811,4 +831,4 @@ GLOBAL_LIST_INIT(binary, list("0","1")) corrupted_text += pick(corruption_options) if(prob(15)) corrupted_text += pick(corruption_options) - return corrupted_text \ No newline at end of file + return corrupted_text diff --git a/code/_globalvars/lists/loadout_categories.dm b/code/_globalvars/lists/loadout_categories.dm new file mode 100644 index 0000000000..0f0ac52214 --- /dev/null +++ b/code/_globalvars/lists/loadout_categories.dm @@ -0,0 +1,13 @@ +GLOBAL_LIST_INIT(loadout_categories, list( + LOADOUT_CATEGORY_BACKPACK = list(LOADOUT_SUBCATEGORY_BACKPACK_GENERAL, LOADOUT_SUBCATEGORY_BACKPACK_TOYS), + LOADOUT_CATEGORY_NECK = list(LOADOUT_SUBCATEGORY_NECK_GENERAL, LOADOUT_SUBCATEGORY_NECK_TIE, LOADOUT_SUBCATEGORY_NECK_SCARVES), + LOADOUT_CATEGORY_MASK = LOADOUT_SUBCATEGORIES_NONE, + LOADOUT_CATEGORY_HANDS = LOADOUT_SUBCATEGORIES_NONE, + LOADOUT_CATEGORY_UNIFORM = list(LOADOUT_SUBCATEGORY_UNIFORM_GENERAL, LOADOUT_SUBCATEGORY_UNIFORM_JOBS, LOADOUT_SUBCATEGORY_UNIFORM_SUITS, LOADOUT_SUBCATEGORY_UNIFORM_SKIRTS, LOADOUT_SUBCATEGORY_UNIFORM_DRESSES, LOADOUT_SUBCATEGORY_UNIFORM_SWEATERS, LOADOUT_SUBCATEGORY_UNIFORM_PANTS, LOADOUT_SUBCATEGORY_UNIFORM_SHORTS), + LOADOUT_CATEGORY_SUIT = list(LOADOUT_SUBCATEGORY_SUIT_GENERAL, LOADOUT_SUBCATEGORY_SUIT_COATS, LOADOUT_SUBCATEGORY_SUIT_JACKETS, LOADOUT_SUBCATEGORY_SUIT_JOBS), + LOADOUT_CATEGORY_HEAD = list(LOADOUT_SUBCATEGORY_HEAD_GENERAL, LOADOUT_SUBCATEGORY_HEAD_JOBS), + LOADOUT_CATEGORY_SHOES = LOADOUT_SUBCATEGORIES_NONE, + LOADOUT_CATEGORY_GLOVES = LOADOUT_SUBCATEGORIES_NONE, + LOADOUT_CATEGORY_GLASSES = LOADOUT_SUBCATEGORIES_NONE, + LOADOUT_CATEGORY_DONATOR = LOADOUT_SUBCATEGORIES_NONE +)) diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm index e334d08040..b80fcf0bbf 100644 --- a/code/_globalvars/lists/names.dm +++ b/code/_globalvars/lists/names.dm @@ -17,6 +17,7 @@ GLOBAL_LIST_INIT(golem_names, world.file2list("strings/names/golem.txt")) GLOBAL_LIST_INIT(moth_first, world.file2list("strings/names/moth_first.txt")) GLOBAL_LIST_INIT(moth_last, world.file2list("strings/names/moth_last.txt")) GLOBAL_LIST_INIT(plasmaman_names, world.file2list("strings/names/plasmaman.txt")) +GLOBAL_LIST_INIT(ethereal_names, world.file2list("strings/names/ethereal.txt")) GLOBAL_LIST_INIT(posibrain_names, world.file2list("strings/names/posibrain.txt")) GLOBAL_LIST_INIT(nightmare_names, world.file2list("strings/names/nightmare.txt")) GLOBAL_LIST_INIT(megacarp_first_names, world.file2list("strings/names/megacarp1.txt")) diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index 8111680c41..6dc1433bc8 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -493,6 +493,16 @@ Recharging stations are available in robotics, the dormitory bathrooms, and the desc = "Unit's power cell is running low. Recharging stations are available in robotics, the dormitory bathrooms, and the AI satellite." icon_state = "lowcell" +/obj/screen/alert/etherealcharge + name = "Low Blood Charge" + desc = "Your blood's electric charge is running low, find a source of charge for your blood. Use a recharging station found in robotics or the dormitory bathrooms, or eat some Ethereal-friendly food." + icon_state = "etherealcharge" + +/obj/screen/alert/ethereal_overcharge + name = "Blood Overcharge" + desc = "Your blood's electric charge is becoming dangerously high, find an outlet for your energy. Use Grab Intent on an APC to channel your energy into it." + icon_state = "ethereal_overcharge" + //Need to cover all use cases - emag, illegal upgrade module, malf AI hack, traitor cyborg /obj/screen/alert/hacked name = "Hacked" diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 4b2cf24f1e..4f59992ace 100644 --- a/code/controllers/subsystem/job.dm +++ b/code/controllers/subsystem/job.dm @@ -674,7 +674,7 @@ SUBSYSTEM_DEF(job) return for(var/i in the_mob.client.prefs.chosen_gear) var/datum/gear/G = i - G = GLOB.loadout_items[slot_to_string(initial(G.category))][initial(G.name)] + G = GLOB.loadout_items[initial(G.category)][initial(G.subcategory)][initial(G.name)] if(!G) continue var/permitted = TRUE @@ -682,14 +682,14 @@ SUBSYSTEM_DEF(job) permitted = FALSE if(G.donoritem && !G.donator_ckey_check(the_mob.client.ckey)) permitted = FALSE - if(!equipbackpackstuff && G.category == SLOT_IN_BACKPACK)//snowflake check since plopping stuff in the backpack doesnt work for pre-job equip loadout stuffs + if(!equipbackpackstuff && G.slot == SLOT_IN_BACKPACK)//snowflake check since plopping stuff in the backpack doesnt work for pre-job equip loadout stuffs permitted = FALSE - if(equipbackpackstuff && G.category != SLOT_IN_BACKPACK)//ditto + if(equipbackpackstuff && G.slot != SLOT_IN_BACKPACK)//ditto permitted = FALSE if(!permitted) continue var/obj/item/I = new G.path - if(!M.equip_to_slot_if_possible(I, G.category, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // If the job's dresscode compliant, try to put it in its slot, first + if(!M.equip_to_slot_if_possible(I, G.slot, disable_warning = TRUE, bypass_equip_delay_self = TRUE)) // If the job's dresscode compliant, try to put it in its slot, first if(iscarbon(M)) var/mob/living/carbon/C = M var/obj/item/storage/backpack/B = C.back diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index 9bda1cd233..c802448536 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -775,12 +775,14 @@ SUBSYSTEM_DEF(shuttle) preview_shuttle = null -/datum/controller/subsystem/shuttle/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "ShuttleManipulator", name, 800, 600, master_ui, state) - ui.open() +/datum/controller/subsystem/shuttle/ui_state(mob/user) + return GLOB.admin_state +/datum/controller/subsystem/shuttle/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ShuttleManipulator") + ui.open() /datum/controller/subsystem/shuttle/ui_data(mob/user) var/list/data = list() diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm index 58bc28fa2f..a5526d2c03 100644 --- a/code/controllers/subsystem/tgui.dm +++ b/code/controllers/subsystem/tgui.dm @@ -1,3 +1,12 @@ +/** + * tgui subsystem + * + * Contains all tgui state and subsystem code. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + SUBSYSTEM_DEF(tgui) name = "tgui" wait = 9 @@ -5,33 +14,338 @@ SUBSYSTEM_DEF(tgui) priority = FIRE_PRIORITY_TGUI runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT - var/list/currentrun = list() - var/list/open_uis = list() // A list of open UIs, grouped by src_object and ui_key. - var/list/processing_uis = list() // A list of processing UIs, ungrouped. - var/basehtml // The HTML base used for all UIs. + /// A list of UIs scheduled to process + var/list/current_run = list() + /// A list of open UIs + var/list/open_uis = list() + /// A list of open UIs, grouped by src_object. + var/list/open_uis_by_src = list() + /// The HTML base used for all UIs. + var/basehtml /datum/controller/subsystem/tgui/PreInit() - basehtml = file2text('tgui/packages/tgui/public/tgui.html') + basehtml = file2text('tgui/packages/tgui/public/tgui.html') /datum/controller/subsystem/tgui/Shutdown() close_all_uis() /datum/controller/subsystem/tgui/stat_entry() - ..("P:[processing_uis.len]") + ..("P:[open_uis.len]") /datum/controller/subsystem/tgui/fire(resumed = 0) - if (!resumed) - src.currentrun = processing_uis.Copy() - //cache for sanic speed (lists are references anyways) - var/list/currentrun = src.currentrun - - while(currentrun.len) - var/datum/tgui/ui = currentrun[currentrun.len] - currentrun.len-- + if(!resumed) + src.current_run = open_uis.Copy() + // Cache for sanic speed (lists are references anyways) + var/list/current_run = src.current_run + while(current_run.len) + var/datum/tgui/ui = current_run[current_run.len] + current_run.len-- + // TODO: Move user/src_object check to process() if(ui && ui.user && ui.src_object) ui.process() else - processing_uis.Remove(ui) - if (MC_TICK_CHECK) + open_uis.Remove(ui) + if(MC_TICK_CHECK) return +/** + * public + * + * Requests a usable tgui window from the pool. + * Returns null if pool was exhausted. + * + * required user mob + * return datum/tgui + */ +/datum/controller/subsystem/tgui/proc/request_pooled_window(mob/user) + if(!user.client) + return null + var/list/windows = user.client.tgui_windows + var/window_id + var/datum/tgui_window/window + var/window_found = FALSE + // Find a usable window + for(var/i in 1 to TGUI_WINDOW_HARD_LIMIT) + window_id = TGUI_WINDOW_ID(i) + window = windows[window_id] + // As we are looping, create missing window datums + if(!window) + window = new(user.client, window_id, pooled = TRUE) + // Skip windows with acquired locks + if(window.locked) + continue + if(window.status == TGUI_WINDOW_READY) + return window + if(window.status == TGUI_WINDOW_CLOSED) + window.status = TGUI_WINDOW_LOADING + window_found = TRUE + break + if(!window_found) + log_tgui(user, "Error: Pool exhausted") + return null + return window + +/** + * public + * + * Force closes all tgui windows. + * + * required user mob + */ +/datum/controller/subsystem/tgui/proc/force_close_all_windows(mob/user) + log_tgui(user, "force_close_all_windows") + if(user.client) + user.client.tgui_windows = list() + for(var/i in 1 to TGUI_WINDOW_HARD_LIMIT) + var/window_id = TGUI_WINDOW_ID(i) + user << browse(null, "window=[window_id]") + +/** + * public + * + * Force closes the tgui window by window_id. + * + * required user mob + * required window_id string + */ +/datum/controller/subsystem/tgui/proc/force_close_window(mob/user, window_id) + log_tgui(user, "force_close_window") + // Close all tgui datums based on window_id. + for(var/datum/tgui/ui in user.tgui_open_uis) + if(ui.window && ui.window.id == window_id) + ui.close(can_be_suspended = FALSE) + // Unset machine just to be sure. + user.unset_machine() + // Close window directly just to be sure. + user << browse(null, "window=[window_id]") + +/** + * public + * + * Try to find an instance of a UI, and push an update to it. + * + * required user mob The mob who opened/is using the UI. + * required src_object datum The object/datum which owns the UI. + * optional ui datum/tgui The UI to be updated, if it exists. + * optional force_open bool If the UI should be re-opened instead of updated. + * + * return datum/tgui The found UI. + */ +/datum/controller/subsystem/tgui/proc/try_update_ui( + mob/user, + datum/src_object, + datum/tgui/ui) + // Look up a UI if it wasn't passed + if(isnull(ui)) + ui = get_open_ui(user, src_object) + // Couldn't find a UI. + if(isnull(ui)) + return null + ui.process_status() + // UI ended up with the closed status + // or is actively trying to close itself. + // FIXME: Doesn't actually fix the paper bug. + if(ui.status <= UI_CLOSE) + ui.close() + return null + ui.send_update() + return ui + +/** + * public + * + * Get a open UI given a user and src_object. + * + * required user mob The mob who opened/is using the UI. + * required src_object datum The object/datum which owns the UI. + * + * return datum/tgui The found UI. + */ +/datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object) + var/key = "[REF(src_object)]" + // No UIs opened for this src_object + if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list)) + return null + for(var/datum/tgui/ui in open_uis_by_src[key]) + // Make sure we have the right user + if(ui.user == user) + return ui + return null + +/** + * public + * + * Update all UIs attached to src_object. + * + * required src_object datum The object/datum which owns the UIs. + * + * return int The number of UIs updated. + */ +/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object) + var/count = 0 + var/key = "[REF(src_object)]" + // No UIs opened for this src_object + if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list)) + return count + for(var/datum/tgui/ui in open_uis_by_src[key]) + // Check if UI is valid. + if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user)) + ui.process(force = 1) + count++ + return count + +/** + * public + * + * Close all UIs attached to src_object. + * + * required src_object datum The object/datum which owns the UIs. + * + * return int The number of UIs closed. + */ +/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object) + var/count = 0 + var/key = "[REF(src_object)]" + // No UIs opened for this src_object + if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list)) + return count + for(var/datum/tgui/ui in open_uis_by_src[key]) + // Check if UI is valid. + if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user)) + ui.close() + count++ + return count + +/** + * public + * + * Close all UIs regardless of their attachment to src_object. + * + * return int The number of UIs closed. + */ +/datum/controller/subsystem/tgui/proc/close_all_uis() + var/count = 0 + for(var/key in open_uis_by_src) + for(var/datum/tgui/ui in open_uis_by_src[key]) + // Check if UI is valid. + if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user)) + ui.close() + count++ + return count + +/** + * public + * + * Update all UIs belonging to a user. + * + * required user mob The mob who opened/is using the UI. + * optional src_object datum If provided, only update UIs belonging this src_object. + * + * return int The number of UIs updated. + */ +/datum/controller/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object) + var/count = 0 + if(length(user?.tgui_open_uis) == 0) + return count + for(var/datum/tgui/ui in user.tgui_open_uis) + if(isnull(src_object) || ui.src_object == src_object) + ui.process(force = 1) + count++ + return count + +/** + * public + * + * Close all UIs belonging to a user. + * + * required user mob The mob who opened/is using the UI. + * optional src_object datum If provided, only close UIs belonging this src_object. + * + * return int The number of UIs closed. + */ +/datum/controller/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object) + var/count = 0 + if(length(user?.tgui_open_uis) == 0) + return count + for(var/datum/tgui/ui in user.tgui_open_uis) + if(isnull(src_object) || ui.src_object == src_object) + ui.close() + count++ + return count + +/** + * private + * + * Add a UI to the list of open UIs. + * + * required ui datum/tgui The UI to be added. + */ +/datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui) + var/key = "[REF(ui.src_object)]" + if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list)) + open_uis_by_src[key] = list() + ui.user.tgui_open_uis |= ui + var/list/uis = open_uis_by_src[key] + uis |= ui + open_uis |= ui + +/** + * private + * + * Remove a UI from the list of open UIs. + * + * required ui datum/tgui The UI to be removed. + * + * return bool If the UI was removed or not. + */ +/datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui) + var/key = "[REF(ui.src_object)]" + if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list)) + return FALSE + // Remove it from the list of processing UIs. + open_uis.Remove(ui) + // If the user exists, remove it from them too. + if(ui.user) + ui.user.tgui_open_uis.Remove(ui) + var/list/uis = open_uis_by_src[key] + uis.Remove(ui) + if(length(uis) == 0) + open_uis_by_src.Remove(key) + return TRUE + +/** + * private + * + * Handle client logout, by closing all their UIs. + * + * required user mob The mob which logged out. + * + * return int The number of UIs closed. + */ +/datum/controller/subsystem/tgui/proc/on_logout(mob/user) + close_user_uis(user) + +/** + * private + * + * Handle clients switching mobs, by transferring their UIs. + * + * required user source The client's original mob. + * required user target The client's new mob. + * + * return bool If the UIs were transferred. + */ +/datum/controller/subsystem/tgui/proc/on_transfer(mob/source, mob/target) + // The old mob had no open UIs. + if(length(source?.tgui_open_uis) == 0) + return FALSE + if(isnull(target.tgui_open_uis) || !istype(target.tgui_open_uis, /list)) + target.tgui_open_uis = list() + // Transfer all the UIs. + for(var/datum/tgui/ui in source.tgui_open_uis) + // Inform the UIs of their new owner. + ui.user = target + target.tgui_open_uis.Add(ui) + // Clear the old list. + source.tgui_open_uis.Cut() + return TRUE diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm index 9c6bb2ebfd..15115b62c0 100644 --- a/code/datums/ai_laws.dm +++ b/code/datums/ai_laws.dm @@ -431,25 +431,31 @@ if(!owner) owner = M -/datum/ai_laws/proc/get_law_list(include_zeroth = 0, show_numbers = 1) +/** + * Generates a list of all laws on this datum, including rendered HTML tags if required + * + * Arguments: + * * include_zeroth - Operator that controls if law 0 or law 666 is returned in the set + * * show_numbers - Operator that controls if law numbers are prepended to the returned laws + * * render_html - Operator controlling if HTML tags are rendered on the returned laws + */ +/datum/ai_laws/proc/get_law_list(include_zeroth = FALSE, show_numbers = TRUE, render_html = TRUE) var/list/data = list() - if (include_zeroth && devillaws && devillaws.len) - for(var/i in devillaws) - data += "[show_numbers ? "666:" : ""] [i]" + if (include_zeroth && devillaws) + for(var/law in devillaws) + data += "[show_numbers ? "666:" : ""] [render_html ? "[law]" : law]" if (include_zeroth && zeroth) - data += "[show_numbers ? "0:" : ""] [zeroth]" + data += "[show_numbers ? "0:" : ""] [render_html ? "[zeroth]" : zeroth]" for(var/law in hacked) if (length(law) > 0) - var/num = ionnum() - data += "[show_numbers ? "[num]:" : ""] [law]" + data += "[show_numbers ? "[ionnum()]:" : ""] [render_html ? "[law]" : law]" for(var/law in ion) if (length(law) > 0) - var/num = ionnum() - data += "[show_numbers ? "[num]:" : ""] [law]" + data += "[show_numbers ? "[ionnum()]:" : ""] [render_html ? "[law]" : law]" var/number = 1 for(var/law in inherent) @@ -459,6 +465,7 @@ for(var/law in supplied) if (length(law) > 0) - data += "[show_numbers ? "[number]:" : ""] [law]" + data += "[show_numbers ? "[number]:" : ""] [render_html ? "[law]" : law]" number++ return data + diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm index b9a1b5ec3b..1a9f4c0512 100644 --- a/code/datums/components/crafting/crafting.dm +++ b/code/datums/components/crafting/crafting.dm @@ -203,9 +203,9 @@ Set var amt to the value current cycle req is pointing to, its amount of type we need to delete Get var/surroundings list of things accessable to crafting by get_environment() Check the type of the current cycle req - If its reagent then do a while loop, inside it try to locate() reagent containers, inside such containers try to locate needed reagent, if there isnt remove thing from surroundings + If its reagent then do a while loop, inside it try to locate() reagent containers, inside such containers try to locate needed reagent, if there isn't remove thing from surroundings If there is enough reagent in the search result then delete the needed amount, create the same type of reagent with the same data var and put it into deletion list - If there isnt enough take all of that reagent from the container, put into deletion list, substract the amt var by the volume of reagent, remove the container from surroundings list and keep searching + If there isn't enough take all of that reagent from the container, put into deletion list, substract the amt var by the volume of reagent, remove the container from surroundings list and keep searching While doing above stuff check deletion list if it already has such reagnet, if yes merge instead of adding second one If its stack check if it has enough amount If yes create new stack with the needed amount and put in into deletion list, substract taken amount from the stack @@ -216,7 +216,7 @@ Then do a loop over parts var of the recipe Do similar stuff to what we have done above, but now in deletion list, until the parts conditions are satisfied keep taking from the deletion list and putting it into parts list for return - After its done loop over deletion list and delete all the shit that wasnt taken by parts loop + After its done loop over deletion list and delete all the shit that wasn't taken by parts loop del_reqs return the list of parts resulting object will receive as argument of CheckParts proc, on the atom level it will add them all to the contents, on all other levels it calls ..() and does whatever is needed afterwards but from contents list already */ @@ -323,9 +323,12 @@ if(user == parent) ui_interact(user) +/datum/component/personal_crafting/ui_state(mob/user) + return GLOB.not_incapacitated_turf_state + //For the UI related things we're going to assume the user is a mob rather than typesetting it to an atom as the UI isn't generated if the parent is an atom -/datum/component/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_turf_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/component/personal_crafting/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) cur_category = categories[1] if(islist(categories[cur_category])) @@ -333,7 +336,7 @@ cur_subcategory = subcats[1] else cur_subcategory = CAT_NONE - ui = new(user, src, ui_key, "PersonalCrafting", "Crafting Menu", 700, 800, master_ui, state) + ui = new(user, src, "PersonalCrafting") ui.open() /datum/component/personal_crafting/ui_data(mob/user) @@ -413,13 +416,8 @@ display_compact = !display_compact . = TRUE if("set_category") - if(!isnull(params["category"])) - cur_category = params["category"] - if(!isnull(params["subcategory"])) - if(params["subcategory"] == "0") - cur_subcategory = "" - else - cur_subcategory = params["subcategory"] + cur_category = params["category"] + cur_subcategory = params["subcategory"] || "" . = TRUE /datum/component/personal_crafting/proc/build_recipe_data(datum/crafting_recipe/R) diff --git a/code/datums/components/crafting/recipes/recipes_misc.dm b/code/datums/components/crafting/recipes/recipes_misc.dm index 214c1b1427..0cb0547978 100644 --- a/code/datums/components/crafting/recipes/recipes_misc.dm +++ b/code/datums/components/crafting/recipes/recipes_misc.dm @@ -126,7 +126,7 @@ /datum/crafting_recipe/brute_pack name = "Suture Pack" - result = /obj/item/stack/medical/suture/one + result = /obj/item/stack/medical/suture/five time = 1 reqs = list(/obj/item/stack/medical/gauze = 1, /datum/reagent/medicine/styptic_powder = 10) @@ -135,7 +135,7 @@ /datum/crafting_recipe/burn_pack name = "Regenerative Mesh" - result = /obj/item/stack/medical/mesh/one + result = /obj/item/stack/medical/mesh/five time = 1 reqs = list(/obj/item/stack/medical/gauze = 1, /datum/reagent/medicine/silver_sulfadiazine = 10) diff --git a/code/datums/components/gps.dm b/code/datums/components/gps.dm index 200ec2b956..c2b3ad1f30 100644 --- a/code/datums/components/gps.dm +++ b/code/datums/components/gps.dm @@ -80,19 +80,15 @@ GLOBAL_LIST_EMPTY(GPS_list) to_chat(user, "[parent] is now tracking, and visible to other GPS devices.") tracking = TRUE -/datum/component/gps/item/ui_interact(mob/user, ui_key = "gps", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state. +/datum/component/gps/item/ui_interact(mob/user, datum/tgui/ui) if(emped) to_chat(user, "[parent] fizzles weakly.") return - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - // Variable window height, depending on how many GPS units there are - // to show, clamped to relatively safe range. - var/gps_window_height = clamp(325 + GLOB.GPS_list.len * 14, 325, 700) - ui = new(user, src, ui_key, "Gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height + ui = new(user, src, "Gps") ui.open() - - ui.set_autoupdate(state = updating) + ui.set_autoupdate(updating) /datum/component/gps/item/ui_data(mob/user) var/list/data = list() diff --git a/code/datums/components/mood.dm b/code/datums/components/mood.dm index 90fea9ec8a..87bc681651 100644 --- a/code/datums/components/mood.dm +++ b/code/datums/components/mood.dm @@ -307,6 +307,10 @@ /datum/component/mood/proc/HandleNutrition(mob/living/L) + if(isethereal(L)) + HandleCharge(L) + if(HAS_TRAIT(L, TRAIT_NOHUNGER)) + return FALSE //no mood events for nutrition switch(L.nutrition) if(NUTRITION_LEVEL_FULL to INFINITY) add_event(null, "nutrition", /datum/mood_event/fat) @@ -321,6 +325,22 @@ if(0 to NUTRITION_LEVEL_STARVING) add_event(null, "nutrition", /datum/mood_event/starving) +/datum/component/mood/proc/HandleCharge(mob/living/carbon/human/H) + var/datum/species/ethereal/E = H.dna.species + switch(E.get_charge(H)) + if(ETHEREAL_CHARGE_NONE to ETHEREAL_CHARGE_LOWPOWER) + add_event(null, "charge", /datum/mood_event/decharged) + if(ETHEREAL_CHARGE_LOWPOWER to ETHEREAL_CHARGE_NORMAL) + add_event(null, "charge", /datum/mood_event/lowpower) + if(ETHEREAL_CHARGE_NORMAL to ETHEREAL_CHARGE_ALMOSTFULL) + clear_event(null, "charge") + if(ETHEREAL_CHARGE_ALMOSTFULL to ETHEREAL_CHARGE_FULL) + add_event(null, "charge", /datum/mood_event/charged) + if(ETHEREAL_CHARGE_FULL to ETHEREAL_CHARGE_OVERLOAD) + add_event(null, "charge", /datum/mood_event/overcharged) + if(ETHEREAL_CHARGE_OVERLOAD to ETHEREAL_CHARGE_DANGEROUS) + add_event(null, "charge", /datum/mood_event/supercharged) + /datum/component/mood/proc/update_beauty(area/A) if(A.outdoors) //if we're outside, we don't care. clear_event(null, "area_beauty") diff --git a/code/datums/components/radioactive.dm b/code/datums/components/radioactive.dm index f12e8bf007..8afa4353b2 100644 --- a/code/datums/components/radioactive.dm +++ b/code/datums/components/radioactive.dm @@ -29,10 +29,18 @@ if(strength > RAD_MINIMUM_CONTAMINATION) SSradiation.warn(src) + //Let's make er glow + //This relies on parent not being a turf or something. IF YOU CHANGE THAT, CHANGE THIS + var/atom/movable/master = parent + master.add_filter("rad_glow", 2, list("type" = "outline", "color" = "#39ff1430", "size" = 2)) + addtimer(CALLBACK(src, .proc/glow_loop, master), rand(1,19))//Things should look uneven + START_PROCESSING(SSradiation, src) /datum/component/radioactive/Destroy() STOP_PROCESSING(SSradiation, src) + var/atom/movable/master = parent + master.remove_filter("rad_glow") return ..() /datum/component/radioactive/process() @@ -46,6 +54,13 @@ if(strength <= RAD_BACKGROUND_RADIATION) return PROCESS_KILL + +/datum/component/radioactive/proc/glow_loop(atom/movable/master) + var/filter = master.get_filter("rad_glow") + if(filter) + animate(filter, alpha = 110, time = 15, loop = -1) + animate(alpha = 40, time = 25) + /datum/component/radioactive/InheritComponent(datum/component/C, i_am_original, _strength, _source, _half_life, _can_contaminate) if(!i_am_original) return diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm index 792222b27b..df44aef4de 100644 --- a/code/datums/components/squeak.dm +++ b/code/datums/components/squeak.dm @@ -11,6 +11,13 @@ // This is to stop squeak spam from inhand usage var/last_use = 0 var/use_delay = 20 + + // squeak cooldowns + var/last_squeak = 0 + var/squeak_delay = 5 + + /// chance we'll be stopped from squeaking by cooldown when something crossing us squeaks + var/cross_squeak_delay_chance = 33 // about 3 things can squeak at a time /datum/component/squeak/Initialize(custom_sounds, volume_override, chance_override, step_delay_override, use_delay_override) if(!isatom(parent)) @@ -19,6 +26,7 @@ if(ismovable(parent)) RegisterSignal(parent, list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_IMPACT), .proc/play_squeak) RegisterSignal(parent, list(COMSIG_MOVABLE_CROSSED, COMSIG_ITEM_WEARERCROSSED), .proc/play_squeak_crossed) + RegisterSignal(parent, COMSIG_CROSS_SQUEAKED, .proc/delay_squeak) RegisterSignal(parent, COMSIG_MOVABLE_DISPOSING, .proc/disposing_react) if(isitem(parent)) RegisterSignal(parent, list(COMSIG_ITEM_ATTACK, COMSIG_ITEM_ATTACK_OBJ, COMSIG_ITEM_HIT_REACT), .proc/play_squeak) @@ -39,20 +47,28 @@ use_delay = use_delay_override /datum/component/squeak/proc/play_squeak() + do_play_squeak() + +/datum/component/squeak/proc/do_play_squeak(bypass_cooldown = FALSE) + if(!bypass_cooldown && ((last_squeak + squeak_delay) >= world.time)) + return FALSE if(prob(squeak_chance)) if(!override_squeak_sounds) playsound(parent, pickweight(default_squeak_sounds), volume, 1, -1) else playsound(parent, pickweight(override_squeak_sounds), volume, 1, -1) + last_squeak = world.time + return TRUE + return FALSE /datum/component/squeak/proc/step_squeak() if(steps > step_delay) - play_squeak() + do_play_squeak(TRUE) steps = 0 else steps++ -/datum/component/squeak/proc/play_squeak_crossed(atom/movable/AM) +/datum/component/squeak/proc/play_squeak_crossed(datum/source, atom/movable/AM) if(isitem(AM)) var/obj/item/I = AM if(I.item_flags & ABSTRACT) @@ -63,13 +79,18 @@ return var/atom/current_parent = parent if(isturf(current_parent.loc)) - play_squeak() + if(do_play_squeak()) + SEND_SIGNAL(AM, COMSIG_CROSS_SQUEAKED) /datum/component/squeak/proc/use_squeak() if(last_use + use_delay < world.time) last_use = world.time play_squeak() +/datum/component/squeak/proc/delay_squeak() + if(prob(cross_squeak_delay_chance)) + last_squeak = world.time + /datum/component/squeak/proc/on_equip(datum/source, mob/equipper, slot) RegisterSignal(equipper, COMSIG_MOVABLE_DISPOSING, .proc/disposing_react, TRUE) diff --git a/code/datums/components/twohanded.dm b/code/datums/components/twohanded.dm index 77a9a79bf4..bcbf979101 100644 --- a/code/datums/components/twohanded.dm +++ b/code/datums/components/twohanded.dm @@ -105,6 +105,8 @@ /// Triggered on attack self of the item containing the component /datum/component/two_handed/proc/on_attack_self(datum/source, mob/user) + if(!user.is_holding(parent)) + return //give no quarter to telekinesis powergaemrs (telekinetic wielding will desync the offhand and result in all sorts of bugs so no until someone codes it properly) if(wielded) unwield(user) else diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm index c7bbe01b2f..460a73bf5a 100644 --- a/code/datums/components/uplink.dm +++ b/code/datums/components/uplink.dm @@ -143,23 +143,19 @@ GLOBAL_LIST_EMPTY(uplinks) // an unlocked uplink blocks also opening the PDA or headset menu return COMPONENT_NO_INTERACT -/datum/component/uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state) +/datum/component/uplink/ui_state(mob/user) + return GLOB.inventory_state + +/datum/component/uplink/ui_interact(mob/user, datum/tgui/ui) active = TRUE - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Uplink", name, 620, 580, master_ui, state) + ui = new(user, src, "Uplink", name) // This UI is only ever opened by one person, // and never is updated outside of user input. ui.set_autoupdate(FALSE) ui.open() -/datum/component/uplink/ui_host(mob/user) - if(istype(parent, /obj/item/implant)) //implants are like organs, not really located inside mobs codewise. - var/obj/item/implant/I = parent - return I.imp_in - return ..() - /datum/component/uplink/ui_data(mob/user) if(!user.mind) return @@ -187,8 +183,18 @@ GLOBAL_LIST_EMPTY(uplinks) is_inaccessible = FALSE if(is_inaccessible) continue + /* + if(I.restricted_species) //catpeople specfic gloves. + if(ishuman(user)) + var/is_inaccessible = TRUE + var/mob/living/carbon/human/H = user + for(var/F in I.restricted_species) + if(F == H.dna.species.id || debug) + is_inaccessible = FALSE + break if(is_inaccessible) continue + */ cat["items"] += list(list( "name" = I.name, "cost" = I.cost, diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index cf942f077f..5aeb7626f6 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -1,13 +1,13 @@ - +//TODO: someone please get rid of this shit /datum/datacore - var/medical[] = list() + var/list/medical = list() var/medicalPrintCount = 0 - var/general[] = list() - var/security[] = list() + var/list/general = list() + var/list/security = list() var/securityPrintCount = 0 var/securityCrimeCounter = 0 - //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). - var/locked[] = list() + ///This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). + var/list/locked = list() /datum/data var/name = "data" @@ -91,6 +91,42 @@ if(foundrecord) foundrecord.fields["rank"] = assignment +/datum/datacore/proc/get_manifest_tg() //copypasted from tg, renamed to avoid namespace conflicts + var/list/manifest_out = list() + var/list/departments = list( + "Command" = GLOB.command_positions, + "Security" = GLOB.security_positions, + "Engineering" = GLOB.engineering_positions, + "Medical" = GLOB.medical_positions, + "Science" = GLOB.science_positions, + "Supply" = GLOB.supply_positions, + "Service" = GLOB.civilian_positions, + "Silicon" = GLOB.nonhuman_positions + ) + for(var/datum/data/record/t in GLOB.data_core.general) + var/name = t.fields["name"] + var/rank = t.fields["rank"] + var/has_department = FALSE + for(var/department in departments) + var/list/jobs = departments[department] + if(rank in jobs) + if(!manifest_out[department]) + manifest_out[department] = list() + manifest_out[department] += list(list( + "name" = name, + "rank" = rank + )) + has_department = TRUE + break + if(!has_department) + if(!manifest_out["Misc"]) + manifest_out["Misc"] = list() + manifest_out["Misc"] += list(list( + "name" = name, + "rank" = rank + )) + return manifest_out + /datum/datacore/proc/get_manifest(monochrome, OOC) var/list/heads = list() var/list/sec = list() diff --git a/code/datums/dna.dm b/code/datums/dna.dm index 8d80bc3f36..de5c1ece91 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -50,6 +50,7 @@ destination.dna.skin_tone_override = skin_tone_override destination.dna.features = features.Copy() destination.set_species(species.type, icon_update=0) + destination.dna.species.say_mod = species.say_mod destination.dna.real_name = real_name destination.dna.nameless = nameless destination.dna.custom_species = custom_species @@ -74,6 +75,7 @@ new_dna.skin_tone_override = skin_tone_override new_dna.features = features.Copy() new_dna.species = new species.type + new_dna.species.say_mod = species.say_mod new_dna.real_name = real_name new_dna.nameless = nameless new_dna.custom_species = custom_species diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm index 0f4f02ae2a..4bf02e8587 100644 --- a/code/datums/holocall.dm +++ b/code/datums/holocall.dm @@ -32,20 +32,29 @@ var/datum/action/innate/end_holocall/hangup //hangup action var/call_start_time + var/head_call = FALSE //calls from a head of staff autoconnect, if the receiving pad is not secure. //creates a holocall made by `caller` from `calling_pad` to `callees` -/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees) +/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees, elevated_access = FALSE) call_start_time = world.time user = caller calling_pad.outgoing_call = src calling_holopad = calling_pad + head_call = elevated_access dialed_holopads = list() for(var/I in callees) var/obj/machinery/holopad/H = I if(!QDELETED(H) && H.is_operational()) dialed_holopads += H - H.say("Incoming call.") + if(head_call) + if(H.secure) + calling_pad.say("Auto-connection refused, falling back to call mode.") + H.say("Incoming call.") + else + H.say("Incoming connection.") + else + H.say("Incoming call.") LAZYADD(H.holo_calls, src) if(!dialed_holopads.len) @@ -79,6 +88,7 @@ dialed_holopads.Cut() if(calling_holopad) + calling_holopad.calling = FALSE calling_holopad.outgoing_call = null calling_holopad.SetLightsAndPower() calling_holopad = null @@ -145,6 +155,7 @@ if(!Check()) return + calling_holopad.calling = FALSE hologram = H.activate_holo(user) hologram.HC = src @@ -160,6 +171,8 @@ hangup = new(eye, src) hangup.Grant(user) + playsound(H, 'sound/machines/ping.ogg', 100) + H.say("Connection established.") //Checks the validity of a holocall and qdels itself if it's not. Returns TRUE if valid, FALSE otherwise /datum/holocall/proc/Check() @@ -178,7 +191,6 @@ . = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME) if(!.) calling_holopad.say("No answer received.") - calling_holopad.temp = "" if(!.) testing("Holocall Check fail") @@ -241,10 +253,10 @@ record.caller_image = holodiskOriginal.record.caller_image record.entries = holodiskOriginal.record.entries.Copy() record.language = holodiskOriginal.record.language - to_chat(user, "You copy the record from [holodiskOriginal] to [src] by connecting the ports!") + to_chat(user, "You copy the record from [holodiskOriginal] to [src] by connecting the ports!") name = holodiskOriginal.name else - to_chat(user, "[holodiskOriginal] has no record on it!") + to_chat(user, "[holodiskOriginal] has no record on it!") ..() /obj/item/disk/holodisk/proc/build_record() @@ -331,6 +343,21 @@ DELAY 20"} /datum/preset_holoimage/engineer + outfit_type = /datum/outfit/job/engineer + +/datum/preset_holoimage/engineer/rig + outfit_type = /datum/outfit/job/engineer/gloved/rig + +/datum/preset_holoimage/engineer/ce + outfit_type = /datum/outfit/job/ce + +/datum/preset_holoimage/engineer/ce/rig + outfit_type = /datum/outfit/job/engineer/gloved/rig + +/datum/preset_holoimage/engineer/atmos + outfit_type = /datum/outfit/job/atmos + +/datum/preset_holoimage/engineer/atmos/rig outfit_type = /datum/outfit/job/engineer/gloved/rig /datum/preset_holoimage/researcher @@ -350,3 +377,90 @@ /datum/preset_holoimage/clown outfit_type = /datum/outfit/job/clown + +/obj/item/disk/holodisk/donutstation/whiteship + name = "Blackbox Print-out #DS024" + desc = "A holodisk containing the last viable recording of DS024's blackbox." + preset_image_type = /datum/preset_holoimage/engineer/ce + preset_record_text = {" + NAME Geysr Shorthalt + SAY Engine renovations complete and the ships been loaded. We all ready? + DELAY 25 + PRESET /datum/preset_holoimage/engineer + NAME Jacob Ullman + SAY Lets blow this popsicle stand of a station. + DELAY 20 + PRESET /datum/preset_holoimage/engineer/atmos + NAME Lindsey Cuffler + SAY Uh, sir? Shouldn't we call for a secondary shuttle? The bluespace drive on this thing made an awfully weird noise when we jumped here.. + DELAY 30 + PRESET /datum/preset_holoimage/engineer/ce + NAME Geysr Shorthalt + SAY Pah! Ship techie at the dock said to give it a good few kicks if it started acting up, let me just.. + DELAY 25 + SOUND punch + SOUND sparks + DELAY 10 + SOUND punch + SOUND sparks + DELAY 10 + SOUND punch + SOUND sparks + SOUND warpspeed + DELAY 15 + PRESET /datum/preset_holoimage/engineer/atmos + NAME Lindsey Cuffler + SAY Uhh.. is it supposed to be doing that?? + DELAY 15 + PRESET /datum/preset_holoimage/engineer/ce + NAME Geysr Shorthalt + SAY See? Working as intended. Now, are we all ready? + DELAY 10 + PRESET /datum/preset_holoimage/engineer + NAME Jacob Ullman + SAY Is it supposed to be glowing like that? + DELAY 20 + SOUND explosion + + "} + +/obj/item/disk/holodisk/ruin/snowengieruin + name = "Blackbox Print-out #EB412" + desc = "A holodisk containing the last moments of EB412. There's a bloody fingerprint on it." + preset_image_type = /datum/preset_holoimage/engineer + preset_record_text = {" + NAME Dave Tundrale + SAY Maria, how's Build? + DELAY 10 + NAME Maria Dell + PRESET /datum/preset_holoimage/engineer/atmos + SAY It's fine, don't worry. I've got Plastic on it. And frankly, i'm kinda busy with, the, uhhm, incinerator. + DELAY 30 + NAME Dave Tundrale + PRESET /datum/preset_holoimage/engineer + SAY Aight, wonderful. The science mans been kinda shit though. No RCDs- + DELAY 20 + NAME Maria Dell + PRESET /datum/preset_holoimage/engineer/atmos + SAY Enough about your RCDs. They're not even that important, just bui- + DELAY 15 + SOUND explosion + DELAY 10 + SAY Oh, shit! + DELAY 10 + PRESET /datum/preset_holoimage/engineer/atmos/rig + LANGUAGE /datum/language/narsie + NAME Unknown + SAY RISE, MY LORD!! + DELAY 10 + LANGUAGE /datum/language/common + NAME Plastic + PRESET /datum/preset_holoimage/engineer/rig + SAY Fuck, fuck, fuck! + DELAY 20 + SAY It's loose! CALL THE FUCKING SHUTT- + DELAY 10 + PRESET /datum/preset_holoimage/corgi + NAME Blackbox Automated Message + SAY Connection lost. Dumping audio logs to disk. + DELAY 50"} diff --git a/code/datums/mood_events/needs_events.dm b/code/datums/mood_events/needs_events.dm index 962681eb94..b307bcb0aa 100644 --- a/code/datums/mood_events/needs_events.dm +++ b/code/datums/mood_events/needs_events.dm @@ -19,6 +19,27 @@ description = "I'm starving!\n" mood_change = -15 +//charge +/datum/mood_event/supercharged + description = "I can't possibly keep all this power inside, I need to release some quick!\n" + mood_change = -10 + +/datum/mood_event/overcharged + description = "I feel dangerously overcharged, perhaps I should release some power.\n" + mood_change = -4 + +/datum/mood_event/charged + description = "I feel the power in my veins!\n" + mood_change = 6 + +/datum/mood_event/lowpower + description = "My power is running low, I should go charge up somewhere.\n" + mood_change = -6 + +/datum/mood_event/decharged + description = "I'm in desperate need of some electricity!\n" + mood_change = -10 + //Disgust /datum/mood_event/gross description = "I saw something gross.\n" diff --git a/code/datums/radiation_wave.dm b/code/datums/radiation_wave.dm index fe018a8761..8dc3b64e16 100644 --- a/code/datums/radiation_wave.dm +++ b/code/datums/radiation_wave.dm @@ -105,7 +105,6 @@ // modify the ignored_things list in __HELPERS/radiation.dm instead var/static/list/blacklisted = typecacheof(list( /turf, - /mob, /obj/structure/cable, /obj/machinery/atmospherics, /obj/item/ammo_casing, diff --git a/code/datums/skills/_check_skills.dm b/code/datums/skills/_check_skills.dm index 41939dbee3..91b36a123f 100644 --- a/code/datums/skills/_check_skills.dm +++ b/code/datums/skills/_check_skills.dm @@ -14,11 +14,14 @@ mind.skill_holder.ui_interact(src) -/datum/skill_holder/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/skill_holder/ui_state(mob/user) + return GLOB.always_state + +/datum/skill_holder/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "SkillPanel", "[owner.name]'s Skills", 620, 580, master_ui, state) - ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input. + ui = new(user, src, "SkillPanel", "[owner.name]'s Skills") + ui.set_autoupdate(FALSE) ui.open() else if(need_static_data_update) update_static_data(user) diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm index d66445b8a1..95a7d8e633 100644 --- a/code/datums/spawners_menu.dm +++ b/code/datums/spawners_menu.dm @@ -6,10 +6,13 @@ qdel(src) owner = new_owner -/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/spawners_menu/ui_state(mob/user) + return GLOB.observer_state + +/datum/spawners_menu/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "SpawnersMenu", "Spawners Menu", 700, 600, master_ui, state) + ui = new(user, src, "SpawnersMenu") ui.open() /datum/spawners_menu/ui_data(mob/user) @@ -42,11 +45,15 @@ if(..()) return - var/spawner_ref = pick(GLOB.mob_spawners[params["name"]]) - var/obj/effect/mob_spawn/MS = locate(spawner_ref) in GLOB.poi_list - if(!MS) + var/group_name = params["name"] + if(!group_name || !(group_name in GLOB.mob_spawners)) + return + var/list/spawnerlist = GLOB.mob_spawners[group_name] + if(!spawnerlist.len) + return + var/obj/effect/mob_spawn/MS = pick(spawnerlist) + if(!istype(MS) || !(MS in GLOB.poi_list)) return - switch(action) if("jump") if(MS) @@ -55,4 +62,4 @@ if("spawn") if(MS) MS.attack_ghost(owner) - . = TRUE \ No newline at end of file + . = TRUE diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index ca304773a7..6e59d4fa2c 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -562,8 +562,8 @@ owner.DefaultCombatKnockdown(15, TRUE, FALSE, 15) if(iscarbon(owner)) var/mob/living/carbon/C = owner - C.silent = max(2, C.silent) - C.stuttering = max(5, C.stuttering) + C.silent = max(5, C.silent) //Increased, now lasts until five seconds after it ends, instead of 2 + C.stuttering = max(10, C.stuttering) //Increased, now lasts for five seconds after the mute ends, instead of 3 if(!old_health) old_health = owner.health if(!old_oxyloss) diff --git a/code/datums/wires/_wires.dm b/code/datums/wires/_wires.dm index 75bd4f5c17..04fbc4a590 100644 --- a/code/datums/wires/_wires.dm +++ b/code/datums/wires/_wires.dm @@ -97,6 +97,12 @@ /datum/wires/proc/get_wire(color) return colors[color] +/datum/wires/proc/get_color_of_wire(wire_type) + for(var/color in colors) + var/other_type = colors[color] + if(wire_type == other_type) + return color + /datum/wires/proc/get_attached(color) if(assemblies[color]) return assemblies[color] @@ -117,7 +123,7 @@ return TRUE /datum/wires/proc/is_dud(wire) - return findtext(wire, WIRE_DUD_PREFIX) + return findtext(wire, WIRE_DUD_PREFIX, 1, length(WIRE_DUD_PREFIX) + 1) /datum/wires/proc/is_dud_color(color) return is_dud(get_wire(color)) @@ -197,6 +203,7 @@ S.forceMove(holder.drop_location()) return S +/// Called from [/atom/proc/emp_act] /datum/wires/proc/emp_pulse() var/list/possible_wires = shuffle(wires) var/remaining_pulses = MAXIMUM_EMP_WIRES @@ -239,11 +246,13 @@ return ..() return UI_CLOSE -/datum/wires/ui_interact(mob/user, ui_key = "wires", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/wires/ui_state(mob/user) + return GLOB.physical_state + +/datum/wires/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if (!ui) - ui = new(user, src, ui_key, "Wires", "[holder.name] Wires", 350, 150 + wires.len * 30, master_ui, state) + ui = new(user, src, "Wires", "[holder.name] Wires") ui.open() /datum/wires/ui_data(mob/user) diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm index 2b13344cc6..6cef7ac50f 100644 --- a/code/datums/wires/airlock.dm +++ b/code/datums/wires/airlock.dm @@ -53,11 +53,10 @@ /datum/wires/airlock/interactable(mob/user) var/obj/machinery/door/airlock/A = holder - if(!A.panel_open) - return FALSE if(!A.hasSiliconAccessInArea(user) && A.isElectrified() && A.shock(user, 100)) return FALSE - return TRUE + if(A.panel_open) + return TRUE /datum/wires/airlock/get_status() var/obj/machinery/door/airlock/A = holder @@ -115,10 +114,7 @@ A.aiControlDisabled = -1 if(WIRE_SHOCK) // Pulse to shock the door for 10 ticks. if(!A.secondsElectrified) - A.set_electrified(30) - if(usr) - LAZYADD(A.shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(usr)]")) - log_combat(usr, A, "electrified") + A.set_electrified(30, usr) if(WIRE_SAFETY) A.safe = !A.safe if(!A.density) @@ -135,21 +131,17 @@ if(WIRE_POWER1, WIRE_POWER2) // Cut to loose power, repair all to gain power. if(mend && !is_cut(WIRE_POWER1) && !is_cut(WIRE_POWER2)) A.regainMainPower() - if(usr) - A.shock(usr, 50) else A.loseMainPower() - if(usr) - A.shock(usr, 50) + if(isliving(usr)) + A.shock(usr, 50) if(WIRE_BACKUP1, WIRE_BACKUP2) // Cut to loose backup power, repair all to gain backup power. if(mend && !is_cut(WIRE_BACKUP1) && !is_cut(WIRE_BACKUP2)) A.regainBackupPower() - if(usr) - A.shock(usr, 50) else A.loseBackupPower() - if(usr) - A.shock(usr, 50) + if(isliving(usr)) + A.shock(usr, 50) if(WIRE_BOLTS) // Cut to drop bolts, mend does nothing. if(!mend) A.bolt() @@ -170,10 +162,7 @@ A.set_electrified(0) else if(A.secondsElectrified != -1) - A.set_electrified(-1) - if(usr) - LAZYADD(A.shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(usr)]")) - log_combat(usr, A, "electrified") + A.set_electrified(-1, usr) if(WIRE_SAFETY) // Cut to disable safeties, mend to re-enable. A.safe = mend if(WIRE_TIMING) // Cut to disable auto-close, mend to re-enable. @@ -184,5 +173,5 @@ A.lights = mend A.update_icon() if(WIRE_ZAP1, WIRE_ZAP2) // Ouch. - if(usr) - A.shock(usr, 50) + if(isliving(usr)) + A.shock(usr, 50) \ No newline at end of file diff --git a/code/datums/wounds/slash.dm b/code/datums/wounds/slash.dm index 5fef49653f..9044835272 100644 --- a/code/datums/wounds/slash.dm +++ b/code/datums/wounds/slash.dm @@ -253,8 +253,8 @@ occur_text = "is cut open, slowly leaking blood" sound_effect = 'sound/effects/wounds/blood1.ogg' severity = WOUND_SEVERITY_MODERATE - initial_flow = 2 - minimum_flow = 0.5 + initial_flow = 1.5 + minimum_flow = 0.375 max_per_type = 3 clot_rate = 0.12 threshold_minimum = 30 @@ -270,8 +270,8 @@ occur_text = "is ripped open, veins spurting blood" sound_effect = 'sound/effects/wounds/blood2.ogg' severity = WOUND_SEVERITY_SEVERE - initial_flow = 3.25 - minimum_flow = 2.75 + initial_flow = 2.4375 + minimum_flow = 2.0625 clot_rate = 0.07 max_per_type = 4 threshold_minimum = 60 @@ -288,8 +288,8 @@ occur_text = "is torn open, spraying blood wildly" sound_effect = 'sound/effects/wounds/blood3.ogg' severity = WOUND_SEVERITY_CRITICAL - initial_flow = 4.25 - minimum_flow = 4 + initial_flow = 3.1875 + minimum_flow = 3 clot_rate = -0.05 // critical cuts actively get worse instead of better max_per_type = 5 threshold_minimum = 90 diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index f87f7a757c..672d5c096f 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -16,6 +16,8 @@ /// If it's valid territory for gangs/cults to summon var/valid_territory = TRUE + /// malf ais can hack this + var/valid_malf_hack = TRUE /// if blobs can spawn there and if it counts towards their score. var/blob_allowed = TRUE /// whether servants can warp into this area from Reebe @@ -572,6 +574,7 @@ GLOBAL_LIST_EMPTY(teleportlocs) power_environ = FALSE always_unpowered = FALSE valid_territory = FALSE + valid_malf_hack = FALSE blob_allowed = FALSE addSorted() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index de9df3a23e..dfa9776489 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -962,15 +962,16 @@ if(source != target) target.log_talk(message, message_type, tag="[tag] from [key_name(source)]", log_globally=FALSE) -/* -Proc for attack log creation, because really why not -1 argument is the actor performing the action -2 argument is the target of the action -3 is a verb describing the action (e.g. punched, throwed, kicked, etc.) -4 is a tool with which the action was made (usually an item) -5 is any additional text, which will be appended to the rest of the log line -*/ - +/** + * Log a combat message in the attack log + * + * Arguments: + * * atom/user - argument is the actor performing the action + * * atom/target - argument is the target of the action + * * what_done - is a verb describing the action (e.g. punched, throwed, kicked, etc.) + * * atom/object - is a tool with which the action was made (usually an item) + * * addition - is any additional text, which will be appended to the rest of the log line + */ /proc/log_combat(atom/user, atom/target, what_done, atom/object=null, addition=null) var/ssource = key_name(user) var/starget = key_name(target) diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm index 154d2df563..406e142a25 100644 --- a/code/game/gamemodes/clock_cult/clock_cult.dm +++ b/code/game/gamemodes/clock_cult/clock_cult.dm @@ -213,6 +213,9 @@ Credit where due: var/list/slots = list("In your left pocket" = SLOT_L_STORE, "In your right pocket" = SLOT_R_STORE, "In your backpack" = SLOT_IN_BACKPACK) if(ishuman(L)) var/mob/living/carbon/human/H = L + var/obj/item/clockwork/replica_fabricator/F = new + if(H.equip_to_slot_or_del(F, SLOT_IN_BACKPACK)) + to_chat(H, "You have been equipped with a replica fabricator, an advanced tool that can convert objects like doors, tables or even coats into clockwork equivalents.") slot = H.equip_in_one_of_slots(S, slots) if(slot == "In your backpack") slot = "In your [H.back.name]" diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 55976d69c2..2415770e66 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -201,15 +201,15 @@ . = ..() . += "Alt-click [src] to [state_open ? "close" : "open"] it." -/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) +/obj/machinery/sleeper/ui_state(mob/user) + if(controls_inside) + return GLOB.notcontained_state + return GLOB.default_state - if(controls_inside && state == GLOB.notcontained_state) - state = GLOB.default_state // If it has a set of controls on the inside, make it actually controllable by the mob in it. - - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/sleeper/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Sleeper", name, 550, 700, master_ui, state) + ui = new(user, src, "Sleeper", name) ui.open() /obj/machinery/sleeper/process() diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 3faf6d5727..50c08a5dcd 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -92,6 +92,7 @@ Class Procs: pressure_resistance = 15 max_integrity = 200 layer = BELOW_OBJ_LAYER //keeps shit coming out of the machine from ending up underneath it. + flags_1 = DEFAULT_RICOCHET_1 flags_ricochet = RICOCHET_HARD ricochet_chance_mod = 0.3 diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm index 8db5ca1d17..b004746051 100644 --- a/code/game/machinery/announcement_system.dm +++ b/code/game/machinery/announcement_system.dm @@ -93,13 +93,10 @@ GLOBAL_LIST_EMPTY(announcement_systems) for(var/channel in channels) radio.talk_into(src, message, channel) -//config stuff - -/obj/machinery/announcement_system/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - . = ..() - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/announcement_system/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AutomatedAnnouncement", "Automated Announcement System", 500, 225, master_ui, state) + ui = new(user, src, "AutomatedAnnouncement") ui.open() /obj/machinery/announcement_system/ui_data() diff --git a/code/game/machinery/bank_machine.dm b/code/game/machinery/bank_machine.dm index 8033f538ba..882d0b989c 100644 --- a/code/game/machinery/bank_machine.dm +++ b/code/game/machinery/bank_machine.dm @@ -3,6 +3,7 @@ desc = "A machine used to deposit and withdraw station funds." icon = 'goon/icons/obj/goon_terminals.dmi' idle_power_usage = 100 + var/siphoning = FALSE var/next_warning = 0 var/obj/item/radio/radio @@ -38,7 +39,6 @@ return return ..() - /obj/machinery/computer/bank_machine/process() ..() if(siphoning) @@ -51,7 +51,7 @@ end_syphon() return - playsound(src.loc, 'sound/items/poster_being_created.ogg', 100, 1) + playsound(src, 'sound/items/poster_being_created.ogg', 100, TRUE) syphoning_credits += 200 D.adjust_money(-200) if(next_warning < world.time && prob(15)) @@ -60,17 +60,20 @@ radio.talk_into(src, message, radio_channel) next_warning = world.time + minimum_time_between_warnings -/obj/machinery/computer/bank_machine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/bank_machine/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "BankMachine", name, 320, 165, master_ui, state) + ui = new(user, src, "BankMachine", name) ui.open() /obj/machinery/computer/bank_machine/ui_data(mob/user) var/list/data = list() var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR) - data["current_balance"] = D.account_balance + + if(D) + data["current_balance"] = D.account_balance + else + data["current_balance"] = 0 data["siphoning"] = siphoning data["station_name"] = station_name() diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index c8efe319db..a2fdc2336f 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -43,10 +43,13 @@ table.computer = src break -/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/operating/ui_state(mob/user) + return GLOB.not_incapacitated_state + +/obj/machinery/computer/operating/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "OperatingComputer", name, 350, 470, master_ui, state) + ui = new(user, src, "OperatingComputer", name) ui.open() /obj/machinery/computer/operating/ui_data(mob/user) @@ -153,6 +156,8 @@ )) return data + + /obj/machinery/computer/operating/ui_act(action, params) if(..()) return diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index d8553854b2..8ebb64097d 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -2,14 +2,13 @@ name = "\improper AI system integrity restorer" desc = "Used with intelliCards containing nonfunctional AIs to restore them to working order." req_access = list(ACCESS_CAPTAIN, ACCESS_ROBOTICS, ACCESS_HEADS) - var/mob/living/silicon/ai/occupier = null - var/active = 0 - circuit = /obj/item/circuitboard/computer/aifixer icon_keyboard = "tech_key" icon_screen = "ai-fixer" light_color = LIGHT_COLOR_PINK - ui_x = 370 - ui_y = 360 + circuit = /obj/item/circuitboard/computer/aifixer + + var/mob/living/silicon/ai/occupier = null + var/active = FALSE /obj/machinery/computer/aifixer/attackby(obj/I, mob/user, params) if(occupier && istype(I, /obj/item/screwdriver)) @@ -20,64 +19,45 @@ else return ..() -/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + +/obj/machinery/computer/aifixer/ui_interact(mob/user, datum/tgui/ui) //artur didn't port this correctly + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AiRestorer", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "AiRestorer", name) ui.open() - var/dat = "" +/obj/machinery/computer/aifixer/ui_data(mob/user) + var/list/data = list() - if (src.occupier) - var/laws - dat += "

Stored AI: [src.occupier.name]

" - dat += "System integrity: [(src.occupier.health+100)/2]%
" + data["ejectable"] = FALSE + data["AI_present"] = FALSE + data["error"] = null + if(!occupier) + data["error"] = "Please transfer an AI unit." + else + data["AI_present"] = TRUE + data["name"] = occupier.name + data["restoring"] = active + data["health"] = (occupier.health + 100) / 2 + data["isDead"] = occupier.stat == DEAD + data["laws"] = occupier.laws.get_law_list(include_zeroth = TRUE, render_html = FALSE) - if (src.occupier.laws.zeroth) - laws += "0: [src.occupier.laws.zeroth]
" + return data - for (var/index = 1, index <= src.occupier.laws.hacked.len, index++) - var/law = src.occupier.laws.hacked[index] - if (length(law) > 0) - var/num = ionnum() - laws += "[num]: [law]
" +/obj/machinery/computer/aifixer/ui_act(action, params) + if(..()) + return + if(!occupier) + active = FALSE - for (var/index = 1, index <= src.occupier.laws.ion.len, index++) - var/law = src.occupier.laws.ion[index] - if (length(law) > 0) - var/num = ionnum() - laws += "[num]: [law]
" - - var/number = 1 - for (var/index = 1, index <= src.occupier.laws.inherent.len, index++) - var/law = src.occupier.laws.inherent[index] - if (length(law) > 0) - laws += "[number]: [law]
" - number++ - - for (var/index = 1, index <= src.occupier.laws.supplied.len, index++) - var/law = src.occupier.laws.supplied[index] - if (length(law) > 0) - laws += "[number]: [law]
" - number++ - - dat += "Laws:
[laws]
" - - if (src.occupier.stat == DEAD) - dat += "AI non-functional" - else - dat += "AI functional" - if (!src.active) - dat += {"

Begin Reconstruction"} - else - dat += "

Reconstruction in process, please wait.
" - dat += {"
Close"} - var/datum/browser/popup = new(user, "computer", "AI System Integrity Restorer", 400, 500) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - return + switch(action) + if("PRG_beginReconstruction") + if(occupier?.health < 100) + to_chat(usr, "Reconstruction in progress. This will take several minutes.") + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE) + active = TRUE + occupier.notify_ghost_cloning("Your core files are being restored!", source = src) + . = TRUE /obj/machinery/computer/aifixer/proc/Fix() use_power(1000) @@ -97,17 +77,6 @@ active = Fix() if(oldstat != occupier.stat) update_icon() - updateDialog() - -/obj/machinery/computer/aifixer/Topic(href, href_list) - if(..()) - return - if(href_list["fix"]) - to_chat(usr, "Reconstruction in progress. This will take several minutes.") - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 25, 0) - active = TRUE - add_fingerprint(usr) - updateUsrDialog() /obj/machinery/computer/aifixer/update_overlays() . = ..() diff --git a/code/game/machinery/computer/apc_control.dm b/code/game/machinery/computer/apc_control.dm index eae2d5fa5b..c14837b8e3 100644 --- a/code/game/machinery/computer/apc_control.dm +++ b/code/game/machinery/computer/apc_control.dm @@ -3,19 +3,19 @@ desc = "Used to remotely control the flow of power to different parts of the station." icon_screen = "solar" icon_keyboard = "power_key" - req_access = list(ACCESS_ENGINE) + req_access = list(ACCESS_CE) circuit = /obj/item/circuitboard/computer/apc_control light_color = LIGHT_COLOR_YELLOW var/mob/living/operator //Who's operating the computer right now var/obj/machinery/power/apc/active_apc //The APC we're using right now - var/list/result_filters //For sorting the results - var/checking_logs = 0 + var/should_log = TRUE + var/restoring = FALSE var/list/logs - var/auth_id = "\[NULL\]" + var/auth_id = "\[NULL\]:" -/obj/machinery/computer/apc_control/Initialize() +/obj/machinery/computer/apc_control/Initialize(mapload, obj/item/circuitboard/C) . = ..() - result_filters = list("Name" = null, "Charge Above" = null, "Charge Below" = null, "Responsive" = null) + logs = list() /obj/machinery/computer/apc_control/process() if(operator && (!operator.Adjacent(src) || stat)) @@ -23,8 +23,8 @@ if(active_apc) if(!active_apc.locked) active_apc.say("Remote access canceled. Interface locked.") - playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, 0) - playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, 0) + playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, FALSE) + playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, FALSE) active_apc.locked = TRUE active_apc.update_icon() active_apc.remote_control = null @@ -34,171 +34,163 @@ if(!IsAdminGhost(user)) to_chat(user,"[src] does not support AI control.") //You already have APC access, cheater! return - ..(user) + ..() /obj/machinery/computer/apc_control/proc/check_apc(obj/machinery/power/apc/APC) return APC.z == z && !APC.malfhack && !APC.aidisabled && !(APC.obj_flags & EMAGGED) && !APC.stat && !istype(APC.area, /area/ai_monitored) && !APC.area.outdoors -/obj/machinery/computer/apc_control/ui_interact(mob/living/user) - . = ..() - var/dat - if(authenticated) - if(!checking_logs) - dat += "Logged in as [auth_id].

" - dat += "Filters
" - dat += "Name: [result_filters["Name"] ? result_filters["Name"] : "None set"]
" - dat += "Charge: \>[result_filters["Charge Above"] ? result_filters["Charge Above"] : "NaN"]% and \<[result_filters["Charge Below"] ? result_filters["Charge Below"] : "NaN"]%
" - dat += "Accessible: [result_filters["Responsive"] ? "Non-Responsive Only" : "All"]

" - for(var/A in GLOB.apcs_list) - if(check_apc(A)) - var/obj/machinery/power/apc/APC = A - if(result_filters["Name"] && !findtext(APC.name, result_filters["Name"]) && !findtext(APC.area.name, result_filters["Name"])) - continue - if(result_filters["Charge Above"] && (!APC.cell || (APC.cell && (APC.cell.charge / APC.cell.maxcharge) < result_filters["Charge Above"] / 100))) - continue - if(result_filters["Charge Below"] && APC.cell && (APC.cell.charge / APC.cell.maxcharge) > result_filters["Charge Below"] / 100) - continue - if(result_filters["Responsive"] && !APC.aidisabled) - continue - dat += "[A]
\ - Charge: [APC.cell ? "[DisplayEnergy(APC.cell.charge)] / [DisplayEnergy(APC.cell.maxcharge)] ([round((APC.cell.charge / APC.cell.maxcharge) * 100)]%)" : "No Powercell Installed"]
\ - Area: [APC.area]
\ - [APC.aidisabled || APC.panel_open ? "APC does not respond to interface query." : "APC responds to interface query."]

" - dat += "Check Logs
" - dat += "Log Out
" - if(obj_flags & EMAGGED) - dat += "WARNING: Logging functionality partially disabled from outside source.
" - dat += "Restore logging functionality?
" - else - if(logs.len) - for(var/entry in logs) - dat += "[entry]
" - else - dat += "No activity has been recorded at this time.
" - if(obj_flags & EMAGGED) - dat += "@#%! CLEAR LOGS" - dat += "Return" - operator = user - else - dat = "Please swipe a valid ID to log in..." - var/datum/browser/popup = new(user, "apc_control", name, 600, 400) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() +/obj/machinery/computer/apc_control/ui_interact(mob/user, datum/tgui/ui) + operator = user + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ApcControl") + ui.open() -/obj/machinery/computer/apc_control/Topic(href, href_list) +/obj/machinery/computer/apc_control/ui_data(mob/user) + var/list/data = list() + data["auth_id"] = auth_id + data["authenticated"] = authenticated + data["emagged"] = obj_flags & EMAGGED + data["logging"] = should_log + data["restoring"] = restoring + data["logs"] = list() + data["apcs"] = list() + + for(var/entry in logs) + data["logs"] += list(list("entry" = entry)) + + for(var/apc in GLOB.apcs_list) + if(check_apc(apc)) + var/obj/machinery/power/apc/A = apc + var/has_cell = (A.cell) ? TRUE : FALSE + data["apcs"] += list(list( + "name" = A.area.name, + "operating" = A.operating, + "charge" = (has_cell) ? A.cell.percent() : "NOCELL", + "load" = DisplayPower(A.lastused_total), + "charging" = A.charging, + "chargeMode" = A.chargemode, + "eqp" = A.equipment, + "lgt" = A.lighting, + "env" = A.environ, + "responds" = A.aidisabled || A.panel_open, + "ref" = REF(A) + ) + ) + return data + +/obj/machinery/computer/apc_control/ui_act(action, params) if(..()) return - if(!usr || !usr.canUseTopic(src) || stat || QDELETED(src)) - return - if(href_list["authenticate"]) - var/obj/item/card/id/ID = usr.get_idcard(TRUE) - if(ID && istype(ID)) - if(check_access(ID)) + switch(action) + if("log-in") + if(obj_flags & EMAGGED) authenticated = TRUE - auth_id = "[ID.registered_name] ([ID.assignment])" - log_activity("logged in") - if(href_list["log_out"]) - log_activity("logged out") - authenticated = FALSE - auth_id = "\[NULL\]" - if(href_list["restore_logging"]) - to_chat(usr, "[icon2html(src, usr)] Logging functionality restored from backup data.") - obj_flags &= ~EMAGGED - LAZYADD(logs, "-=- Logging restored to full functionality at this point -=-") - if(href_list["access_apc"]) - playsound(src, "terminal_type", 50, 0) - var/obj/machinery/power/apc/APC = locate(href_list["access_apc"]) in GLOB.apcs_list - if(!APC || APC.aidisabled || APC.panel_open || QDELETED(APC)) - to_chat(usr, "[icon2html(src, usr)] APC does not return interface request. Remote access may be disabled.") - return - if(active_apc) - to_chat(usr, "[icon2html(src, usr)] Disconnected from [active_apc].") - active_apc.say("Remote access canceled. Interface locked.") - playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, 0) - playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, 0) - active_apc.locked = TRUE - active_apc.update_icon() - active_apc.remote_control = null - active_apc = null - to_chat(usr, "[icon2html(src, usr)] Connected to APC in [get_area_name(APC.area, TRUE)]. Interface request sent.") - log_activity("remotely accessed APC in [get_area_name(APC.area, TRUE)]") - APC.remote_control = src - APC.ui_interact(usr) - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - message_admins("[ADMIN_LOOKUPFLW(usr)] remotely accessed [APC] from [src] at [AREACOORD(src)].") - log_game("[key_name(usr)] remotely accessed [APC] from [src] at [AREACOORD(src)].") - if(APC.locked) - APC.say("Remote access detected. Interface unlocked.") - playsound(APC, 'sound/machines/boltsup.ogg', 25, 0) - playsound(APC, 'sound/machines/terminal_alert.ogg', 50, 0) - APC.locked = FALSE - APC.update_icon() - active_apc = APC - if(href_list["name_filter"]) - playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) - var/new_filter = stripped_input(usr, "What name are you looking for?", name) - if(!src || !usr || !usr.canUseTopic(src) || stat || QDELETED(src)) - return - log_activity("changed name filter to \"[new_filter]\"") - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - result_filters["Name"] = new_filter - if(href_list["above_filter"]) - playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) - var/new_filter = input(usr, "Enter a percentage from 1-100 to sort by (greater than).", name) as null|num - if(!src || !usr || !usr.canUseTopic(src) || stat || QDELETED(src)) - return - log_activity("changed greater than charge filter to \"[new_filter]\"") - if(new_filter) - new_filter = clamp(new_filter, 0, 100) - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - result_filters["Charge Above"] = new_filter - if(href_list["below_filter"]) - playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) - var/new_filter = input(usr, "Enter a percentage from 1-100 to sort by (lesser than).", name) as null|num - if(!src || !usr || !usr.canUseTopic(src) || stat || QDELETED(src)) - return - log_activity("changed lesser than charge filter to \"[new_filter]\"") - if(new_filter) - new_filter = clamp(new_filter, 0, 100) - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - result_filters["Charge Below"] = new_filter - if(href_list["access_filter"]) - if(isnull(result_filters["Responsive"])) - result_filters["Responsive"] = 1 - log_activity("sorted by non-responsive APCs only") - else - result_filters["Responsive"] = !result_filters["Responsive"] - log_activity("sorted by all APCs") - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - if(href_list["check_logs"]) - checking_logs = TRUE - log_activity("checked logs") - if(href_list["check_apcs"]) - checking_logs = FALSE - log_activity("checked APCs") - if(href_list["clear_logs"]) - logs = list() - ui_interact(usr) //Refresh the UI after a filter changes + auth_id = "Unknown (Unknown):" + log_activity("[auth_id] logged in to the terminal") + return + var/obj/item/card/id/ID = operator.get_idcard(TRUE) + if(ID && istype(ID)) + if(check_access(ID)) + authenticated = TRUE + auth_id = "[ID.registered_name] ([ID.assignment]):" + log_activity("[auth_id] logged in to the terminal") + playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE) + else + auth_id = "[ID.registered_name] ([ID.assignment]):" + log_activity("[auth_id] attempted to log into the terminal") + return + auth_id = "Unknown (Unknown):" + log_activity("[auth_id] attempted to log into the terminal") + if("log-out") + log_activity("[auth_id] logged out of the terminal") + playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE) + authenticated = FALSE + auth_id = "\[NULL\]" + if("toggle-logs") + should_log = !should_log + log_game("[key_name(operator)] set the logs of [src] in [AREACOORD(src)] [should_log ? "On" : "Off"]") + if("restore-console") + restoring = TRUE + addtimer(CALLBACK(src, .proc/restore_comp), rand(3,5) * 9) + if("access-apc") + var/ref = params["ref"] + playsound(src, "terminal_type", 50, FALSE) + var/obj/machinery/power/apc/APC = locate(ref) in GLOB.apcs_list + if(!APC) + return + if(active_apc) + to_chat(operator, "[icon2html(src, auth_id)] Disconnected from [active_apc].") + active_apc.say("Remote access canceled. Interface locked.") + playsound(active_apc, 'sound/machines/boltsdown.ogg', 25, FALSE) + playsound(active_apc, 'sound/machines/terminal_alert.ogg', 50, FALSE) + active_apc.locked = TRUE + active_apc.update_icon() + active_apc.remote_control = null + active_apc = null + APC.remote_control = src + APC.ui_interact(operator) + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE) + log_game("[key_name(operator)] remotely accessed [APC] from [src] at [AREACOORD(src)].") + log_activity("[auth_id] remotely accessed APC in [get_area_name(APC.area, TRUE)]") + if(APC.locked) + APC.say("Remote access detected. Interface unlocked.") + playsound(APC, 'sound/machines/boltsup.ogg', 25, FALSE) + playsound(APC, 'sound/machines/terminal_alert.ogg', 50, FALSE) + APC.locked = FALSE + APC.update_icon() + active_apc = APC + if("check-logs") + log_activity("Checked Logs") + if("check-apcs") + log_activity("Checked APCs") + if("toggle-minor") + var/ref = params["ref"] + var/type = params["type"] + var/value = params["value"] + var/obj/machinery/power/apc/target = locate(ref) in GLOB.apcs_list + if(!target) + return + target.vars[type] = target.setsubsystem(text2num(value)) + target.update_icon() + target.update() + var/setTo = "" + switch(target.vars[type]) + if(0) + setTo = "Off" + if(1) + setTo = "Auto Off" + if(2) + setTo = "On" + if(3) + setTo = "Auto On" + log_activity("Set APC [target.area.name] [type] to [setTo]") + log_game("[key_name(operator)] Set APC [target.area.name] [type] to [setTo]]") + if("breaker") + var/ref = params["ref"] + var/obj/machinery/power/apc/target = locate(ref) in GLOB.apcs_list + target.toggle_breaker() + var/setTo = target.operating ? "On" : "Off" + log_activity("Turned APC [target.area.name]'s breaker [setTo]") /obj/machinery/computer/apc_control/emag_act(mob/user) - . = ..() - if(!authenticated) - to_chat(user, "You bypass [src]'s access requirements using your emag.") - authenticated = TRUE - log_activity("logged in") - else - if(obj_flags & EMAGGED) - return - user.visible_message("You emag [src], disabling precise logging and allowing you to clear logs.") - log_game("[key_name(user)] emagged [src] at [AREACOORD(src)], disabling operator tracking.") - obj_flags |= EMAGGED - playsound(src, "sparks", 50, 1) - return TRUE + if(obj_flags & EMAGGED) + return + obj_flags |= EMAGGED + log_game("[key_name(user)] emagged [src] at [AREACOORD(src)]") + playsound(src, "sparks", 50, TRUE) /obj/machinery/computer/apc_control/proc/log_activity(log_text) - var/op_string = operator && !(obj_flags & EMAGGED) ? operator : "\[NULL OPERATOR\]" - LAZYADD(logs, "([STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]) [op_string] [log_text]") + if(!should_log) + return + LAZYADD(logs, "([STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]): [auth_id] [log_text]") + +/obj/machinery/computer/apc_control/proc/restore_comp() + obj_flags &= ~EMAGGED + should_log = TRUE + log_game("[key_name(operator)] restored the logs of [src] in [AREACOORD(src)]") + log_activity("-=- Logging restored to full functionality at this point -=-") + restoring = FALSE /mob/proc/using_power_flow_console() for(var/obj/machinery/computer/apc_control/A in range(1, src)) diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index fbedf62734..50a6d10302 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -19,11 +19,10 @@ SSradio.remove_object(src, receive_frequency) return ..() -/obj/machinery/computer/atmos_alert/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/atmos_alert/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AtmosAlertConsole", name, 350, 300, master_ui, state) + ui = new(user, src, "AtmosAlertConsole", name) ui.open() /obj/machinery/computer/atmos_alert/ui_data(mob/user) @@ -45,11 +44,11 @@ if("clear") var/zone = params["zone"] if(zone in priority_alarms) - to_chat(usr, "Priority alarm for [zone] cleared.") + to_chat(usr, "Priority alarm for [zone] cleared.") priority_alarms -= zone . = TRUE if(zone in minor_alarms) - to_chat(usr, "Minor alarm for [zone] cleared.") + to_chat(usr, "Minor alarm for [zone] cleared.") minor_alarms -= zone . = TRUE update_icon() diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm index 6ee975b338..4ba8d9f3d3 100644 --- a/code/game/machinery/computer/atmos_control.dm +++ b/code/game/machinery/computer/atmos_control.dm @@ -91,8 +91,6 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers) icon_screen = "tank" icon_keyboard = "atmos_key" circuit = /obj/item/circuitboard/computer/atmos_control - ui_x = 400 - ui_y = 925 var/frequency = FREQ_ATMOS_STORAGE var/list/sensors = list( @@ -123,11 +121,10 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers) SSradio.remove_object(src, frequency) return ..() -/obj/machinery/computer/atmos_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/atmos_control/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AtmosControlConsole", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "AtmosControlConsole", name) ui.open() /obj/machinery/computer/atmos_control/ui_data(mob/user) @@ -265,13 +262,6 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers) for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in devices) U.broadcast_status() -/obj/machinery/computer/atmos_control/tank/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "AtmosControlConsole", name, ui_x, ui_y, master_ui, state) - ui.open() - /obj/machinery/computer/atmos_control/tank/ui_data(mob/user) var/list/data = ..() data["tank"] = TRUE diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 2b3416e1c8..d42291cd3c 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -5,8 +5,6 @@ icon_keyboard = "security_key" circuit = /obj/item/circuitboard/computer/security light_color = LIGHT_COLOR_RED - ui_x = 870 - ui_y = 708 var/list/network = list("ss13") var/obj/machinery/camera/active_camera @@ -55,11 +53,9 @@ network -= i network += "[idnum][i]" -/obj/machinery/computer/security/ui_interact(\ - mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) +/obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui) // Update UI - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui) // Show static if can't use the camera if(!active_camera?.can_use()) show_camera_static() @@ -76,10 +72,11 @@ use_power(active_power_usage) // Register map objects user.client.register_map_obj(cam_screen) - user.client.register_map_obj(cam_plane_master) + for(var/plane in cam_plane_master) + user.client.register_map_obj(plane) user.client.register_map_obj(cam_background) // Open UI - ui = new(user, src, ui_key, "CameraConsole", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "CameraConsole", name) ui.open() /obj/machinery/computer/security/ui_data() diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index cd8b5c1c59..19da4f75d8 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -76,11 +76,10 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new) /datum/crewmonitor/Destroy() return ..() -/datum/crewmonitor/ui_interact(mob/user, ui_key = "crew", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/crewmonitor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if (!ui) - ui = new(user, src, ui_key, "CrewConsole", "crew monitor", 800, 600 , master_ui, state) + ui = new(user, src, "CrewConsole") ui.open() /datum/crewmonitor/proc/show(mob/M, source) diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm index 2dce50aa88..286c106788 100644 --- a/code/game/machinery/computer/dna_console.dm +++ b/code/game/machinery/computer/dna_console.dm @@ -220,9 +220,7 @@ /obj/machinery/computer/scan_consolenew/examine(mob/user) . = ..() -/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - . = ..() - +/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, datum/tgui/ui) // Most of ui_interact is spent setting variables for passing to the tgui // interface. // We can also do some general state processing here too as it's a good @@ -264,10 +262,9 @@ time_to_pulse = round((rad_pulse_timer - world.time)/10) // Attempt to update tgui ui, open and update if needed. - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "DnaConsole", name, 539, 710, master_ui, state) + ui = new(user, src, "DnaConsole") ui.open() /obj/machinery/computer/scan_consolenew/ui_data(mob/user) diff --git a/code/game/machinery/computer/launchpad_control.dm b/code/game/machinery/computer/launchpad_control.dm index fdbe2f1060..b2f7ae73a1 100644 --- a/code/game/machinery/computer/launchpad_control.dm +++ b/code/game/machinery/computer/launchpad_control.dm @@ -53,10 +53,10 @@ var/obj/machinery/launchpad/pad = launchpads[number] return pad -/obj/machinery/computer/launchpad/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/launchpad/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "LaunchpadConsole", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "LaunchpadConsole", name) ui.open() /obj/machinery/computer/launchpad/ui_data(mob/user) @@ -128,4 +128,4 @@ if("pull") teleport(usr, current_pad, FALSE) . = TRUE - . = TRUE \ No newline at end of file + . = TRUE diff --git a/code/game/machinery/computer/prisoner/gulag_teleporter.dm b/code/game/machinery/computer/prisoner/gulag_teleporter.dm index dd925fbe3b..13727b585b 100644 --- a/code/game/machinery/computer/prisoner/gulag_teleporter.dm +++ b/code/game/machinery/computer/prisoner/gulag_teleporter.dm @@ -6,8 +6,6 @@ icon_keyboard = "security_key" req_access = list(ACCESS_ARMORY) circuit = /obj/item/circuitboard/computer/gulag_teleporter_console - ui_x = 350 - ui_y = 295 var/default_goal = 200 var/obj/machinery/gulag_teleporter/teleporter = null @@ -21,11 +19,10 @@ . = ..() scan_machinery() -/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "GulagTeleporterConsole", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "GulagTeleporterConsole", name) ui.open() /obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_data(mob/user) diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 4fd9665b1d..401d05da12 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -23,11 +23,10 @@ return return TRUE -/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/robotics/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "RoboticsControlConsole", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "RoboticsControlConsole", name) ui.open() /obj/machinery/computer/robotics/ui_data(mob/user) @@ -123,5 +122,5 @@ var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread s.set_up(3, TRUE, D) s.start() - D.visible_message("\the [D] self destructs!") + D.visible_message("\the [D] self-destructs!") D.gib() diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm index b4340b9350..7b2e6ab455 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -16,11 +16,10 @@ GLOB.alert_consoles -= src return ..() -/obj/machinery/computer/station_alert/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/station_alert/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "StationAlertConsole", name, 325, 500, master_ui, state) + ui = new(user, src, "StationAlertConsole", name) ui.open() /obj/machinery/computer/station_alert/ui_data(mob/user) @@ -31,6 +30,7 @@ data["alarms"][class] = list() for(var/area in alarms[class]) data["alarms"][class] += area + return data /obj/machinery/computer/station_alert/proc/triggerAlarm(class, area/A, O, obj/source) diff --git a/code/game/machinery/computer/teleporter.dm b/code/game/machinery/computer/teleporter.dm index e7957a5b75..3a8ff7bf19 100644 --- a/code/game/machinery/computer/teleporter.dm +++ b/code/game/machinery/computer/teleporter.dm @@ -34,11 +34,10 @@ break return power_station -obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/teleporter/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Teleporter", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "Teleporter", name) ui.open() /obj/machinery/computer/teleporter/ui_data(mob/user) @@ -56,7 +55,6 @@ obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", datum/t return data - /obj/machinery/computer/teleporter/ui_act(action, params) if(..()) return @@ -92,16 +90,18 @@ obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", datum/t say("Processing hub calibration to target...") calibrating = TRUE power_station.update_icon() - spawn(50 * (3 - power_station.teleporter_hub.accuracy)) //Better parts mean faster calibration - calibrating = FALSE - if(check_hub_connection()) - power_station.teleporter_hub.calibrated = TRUE - say("Calibration complete.") - else - say("Error: Unable to detect hub.") - power_station.update_icon() + addtimer(CALLBACK(src, .proc/finish_calibration), 50 * (3 - power_station.teleporter_hub.accuracy)) //Better parts mean faster calibration . = TRUE +/obj/machinery/computer/teleporter/proc/finish_calibration() + calibrating = FALSE + if(check_hub_connection()) + power_station.teleporter_hub.calibrated = TRUE + say("Calibration complete.") + else + say("Error: Unable to detect hub.") + power_station.update_icon() + /obj/machinery/computer/teleporter/proc/check_hub_connection() if(!power_station) return FALSE diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm index 9ecc19e40f..756f924cfa 100644 --- a/code/game/machinery/dance_machine.dm +++ b/code/game/machinery/dance_machine.dm @@ -9,6 +9,7 @@ var/active = FALSE var/list/rangers = list() var/stop = 0 + var/volume = 70 var/datum/track/selection = null /obj/machinery/jukebox/disco @@ -51,74 +52,91 @@ else icon_state = "[initial(icon_state)]" -/obj/machinery/jukebox/ui_interact(mob/user) - . = ..() - if(!user.canUseTopic(src, !hasSiliconAccessInArea(user))) - return - if (!anchored) +/obj/machinery/jukebox/ui_status(mob/user) + if(!anchored) to_chat(user,"This device must be anchored by a wrench!") - return - if(!allowed(user)) + return UI_CLOSE + if(!allowed(user) && !isobserver(user)) to_chat(user,"Error: Access Denied.") - user.playsound_local(src,'sound/misc/compiler-failure.ogg', 25, 1) - return - if(!SSjukeboxes.songs.len) + user.playsound_local(src, 'sound/misc/compiler-failure.ogg', 25, TRUE) + return UI_CLOSE + if(!SSjukeboxes.songs.len && !isobserver(user)) to_chat(user,"Error: No music tracks have been authorized for your station. Petition Central Command to resolve this issue.") - playsound(src,'sound/misc/compiler-failure.ogg', 25, 1) - return - var/list/dat = list() - dat +="
" - dat += "[!active ? "BREAK IT DOWN" : "SHUT IT DOWN"]
" - dat += "

" - dat += " Select Track
" - if(istype(selection)) - dat += "Track Selected: [selection.song_name]
" - dat += "Track Length: [DisplayTimeText(selection.song_length)]

" - else - dat += "Track Selected: None!

" - var/datum/browser/popup = new(user, "vending", "[name]", 400, 350) - popup.set_content(dat.Join()) - popup.open() + playsound(src, 'sound/misc/compiler-failure.ogg', 25, TRUE) + return UI_CLOSE + return ..() +/obj/machinery/jukebox/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Jukebox", name) + ui.open() -/obj/machinery/jukebox/Topic(href, href_list) - if(..()) +/obj/machinery/jukebox/ui_data(mob/user) + var/list/data = list() + data["active"] = active + data["songs"] = list() + for(var/datum/track/S in SSjukeboxes.songs) + var/list/track_data = list( + name = S.song_name + ) + data["songs"] += list(track_data) + data["track_selected"] = null + data["track_length"] = null + data["track_beat"] = null + if(selection) + data["track_selected"] = selection.song_name + data["track_length"] = DisplayTimeText(selection.song_length) + data["track_beat"] = selection.song_beat + data["volume"] = volume + return data + +/obj/machinery/jukebox/ui_act(action, list/params) + . = ..() + if(.) return - add_fingerprint(usr) - switch(href_list["action"]) + + switch(action) if("toggle") - if (QDELETED(src)) + if(QDELETED(src)) return if(!active) if(stop > world.time) to_chat(usr, "Error: The device is still resetting from the last activation, it will be ready again in [DisplayTimeText(stop-world.time)].") - playsound(src, 'sound/misc/compiler-failure.ogg', 50, 1) + playsound(src, 'sound/misc/compiler-failure.ogg', 50, TRUE) return - if(!istype(selection)) - to_chat(usr, "Error: Severe user incompetence detected.") - playsound(src, 'sound/misc/compiler-failure.ogg', 50, 1) - return - if(!activate_music()) - to_chat(usr, "Error: Generic hardware failure.") - playsound(src, 'sound/misc/compiler-failure.ogg', 50, 1) - return - updateUsrDialog() - else if(active) + activate_music() + START_PROCESSING(SSobj, src) + return TRUE + else stop = 0 - updateUsrDialog() - if("select") + return TRUE + if("select_track") if(active) to_chat(usr, "Error: You cannot change the song until the current one is over.") return - var/list/available = list() for(var/datum/track/S in SSjukeboxes.songs) available[S.song_name] = S - var/selected = input(usr, "Choose your song", "Track:") as null|anything in available + var/selected = params["track"] if(QDELETED(src) || !selected || !istype(available[selected], /datum/track)) return selection = available[selected] - updateUsrDialog() + return TRUE + if("set_volume") + var/new_volume = params["volume"] + if(new_volume == "reset") + volume = initial(volume) + return TRUE + else if(new_volume == "min") + volume = 0 + return TRUE + else if(new_volume == "max") + volume = 100 + return TRUE + else if(text2num(new_volume) != null) + volume = text2num(new_volume) + return TRUE /obj/machinery/jukebox/proc/activate_music() var/jukeboxslottotake = SSjukeboxes.addjukebox(src, selection, 2) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 4771e2eca7..515a4672a5 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -50,7 +50,7 @@ integrity_failure = 0.25 damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N autoclose = TRUE - secondsElectrified = 0 //How many seconds remain until the door is no longer electrified. -1 if it is permanently electrified until someone fixes it. + secondsElectrified = NOT_ELECTRIFIED //How many seconds remain until the door is no longer electrified. -1 if it is permanently electrified until someone fixes it. assemblytype = /obj/structure/door_assembly normalspeed = 1 explosion_block = 1 @@ -298,10 +298,10 @@ /obj/machinery/door/airlock/Destroy() QDEL_NULL(wires) + QDEL_NULL(electronics) if(charge) qdel(charge) charge = null - QDEL_NULL(electronics) if (cyclelinkedairlock) if (cyclelinkedairlock.cyclelinkedairlock == src) cyclelinkedairlock.cyclelinkedairlock = null @@ -309,7 +309,7 @@ if(id_tag) for(var/obj/machinery/doorButtons/D in GLOB.machines) D.removeMe(src) - qdel(note) + QDEL_NULL(note) for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) diag_hud.remove_from_hud(src) return ..() @@ -765,8 +765,8 @@ /obj/machinery/door/airlock/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) if(!(issilicon(user) || IsAdminGhost(user))) - if(src.isElectrified()) - if(src.shock(user, 100)) + if(isElectrified()) + if(shock(user, 100)) return if(ishuman(user) && prob(40) && src.density) @@ -790,15 +790,15 @@ return ..() /obj/machinery/door/airlock/proc/electrified_loop() - while (secondsElectrified > 0) + while (secondsElectrified > NOT_ELECTRIFIED) sleep(10) if(QDELETED(src)) return - secondsElectrified -= 1 + secondsElectrified-- updateDialog() // This is to protect against changing to permanent, mid loop. - if(secondsElectrified==0) + if(secondsElectrified == NOT_ELECTRIFIED) set_electrified(NOT_ELECTRIFIED) else set_electrified(ELECTRIFIED_PERMANENT) @@ -825,8 +825,8 @@ /obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params) if(!issilicon(user) && !IsAdminGhost(user)) - if(src.isElectrified()) - if(src.shock(user, 75)) + if(isElectrified()) + if(shock(user, 75)) return add_fingerprint(user) @@ -839,7 +839,7 @@ to_chat(user, "You need at least 2 metal sheets to reinforce [src].") return to_chat(user, "You start reinforcing [src].") - if(do_after(user, 20, 1, target = src)) + if(do_after(user, 20, TRUE, target = src)) if(!panel_open || !S.use(2)) return user.visible_message("[user] reinforces \the [src] with metal.", @@ -1070,7 +1070,7 @@ INVOKE_ASYNC(src, (density ? .proc/open : .proc/close), 2) if(istype(I, /obj/item/crowbar/power)) - if(isElectrified()) + if(hasPower() && isElectrified()) shock(user,100)//it's like sticking a forck in a power socket return @@ -1090,12 +1090,11 @@ time_to_open = 50 playsound(src, 'sound/machines/airlock_alien_prying.ogg',100,1) //is it aliens or just the CE being a dick? prying_so_hard = TRUE - var/result = do_after(user, time_to_open,target = src) - prying_so_hard = FALSE - if(result) + if(do_after(user, time_to_open,target = src)) open(2) if(density && !open(2)) to_chat(user, "Despite your attempts, [src] refuses to open.") + prying_so_hard = FALSE /obj/machinery/door/airlock/open(forced=0) if( operating || welded || locked ) @@ -1359,12 +1358,25 @@ wires.cut_all() update_icon() -/obj/machinery/door/airlock/proc/set_electrified(seconds) +/obj/machinery/door/airlock/proc/set_electrified(seconds, mob/user) secondsElectrified = seconds diag_hud_set_electrified() - if(secondsElectrified > 0) + if(secondsElectrified > NOT_ELECTRIFIED) INVOKE_ASYNC(src, .proc/electrified_loop) + if(user) + var/message + switch(secondsElectrified) + if(ELECTRIFIED_PERMANENT) + message = "permanently shocked" + if(NOT_ELECTRIFIED) + message = "unshocked" + else + message = "temp shocked for [secondsElectrified] seconds" + LAZYADD(shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(user)] - ([uppertext(message)])")) + log_combat(user, src, message) + //add_hiddenprint(user) + /obj/machinery/door/airlock/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) . = ..() if(obj_integrity < (0.75 * max_integrity)) @@ -1438,11 +1450,10 @@ else if(istype(note, /obj/item/photo)) return "photo" -/obj/machinery/door/airlock/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/door/airlock/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AiAirlock", name, 500, 390, master_ui, state) + ui = new(user, src, "AiAirlock", name) ui.open() return TRUE @@ -1450,13 +1461,13 @@ var/list/data = list() var/list/power = list() - power["main"] = src.secondsMainPowerLost ? 0 : 2 // boolean - power["main_timeleft"] = src.secondsMainPowerLost - power["backup"] = src.secondsBackupPowerLost ? 0 : 2 // boolean - power["backup_timeleft"] = src.secondsBackupPowerLost + power["main"] = secondsMainPowerLost ? 0 : 2 // boolean + power["main_timeleft"] = secondsMainPowerLost + power["backup"] = secondsBackupPowerLost ? 0 : 2 // boolean + power["backup_timeleft"] = secondsBackupPowerLost data["power"] = power - data["shock"] = secondsElectrified == 0 ? 2 : 0 + data["shock"] = secondsElectrified == NOT_ELECTRIFIED ? 2 : 0 data["shock_timeleft"] = secondsElectrified data["id_scanner"] = !aiDisabledIdScanner data["emergency"] = emergency // access @@ -1493,14 +1504,14 @@ loseMainPower() update_icon() else - to_chat(usr, "Main power is already offline.") + to_chat(usr, "Main power is already offline.") . = TRUE if("disrupt-backup") if(!secondsBackupPowerLost) loseBackupPower() update_icon() else - to_chat(usr, "Backup power is already offline.") + to_chat(usr, "Backup power is already offline.") . = TRUE if("shock-restore") shock_restore(usr) @@ -1529,7 +1540,6 @@ . = TRUE if("speed-toggle") normalspeed = !normalspeed - . = TRUE if("open-close") user_toggle_open(usr) diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index a0ce3954c3..ef2be8d744 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -15,11 +15,13 @@ . = ..() . += "Has a neat selection menu for modifying airlock access levels." -/obj/item/electronics/airlock/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/electronics/airlock/ui_state(mob/user) + return GLOB.hands_state + +/obj/item/electronics/airlock/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AirlockElectronics", name, 420, 485, master_ui, state) + ui = new(user, src, "AirlockElectronics", name) ui.open() /obj/item/electronics/airlock/ui_static_data(mob/user) diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index 5a1ed7dd19..b2e0050819 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -140,11 +140,10 @@ . = new_time == timer_duration //return 1 on no change timer_duration = new_time -/obj/machinery/door_timer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/door_timer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "BrigTimer", name, 300, 138, master_ui, state) + ui = new(user, src, "BrigTimer", name) ui.open() //icon update function @@ -235,7 +234,7 @@ preset_time = PRESET_LONG . = set_timer(preset_time) if(timing) - activation_time = REALTIMEOFDAY + activation_time = world.time else . = FALSE diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 4248e1684f..d4ba70bb6c 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -11,7 +11,7 @@ max_integrity = 350 armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 70) CanAtmosPass = ATMOS_PASS_DENSITY - flags_1 = PREVENT_CLICK_UNDER_1 + flags_1 = PREVENT_CLICK_UNDER_1|DEFAULT_RICOCHET_1 ricochet_chance_mod = 0.8 interaction_flags_atom = INTERACT_ATOM_UI_INTERACT diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 2d41575e70..228c2e1f52 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -298,7 +298,7 @@ /obj/machinery/door/firedoor/border_only icon = 'icons/obj/doors/edge_Doorfire.dmi' - flags_1 = ON_BORDER_1 + flags_1 = ON_BORDER_1|DEFAULT_RICOCHET_1 CanAtmosPass = ATMOS_PASS_PROC /obj/machinery/door/firedoor/border_only/closed @@ -320,7 +320,7 @@ to_chat(M, "You pull [M.pulling] through [src] right as it closes") M.pulling.forceMove(T1) M.start_pulling(M2) - + for(var/mob/living/M in T2) if(M.stat == CONSCIOUS && M.pulling && M.pulling.loc == T1 && !M.pulling.anchored && M.pulling.move_resist <= M.move_force) var/mob/living/M2 = M.pulling diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index c9c577231e..22bacf6aa1 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -11,7 +11,7 @@ integrity_failure = 0 armor = list("melee" = 20, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 70, "acid" = 100) visible = FALSE - flags_1 = ON_BORDER_1 + flags_1 = ON_BORDER_1|DEFAULT_RICOCHET_1 opacity = 0 CanAtmosPass = ATMOS_PASS_PROC interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_REQUIRES_SILICON | INTERACT_MACHINE_OPEN diff --git a/code/game/machinery/gulag_item_reclaimer.dm b/code/game/machinery/gulag_item_reclaimer.dm index 45484caa77..93c6e66867 100644 --- a/code/game/machinery/gulag_item_reclaimer.dm +++ b/code/game/machinery/gulag_item_reclaimer.dm @@ -20,18 +20,15 @@ return ..() /obj/machinery/gulag_item_reclaimer/emag_act(mob/user) - . = ..() if(obj_flags & EMAGGED) // emagging lets anyone reclaim all the items return req_access = list() obj_flags |= EMAGGED - return TRUE -/obj/machinery/gulag_item_reclaimer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/gulag_item_reclaimer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "GulagItemReclaimer", name, 300, 400, master_ui, state) + ui = new(user, src, "GulagItemReclaimer", name) ui.open() /obj/machinery/gulag_item_reclaimer/ui_data(mob/user) @@ -60,20 +57,22 @@ mobs += list(mob_info) data["mobs"] = mobs - - data["can_reclaim"] = can_reclaim return data -/obj/machinery/gulag_item_reclaimer/ui_act(action, list/params) +/obj/machinery/gulag_item_reclaimer/ui_act(action, params) + if(..()) + return + switch(action) if("release_items") - var/mob/M = locate(params["mobref"]) - if(M == usr || allowed(usr)) - drop_items(M) - else - to_chat(usr, "Access denied.") + var/mob/living/carbon/human/H = locate(params["mobref"]) in stored_items + if(H != usr && !allowed(usr)) + to_chat(usr, "Access denied.") + return + drop_items(H) + . = TRUE /obj/machinery/gulag_item_reclaimer/proc/drop_items(mob/user) if(!stored_items[user]) diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 25f4248d6d..4a576c5a6e 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -25,7 +25,6 @@ Possible to do for anyone motivated enough: */ GLOBAL_LIST_EMPTY(network_holopads) - #define HOLOPAD_PASSIVE_POWER_USAGE 1 #define HOLOGRAM_POWER_USAGE 2 @@ -34,34 +33,64 @@ GLOBAL_LIST_EMPTY(network_holopads) desc = "It's a floor-mounted device for projecting holographic images." icon_state = "holopad0" layer = LOW_OBJ_LAYER - plane = ABOVE_WALL_PLANE + plane = FLOOR_PLANE flags_1 = HEAR_1 + req_access = list(ACCESS_KEYCARD_AUTH) //Used to allow for forced connecting to other (not secure) holopads. Anyone can make a call, though. use_power = IDLE_POWER_USE idle_power_usage = 5 active_power_usage = 100 max_integrity = 300 armor = list("melee" = 50, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0) circuit = /obj/item/circuitboard/machine/holopad - var/list/masters //List of living mobs that use the holopad - var/list/holorays //Holoray-mob link. - var/last_request = 0 //to prevent request spam. ~Carn - var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. - var/temp = "" - var/list/holo_calls //array of /datum/holocalls - var/datum/holocall/outgoing_call //do not modify the datums only check and call the public procs - var/obj/item/disk/holodisk/disk //Record disk - var/replay_mode = FALSE //currently replaying a recording - var/loop_mode = FALSE //currently looping a recording - var/record_mode = FALSE //currently recording - var/record_start = 0 //recording start time - var/record_user //user that inititiated the recording - var/obj/effect/overlay/holo_pad_hologram/replay_holo //replay hologram - var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging + /// List of living mobs that use the holopad + var/list/masters + /// Holoray-mob link + var/list/holorays + /// To prevent request spam. ~Carn + var/last_request = 0 + /// Change to change how far the AI can move away from the holopad before deactivating + var/holo_range = 5 + /// Array of /datum/holocalls + var/list/holo_calls + /// Currently outgoing holocall, do not modify the datums only check and call the public procs + var/datum/holocall/outgoing_call + /// Record disk + var/obj/item/disk/holodisk/disk + /// Currently replaying a recording + var/replay_mode = FALSE + /// Currently looping a recording + var/loop_mode = FALSE + /// Currently recording + var/record_mode = FALSE + /// Recording start time + var/record_start = 0 + /// User that inititiated the recording + var/record_user + /// Replay hologram + var/obj/effect/overlay/holo_pad_hologram/replay_holo + /// Calls will be automatically answered after a couple rings, here for debugging + var/static/force_answer_call = FALSE + // var/static/list/holopads = list() var/obj/effect/overlay/holoray/ray var/ringing = FALSE var/offset = FALSE var/on_network = TRUE + /// For pads in secure areas; do not allow forced connecting + var/secure = FALSE + /// If we are currently calling another holopad + var/calling = FALSE +/* +/obj/machinery/holopad/secure + name = "secure holopad" + desc = "It's a floor-mounted device for projecting holographic images. This one will refuse to auto-connect incoming calls." + secure = TRUE +/obj/machinery/holopad/secure/Initialize() + . = ..() + var/obj/item/circuitboard/machine/holopad/board = circuit + board.secure = TRUE + board.build_path = /obj/machinery/holopad/secure +*/ /obj/machinery/holopad/tutorial resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF flags_1 = NODECONSTRUCT_1 @@ -121,10 +150,8 @@ GLOBAL_LIST_EMPTY(network_holopads) return ..() /obj/machinery/holopad/power_change() - if (powered()) - stat &= ~NOPOWER - else - stat |= NOPOWER + . = ..() + if (!powered()) if(replay_mode) replay_stop() if(record_mode) @@ -163,171 +190,150 @@ GLOBAL_LIST_EMPTY(network_holopads) if(istype(P,/obj/item/disk/holodisk)) if(disk) - to_chat(user,"There's already a disk inside [src]") + to_chat(user,"There's already a disk inside [src]!") return if (!user.transferItemToLoc(P,src)) return - to_chat(user,"You insert [P] into [src]") + to_chat(user,"You insert [P] into [src].") disk = P - updateDialog() return return ..() +/obj/machinery/holopad/ui_status(mob/user) + if(!is_operational()) + return UI_CLOSE + if(outgoing_call && !calling) + return UI_CLOSE + return ..() -/obj/machinery/holopad/ui_interact(mob/living/carbon/human/user) //Carn: Hologram requests. +/obj/machinery/holopad/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Holopad", name) + ui.open() + +/obj/machinery/holopad/ui_data(mob/user) + var/list/data = list() + data["calling"] = calling + data["on_network"] = on_network + data["on_cooldown"] = last_request + 200 < world.time ? FALSE : TRUE + data["allowed"] = allowed(user) + data["disk"] = disk ? TRUE : FALSE + data["disk_record"] = disk?.record ? TRUE : FALSE + data["replay_mode"] = replay_mode + data["loop_mode"] = loop_mode + data["record_mode"] = record_mode + data["holo_calls"] = list() + for(var/I in holo_calls) + var/datum/holocall/HC = I + var/list/call_data = list( + caller = HC.user, + connected = HC.connected_holopad == src ? TRUE : FALSE, + ref = REF(HC) + ) + data["holo_calls"] += list(call_data) + return data + +/obj/machinery/holopad/ui_act(action, list/params) . = ..() - if(!istype(user)) + if(.) return - if(outgoing_call || user.incapacitated() || !is_operational()) - return - - user.set_machine(src) - var/dat - if(temp) - dat = temp - else - if(on_network) - dat += "Request an AI's presence
" - dat += "Call another holopad
" - if(disk) - if(disk.record) - //Replay - dat += "Replay disk recording
" - dat += "Loop disk recording
" - //Clear - dat += "Clear disk recording
" + switch(action) + if("AIrequest") + if(last_request + 200 < world.time) + last_request = world.time + to_chat(usr, "You requested an AI's presence.") + var/area/area = get_area(src) + for(var/mob/living/silicon/ai/AI in GLOB.silicon_mobs) + if(!AI.client) + continue + to_chat(AI, "Your presence is requested at \the [area].") + return TRUE else - //Record - dat += "Start new recording
" - //Eject - dat += "Eject disk
" + to_chat(usr, "A request for AI presence was already sent recently.") + return + if("holocall") + if(outgoing_call) + return + if(usr.loc == loc) + var/list/callnames = list() + for(var/I in GLOB.network_holopads) + var/area/A = get_area(I) + if(A) + LAZYADD(callnames[A], I) + callnames -= get_area(src) + var/result = input(usr, "Choose an area to call", "Holocall") as null|anything in sortNames(callnames) + if(QDELETED(usr) || !result || outgoing_call) + return + if(usr.loc == loc) + var/input = text2num(params["headcall"]) + var/headcall = input == 1 ? TRUE : FALSE + new /datum/holocall(usr, src, callnames[result], headcall) + calling = TRUE + return TRUE + else + to_chat(usr, "You must stand on the holopad to make a call!") + if("connectcall") + var/datum/holocall/call_to_connect = locate(params["holopad"]) in holo_calls + if(!QDELETED(call_to_connect)) + call_to_connect.Answer(src) + return TRUE + if("disconnectcall") + var/datum/holocall/call_to_disconnect = locate(params["holopad"]) in holo_calls + if(!QDELETED(call_to_disconnect)) + call_to_disconnect.Disconnect(src) + return TRUE + if("disk_eject") + if(disk && !replay_mode) + disk.forceMove(drop_location()) + disk = null + return TRUE + if("replay_mode") + if(replay_mode) + replay_stop() + return TRUE + else + replay_start() + return TRUE + if("loop_mode") + loop_mode = !loop_mode + return TRUE + if("record_mode") + if(record_mode) + record_stop() + return TRUE + else + record_start(usr) + return TRUE + if("record_clear") + record_clear() + return TRUE + if("offset") + offset++ + if(offset > 4) + offset = FALSE + var/turf/new_turf + if(!offset) + new_turf = get_turf(src) + else + new_turf = get_step(src, GLOB.cardinals[offset]) + replay_holo.forceMove(new_turf) + return TRUE + if("hang_up") + if(outgoing_call) + outgoing_call.Disconnect(src) + return TRUE - if(LAZYLEN(holo_calls)) - dat += "=====================================================
" - - if(on_network) - var/one_answered_call = FALSE - var/one_unanswered_call = FALSE - for(var/I in holo_calls) - var/datum/holocall/HC = I - if(HC.connected_holopad != src) - dat += "Answer call from [get_area(HC.calling_holopad)]
" - one_unanswered_call = TRUE - else - one_answered_call = TRUE - - if(one_answered_call && one_unanswered_call) - dat += "=====================================================
" - //we loop twice for formatting - for(var/I in holo_calls) - var/datum/holocall/HC = I - if(HC.connected_holopad == src) - dat += "Disconnect call from [HC.user]
" - - - var/datum/browser/popup = new(user, "holopad", name, 300, 175) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - -//Stop ringing the AI!! +/** + * hangup_all_calls: Disconnects all current holocalls from the holopad + */ /obj/machinery/holopad/proc/hangup_all_calls() for(var/I in holo_calls) var/datum/holocall/HC = I HC.Disconnect(src) -/obj/machinery/holopad/Topic(href, href_list) - if(..() || isAI(usr)) - return - add_fingerprint(usr) - if(!is_operational()) - return - if (href_list["AIrequest"]) - if(last_request + 200 < world.time) - last_request = world.time - temp = "You requested an AI's presence.
" - temp += "Main Menu" - var/area/area = get_area(src) - for(var/mob/living/silicon/ai/AI in GLOB.silicon_mobs) - if(!AI.client) - continue - to_chat(AI, "Your presence is requested at \the [area].") - else - temp = "A request for AI presence was already sent recently.
" - temp += "Main Menu" - - else if(href_list["Holocall"]) - if(outgoing_call) - return - - temp = "You must stand on the holopad to make a call!
" - temp += "Main Menu" - if(usr.loc == loc) - var/list/callnames = list() - for(var/I in GLOB.network_holopads) - var/area/A = get_area(I) - if(A) - LAZYADD(callnames[A], I) - callnames -= get_area(src) - - var/result = input(usr, "Choose an area to call", "Holocall") as null|anything in callnames - if(QDELETED(usr) || !result || outgoing_call) - return - - if(usr.loc == loc) - temp = "Dialing...
" - temp += "Main Menu" - new /datum/holocall(usr, src, callnames[result]) - - else if(href_list["connectcall"]) - var/datum/holocall/call_to_connect = locate(href_list["connectcall"]) - if(!QDELETED(call_to_connect)) - call_to_connect.Answer(src) - temp = "" - - else if(href_list["disconnectcall"]) - var/datum/holocall/call_to_disconnect = locate(href_list["disconnectcall"]) - if(!QDELETED(call_to_disconnect)) - call_to_disconnect.Disconnect(src) - temp = "" - - else if(href_list["mainmenu"]) - temp = "" - if(outgoing_call) - outgoing_call.Disconnect() - - else if(href_list["disk_eject"]) - if(disk && !replay_mode) - disk.forceMove(drop_location()) - disk = null - - else if(href_list["replay_stop"]) - replay_stop() - else if(href_list["replay_start"]) - replay_start() - else if(href_list["loop_start"]) - loop_mode = TRUE - replay_start() - else if(href_list["record_start"]) - record_start(usr) - else if(href_list["record_stop"]) - record_stop() - else if(href_list["record_clear"]) - record_clear() - else if(href_list["offset"]) - offset++ - if (offset > 4) - offset = FALSE - var/turf/new_turf - if (!offset) - new_turf = get_turf(src) - else - new_turf = get_step(src, GLOB.cardinals[offset]) - replay_holo.forceMove(new_turf) - updateDialog() - //do not allow AIs to answer calls or people will use it to meta the AI sattelite /obj/machinery/holopad/attack_ai(mob/living/silicon/ai/user) if (!istype(user)) @@ -366,6 +372,9 @@ GLOBAL_LIST_EMPTY(network_holopads) if(force_answer_call && world.time > (HC.call_start_time + (HOLOPAD_MAX_DIAL_TIME / 2))) HC.Answer(src) break + if(!secure) //HC.head_call && + HC.Answer(src) + break if(outgoing_call) HC.Disconnect(src)//can't answer calls while calling else @@ -412,17 +421,17 @@ GLOBAL_LIST_EMPTY(network_holopads) /*This is the proc for special two-way communication between AI and holopad/people talking near holopad. For the other part of the code, check silicon say.dm. Particularly robot talk.*/ -/obj/machinery/holopad/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source) +/obj/machinery/holopad/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list()) . = ..() if(speaker && LAZYLEN(masters) && !radio_freq)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios. for(var/mob/living/silicon/ai/master in masters) if(masters[master] && speaker != master) - master.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mode) + master.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mods) for(var/I in holo_calls) var/datum/holocall/HC = I if(HC.connected_holopad == src && speaker != HC.hologram) - HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mode, source) + HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mods) if(outgoing_call && speaker == outgoing_call.user) outgoing_call.hologram.say(raw_message) @@ -449,7 +458,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ else icon_state = "holopad0" -/obj/machinery/holopad/proc/set_holo(mob/living/user, var/obj/effect/overlay/holo_pad_hologram/h) +/obj/machinery/holopad/proc/set_holo(mob/living/user, obj/effect/overlay/holo_pad_hologram/h) LAZYSET(masters, user, h) LAZYSET(holorays, user, new /obj/effect/overlay/holoray(loc)) var/mob/living/silicon/ai/AI = user @@ -504,7 +513,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ else return FALSE -/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf, direction) +/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf) if(LAZYLEN(masters) && masters[user]) var/obj/effect/overlay/holo_pad_hologram/holo = masters[user] var/transfered = FALSE @@ -516,8 +525,6 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ transfered = TRUE //All is good. holo.forceMove(new_turf) - if(direction) - holo.setDir(direction) if(!transfered) update_holoray(user,new_turf) return TRUE @@ -568,22 +575,15 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ if(!replay_mode) replay_mode = TRUE replay_holo = setup_replay_holo(disk.record) - temp = "Replaying...
" - temp += "Change offset
" - temp += "End replay" SetLightsAndPower() replay_entry(1) - return /obj/machinery/holopad/proc/replay_stop() if(replay_mode) replay_mode = FALSE - loop_mode = FALSE offset = FALSE - temp = null QDEL_NULL(replay_holo) SetLightsAndPower() - updateDialog() /obj/machinery/holopad/proc/record_start(mob/living/user) if(!user || !disk || disk.record) @@ -593,8 +593,6 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ record_start = world.time record_user = user disk.record.set_caller_image(user) - temp = "Recording...
" - temp += "End recording." /obj/machinery/holopad/proc/record_message(mob/living/speaker,message,language) if(!record_mode) @@ -641,7 +639,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ if(replay_holo) replay_holo.say(message) if(HOLORECORD_SOUND) - playsound(src,entry[2],50,1) + playsound(src,entry[2],50,TRUE) if(HOLORECORD_DELAY) addtimer(CALLBACK(src,.proc/replay_entry,entry_number+1),entry[2]) return @@ -660,14 +658,11 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ /obj/machinery/holopad/proc/record_stop() if(record_mode) record_mode = FALSE - temp = null record_user = null - updateDialog() /obj/machinery/holopad/proc/record_clear() if(disk && disk.record) QDEL_NULL(disk.record) - updateDialog() /obj/effect/overlay/holo_pad_hologram initial_language_holder = /datum/language_holder/universal diff --git a/code/game/machinery/hypnochair.dm b/code/game/machinery/hypnochair.dm index 16b464ced6..41f420204e 100644 --- a/code/game/machinery/hypnochair.dm +++ b/code/game/machinery/hypnochair.dm @@ -6,14 +6,12 @@ circuit = /obj/item/circuitboard/machine/hypnochair density = TRUE opacity = 0 - ui_x = 375 - ui_y = 480 + var/mob/living/carbon/victim = null ///Keeps track of the victim to apply effects if it teleports away var/interrogating = FALSE ///Is the device currently interrogating someone? var/start_time = 0 ///Time when the interrogation was started, to calculate effect in case of interruption var/trigger_phrase = "" ///Trigger phrase to implant var/timerid = 0 ///Timer ID for interrogations - var/message_cooldown = 0 ///Cooldown for breakout message /obj/machinery/hypnochair/Initialize() @@ -25,24 +23,24 @@ if(!occupant && default_deconstruction_screwdriver(user, icon_state, icon_state, I)) update_icon() return - if(default_pry_open(I)) return - if(default_deconstruction_crowbar(I)) return - return ..() -/obj/machinery/hypnochair/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/hypnochair/ui_state(mob/user) + return GLOB.notcontained_state + +/obj/machinery/hypnochair/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "HypnoChair", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "HypnoChair", name) ui.open() /obj/machinery/hypnochair/ui_data() var/list/data = list() - data["occupied"] = occupant ? TRUE : FALSE + data["occupied"] = occupant ? 1 : 0 data["open"] = state_open data["interrogating"] = interrogating @@ -201,3 +199,4 @@ if(!(L.mobility_flags & MOBILITY_STAND)) return close_machine(target) + diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm index 63ef40e045..e3df79e291 100644 --- a/code/game/machinery/launch_pad.dm +++ b/code/game/machinery/launch_pad.dm @@ -282,12 +282,14 @@ ui_interact(user) to_chat(user, "[src] projects a display onto your retina.") -/obj/item/launchpad_remote/ui_interact(mob/user, ui_key = "launchpad_remote", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "LaunchpadRemote", "Briefcase Launchpad Remote", 300, 240, master_ui, state) //width, height - ui.open() +/obj/item/launchpad_remote/ui_state(mob/user) + return GLOB.inventory_state +/obj/item/launchpad_remote/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "LaunchpadRemote") + ui.open() ui.set_autoupdate(TRUE) /obj/item/launchpad_remote/ui_data(mob/user) diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index f69a722473..54d320e93b 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -4,6 +4,15 @@ #define POPUP_ANIM_TIME 5 #define POPDOWN_ANIM_TIME 5 //Be sure to change the icon animation at the same time or it'll look bad +#define TURRET_FLAG_SHOOT_ALL_REACT (1<<0) // The turret gets pissed off and shoots at people nearby (unless they have sec access!) +#define TURRET_FLAG_AUTH_WEAPONS (1<<1) // Checks if it can shoot people that have a weapon they aren't authorized to have +#define TURRET_FLAG_SHOOT_CRIMINALS (1<<2) // Checks if it can shoot people that are wanted +#define TURRET_FLAG_SHOOT_ALL (1<<3) // The turret gets pissed off and shoots at people nearby (unless they have sec access!) +#define TURRET_FLAG_SHOOT_ANOMALOUS (1<<4) // Checks if it can shoot at unidentified lifeforms (ie xenos) +#define TURRET_FLAG_SHOOT_UNSHIELDED (1<<5) // Checks if it can shoot people that aren't mindshielded and who arent heads +#define TURRET_FLAG_SHOOT_BORGS (1<<6) // checks if it can shoot cyborgs +#define TURRET_FLAG_SHOOT_HEADS (1<<7) // checks if it can shoot at heads of staff + /obj/machinery/porta_turret name = "turret" icon = 'icons/obj/turrets.dmi' @@ -15,70 +24,79 @@ use_power = IDLE_POWER_USE //this turret uses and requires power idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power - req_access = list(ACCESS_SEC_DOORS) + req_access = list(ACCESS_SECURITY) /// Only people with Security access power_channel = EQUIP //drains power from the EQUIPMENT channel - speed_process = TRUE - - var/base_icon_state = "standard" - var/scan_range = 7 - var/atom/base = null //for turrets inside other objects - - var/raised = 0 //if the turret cover is "open" and the turret is raised - var/raising= 0 //if the turret is currently opening or closing its cover - max_integrity = 160 //the turret's health integrity_failure = 0.5 armor = list("melee" = 50, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) - - var/locked = TRUE //if the turret's behaviour control access is locked - var/controllock = FALSE //if the turret responds to control panels - - var/installation = /obj/item/gun/energy/e_gun/turret //the type of weapon installed by default + /// Base turret icon state + var/base_icon_state = "standard" + /// Scan range of the turret for locating targets + var/scan_range = 7 + /// For turrets inside other objects + var/atom/base = null + /// If the turret cover is "open" and the turret is raised + var/raised = FALSE + /// If the turret is currently opening or closing its cover + var/raising = FALSE + /// If the turret's behaviour control access is locked + var/locked = TRUE + /// If the turret responds to control panels + var/controllock = FALSE + /// The type of weapon installed by default + var/installation = /obj/item/gun/energy/e_gun/turret + /// What stored gun is in the turret var/obj/item/gun/stored_gun = null - var/gun_charge = 0 //the charge of the gun when retrieved from wreckage - + /// The charge of the gun when retrieved from wreckage + var/gun_charge = 0 + /// In which mode is turret in, stun or lethal var/mode = TURRET_STUN - - var/stun_projectile = null //stun mode projectile type + /// Stun mode projectile type + var/stun_projectile = null + /// Sound of stun projectile var/stun_projectile_sound - var/nonlethal_projectile //projectile to use in stun mode when the target is resting, if any + /// Projectile to use in stun mode when the target is resting, if any + var/nonlethal_projectile + /// Sound of stun projectile wen the target is resting, optional var/nonlethal_projectile_sound - var/lethal_projectile = null //lethal mode projectile type + /// Lethal mode projectile type + var/lethal_projectile = null + /// Sound of lethal projectile var/lethal_projectile_sound - - var/reqpower = 500 //power needed per shot - var/always_up = 0 //Will stay active - var/has_cover = 1 //Hides the cover - - var/obj/machinery/porta_turret_cover/cover = null //the cover that is covering this turret - - var/last_fired = 0 //world.time the turret last fired - var/shot_delay = 15 //ticks until next shot (1.5 ?) - var/shot_stagger = 0 // sleep() shots to stagger attacks - - var/check_records = 1 //checks if it can use the security records - var/criminals = 1 //checks if it can shoot people on arrest - var/auth_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have - var/stun_all = 0 //if this is active, the turret shoots everything that isn't security or head of staff - var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos) - var/shoot_unloyal = 0 //checks if it can shoot people that aren't loyalty implantd - - var/attacked = 0 //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!) - - var/on = TRUE //determines if the turret is on - - var/list/faction = list("turret") // Same faction mobs will never be shot at, no matter the other settings - - var/datum/effect_system/spark_spread/spark_system //the spark system, used for generating... sparks? - + /// Power needed per shot + var/reqpower = 500 + /// Will stay active + var/always_up = FALSE + /// Hides the cover + var/has_cover = TRUE + /// The cover that is covering this turret + var/obj/machinery/porta_turret_cover/cover = null + /// World.time the turret last fired + var/last_fired = 0 + /// Ticks until next shot (1.5 ?) + var/shot_delay = 15 + /// Turret flags about who is turret allowed to shoot + var/turret_flags = TURRET_FLAG_SHOOT_CRIMINALS | TURRET_FLAG_SHOOT_ANOMALOUS + /// Determines if the turret is on + var/on = TRUE + /// Same faction mobs will never be shot at, no matter the other settings + var/list/faction = list("turret") + /// The spark system, used for generating... sparks? + var/datum/effect_system/spark_spread/spark_system + /// Linked turret control panel of the turret var/obj/machinery/turretid/cp = null - - var/wall_turret_direction //The turret will try to shoot from a turf in that direction when in a wall - - var/manual_control = FALSE // + /// The turret will try to shoot from a turf in that direction when in a wall + var/wall_turret_direction + /// If the turret is manually controlled + var/manual_control = FALSE + /// Action button holder for quitting manual control var/datum/action/turret_quit/quit_action + /// Action button holder for switching between turret modes when manually controlling var/datum/action/turret_toggle/toggle_action + /// Mob that is remotely controlling the turret var/mob/remote_controller + /// MISSING: + var/shot_stagger = 0 /obj/machinery/porta_turret/Initialize() . = ..() @@ -100,6 +118,27 @@ if(!has_cover) INVOKE_ASYNC(src, .proc/popUp) +/obj/machinery/porta_turret/proc/toggle_on(var/set_to) + var/current = on + if (!isnull(set_to)) + on = set_to + else + on = !on + if (current != on) + check_should_process() + if (!on) + popDown() + +/obj/machinery/porta_turret/proc/check_should_process() + if (datum_flags & DF_ISPROCESSING) + if (!on || !anchored || (stat & BROKEN) || !powered()) + //end_processing() + STOP_PROCESSING(SSmachines, src) + else + if (on && anchored && !(stat & BROKEN) && powered()) + START_PROCESSING(SSmachines, src) + //begin_processing() + /obj/machinery/porta_turret/update_icon_state() if(!anchored) icon_state = "turretCover" @@ -119,7 +158,6 @@ else icon_state = "[base_icon_state]_unpowered" - /obj/machinery/porta_turret/proc/setup(obj/item/gun/turret_gun) if(stored_gun) qdel(stored_gun) @@ -160,83 +198,88 @@ remove_control() return ..() -/obj/machinery/porta_turret/ui_interact(mob/user) - . = ..() - var/dat - dat += "Status: [on ? "On" : "Off"]
" - dat += "Behaviour controls are [locked ? "locked" : "unlocked"]
" +/obj/machinery/porta_turret/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PortableTurret", name) + ui.open() - if(!locked) - dat += "Check for Weapon Authorization: [auth_weapons ? "Yes" : "No"]
" - dat += "Check Security Records: [check_records ? "Yes" : "No"]
" - dat += "Neutralize Identified Criminals: [criminals ? "Yes" : "No"]
" - dat += "Neutralize All Non-Security and Non-Command Personnel: [stun_all ? "Yes" : "No"]
" - dat += "Neutralize All Unidentified Life Signs: [check_anomalies ? "Yes" : "No"]
" - dat += "Neutralize All Non-Loyalty Implanted Personnel: [shoot_unloyal ? "Yes" : "No"]
" +/obj/machinery/porta_turret/ui_data(mob/user) + var/list/data = list( + "locked" = locked, + "on" = on, + "check_weapons" = turret_flags & TURRET_FLAG_AUTH_WEAPONS, + "neutralize_criminals" = turret_flags & TURRET_FLAG_SHOOT_CRIMINALS, + "neutralize_all" = turret_flags & TURRET_FLAG_SHOOT_ALL, + "neutralize_unidentified" = turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS, + "neutralize_nonmindshielded" = turret_flags & TURRET_FLAG_SHOOT_UNSHIELDED, + "neutralize_cyborgs" = turret_flags & TURRET_FLAG_SHOOT_BORGS, + "ignore_heads" = turret_flags & TURRET_FLAG_SHOOT_HEADS, + "manual_control" = manual_control, + "silicon_user" = FALSE, + "allow_manual_control" = FALSE, + "lasertag_turret" = istype(src, /obj/machinery/porta_turret/lasertag), + ) if(issilicon(user)) + data["silicon_user"] = TRUE if(!manual_control) var/mob/living/silicon/S = user if(S.hack_software) - dat += "Assume direct control : Manual Control
" - else - dat += "Warning! Remote control protocol enabled.
" + data["allow_manual_control"] = TRUE + return data - - var/datum/browser/popup = new(user, "autosec", "Automatic Portable Turret Installation", 300, 300) - popup.set_content(dat) - popup.open() - -/obj/machinery/porta_turret/Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - add_fingerprint(usr) - - if(href_list["power"] && !locked) - if(anchored) //you can't turn a turret on/off if it's not anchored/secured - on = !on //toggle on/off - else - to_chat(usr, "It has to be secured first!") - interact(usr) +/obj/machinery/porta_turret/ui_act(action, list/params) + . = ..() + if(.) return - if(href_list["operation"]) - switch(href_list["operation"]) //toggles customizable behavioural protocols - if("authweapon") - auth_weapons = !auth_weapons - if("checkrecords") - check_records = !check_records - if("shootcrooks") - criminals = !criminals - if("shootall") - stun_all = !stun_all - if("checkxenos") - check_anomalies = !check_anomalies - if("checkloyal") - shoot_unloyal = !shoot_unloyal - if("manual") - if(issilicon(usr) && !manual_control) - give_control(usr) - interact(usr) + switch(action) + if("power") + if(anchored) + toggle_on() + return TRUE + else + to_chat(usr, "It has to be secured first!") + if("authweapon") + turret_flags ^= TURRET_FLAG_AUTH_WEAPONS + return TRUE + if("shootcriminals") + turret_flags ^= TURRET_FLAG_SHOOT_CRIMINALS + return TRUE + if("shootall") + turret_flags ^= TURRET_FLAG_SHOOT_ALL + return TRUE + if("checkxenos") + turret_flags ^= TURRET_FLAG_SHOOT_ANOMALOUS + return TRUE + if("checkloyal") + turret_flags ^= TURRET_FLAG_SHOOT_UNSHIELDED + return TRUE + if("shootborgs") + turret_flags ^= TURRET_FLAG_SHOOT_BORGS + return TRUE + if("shootheads") + turret_flags ^= TURRET_FLAG_SHOOT_HEADS + return TRUE + if("manual") + if(!issilicon(usr)) + return + give_control(usr) + return TRUE + +/obj/machinery/porta_turret/ui_host(mob/user) + if(has_cover && cover) + return cover + if(base) + return base + return src /obj/machinery/porta_turret/power_change() - if(!anchored) + . = ..() + if(!anchored || (stat & BROKEN) || !powered()) update_icon() remove_control() - return - if(stat & BROKEN) - update_icon() - remove_control() - else - if( powered() ) - stat &= ~NOPOWER - update_icon() - else - spawn(rand(0, 15)) - stat |= NOPOWER - remove_control() - update_icon() - + check_should_process() /obj/machinery/porta_turret/attackby(obj/item/I, mob/user, params) if(stat & BROKEN) @@ -284,8 +327,10 @@ locked = !locked to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].") else - to_chat(user, "Access denied.") + to_chat(user, "Access denied.") else if(istype(I, /obj/item/multitool) && !locked) + if(!multitool_check_buffer(user, I)) + return var/obj/item/multitool/M = I M.buffer = src to_chat(user, "You add [src] to multitool buffer.") @@ -293,19 +338,17 @@ return ..() /obj/machinery/porta_turret/emag_act(mob/user) - . = ..() if(obj_flags & EMAGGED) return to_chat(user, "You short out [src]'s threat assessment circuits.") - visible_message("[src] hums oddly...") + audible_message("[src] hums oddly...") obj_flags |= EMAGGED controllock = TRUE - on = FALSE //turns off the turret temporarily + toggle_on(FALSE) //turns off the turret temporarily update_icon() - sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit - on = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here - return TRUE - + //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit + addtimer(CALLBACK(src, .proc/toggle_on, TRUE), 6 SECONDS) + //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here /obj/machinery/porta_turret/emp_act(severity) . = ..() @@ -314,63 +357,41 @@ if(on) //if the turret is on, the EMP no matter how severe disables the turret for a while //and scrambles its settings, with a slight chance of having an emag effect - check_records = pick(0, 1) - criminals = pick(0, 1) - auth_weapons = pick(0, 1) - stun_all = pick(0, 0, 0, 0, 1) //stun_all is a pretty big deal, so it's least likely to get turned on + if(prob(50)) + turret_flags |= TURRET_FLAG_SHOOT_CRIMINALS + if(prob(50)) + turret_flags |= TURRET_FLAG_AUTH_WEAPONS + if(prob(20)) + turret_flags |= TURRET_FLAG_SHOOT_ALL // Shooting everyone is a pretty big deal, so it's least likely to get turned on - on = FALSE + toggle_on(FALSE) remove_control() - spawn(rand(60,600)) - if(!on) - on = TRUE + addtimer(CALLBACK(src, .proc/toggle_on, TRUE), rand(60,600)) /obj/machinery/porta_turret/take_damage(damage, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) . = ..() - if(.) //damage received + if(. && obj_integrity > 0) //damage received if(prob(30)) spark_system.start() - if(on && !attacked && !(obj_flags & EMAGGED)) - attacked = TRUE + if(on && !(turret_flags & TURRET_FLAG_SHOOT_ALL_REACT) && !(obj_flags & EMAGGED)) + turret_flags |= TURRET_FLAG_SHOOT_ALL_REACT addtimer(CALLBACK(src, .proc/reset_attacked), 60) /obj/machinery/porta_turret/proc/reset_attacked() - attacked = FALSE + turret_flags &= ~TURRET_FLAG_SHOOT_ALL_REACT /obj/machinery/porta_turret/deconstruct(disassembled = TRUE) qdel(src) /obj/machinery/porta_turret/obj_break(damage_flag) - if(!(flags_1 & NODECONSTRUCT_1) && !(stat & BROKEN)) - stat |= BROKEN //enables the BROKEN bit + . = ..() + if(.) power_change() invisibility = 0 spark_system.start() //creates some sparks because they look cool qdel(cover) //deletes the cover - no need on keeping it there! -//turret healing -/obj/machinery/porta_turret/examine(mob/user) - . = ..() - if(obj_integrity < max_integrity) - . += "Use a welder to fix it." - -/obj/machinery/porta_turret/welder_act(mob/living/user, obj/item/I) - . = TRUE - if(obj_integrity < max_integrity) - if(!I.tool_start_check(user, amount=0)) - return - user.visible_message("[user] is welding the turret.", \ - "You begin repairing the turret...", \ - "You hear welding.") - if(I.use_tool(src, user, 40, volume=50)) - obj_integrity = max_integrity - user.visible_message("[user.name] has repaired [src].", \ - "You finish repairing the turret.") - else - to_chat(user, "The turret doesn't need repairing.") - - /obj/machinery/porta_turret/process() //the main machinery process if(cover == null && anchored) //if it has no cover and is anchored @@ -382,35 +403,43 @@ cover.parent_turret = src //assign the cover its parent_turret, which would be this (src) if(!on || (stat & (NOPOWER|BROKEN)) || manual_control) - return + return PROCESS_KILL var/list/targets = list() for(var/mob/A in view(scan_range, base)) if(A.invisibility > SEE_INVISIBLE_LIVING) continue - if(check_anomalies)//if it's set to check for simple animals + if(turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS)//if it's set to check for simple animals if(isanimal(A)) var/mob/living/simple_animal/SA = A if(SA.stat || in_faction(SA)) //don't target if dead or in faction continue targets += SA - if(issilicon(A)) - var/mob/living/silicon/sillycone = A - if(sillycone.stat || in_faction(sillycone)) + continue + + if(issilicon(A)) + var/mob/living/silicon/sillycone = A + + if(ispAI(A)) + continue + + if((turret_flags & TURRET_FLAG_SHOOT_BORGS) && sillycone.stat != DEAD && iscyborg(sillycone)) + targets += sillycone + continue + + if(sillycone.stat || in_faction(sillycone)) + continue + + if(iscyborg(sillycone)) + var/mob/living/silicon/robot/sillyconerobot = A + if(LAZYLEN(faction) && (ROLE_SYNDICATE in faction) && sillyconerobot.emagged == TRUE) continue - if(iscyborg(sillycone)) - var/mob/living/silicon/robot/sillyconerobot = A - if(LAZYLEN(faction) && (ROLE_SYNDICATE in faction) && sillyconerobot.emagged == TRUE) - continue - - targets += sillycone - - if(iscarbon(A)) + else if(iscarbon(A)) var/mob/living/carbon/C = A - //If not emagged, only target non downed carbons - if(mode != TURRET_LETHAL && (C.stat || C.handcuffed || (C.combat_flags & COMBAT_FLAG_HARD_STAMCRIT)))//CIT CHANGE - replaces check for lying with check for recoveringstam + //If not emagged, only target carbons that can use items + if(mode != TURRET_LETHAL && (C.stat || C.handcuffed || !(C.mobility_flags & MOBILITY_USE))) continue //If emagged, target all but dead carbons @@ -419,12 +448,13 @@ //if the target is a human and not in our faction, analyze threat level if(ishuman(C) && !in_faction(C)) + if(assess_perp(C) >= 4) targets += C - - else if(check_anomalies) //non humans who are not simple animals (xenos etc) + else if(turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS) //non humans who are not simple animals (xenos etc) if(!in_faction(C)) targets += C + for(var/A in GLOB.mechas_list) if((get_dist(A, base) < scan_range) && can_see(base, A, scan_range)) var/obj/mecha/Mech = A @@ -432,6 +462,10 @@ if(assess_perp(Mech.occupant) >= 4) targets += Mech + if((turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS) && GLOB.blobs.len && (mode == TURRET_LETHAL)) + for(var/obj/structure/blob/B in view(scan_range, base)) + targets += B + if(targets.len) tryToShootAt(targets) else if(!always_up) @@ -488,36 +522,37 @@ if(obj_flags & EMAGGED) return 10 //if emagged, always return 10. - if((stun_all || attacked) && !allowed(perp)) + if((turret_flags & (TURRET_FLAG_SHOOT_ALL | TURRET_FLAG_SHOOT_ALL_REACT)) && !allowed(perp)) //if the turret has been attacked or is angry, target all non-sec people if(!allowed(perp)) return 10 - if(auth_weapons) //check for weapon authorization + if(turret_flags & TURRET_FLAG_AUTH_WEAPONS) //check for weapon authorization if(isnull(perp.wear_id) || istype(perp.wear_id.GetID(), /obj/item/card/id/syndicate)) if(allowed(perp)) //if the perp has security access, return 0 return 0 - if(perp.is_holding_item_of_type(/obj/item/gun) || perp.is_holding_item_of_type(/obj/item/melee/baton)) threatcount += 4 if(istype(perp.belt, /obj/item/gun) || istype(perp.belt, /obj/item/melee/baton)) threatcount += 2 - if(check_records) //if the turret can check the records, check if they are set to *Arrest* on records + if(turret_flags & TURRET_FLAG_SHOOT_CRIMINALS) //if the turret can check the records, check if they are set to *Arrest* on records var/perpname = perp.get_face_name(perp.get_id_name()) var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security) if(!R || (R.fields["criminal"] == "*Arrest*")) threatcount += 4 - if(shoot_unloyal) - if (!HAS_TRAIT(perp, TRAIT_MINDSHIELD)) - threatcount += 4 + if((turret_flags & TURRET_FLAG_SHOOT_UNSHIELDED) && (!HAS_TRAIT(perp, TRAIT_MINDSHIELD))) + threatcount += 4 + + // If we aren't shooting heads then return a threatcount of 0 + if (!(turret_flags & TURRET_FLAG_SHOOT_HEADS) && (perp.get_assignment() in GLOB.command_positions)) + return 0 return threatcount - /obj/machinery/porta_turret/proc/in_faction(mob/target) for(var/faction1 in faction) if(faction1 in target.faction) @@ -563,15 +598,14 @@ T = closer break - var/mob/living/carbon/C - if(iscarbon(target)) - C = target - update_icon() var/obj/item/projectile/A //any emagged turrets drains 2x power and uses a different projectile? if(mode == TURRET_STUN) - if(nonlethal_projectile && C && C.resting) + var/mob/living/carbon/C = null + if(iscarbon(target)) + C = target + if(nonlethal_projectile && C?.resting) use_power(reqpower*0.5) A = new nonlethal_projectile(T) playsound(loc, nonlethal_projectile_sound, 75, 1) @@ -582,7 +616,7 @@ else use_power(reqpower * 2) A = new lethal_projectile(T) - playsound(loc, lethal_projectile_sound, 75, 1) + playsound(loc, lethal_projectile_sound, 75, TRUE) //Shooting Code: @@ -592,16 +626,15 @@ A.fire() return A -/obj/machinery/porta_turret/proc/setState(on, mode) +/obj/machinery/porta_turret/proc/setState(on, mode, shoot_cyborgs) if(controllock) return - src.on = on - if(!on) - popDown() + + shoot_cyborgs ? (turret_flags |= TURRET_FLAG_SHOOT_BORGS) : (turret_flags &= ~TURRET_FLAG_SHOOT_BORGS) + toggle_on(on) src.mode = mode power_change() - /datum/action/turret_toggle name = "Toggle Mode" icon_icon = 'icons/mob/actions/actions_mecha.dmi' @@ -685,7 +718,15 @@ /obj/machinery/porta_turret/syndicate/ComponentInitialize() . = ..() - AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES) + // AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES) + AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES) //this one or ^ one? + + +/obj/machinery/porta_turret/syndicate/setup() + return + +/obj/machinery/porta_turret/syndicate/assess_perp(mob/living/carbon/human/perp) + return 10 //Syndicate turrets shoot everything not in their faction /obj/machinery/porta_turret/syndicate/energy icon_state = "standard_stun" @@ -698,7 +739,6 @@ lethal_projectile_sound = 'sound/weapons/laser.ogg' desc = "An energy blaster auto-turret." - /obj/machinery/porta_turret/syndicate/energy/heavy icon_state = "standard_stun" base_icon_state = "standard" @@ -715,14 +755,13 @@ integrity_failure = 0.08 armor = list("melee" = 50, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) - -/obj/machinery/porta_turret/syndicate/setup() - return - -/obj/machinery/porta_turret/syndicate/assess_perp(mob/living/carbon/human/perp) - return 10 //Syndicate turrets shoot everything not in their faction +/obj/machinery/porta_turret/syndicate/energy/raven + stun_projectile = /obj/item/projectile/beam/laser + stun_projectile_sound = 'sound/weapons/laser.ogg' + faction = list("neutral","silicon","turret") /obj/machinery/porta_turret/syndicate/pod + integrity_failure = 0.5 max_integrity = 40 stun_projectile = /obj/item/projectile/bullet/syndicate_turret lethal_projectile = /obj/item/projectile/bullet/syndicate_turret @@ -749,6 +788,7 @@ faction = list("silicon") nonlethal_projectile = /obj/item/projectile/beam/disabler nonlethal_projectile_sound = 'sound/weapons/taser2.ogg' + turret_flags = TURRET_FLAG_SHOOT_CRIMINALS | TURRET_FLAG_SHOOT_ANOMALOUS | TURRET_FLAG_SHOOT_HEADS /obj/machinery/porta_turret/ai/assess_perp(mob/living/carbon/human/perp) return 10 //AI turrets shoot at everything not in their faction @@ -804,6 +844,7 @@ /obj/machinery/porta_turret/centcom_shuttle/weak max_integrity = 120 + integrity_failure = 0.5 name = "Old Laser Turret" desc = "A turret built with substandard parts and run down further with age. Still capable of delivering lethal lasers to the odd space carp, but not much else." stun_projectile = /obj/item/projectile/beam/weak/penetrator @@ -817,7 +858,6 @@ stun_projectile_sound = 'sound/weapons/gunshot.ogg' desc = "A ballistic machine gun auto-turret." - //////////////////////// //Turret Control Panel// //////////////////////// @@ -828,14 +868,22 @@ icon = 'icons/obj/machines/turret_control.dmi' icon_state = "control_standby" density = FALSE - var/enabled = 1 - var/lethal = 0 - var/locked = TRUE - var/control_area = null //can be area name, path or nothing. - var/ailock = 0 // AI cannot use this req_access = list(ACCESS_AI_UPLOAD) - var/list/obj/machinery/porta_turret/turrets = list() resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + /// Variable dictating if linked turrets are active and will shoot targets + var/enabled = TRUE + /// Variable dictating if linked turrets will shoot lethal projectiles + var/lethal = FALSE + /// Variable dictating if the panel is locked, preventing changes to turret settings + var/locked = TRUE + /// An area in which linked turrets are located, it can be an area name, path or nothing + var/control_area = null + /// AI is unable to use this machine if set to TRUE + var/ailock = FALSE + /// Variable dictating if linked turrets will shoot cyborgs + var/shoot_cyborgs = FALSE + /// List of all linked turrets + var/list/turrets = list() /obj/machinery/turretid/Initialize(mapload, ndir = 0, built = 0) . = ..() @@ -868,112 +916,111 @@ T.cp = src /obj/machinery/turretid/examine(mob/user) - . = ..() - if(hasSiliconAccessInArea(user) && (!stat & BROKEN)) - . += "Ctrl-click [src] to [ enabled ? "disable" : "enable"] turrets." - . += "Alt-click [src] to set turrets to [ lethal ? "stun" : "kill"]." + . += ..() + if(issilicon(user) && !(stat & BROKEN)) + . += {"Ctrl-click [src] to [ enabled ? "disable" : "enable"] turrets. + Alt-click [src] to set turrets to [ lethal ? "stun" : "kill"]."} /obj/machinery/turretid/attackby(obj/item/I, mob/user, params) if(stat & BROKEN) return if (istype(I, /obj/item/multitool)) + if(!multitool_check_buffer(user, I)) + return var/obj/item/multitool/M = I if(M.buffer && istype(M.buffer, /obj/machinery/porta_turret)) turrets |= M.buffer - to_chat(user, "You link \the [M.buffer] with \the [src]") + to_chat(user, "You link \the [M.buffer] with \the [src].") return - if (hasSiliconAccessInArea(user)) + if (issilicon(user)) return attack_hand(user) if ( get_dist(src, user) == 0 ) // trying to unlock the interface if (allowed(usr)) if(obj_flags & EMAGGED) - to_chat(user, "The turret control is unresponsive.") + to_chat(user, "The turret control is unresponsive!") return locked = !locked to_chat(user, "You [ locked ? "lock" : "unlock"] the panel.") - if (locked) - if (user.machine==src) - user.unset_machine() - user << browse(null, "window=turretid") - else - if (user.machine==src) - attack_hand(user) else - to_chat(user, "Access denied.") + to_chat(user, "Access denied.") /obj/machinery/turretid/emag_act(mob/user) - . = ..() if(obj_flags & EMAGGED) return - to_chat(user, "You short out the turret controls' access analysis module.") + to_chat(user, "You short out the turret controls' access analysis module.") obj_flags |= EMAGGED locked = FALSE - if(user && user.machine == src) - attack_hand(user) - return TRUE /obj/machinery/turretid/attack_ai(mob/user) if(!ailock || IsAdminGhost(user)) return attack_hand(user) else - to_chat(user, "There seems to be a firewall preventing you from accessing this device.") + to_chat(user, "There seems to be a firewall preventing you from accessing this device!") -/obj/machinery/turretid/ui_interact(mob/user) +/obj/machinery/turretid/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TurretControl", name) + ui.open() + +/obj/machinery/turretid/ui_data(mob/user) + var/list/data = list() + data["locked"] = locked + data["siliconUser"] = hasSiliconAccessInArea(user) || IsAdminGhost(user) + data["enabled"] = enabled + data["lethal"] = lethal + data["shootCyborgs"] = shoot_cyborgs + return data + +/obj/machinery/turretid/ui_act(action, list/params) . = ..() - if ( get_dist(src, user) > 0 ) - if ( !(hasSiliconAccessInArea(user) || IsAdminGhost(user)) ) - to_chat(user, "You are too far away.") - user.unset_machine() - user << browse(null, "window=turretid") - return - - var/t = "" - - if(locked && !(hasSiliconAccessInArea(user) || IsAdminGhost(user))) - t += "
Swipe ID card to unlock interface
" - else - if(!hasSiliconAccessInArea(user) && !IsAdminGhost(user)) - t += "
Swipe ID card to lock interface
" - t += "Turrets [enabled?"activated":"deactivated"] - [enabled?"Disable":"Enable"]?
" - t += "Currently set for [lethal?"lethal":"stun repeatedly"] - Change to [lethal?"Stun repeatedly":"Lethal"]?
" - - var/datum/browser/popup = new(user, "turretid", "Turret Control Panel ([get_area_name(src, TRUE)])") - popup.set_content(t) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - -/obj/machinery/turretid/Topic(href, href_list) - if(..()) + if(.) return - if (locked) - if(!(hasSiliconAccessInArea(usr) || IsAdminGhost(usr))) - to_chat(usr, "Control panel is locked!") - return - if (href_list["toggleOn"]) - toggle_on() - else if (href_list["toggleLethal"]) - toggle_lethal() - attack_hand(usr) -/obj/machinery/turretid/proc/toggle_lethal() + switch(action) + if("lock") + if(!hasSiliconAccessInArea(usr) || IsAdminGhost(usr)) + return + if((obj_flags & EMAGGED) || (stat & BROKEN)) + to_chat(usr, "The turret control is unresponsive!") + return + locked = !locked + return TRUE + if("power") + toggle_on(usr) + return TRUE + if("mode") + toggle_lethal(usr) + return TRUE + if("shoot_silicons") + shoot_silicons(usr) + return TRUE + +/obj/machinery/turretid/proc/toggle_lethal(mob/user) lethal = !lethal + add_hiddenprint(user) + log_combat(user, src, "[lethal ? "enabled" : "disabled"] lethals on") updateTurrets() -/obj/machinery/turretid/proc/toggle_on() +/obj/machinery/turretid/proc/toggle_on(mob/user) enabled = !enabled + add_hiddenprint(user) + log_combat(user, src, "[enabled ? "enabled" : "disabled"]") + updateTurrets() + +/obj/machinery/turretid/proc/shoot_silicons(mob/user) + shoot_cyborgs = !shoot_cyborgs + add_hiddenprint(user) + log_combat(user, src, "[shoot_cyborgs ? "Shooting Borgs" : "Not Shooting Borgs"]") updateTurrets() /obj/machinery/turretid/proc/updateTurrets() for (var/obj/machinery/porta_turret/aTurret in turrets) - aTurret.setState(enabled, lethal) - update_icon() - -/obj/machinery/turretid/power_change() - ..() + aTurret.setState(enabled, lethal, shoot_cyborgs) update_icon() /obj/machinery/turretid/update_icon_state() @@ -1051,11 +1098,7 @@ /obj/machinery/porta_turret/lasertag req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE) - check_records = 0 - criminals = 0 - auth_weapons = 1 - stun_all = 0 - check_anomalies = 0 + turret_flags = TURRET_FLAG_AUTH_WEAPONS var/team_color /obj/machinery/porta_turret/lasertag/assess_perp(mob/living/carbon/human/perp) @@ -1083,20 +1126,14 @@ if(properties["team_color"]) team_color = properties["team_color"] -/obj/machinery/porta_turret/lasertag/ui_interact(mob/user) - . = ..() +/obj/machinery/porta_turret/lasertag/ui_status(mob/user) if(ishuman(user)) var/mob/living/carbon/human/H = user if(team_color == "blue" && istype(H.wear_suit, /obj/item/clothing/suit/redtag)) - return + return UI_CLOSE if(team_color == "red" && istype(H.wear_suit, /obj/item/clothing/suit/bluetag)) - return - - var/dat = "Status: [on ? "On" : "Off"]" - - var/datum/browser/popup = new(user, "autosec", "Automatic Portable Turret Installation", 300, 300) - popup.set_content(dat) - popup.open() + return UI_CLOSE + return ..() //lasertag presets /obj/machinery/porta_turret/lasertag/red @@ -1112,11 +1149,9 @@ if(on) if(team_color == "blue") if(istype(P, /obj/item/projectile/beam/lasertag/redtag)) - on = FALSE - spawn(100) - on = TRUE + toggle_on(FALSE) + addtimer(CALLBACK(src, .proc/toggle_on, TRUE), 10 SECONDS) else if(team_color == "red") if(istype(P, /obj/item/projectile/beam/lasertag/bluetag)) - on = FALSE - spawn(100) - on = TRUE + toggle_on(FALSE) + addtimer(CALLBACK(src, .proc/toggle_on, TRUE), 10 SECONDS) diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index bbafe99006..0accd0994c 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -126,17 +126,6 @@ update_icon() /obj/machinery/recharge_station/proc/process_occupant() - if(occupant && iscyborg(occupant)) - var/mob/living/silicon/robot/R = occupant - restock_modules() - if(repairs) - R.heal_bodypart_damage(repairs, repairs - 1) - if(R.cell) - R.cell.charge = min(R.cell.charge + recharge_speed, R.cell.maxcharge) - -/obj/machinery/recharge_station/proc/restock_modules() - if(occupant) - var/mob/living/silicon/robot/R = occupant - if(R && R.module) - var/coeff = recharge_speed * 0.005 - R.module.respawn_consumable(R, coeff) + if(!occupant) + return + SEND_SIGNAL(occupant, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, recharge_speed, repairs) diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index e6137c08c2..c6484b051c 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -170,12 +170,10 @@ ..() default_unfasten_wrench(user, I, 5) return TRUE - -/obj/machinery/space_heater/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/space_heater/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "SpaceHeater", name, 400, 305, master_ui, state) + ui = new(user, src, "SpaceHeater", name) ui.open() /obj/machinery/space_heater/ui_data() @@ -210,7 +208,7 @@ if("power") on = !on mode = HEATER_MODE_STANDBY - usr.visible_message("[usr] switches [on ? "on" : "off"] \the [src].", "You switch [on ? "on" : "off"] \the [src].") + usr.visible_message("[usr] switches [on ? "on" : "off"] \the [src].", "You switch [on ? "on" : "off"] \the [src].") update_icon() if (on) START_PROCESSING(SSmachines, src) @@ -222,16 +220,7 @@ if(!panel_open) return var/target = params["target"] - var/adjust = text2num(params["adjust"]) - if(target == "input") - target = input("New target temperature:", name, round(targetTemperature - T0C, 1)) as num|null - if(!isnull(target) && !..()) - target += T0C - . = TRUE - else if(adjust) - target = targetTemperature + adjust - . = TRUE - else if(text2num(target) != null) + if(text2num(target) != null) target= text2num(target) + T0C . = TRUE if(.) diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 75772786fc..cecf351ea9 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -377,11 +377,13 @@ return ..() -/obj/machinery/suit_storage_unit/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/suit_storage_unit/ui_state(mob/user) + return GLOB.notcontained_state + +/obj/machinery/suit_storage_unit/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "SuitStorageUnit", name, 400, 305, master_ui, state) + ui = new(user, src, "SuitStorageUnit", name) ui.open() /obj/machinery/suit_storage_unit/ui_data() diff --git a/code/game/machinery/telecomms/computers/logbrowser.dm b/code/game/machinery/telecomms/computers/logbrowser.dm index 9867a1c133..ca04009e3b 100644 --- a/code/game/machinery/telecomms/computers/logbrowser.dm +++ b/code/game/machinery/telecomms/computers/logbrowser.dm @@ -17,11 +17,10 @@ var/notice = "" var/universal_translate = FALSE // set to TRUE(1) if it can translate nonhuman speech -/obj/machinery/computer/telecomms/server/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/telecomms/server/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "TelecommsLogBrowser", "Telecomms Server Monitor", 575, 400, master_ui, state) + ui = new(user, src, "TelecommsLogBrowser", "Telecomms Server Monitor") ui.open() /obj/machinery/computer/telecomms/server/ui_data(mob/user) diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm index 11d50a2858..539025e4d5 100644 --- a/code/game/machinery/telecomms/computers/message.dm +++ b/code/game/machinery/telecomms/computers/message.dm @@ -36,12 +36,11 @@ light_color = LIGHT_COLOR_GREEN -/obj/machinery/computer/message_monitor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE,\ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/message_monitor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "TelecommsPDALog", name, 727, 510, master_ui, state) + ui = new(user, src, "TelecommsPDALog", name) ui.open() /obj/machinery/computer/message_monitor/ui_static_data(mob/user) diff --git a/code/game/machinery/telecomms/computers/telemonitor.dm b/code/game/machinery/telecomms/computers/telemonitor.dm index 11312fcfaf..b711b9fa40 100644 --- a/code/game/machinery/telecomms/computers/telemonitor.dm +++ b/code/game/machinery/telecomms/computers/telemonitor.dm @@ -17,12 +17,11 @@ circuit = /obj/item/circuitboard/computer/comm_monitor -/obj/machinery/computer/telecomms/monitor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE,\ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/telecomms/monitor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "TelecommsMonitor", name, 575, 400, master_ui, state) + ui = new(user, src, "TelecommsMonitor", name) ui.open() /obj/machinery/computer/telecomms/monitor/ui_data(mob/user) diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm index 86aa7905d5..cc8784c5b8 100644 --- a/code/game/machinery/telecomms/machine_interactions.dm +++ b/code/game/machinery/telecomms/machine_interactions.dm @@ -27,16 +27,15 @@ else return ..() -/obj/machinery/telecomms/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE,\ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) +/obj/machinery/telecomms/ui_interact(mob/user, datum/tgui/ui) if(!canInteract(user)) if(ui) ui.close() //haha no. return - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "TelecommsInteraction", "[name] Access", 520, 500, master_ui, state) + ui = new(user, src, "TelecommsInteraction", "[name] Access") ui.open() /obj/machinery/telecomms/ui_data(mob/user) diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index 86cdd87505..ef1565502e 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -11,10 +11,15 @@ var/equip_ready = 1 //whether the equipment is ready for use. (or deactivated/activated for static stuff) var/energy_drain = 0 var/obj/mecha/chassis = null - var/range = MELEE //bitFflags + /// Bitflag. Determines the range of the equipment. + var/range = MELEE + /// Bitflag. Used by exosuit fabricator to assign sub-categories based on which exosuits can equip this. + var/mech_flags = NONE var/salvageable = 1 + //var/detachable = TRUE // Set to FALSE for built-in equipment that cannot be removed var/selectable = 1 // Set to 0 for passive equipment such as mining scanner or armor plates var/harmful = FALSE //Controls if equipment can be used to attack by a pacifist. + //var/destroy_sound = 'sound/mecha/critdestr.ogg' /obj/item/mecha_parts/mecha_equipment/proc/update_chassis_page() if(chassis) @@ -35,9 +40,14 @@ if(chassis.selected == src) chassis.selected = null src.update_chassis_page() - chassis.occupant_message("[src] is destroyed!") + //log_message("[src] is destroyed.", LOG_MECHA) chassis.log_append_to_last("[src] is destroyed.",1) - SEND_SOUND(chassis.occupant, sound(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon) ? 'sound/mecha/weapdestr.ogg' : 'sound/mecha/critdestr.ogg', volume=50)) + if(chassis.occupant) + chassis.occupant_message("[src] is destroyed!") + SEND_SOUND(chassis.occupant, sound(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon) ? 'sound/mecha/weapdestr.ogg' : 'sound/mecha/critdestr.ogg', volume=50)) + //chassis.occupant.playsound_local(chassis, destroy_sound, 50) + //if(!detachable) //If we're a built-in nondetachable equipment, let's lock up the slot that we were in. + // chassis.max_equip-- chassis = null return ..() @@ -59,7 +69,7 @@ return txt /obj/item/mecha_parts/mecha_equipment/proc/is_ranged()//add a distance restricted equipment. Why not? - return range&RANGED + return range&RANGED //rename to MECHA_RANGE and MECHA_MELEE /obj/item/mecha_parts/mecha_equipment/proc/is_melee() return range&MELEE @@ -72,10 +82,10 @@ return 0 if(!equip_ready) return 0 - if(crit_fail) - return 0 if(energy_drain && !chassis.has_charge(energy_drain)) return 0 + if(crit_fail) + return 0 if(chassis.equipment_disabled) to_chat(chassis.occupant, "Error -- Equipment control unit is unresponsive.") return 0 @@ -117,8 +127,6 @@ chassis = M forceMove(M) M.mecha_log_message("[src] initialized.") - if(!M.selected && selectable) - M.selected = src src.update_chassis_page() return @@ -150,7 +158,7 @@ chassis.occupant_message("[icon2html(src, chassis.occupant)] [message]") return -/obj/item/mecha_parts/mecha_equipment/proc/mecha_log_message(message, color) +/obj/item/mecha_parts/mecha_equipment/proc/mecha_log_message(message, color) //on tg this just overrides log_message log_message(message, LOG_GAME, color) //pass to default admin logging too if(chassis) chassis.mecha_log_message(message, color) //and pass to our chassis diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm index 4a4d58ea73..1435f78718 100644 --- a/code/game/mecha/equipment/tools/medical_tools.dm +++ b/code/game/mecha/equipment/tools/medical_tools.dm @@ -1,6 +1,7 @@ // Sleeper, Medical Beam, and Syringe gun /obj/item/mecha_parts/mecha_equipment/medical + mech_flags = EXOSUIT_MODULE_MEDICAL /obj/item/mecha_parts/mecha_equipment/medical/Initialize() . = ..() diff --git a/code/game/mecha/equipment/tools/mining_tools.dm b/code/game/mecha/equipment/tools/mining_tools.dm index d4a29f639d..7c7c6be2bc 100644 --- a/code/game/mecha/equipment/tools/mining_tools.dm +++ b/code/game/mecha/equipment/tools/mining_tools.dm @@ -17,6 +17,7 @@ toolspeed = 0.9 var/drill_delay = 7 var/drill_level = DRILL_BASIC + mech_flags = EXOSUIT_MODULE_WORKING | EXOSUIT_MODULE_COMBAT /obj/item/mecha_parts/mecha_equipment/drill/Initialize() . = ..() @@ -153,6 +154,7 @@ selectable = 0 equip_cooldown = 15 var/scanning_time = 0 + mech_flags = EXOSUIT_MODULE_WORKING /obj/item/mecha_parts/mecha_equipment/mining_scanner/Initialize() . = ..() diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm index 759fcd996d..1c4586205a 100644 --- a/code/game/mecha/equipment/tools/work_tools.dm +++ b/code/game/mecha/equipment/tools/work_tools.dm @@ -8,11 +8,12 @@ icon_state = "mecha_clamp" equip_cooldown = 15 energy_drain = 10 + tool_behaviour = TOOL_RETRACTOR + toolspeed = 0.8 var/dam_force = 20 var/obj/mecha/working/ripley/cargo_holder harmful = TRUE - tool_behaviour = TOOL_RETRACTOR - toolspeed = 0.8 + mech_flags = EXOSUIT_MODULE_RIPLEY /obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/can_attach(obj/mecha/working/ripley/M as obj) if(..()) @@ -180,6 +181,7 @@ equip_cooldown = 5 energy_drain = 0 range = MELEE|RANGED + mech_flags = EXOSUIT_MODULE_WORKING /obj/item/mecha_parts/mecha_equipment/extinguisher/Initialize() . = ..() diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 65aa001fe1..f2bced4aa8 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -9,6 +9,7 @@ var/projectile_delay = 0 var/firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect //the visual effect appearing when the weapon is fired. var/kickback = TRUE //Will using this weapon in no grav push mecha back. + mech_flags = EXOSUIT_MODULE_COMBAT /obj/item/mecha_parts/mecha_equipment/weapon/can_attach(obj/mecha/combat/M) if(..()) diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm index dcd8a12b38..e9f682e929 100644 --- a/code/game/mecha/mech_bay.dm +++ b/code/game/mecha/mech_bay.dm @@ -81,10 +81,10 @@ var/obj/machinery/mech_bay_recharge_port/recharge_port light_color = LIGHT_COLOR_PINK -/obj/machinery/computer/mech_bay_power_console/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/mech_bay_power_console/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "MechBayPowerConsole", "Mech Bay Power Control Console", 400, 200, master_ui, state) + ui = new(user, src, "MechBayPowerConsole", name) ui.open() /obj/machinery/computer/mech_bay_power_console/ui_act(action, params) @@ -104,7 +104,6 @@ data["recharge_port"]["mech"] = list("health" = recharge_port.recharging_mech.obj_integrity, "maxhealth" = recharge_port.recharging_mech.max_integrity, "cell" = null, "name" = recharge_port.recharging_mech.name,) if(recharge_port.recharging_mech.cell && !QDELETED(recharge_port.recharging_mech.cell)) data["recharge_port"]["mech"]["cell"] = list( - "critfail" = recharge_port.recharging_mech.cell.crit_fail, "charge" = recharge_port.recharging_mech.cell.charge, "maxcharge" = recharge_port.recharging_mech.cell.maxcharge ) diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index ca481f0d80..002753c5d4 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -9,17 +9,41 @@ active_power_usage = 5000 req_access = list(ACCESS_ROBOTICS) circuit = /obj/item/circuitboard/machine/mechfab - var/time_coeff = 1 - var/component_coeff = 1 - var/datum/techweb/specialized/autounlocking/exofab/stored_research - var/sync = 0 - var/part_set - var/datum/design/being_built + // processing_flags = START_PROCESSING_MANUALLY + + // subsystem_type = /datum/controller/subsystem/processing/fastprocess + + /// Current items in the build queue. var/list/queue = list() - var/processing_queue = 0 - var/screen = "main" - var/temp - var/offstation_security_levels = TRUE + /// Whether or not the machine is building the entire queue automagically. + var/process_queue = FALSE + + /// The current design datum that the machine is building. + var/datum/design/being_built + /// World time when the build will finish. + var/build_finish = 0 + /// World time when the build started. + var/build_start = 0 + /// Reference to all materials used in the creation of the item being_built. + var/list/build_materials + /// Part currently stored in the Exofab. + var/obj/item/stored_part + + /// Coefficient for the speed of item building. Based on the installed parts. + var/time_coeff = 1 + /// Coefficient for the efficiency of material usage in item building. Based on the installed parts. + var/component_coeff = 1 + + /// Copy of the currently synced techweb. + var/datum/techweb/specialized/autounlocking/exofab/stored_research + + /// Whether the Exofab links to the ore silo on init. Special derelict or maintanance variants should set this to FALSE. + var/link_on_init = TRUE + + /// Reference to a remote material inventory, such as an ore silo. + var/datum/component/remote_materials/rmat + + /// A list of categories that valid MECHFAB design datums will broadly categorise themselves under. var/list/part_sets = list( "Cyborg", "Ripley", @@ -33,25 +57,17 @@ "Exosuit Equipment", "Exosuit Ammunition", "Cyborg Upgrade Modules", + "Cybernetics", + "Implants", + "Control Interfaces", "Misc" ) -/obj/machinery/mecha_part_fabricator/Initialize() - var/static/list/allowed_types = list( - /datum/material/iron, - /datum/material/glass, - /datum/material/silver, - /datum/material/gold, - /datum/material/diamond, - /datum/material/plasma, - /datum/material/uranium, - /datum/material/bananium, - /datum/material/titanium, - /datum/material/bluespace - ) - var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, allowed_types, 0, TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) - materials.precise_insertion = TRUE +/obj/machinery/mecha_part_fabricator/Initialize(mapload) stored_research = new + rmat = AddComponent(/datum/component/remote_materials, "mechfab", mapload && link_on_init) + + RefreshParts() //Recalculating local material sizes if the fab isn't linked return ..() /obj/machinery/mecha_part_fabricator/RefreshParts() @@ -60,8 +76,7 @@ //maximum stocking amount (default 300000, 600000 at T4) for(var/obj/item/stock_parts/matter_bin/M in component_parts) T += M.rating - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - materials.max_amount = (200000 + (T*50000)) + rmat.set_local_size((200000 + (T*50000))) //resources adjustment coefficient (1 -> 0.85 -> 0.7 -> 0.55) T = 1.15 @@ -74,89 +89,157 @@ for(var/obj/item/stock_parts/manipulator/Ml in component_parts) T += Ml.rating time_coeff = round(initial(time_coeff) - (initial(time_coeff)*(T))/5,0.01) - var/obj/item/circuitboard/machine/mechfab/C = circuit - offstation_security_levels = C.offstation_security_levels + + // Adjust the build time of any item currently being built. + if(being_built) + var/last_const_time = build_finish - build_start + var/new_const_time = get_construction_time_w_coeff(initial(being_built.construction_time)) + var/const_time_left = build_finish - world.time + var/new_build_time = (new_const_time / last_const_time) * const_time_left + build_finish = world.time + new_build_time + + update_static_data(usr) /obj/machinery/mecha_part_fabricator/examine(mob/user) . = ..() - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Storing up to [materials.max_amount] material units.
Material consumption at [component_coeff*100]%.
Build time reduced by [100-time_coeff*100]%.
" + . += "The status display reads: Storing up to [rmat.local_size] material units.
Material consumption at [component_coeff*100]%.
Build time reduced by [100-time_coeff*100]%.
" -/obj/machinery/mecha_part_fabricator/emag_act() - . = ..() - if(obj_flags & EMAGGED) - return - obj_flags |= EMAGGED - req_access = list() - INVOKE_ASYNC(src, .proc/error_action_sucessful) - return TRUE - -/obj/machinery/mecha_part_fabricator/proc/error_action_sucessful() - say("DB error \[Code 0x00F1\]") - sleep(10) - say("Attempting auto-repair...") - sleep(15) - say("User DB corrupted \[Code 0x00FA\]. Truncating data structure...") - sleep(30) - say("User DB truncated. Please contact your Nanotrasen system operator for future assistance.") - - -/obj/machinery/mecha_part_fabricator/proc/output_parts_list(set_name) - var/output = "" - for(var/v in stored_research.researched_designs) - var/datum/design/D = SSresearch.techweb_design_by_id(v) - if(D.build_type & MECHFAB) - if(!(set_name in D.category)) - continue - output += "
[output_part_info(D)]
\[" - if(check_clearance(D) && check_resources(D)) - output += "Build | " - output += "Add to queue\]\[?\]
" - return output - -/obj/machinery/mecha_part_fabricator/proc/check_clearance(datum/design/D) - if(!(obj_flags & EMAGGED) && (offstation_security_levels || is_station_level(z)) && !ISINRANGE(GLOB.security_level, D.min_security_level, D.max_security_level)) - return FALSE - return TRUE - -/obj/machinery/mecha_part_fabricator/proc/output_part_info(datum/design/D) - var/clearance = !(obj_flags & EMAGGED) && (offstation_security_levels || is_station_level(z)) - var/sec_text = "" - if(clearance && (D.min_security_level > SEC_LEVEL_GREEN || D.max_security_level < SEC_LEVEL_DELTA)) - sec_text = " (Allowed security levels: " - for(var/n in D.min_security_level to D.max_security_level) - sec_text += NUM2SECLEVEL(n) - if(n + 1 <= D.max_security_level) - sec_text += ", " - sec_text += ") " - var/output = "[initial(D.name)] (Cost: [output_part_cost(D)]) [sec_text][get_construction_time_w_coeff(D)/10]sec" - return output - -/obj/machinery/mecha_part_fabricator/proc/output_part_cost(datum/design/D) - var/i = 0 - var/output +/** + * Generates an info list for a given part. + * + * Returns a list of part information. + * * D - Design datum to get information on. + * * categories - Boolean, whether or not to parse snowflake categories into the part information list. + */ +/obj/machinery/mecha_part_fabricator/proc/output_part_info(datum/design/D, categories = FALSE) + var/cost = list() for(var/c in D.materials) var/datum/material/M = c - output += "[i?" | ":null][get_resource_cost_w_coeff(D, M)] [M.name]" - i++ - return output + cost[M.name] = get_resource_cost_w_coeff(D, M) + var/obj/built_item = D.build_path + + var/list/category_override = null + var/list/sub_category = null + + if(categories) + // Handle some special cases to build up sub-categories for the fab interface. + // Start with checking if this design builds a cyborg module. + if(built_item in typesof(/obj/item/borg/upgrade)) + var/obj/item/borg/upgrade/U = built_item + var/module_types = initial(U.module_flags) + sub_category = list() + if(module_types) + if(module_types & BORG_MODULE_SECURITY) + sub_category += "Security" + if(module_types & BORG_MODULE_MINER) + sub_category += "Mining" + if(module_types & BORG_MODULE_JANITOR) + sub_category += "Janitor" + if(module_types & BORG_MODULE_MEDICAL) + sub_category += "Medical" + if(module_types & BORG_MODULE_ENGINEERING) + sub_category += "Engineering" + else + sub_category += "All Cyborgs" + // Else check if this design builds a piece of exosuit equipment. + else if(built_item in typesof(/obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/E = built_item + var/mech_types = initial(E.mech_flags) + sub_category = "Equipment" + if(mech_types) + category_override = list() + if(mech_types & EXOSUIT_MODULE_RIPLEY) + category_override += "Ripley" + if(mech_types & EXOSUIT_MODULE_FIREFIGHTER) + category_override += "Firefighter" + if(mech_types & EXOSUIT_MODULE_ODYSSEUS) + category_override += "Odysseus" + // if(mech_types & EXOSUIT_MODULE_CLARKE) + // category_override += "Clarke" + if(mech_types & EXOSUIT_MODULE_GYGAX_MED) + category_override += "Medical-Spec Gygax" + if(mech_types & EXOSUIT_MODULE_GYGAX) + category_override += "Gygax" + if(mech_types & EXOSUIT_MODULE_DURAND) + category_override += "Durand" + if(mech_types & EXOSUIT_MODULE_HONK) + category_override += "H.O.N.K" + if(mech_types & EXOSUIT_MODULE_PHAZON) + category_override += "Phazon" + + + var/list/part = list( + "name" = D.name, + "desc" = initial(built_item.desc), + "printTime" = get_construction_time_w_coeff(initial(D.construction_time))/10, + "cost" = cost, + "id" = D.id, + "subCategory" = sub_category, + "categoryOverride" = category_override, + "searchMeta" = "UNKNOWN"//D.search_metadata + ) + + return part + +/** + * Generates a list of resources / materials available to this Exosuit Fab + * + * Returns null if there is no material container available. + * List format is list(material_name = list(amount = ..., ref = ..., etc.)) + */ /obj/machinery/mecha_part_fabricator/proc/output_available_resources() - var/output - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - for(var/mat_id in materials.materials) - var/datum/material/M = mat_id - var/amount = materials.materials[mat_id] - output += "[M.name]: [amount] cm³" - if(amount >= MINERAL_MATERIAL_AMOUNT) - output += "- Remove \[1\]" - if(amount >= (MINERAL_MATERIAL_AMOUNT * 10)) - output += " | \[10\]" - output += " | \[All\]" - output += "
" - return output + var/datum/component/material_container/materials = rmat.mat_container + var/list/material_data = list() + + if(materials) + for(var/mat_id in materials.materials) + var/datum/material/M = mat_id + var/list/material_info = list() + var/amount = materials.materials[mat_id] + + material_info = list( + "name" = M.name, + "ref" = REF(M), + "amount" = amount, + "sheets" = round(amount / MINERAL_MATERIAL_AMOUNT), + "removable" = amount >= MINERAL_MATERIAL_AMOUNT + ) + + material_data += list(material_info) + + return material_data + + return null + +/** + * Intended to be called when an item starts printing. + * + * Adds the overlay to show the fab working and sets active power usage settings. + */ +/obj/machinery/mecha_part_fabricator/proc/on_start_printing() + add_overlay("fab-active") + use_power = ACTIVE_POWER_USE + +/** + * Intended to be called when the exofab has stopped working and is no longer printing items. + * + * Removes the overlay to show the fab working and sets idle power usage settings. Additionally resets the description and turns off queue processing. + */ +/obj/machinery/mecha_part_fabricator/proc/on_finish_printing() + cut_overlay("fab-active") + use_power = IDLE_POWER_USE + desc = initial(desc) + process_queue = FALSE + +/** + * Calculates resource/material costs for printing an item based on the machine's resource coefficient. + * + * Returns a list of k,v resources with their amounts. + * * D - Design datum to calculate the modified resource cost of. + */ /obj/machinery/mecha_part_fabricator/proc/get_resources_w_coeff(datum/design/D) var/list/resources = list() for(var/R in D.materials) @@ -164,294 +247,419 @@ resources[M] = get_resource_cost_w_coeff(D, M) return resources +/** + * Checks if the Exofab has enough resources to print a given item. + * + * Returns FALSE if the design has no reagents used in its construction (?) or if there are insufficient resources. + * Returns TRUE if there are sufficient resources to print the item. + * * D - Design datum to calculate the modified resource cost of. + */ /obj/machinery/mecha_part_fabricator/proc/check_resources(datum/design/D) - if(D.reagents_list.len) // No reagents storage - no reagent designs. + if(length(D.reagents_list)) // No reagents storage - no reagent designs. return FALSE - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + var/datum/component/material_container/materials = rmat.mat_container if(materials.has_materials(get_resources_w_coeff(D))) return TRUE return FALSE -/obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D) +/** + * Attempts to build the next item in the build queue. + * + * Returns FALSE if either there are no more parts to build or the next part is not buildable. + * Returns TRUE if the next part has started building. + * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build. + */ +/obj/machinery/mecha_part_fabricator/proc/build_next_in_queue(verbose = TRUE) + if(!length(queue)) + return FALSE + + var/datum/design/D = queue[1] + if(build_part(D, verbose)) + remove_from_queue(1) + return TRUE + + return FALSE + +/** + * Starts the build process for a given design datum. + * + * Returns FALSE if the procedure fails. Returns TRUE when being_built is set. + * Uses materials. + * * D - Design datum to attempt to print. + * * verbose - Whether the machine should use say() procs. Set to FALSE to disable the machine saying reasons for failure to build. + */ +/obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D, verbose = TRUE) + if(!D) + return FALSE + + var/datum/component/material_container/materials = rmat.mat_container + if (!materials) + if(verbose) + say("No access to material storage, please contact the quartermaster.") + return FALSE + if (rmat.on_hold()) + if(verbose) + say("Mineral access is on hold, please contact the quartermaster.") + return FALSE + if(!check_resources(D)) + if(verbose) + say("Not enough resources. Processing stopped.") + return FALSE + + build_materials = get_resources_w_coeff(D) + + materials.use_materials(build_materials) being_built = D - desc = "It's building \a [initial(D.name)]." - var/list/res_coef = get_resources_w_coeff(D) + build_finish = world.time + get_construction_time_w_coeff(initial(D.construction_time)) + build_start = world.time + desc = "It's building \a [D.name]." - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - materials.use_materials(res_coef) - add_overlay("fab-active") - use_power = ACTIVE_POWER_USE - updateUsrDialog() - sleep(get_construction_time_w_coeff(D)) - use_power = IDLE_POWER_USE - cut_overlay("fab-active") - desc = initial(desc) + rmat.silo_log(src, "built", -1, "[D.name]", build_materials) - var/location = get_step(src,(dir)) - var/obj/item/I = new D.build_path(location) - I.set_custom_materials(res_coef) - say("\The [I] is complete.") - being_built = null - - updateUsrDialog() return TRUE -/obj/machinery/mecha_part_fabricator/proc/update_queue_on_page() - send_byjax(usr,"mecha_fabricator.browser","queue",list_queue()) - return +/obj/machinery/mecha_part_fabricator/process() + // If there's a stored part to dispense due to an obstruction, try to dispense it. + if(stored_part) + var/turf/exit = get_step(src,(dir)) + if(exit.density) + return TRUE -/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(set_name) - if(set_name in part_sets) - for(var/v in stored_research.researched_designs) - var/datum/design/D = SSresearch.techweb_design_by_id(v) - if(D.build_type & MECHFAB) - if(set_name in D.category) - add_to_queue(D) + say("Obstruction cleared. \The [stored_part] is complete.") + stored_part.forceMove(exit) + stored_part = null -/obj/machinery/mecha_part_fabricator/proc/add_to_queue(D) + // If there's nothing being built, try to build something + if(!being_built) + // If we're not processing the queue anymore or there's nothing to build, end processing. + if(!process_queue || !build_next_in_queue()) + on_finish_printing() + STOP_PROCESSING(SSfastprocess, src) + //end_processing() + return TRUE + on_start_printing() + + // If there's an item being built, check if it is complete. + if(being_built && (build_finish < world.time)) + // Then attempt to dispense it and if appropriate build the next item. + dispense_built_part(being_built) + if(process_queue) + build_next_in_queue(FALSE) + return TRUE + +/** + * Dispenses a part to the tile infront of the Exosuit Fab. + * + * Returns FALSE is the machine cannot dispense the part on the appropriate turf. + * Return TRUE if the part was successfully dispensed. + * * D - Design datum to attempt to dispense. + */ +/obj/machinery/mecha_part_fabricator/proc/dispense_built_part(datum/design/D) + var/obj/item/I = new D.build_path(src) + // I.material_flags |= MATERIAL_NO_EFFECTS //Find a better way to do this. + I.set_custom_materials(build_materials) + + being_built = null + + var/turf/exit = get_step(src,(dir)) + if(exit.density) + say("Error! Part outlet is obstructed.") + desc = "It's trying to dispense \a [D.name], but the part outlet is obstructed." + stored_part = I + return FALSE + + say("\The [I] is complete.") + I.forceMove(exit) + return TRUE + +/** + * Adds a list of datum designs to the build queue. + * + * Will only add designs that are in this machine's stored techweb. + * Does final checks for datum IDs and makes sure this machine can build the designs. + * * part_list - List of datum design ids for designs to add to the queue. + */ +/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(list/part_list) + for(var/v in stored_research.researched_designs) + var/datum/design/D = SSresearch.techweb_design_by_id(v) + if((D.build_type & MECHFAB) && (D.id in part_list)) + add_to_queue(D) + +/** + * Adds a datum design to the build queue. + * + * Returns TRUE if successful and FALSE if the design was not added to the queue. + * * D - Datum design to add to the queue. + */ +/obj/machinery/mecha_part_fabricator/proc/add_to_queue(datum/design/D) if(!istype(queue)) queue = list() if(D) queue[++queue.len] = D - return queue.len + return TRUE + return FALSE +/** + * Removes datum design from the build queue based on index. + * + * Returns TRUE if successful and FALSE if a design was not removed from the queue. + * * index - Index in the build queue of the element to remove. + */ /obj/machinery/mecha_part_fabricator/proc/remove_from_queue(index) - if(!isnum(index) || !ISINTEGER(index) || !istype(queue) || (index<1 || index>queue.len)) + if(!isnum(index) || !ISINTEGER(index) || !istype(queue) || (index<1 || index>length(queue))) return FALSE queue.Cut(index,++index) return TRUE -/obj/machinery/mecha_part_fabricator/proc/process_queue() - var/datum/design/D = queue[1] - if(!D) - remove_from_queue(1) - if(queue.len) - return process_queue() - else - return - temp = null - while(D) - if(stat&(NOPOWER|BROKEN)) - return FALSE - if(!check_clearance(D)) - say("Security level not met. Queue processing stopped.") - temp = {"Security level not met to build next part.
- Try again | Return"} - return FALSE - if(!check_resources(D)) - say("Not enough resources. Queue processing stopped.") - temp = {"Not enough resources to build next part.
-
Try again | Return"} - return FALSE - remove_from_queue(1) - build_part(D) - D = listgetindex(queue, 1) - say("Queue processing finished successfully.") - +/** + * Generates a list of parts formatted for tgui based on the current build queue. + * + * Returns a formatted list of lists containing formatted part information for every part in the build queue. + */ /obj/machinery/mecha_part_fabricator/proc/list_queue() - var/output = "Queue contains:" - if(!istype(queue) || !queue.len) - output += "
Nothing" - else - output += "
    " - var/i = 0 - for(var/datum/design/D in queue) - i++ - var/obj/part = D.build_path - output += "" - output += initial(part.name) + " - " - output += "[i>1?"":null] " - output += "[i↓":null] " - output += "Remove" + if(!istype(queue) || !length(queue)) + return null - output += "
" - output += "\[Process queue | Clear queue\]" - return output + var/list/queued_parts = list() + for(var/datum/design/D in queue) + var/list/part = output_part_info(D) + queued_parts += list(part) + return queued_parts +/** + * Syncs machine with R&D servers. + * + * Requires an R&D Console visible within 7 tiles. Copies techweb research. Updates tgui's state data. + */ /obj/machinery/mecha_part_fabricator/proc/sync() - temp = "Updating local R&D database..." - updateUsrDialog() - sleep(30) //only sleep if called by user - - for(var/obj/machinery/computer/rdconsole/RDC in oview(7,src)) + for(var/obj/machinery/computer/rdconsole/RDC in orange(7,src)) RDC.stored_research.copy_research_to(stored_research) - temp = "Processed equipment designs.
" - //check if the tech coefficients have changed - temp += "Return" - - updateUsrDialog() + update_static_data(usr) say("Successfully synchronized with R&D server.") return - temp = "Unable to connect to local R&D Database.
Please check your connections and try again.
Return" - updateUsrDialog() + say("Unable to connect to local R&D server.") return +/** + * Calculates the coefficient-modified resource cost of a single material component of a design's recipe. + * + * Returns coefficient-modified resource cost for the given material component. + * * D - Design datum to pull the resource cost from. + * * resource - Material datum reference to the resource to calculate the cost of. + * * roundto - Rounding value for round() proc + */ /obj/machinery/mecha_part_fabricator/proc/get_resource_cost_w_coeff(datum/design/D, var/datum/material/resource, roundto = 1) return round(D.materials[resource]*component_coeff, roundto) -/obj/machinery/mecha_part_fabricator/proc/get_construction_time_w_coeff(datum/design/D, roundto = 1) //aran - return round(initial(D.construction_time)*time_coeff, roundto) +/** + * Calculates the coefficient-modified build time of a design. + * + * Returns coefficient-modified build time of a given design. + * * D - Design datum to calculate the modified build time of. + * * roundto - Rounding value for round() proc + */ +/obj/machinery/mecha_part_fabricator/proc/get_construction_time_w_coeff(construction_time, roundto = 1) //aran + return round(construction_time*time_coeff, roundto) -/obj/machinery/mecha_part_fabricator/ui_interact(mob/user as mob) - . = ..() - var/dat, left_part - user.set_machine(src) - var/turf/exit = get_step(src,(dir)) - if(exit.density) - say("Error! Part outlet is obstructed.") - return - if(temp) - left_part = temp - else if(being_built) - var/obj/I = being_built.build_path - left_part = {"Building [initial(I.name)].
- Please wait until completion...
"} +/obj/machinery/mecha_part_fabricator/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/sheetmaterials) + ) + +/obj/machinery/mecha_part_fabricator/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ExosuitFabricator") + ui.open() + +/obj/machinery/mecha_part_fabricator/ui_static_data(mob/user) + var/list/data = list() + + var/list/final_sets = list() + var/list/buildable_parts = list() + + for(var/part_set in part_sets) + final_sets += part_set + + for(var/v in stored_research.researched_designs) + var/datum/design/D = SSresearch.techweb_design_by_id(v) + if(D.build_type & MECHFAB) + // This is for us. + var/list/part = output_part_info(D, TRUE) + + if(part["category_override"]) + for(var/cat in part["category_override"]) + buildable_parts[cat] += list(part) + if(!(cat in part_sets)) + final_sets += cat + continue + + for(var/cat in part_sets) + // Find all matching categories. + if(!(cat in D.category)) + continue + + buildable_parts[cat] += list(part) + + data["partSets"] = final_sets + data["buildableParts"] = buildable_parts + + return data + +/obj/machinery/mecha_part_fabricator/ui_data(mob/user) + var/list/data = list() + + data["materials"] = output_available_resources() + + if(being_built) + var/list/part = list( + "name" = being_built.name, + "duration" = build_finish - world.time, + "printTime" = get_construction_time_w_coeff(initial(being_built.construction_time)) + ) + data["buildingPart"] = part else - switch(screen) - if("main") - left_part = output_available_resources()+"
" - left_part += "Sync with R&D servers
" - for(var/part_set in part_sets) - left_part += "[part_set] - \[Add all parts to queue\]
" - if("parts") - left_part += output_parts_list(part_set) - left_part += "
Return" - dat = {" - - - [name] data - - - - - - - - - -
- [left_part] - - [list_queue()] -
- - "} - user << browse(dat, "window=mecha_fabricator;size=1000x430") - onclose(user, "mecha_fabricator") - return + data["buildingPart"] = null -/obj/machinery/mecha_part_fabricator/Topic(href, href_list) + data["queue"] = list_queue() + + if(stored_part) + data["storedPart"] = stored_part.name + else + data["storedPart"] = null + + data["isProcessingQueue"] = process_queue + + return data + +/obj/machinery/mecha_part_fabricator/ui_act(action, var/list/params) if(..()) - return - if(href_list["part_set"]) - var/tpart_set = href_list["part_set"] - if(tpart_set) - if(tpart_set=="clear") - part_set = null - else - part_set = tpart_set - screen = "parts" - if(href_list["part"]) - var/T = href_list["part"] - for(var/v in stored_research.researched_designs) - var/datum/design/D = SSresearch.techweb_design_by_id(v) - if(D.build_type & MECHFAB) - if(D.id == T) - if(!processing_queue) - build_part(D) - else - add_to_queue(D) - break - if(href_list["add_to_queue"]) - var/T = href_list["add_to_queue"] - for(var/v in stored_research.researched_designs) - var/datum/design/D = SSresearch.techweb_design_by_id(v) - if(D.build_type & MECHFAB) - if(D.id == T) + return TRUE + + . = TRUE + + add_fingerprint(usr) + usr.set_machine(src) + + switch(action) + if("sync_rnd") + // Sync with R&D Servers + sync() + return + if("add_queue_set") + // Add all parts of a set to queue + var/part_list = params["part_list"] + add_part_set_to_queue(part_list) + return + if("add_queue_part") + // Add a specific part to queue + var/T = params["id"] + for(var/v in stored_research.researched_designs) + var/datum/design/D = SSresearch.techweb_design_by_id(v) + if((D.build_type & MECHFAB) && (D.id == T)) add_to_queue(D) break - return update_queue_on_page() - if(href_list["remove_from_queue"]) - remove_from_queue(text2num(href_list["remove_from_queue"])) - return update_queue_on_page() - if(href_list["partset_to_queue"]) - add_part_set_to_queue(href_list["partset_to_queue"]) - return update_queue_on_page() - if(href_list["process_queue"]) - spawn(0) - if(processing_queue || being_built) - return FALSE - processing_queue = 1 - process_queue() - processing_queue = 0 - if(href_list["clear_temp"]) - temp = null - if(href_list["screen"]) - screen = href_list["screen"] - if(href_list["queue_move"] && href_list["index"]) - var/index = text2num(href_list["index"]) - var/new_index = index + text2num(href_list["queue_move"]) - if(isnum(index) && isnum(new_index) && ISINTEGER(index) && ISINTEGER(new_index)) - if(ISINRANGE(new_index,1,queue.len)) - queue.Swap(index,new_index) - return update_queue_on_page() - if(href_list["clear_queue"]) - queue = list() - return update_queue_on_page() - if(href_list["sync"]) - sync() - if(href_list["part_desc"]) - var/T = href_list["part_desc"] - for(var/v in stored_research.researched_designs) - var/datum/design/D = SSresearch.techweb_design_by_id(v) - if(D.build_type & MECHFAB) - if(D.id == T) - var/obj/part = D.build_path - temp = {"

[initial(part.name)] description:

- [initial(part.desc)]
- Return - "} - break + return + if("del_queue_part") + // Delete a specific from from the queue + var/index = text2num(params["index"]) + remove_from_queue(index) + return + if("clear_queue") + // Delete everything from queue + queue.Cut() + return + if("build_queue") + // Build everything in queue + if(process_queue) + return + process_queue = TRUE - if(href_list["remove_mat"] && href_list["material"]) - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - var/datum/material/Mat = locate(href_list["material"]) - materials.retrieve_sheets(text2num(href_list["remove_mat"]), Mat) + if(!being_built) + START_PROCESSING(SSfastprocess, src) + //begin_processing() + return + if("stop_queue") + // Pause queue building. Also known as stop. + process_queue = FALSE + return + if("build_part") + // Build a single part + if(being_built || process_queue) + return - updateUsrDialog() - return + var/id = params["id"] + var/datum/design/D = SSresearch.techweb_design_by_id(id) -/obj/machinery/mecha_part_fabricator/on_deconstruction() - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - materials.retrieve_all() - ..() + if(!(D.build_type & MECHFAB) || !(D.id == id)) + return + + if(build_part(D)) + on_start_printing() + START_PROCESSING(SSfastprocess, src) + //begin_processing() teege has this as a helper proc. please port it! + + return + if("move_queue_part") + // Moves a part up or down in the queue. + var/index = text2num(params["index"]) + var/new_index = index + text2num(params["newindex"]) + if(isnum(index) && isnum(new_index) && ISINTEGER(index) && ISINTEGER(new_index)) + if(ISINRANGE(new_index,1,length(queue))) + queue.Swap(index,new_index) + return + if("remove_mat") + // Remove a material from the fab + var/mat_ref = params["ref"] + var/amount = text2num(params["amount"]) + var/datum/material/mat = locate(mat_ref) + eject_sheets(mat, amount) + return + + return FALSE + +/** + * Eject material sheets. + * + * Returns the number of sheets successfully ejected. + * eject_sheet - Byond REF of the material to eject. + * eject_amt - Number of sheets to attempt to eject. + */ +/obj/machinery/mecha_part_fabricator/proc/eject_sheets(eject_sheet, eject_amt) + var/datum/component/material_container/mat_container = rmat.mat_container + if (!mat_container) + say("No access to material storage, please contact the quartermaster.") + return 0 + if (rmat.on_hold()) + say("Mineral access is on hold, please contact the quartermaster.") + return 0 + var/count = mat_container.retrieve_sheets(text2num(eject_amt), eject_sheet, drop_location()) + var/list/matlist = list() + matlist[eject_sheet] = text2num(eject_amt) + rmat.silo_log(src, "ejected", -count, "sheets", matlist) + return count /obj/machinery/mecha_part_fabricator/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted) var/datum/material/M = id_inserted add_overlay("fab-load-[M.name]") addtimer(CALLBACK(src, /atom/proc/cut_overlay, "fab-load-[M.name]"), 10) - updateUsrDialog() -/obj/machinery/mecha_part_fabricator/attackby(obj/item/W, mob/user, params) - if(default_deconstruction_screwdriver(user, "fab-o", "fab-idle", W)) +/obj/machinery/mecha_part_fabricator/screwdriver_act(mob/living/user, obj/item/I) + if(..()) return TRUE + if(being_built) + to_chat(user, "\The [src] is currently processing! Please wait until completion.") + return FALSE + return default_deconstruction_screwdriver(user, "fab-o", "fab-idle", I) - if(default_deconstruction_crowbar(W)) +/obj/machinery/mecha_part_fabricator/crowbar_act(mob/living/user, obj/item/I) + if(..()) return TRUE - - return ..() + if(being_built) + to_chat(user, "\The [src] is currently processing! Please wait until completion.") + return FALSE + return default_deconstruction_crowbar(I) /obj/machinery/mecha_part_fabricator/proc/is_insertion_ready(mob/user) if(panel_open) @@ -463,6 +671,9 @@ return TRUE +/obj/machinery/mecha_part_fabricator/maint + link_on_init = FALSE + /obj/machinery/mecha_part_fabricator/offstation - offstation_security_levels = FALSE + link_on_init = FALSE circuit = /obj/item/circuitboard/machine/mechfab/offstation diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm index c7db051331..50d18c6fda 100644 --- a/code/game/mecha/mecha_control_console.dm +++ b/code/game/mecha/mecha_control_console.dm @@ -5,14 +5,11 @@ icon_keyboard = "tech_key" req_access = list(ACCESS_ROBOTICS) circuit = /obj/item/circuitboard/computer/mecha_control - ui_x = 500 - ui_y = 500 -/obj/machinery/computer/mecha/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/mecha/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "ExosuitControlConsole", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "ExosuitControlConsole", name) ui.open() /obj/machinery/computer/mecha/ui_data(mob/user) diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index e75a18dca8..41270bb09e 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -8,7 +8,7 @@ anchored = TRUE light_range = 3 var/movechance = 70 - var/obj/item/assembly/signaler/anomaly/aSignal + var/obj/item/assembly/signaler/anomaly/aSignal = /obj/item/assembly/signaler/anomaly var/area/impact_area var/lifespan = 990 @@ -23,8 +23,10 @@ START_PROCESSING(SSobj, src) impact_area = get_area(src) - aSignal = new(src) - aSignal.name = "[name] core" + if (!impact_area) + return INITIALIZE_HINT_QDEL + + aSignal = new aSignal(src) aSignal.code = rand(1,100) aSignal.anomaly_type = type @@ -75,7 +77,7 @@ /obj/effect/anomaly/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/analyzer)) + if(I.tool_behaviour == TOOL_ANALYZER) //revert if runtimed to_chat(user, "Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(aSignal.frequency)], code [aSignal.code].") /////////////////////// @@ -85,6 +87,7 @@ icon_state = "shield2" density = FALSE var/boing = 0 + aSignal = /obj/item/assembly/signaler/anomaly/grav /obj/effect/anomaly/grav/anomalyEffect() ..() @@ -95,17 +98,23 @@ for(var/mob/living/M in range(0, src)) gravShock(M) for(var/mob/living/M in orange(4, src)) - step_towards(M,src) + if(!M.mob_negates_gravity()) //delete when runtimed + step_towards(M,src) for(var/obj/O in range(0,src)) if(!O.anchored) + //if(isturf(O.loc)) + // var/turf/T = O.loc + // if(T.intact && HAS_TRAIT(O, TRAIT_T_RAY_VISIBLE)) + // continue var/mob/living/target = locate() in view(4,src) if(target && !target.stat) O.throw_at(target, 5, 10) -/obj/effect/anomaly/grav/Crossed(mob/A) - gravShock(A) +/obj/effect/anomaly/grav/Crossed(atom/movable/AM) + . = ..() + gravShock(AM) -/obj/effect/anomaly/grav/Bump(mob/A) +/obj/effect/anomaly/grav/Bump(atom/A) gravShock(A) /obj/effect/anomaly/grav/Bumped(atom/movable/AM) @@ -138,6 +147,7 @@ name = "flux wave anomaly" icon_state = "electricity2" density = TRUE + aSignal = /obj/item/assembly/signaler/anomaly/flux var/canshock = FALSE var/shockdamage = 20 var/explosive = TRUE @@ -148,11 +158,12 @@ for(var/mob/living/M in range(0, src)) mobShock(M) -/obj/effect/anomaly/flux/Crossed(mob/living/M) - mobShock(M) +/obj/effect/anomaly/flux/Crossed(atom/movable/AM) + . = ..() + mobShock(AM) -/obj/effect/anomaly/flux/Bump(mob/living/M) - mobShock(M) +/obj/effect/anomaly/flux/Bump(atom/A) + mobShock(A) /obj/effect/anomaly/flux/Bumped(atom/movable/AM) mobShock(AM) @@ -160,7 +171,7 @@ /obj/effect/anomaly/flux/proc/mobShock(mob/living/M) if(canshock && istype(M)) canshock = FALSE //Just so you don't instakill yourself if you slam into the anomaly five times in a second. - M.electrocute_act(shockdamage, "[name]", flags = SHOCK_NOGLOVES) + M.electrocute_act(shockdamage, name, flags = SHOCK_NOGLOVES) /obj/effect/anomaly/flux/detonate() if(explosive) @@ -176,6 +187,7 @@ icon = 'icons/obj/projectiles.dmi' icon_state = "bluespace" density = TRUE + aSignal = /obj/item/assembly/signaler/anomaly/bluespace /obj/effect/anomaly/bluespace/anomalyEffect() ..() @@ -187,7 +199,7 @@ do_teleport(AM, locate(AM.x, AM.y, AM.z), 8, channel = TELEPORT_CHANNEL_BLUESPACE) /obj/effect/anomaly/bluespace/detonate() - var/turf/T = safepick(get_area_turfs(impact_area)) + var/turf/T = pick(get_area_turfs(impact_area)) if(T) // Calculate new position (searches through beacons in world) var/obj/item/beacon/chosen @@ -224,21 +236,23 @@ if(!A.Move(newloc) && newloc) // if the atom, for some reason, can't move, FORCE them to move! :) We try Move() first to invoke any movement-related checks the atom needs to perform after moving A.forceMove(newloc) - spawn() - if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect - var/mob/M = A - if(M.client) - var/obj/blueeffect = new /obj(src) - blueeffect.screen_loc = "WEST,SOUTH to EAST,NORTH" - blueeffect.icon = 'icons/effects/effects.dmi' - blueeffect.icon_state = "shieldsparkles" - blueeffect.layer = FLASH_LAYER - blueeffect.plane = FULLSCREEN_PLANE - blueeffect.mouse_opacity = MOUSE_OPACITY_TRANSPARENT - M.client.screen += blueeffect - sleep(20) - M.client.screen -= blueeffect - qdel(blueeffect) + if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect + var/mob/M = A + if(M.client) + INVOKE_ASYNC(src, .proc/blue_effect, M) + +/obj/effect/anomaly/bluespace/proc/blue_effect(mob/M) + var/obj/blueeffect = new /obj(src) + blueeffect.screen_loc = "WEST,SOUTH to EAST,NORTH" + blueeffect.icon = 'icons/effects/effects.dmi' + blueeffect.icon_state = "shieldsparkles" + blueeffect.layer = FLASH_LAYER + blueeffect.plane = FULLSCREEN_PLANE + blueeffect.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + M.client.screen += blueeffect + sleep(20) + M.client.screen -= blueeffect + qdel(blueeffect) ///////////////////// @@ -246,6 +260,7 @@ name = "pyroclastic anomaly" icon_state = "mustard" var/ticks = 0 + aSignal = /obj/item/assembly/signaler/anomaly/pyro /obj/effect/anomaly/pyro/anomalyEffect() ..() @@ -278,6 +293,7 @@ name = "vortex anomaly" icon_state = "bhole3" desc = "That's a nice station you have there. It'd be a shame if something happened to it." + aSignal = /obj/item/assembly/signaler/anomaly/vortex /obj/effect/anomaly/bhole/anomalyEffect() ..() diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index 0b227be247..a7b5091522 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -1,10 +1,10 @@ -/proc/create_portal_pair(turf/source, turf/destination, _creator = null, _lifespan = 300, accuracy = 0, newtype = /obj/effect/portal, atmos_link_override) +/proc/create_portal_pair(turf/source, turf/destination, _lifespan = 300, accuracy = 0, newtype = /obj/effect/portal, atmos_link_override) if(!istype(source) || !istype(destination)) return var/turf/actual_destination = get_teleport_turf(destination, accuracy) - var/obj/effect/portal/P1 = new newtype(source, _creator, _lifespan, null, FALSE, null, atmos_link_override) - var/obj/effect/portal/P2 = new newtype(actual_destination, _creator, _lifespan, P1, TRUE, null, atmos_link_override) + var/obj/effect/portal/P1 = new newtype(source, _lifespan, null, FALSE, null, atmos_link_override) + var/obj/effect/portal/P2 = new newtype(actual_destination, _lifespan, P1, TRUE, null, atmos_link_override) if(!istype(P1)||!istype(P2)) return P1.link_portal(P2) @@ -21,7 +21,6 @@ var/obj/effect/portal/linked var/hardlinked = TRUE //Requires a linked portal at all times. Destroy if there's no linked portal, if there is destroy it when this one is deleted. var/teleport_channel = TELEPORT_CHANNEL_BLUESPACE - var/creator var/turf/hard_target //For when a portal needs a hard target and isn't to be linked. var/atmos_link = FALSE //Link source/destination atmos. var/turf/open/atmos_source //Atmos link source @@ -35,6 +34,7 @@ name = "wormhole" icon = 'icons/obj/objects.dmi' icon_state = "anom" + layer = RIPPLE_LAYER mech_sized = TRUE teleport_channel = TELEPORT_CHANNEL_WORMHOLE @@ -49,7 +49,9 @@ user.forceMove(get_turf(src)) return TRUE -/obj/effect/portal/Crossed(atom/movable/AM, oldloc) +/obj/effect/portal/Crossed(atom/movable/AM, oldloc, force_stop = 0) + if(force_stop) + return ..() if(isobserver(AM)) return ..() if(linked && (get_turf(oldloc) == get_turf(linked))) @@ -66,7 +68,7 @@ if(Adjacent(user)) user.forceMove(get_turf(src)) -/obj/effect/portal/Initialize(mapload, _creator, _lifespan = 0, obj/effect/portal/_linked, automatic_link = FALSE, turf/hard_target_override, atmos_link_override) +/obj/effect/portal/Initialize(mapload, _lifespan = 0, obj/effect/portal/_linked, automatic_link = FALSE, turf/hard_target_override, atmos_link_override) . = ..() GLOB.portals += src if(!istype(_linked) && automatic_link) @@ -78,7 +80,6 @@ atmos_link = atmos_link_override link_portal(_linked) hardlinked = automatic_link - creator = _creator if(isturf(hard_target_override)) hard_target = hard_target_override @@ -129,10 +130,7 @@ LAZYREMOVE(atmos_destination.atmos_adjacent_turfs, atmos_source) atmos_destination = null -/obj/effect/portal/Destroy() //Calls on_portal_destroy(destroyed portal, location of destroyed portal) on creator if creator has such call. - if(creator && hascall(creator, "on_portal_destroy")) - call(creator, "on_portal_destroy")(src, src.loc) - creator = null +/obj/effect/portal/Destroy() GLOB.portals -= src unlink_atmos() if(hardlinked && !QDELETED(linked)) @@ -153,8 +151,6 @@ return FALSE if(!force && (!ismecha(M) && !istype(M, /obj/item/projectile) && M.anchored && !allow_anchored)) return - if(ismegafauna(M)) - message_admins("[M] has used a portal at [ADMIN_VERBOSEJMP(src)] made by [usr].") var/no_effect = FALSE if(last_effect == world.time) no_effect = TRUE diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index 8944b55cc4..6564a83729 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -380,8 +380,6 @@ name = "random keg spawner" lootcount = 1 loot = list(/obj/structure/reagent_dispensers/keg/mead = 5, - /obj/structure/reagent_dispensers/keg/aphro = 2, - /obj/structure/reagent_dispensers/keg/aphro/strong = 2, /obj/structure/reagent_dispensers/keg/gargle = 1) /obj/effect/spawner/lootdrop/coin diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm index 93d73b2b5f..b3d5ed431a 100644 --- a/code/game/objects/items/RPD.dm +++ b/code/game/objects/items/RPD.dm @@ -238,24 +238,22 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( playsound(get_turf(user), 'sound/items/deconstruct.ogg', 50, 1) return(BRUTELOSS) -/obj/item/pipe_dispenser/ui_base_html(html) - var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/pipes) - . = replacetext(html, "", assets.css_tag()) +/obj/item/pipe_dispenser/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/pipes), + ) -/obj/item/pipe_dispenser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/pipe_dispenser/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/pipes) - assets.send(user) - - ui = new(user, src, ui_key, "RapidPipeDispenser", name, 425, 515, master_ui, state) + ui = new(user, src, "RapidPipeDispenser", name) ui.open() /obj/item/pipe_dispenser/ui_data(mob/user) var/list/data = list( "category" = category, "piping_layer" = piping_layer, + // "ducting_layer" = ducting_layer, //uhh is this for chem thing? "preview_rows" = recipe.get_preview(p_dir), "categories" = list(), "selected_color" = paint_color, @@ -284,7 +282,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( /obj/item/pipe_dispenser/ui_act(action, params) if(..()) return - if(!usr.canUseTopic(src)) + if(!usr.canUseTopic(src, BE_CLOSE)) return var/playeffect = TRUE switch(action) @@ -304,6 +302,9 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( if("piping_layer") piping_layer = text2num(params["piping_layer"]) playeffect = FALSE + // if("ducting_layer") + // ducting_layer = text2num(params["ducting_layer"]) + // playeffect = FALSE if("pipe_type") var/static/list/recipes if(!recipes) @@ -320,11 +321,9 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( mode &= ~n else mode |= n - - if(playeffect && world.time >= effectcooldown) + if(playeffect) spark_system.start() - effectcooldown = world.time + 100 - playsound(get_turf(src), 'sound/effects/pop.ogg', 50, 0) + playsound(get_turf(src), 'sound/effects/pop.ogg', 50, FALSE) return TRUE /obj/item/pipe_dispenser/pre_attack(atom/A, mob/user) diff --git a/code/game/objects/items/airlock_painter.dm b/code/game/objects/items/airlock_painter.dm index 4f3761f899..b28de437ed 100644 --- a/code/game/objects/items/airlock_painter.dm +++ b/code/game/objects/items/airlock_painter.dm @@ -180,10 +180,10 @@ stored_decal_total = "[stored_decal][yellow_fix][stored_color]" return -/obj/item/airlock_painter/decal/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/airlock_painter/decal/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "DecalPainter", name, 500, 400, master_ui, state) + ui = new(user, src, "DecalPainter", name) ui.open() /obj/item/airlock_painter/decal/ui_data(mob/user) diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index a366a2ef88..7f8b720509 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -147,23 +147,23 @@ to_chat(user, "There is not enough of [src] left!") . = TRUE -/obj/item/toy/crayon/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - // tgui is a plague upon this codebase +/obj/item/toy/crayon/ui_state(mob/user) + return GLOB.hands_state - SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/toy/crayon/ui_interact(mob/user, datum/tgui/ui) + // tgui is a plague upon this codebase + // no u + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Crayon", name, 600, 600, - master_ui, state) + ui = new(user, src, "Crayon", name) ui.open() /obj/item/toy/crayon/spraycan/AltClick(mob/user) - . = ..() if(user.canUseTopic(src, BE_CLOSE, ismonkey(user))) if(has_cap) is_capped = !is_capped to_chat(user, "The cap on [src] is now [is_capped ? "on" : "off"].") update_icon() - return TRUE /obj/item/toy/crayon/CtrlClick(mob/user) if(can_change_colour && !isturf(loc) && user.canUseTopic(src, BE_CLOSE, ismonkey(user))) diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index d706f5468a..ec68cdb40c 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -1027,6 +1027,7 @@ GLOBAL_LIST_EMPTY(PDAs) else if(is_type_in_list(C, contained_item)) //Checks if there is a pen if(inserted_item) to_chat(user, "There is already \a [inserted_item] in \the [src]!") + return ..() else if(!user.transferItemToLoc(C, src)) return diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index bfb9348d97..503e2a9473 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -54,18 +54,20 @@ name = initial(name) icon_state = initial(icon_state) -/obj/item/aicard/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/aicard/ui_state(mob/user) + return GLOB.hands_state + +/obj/item/aicard/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Intellicard", name, 500, 500, master_ui, state) + ui = new(user, src, "Intellicard", name) ui.open() /obj/item/aicard/ui_data() var/list/data = list() if(AI) data["name"] = AI.name - data["laws"] = AI.laws.get_law_list(include_zeroth = 1) + data["laws"] = AI.laws.get_law_list(include_zeroth = TRUE, render_html = FALSE) data["health"] = (AI.health + 100) / 2 data["wireless"] = !AI.control_disabled //todo disabled->enabled data["radio"] = AI.radio_enabled @@ -86,19 +88,19 @@ if(confirm == "Yes" && !..()) flush = TRUE if(AI && AI.loc == src) - to_chat(AI, "Your core files are being wiped!") + to_chat(AI, "Your core files are being wiped!") while(AI.stat != DEAD && flush) - AI.adjustOxyLoss(1) + AI.adjustOxyLoss(5) AI.updatehealth() sleep(5) flush = FALSE . = TRUE if("wireless") AI.control_disabled = !AI.control_disabled - to_chat(AI, "[src]'s wireless port has been [AI.control_disabled ? "disabled" : "enabled"]!") + to_chat(AI, "[src]'s wireless port has been [AI.control_disabled ? "disabled" : "enabled"]!") . = TRUE if("radio") AI.radio_enabled = !AI.radio_enabled - to_chat(AI, "Your Subspace Transceiver has been [AI.radio_enabled ? "enabled" : "disabled"]!") + to_chat(AI, "Your Subspace Transceiver has been [AI.radio_enabled ? "enabled" : "disabled"]!") . = TRUE update_icon() diff --git a/code/game/objects/items/devices/dogborg_sleeper.dm b/code/game/objects/items/devices/dogborg_sleeper.dm index d8abccb336..1f91196b24 100644 --- a/code/game/objects/items/devices/dogborg_sleeper.dm +++ b/code/game/objects/items/devices/dogborg_sleeper.dm @@ -163,12 +163,13 @@ return ui_interact(user) -/obj/item/dogborg/sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) +/obj/item/dogborg/sleeper/ui_state(mob/user) + return GLOB.notcontained_state - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/dogborg/sleeper/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "dogborg_sleeper", name, 375, 550, master_ui, state) + ui = new(user, src, "dogborg_sleeper", name, 375, 550) //UI DOES NOT EXIST ui.open() /obj/item/dogborg/sleeper/ui_data() diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm index ee26d81a0d..bb46577702 100644 --- a/code/game/objects/items/devices/gps.dm +++ b/code/game/objects/items/devices/gps.dm @@ -1,4 +1,4 @@ -GLOBAL_LIST_EMPTY(GPS_list) + /obj/item/gps name = "global positioning system" desc = "Helping lost spacemen find their way through the planets since 2016." @@ -8,138 +8,10 @@ GLOBAL_LIST_EMPTY(GPS_list) slot_flags = ITEM_SLOT_BELT obj_flags = UNIQUE_RENAME var/gpstag = "COM0" - var/emped = FALSE - var/tracking = TRUE - var/updating = TRUE //Automatic updating of GPS list. Can be set to manual by user. - var/global_mode = TRUE //If disabled, only GPS signals of the same Z level are shown - -/obj/item/gps/examine(mob/user) - . = ..() - var/turf/curr = get_turf(src) - . += "The screen says: [get_area_name(curr, TRUE)] ([curr.x], [curr.y], [curr.z])" - . += "Alt-click to switch it [tracking ? "off":"on"]." /obj/item/gps/Initialize() . = ..() - GLOB.GPS_list += src - name = "global positioning system ([gpstag])" - add_overlay("working") - -/obj/item/gps/Destroy() - GLOB.GPS_list -= src - return ..() - -/obj/item/gps/emp_act(severity) - . = ..() - if (. & EMP_PROTECT_SELF) - return - emped = TRUE - cut_overlay("working") - add_overlay("emp") - addtimer(CALLBACK(src, .proc/reboot), 300, TIMER_UNIQUE|TIMER_OVERRIDE) //if a new EMP happens, remove the old timer so it doesn't reactivate early - SStgui.close_uis(src) //Close the UI control if it is open. - -/obj/item/gps/proc/reboot() - emped = FALSE - cut_overlay("emp") - add_overlay("working") - -/obj/item/gps/AltClick(mob/user) - . = ..() - if(!user.canUseTopic(src, BE_CLOSE)) - return - toggletracking(user) - return TRUE - -/obj/item/gps/proc/toggletracking(mob/user) - if(!user.canUseTopic(src, BE_CLOSE)) - return //user not valid to use gps - if(emped) - to_chat(user, "It's busted!") - return - if(tracking) - cut_overlay("working") - to_chat(user, "[src] is no longer tracking, or visible to other GPS devices.") - tracking = FALSE - else - add_overlay("working") - to_chat(user, "[src] is now tracking, and visible to other GPS devices.") - tracking = TRUE - - -/obj/item/gps/ui_interact(mob/user, ui_key = "gps", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state. - if(emped) - to_chat(user, "[src] fizzles weakly.") - return - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // Variable window height, depending on how many GPS units there are - // to show, clamped to relatively safe range. - var/gps_window_height = clamp(325 + GLOB.GPS_list.len * 14, 325, 700) - ui = new(user, src, ui_key, "Gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height - ui.open() - - ui.set_autoupdate(state = updating) - - -/obj/item/gps/ui_data(mob/user) - var/list/data = list() - data["power"] = tracking - data["tag"] = gpstag - data["updating"] = updating - data["globalmode"] = global_mode - if(!tracking || emped) //Do not bother scanning if the GPS is off or EMPed - return data - - var/turf/curr = get_turf(src) - data["current"] = "[get_area_name(curr, TRUE)] ([curr.x], [curr.y], [curr.z])" - data["currentArea"] = "[get_area_name(curr, TRUE)]" - data["currentCoords"] = "[curr.x], [curr.y], [curr.z]" - - var/list/signals = list() - data["signals"] = list() - - for(var/gps in GLOB.GPS_list) - var/obj/item/gps/G = gps - if(G.emped || !G.tracking || G == src) - continue - var/turf/pos = get_turf(G) - if(!global_mode && pos.z != curr.z) - continue - var/list/signal = list() - signal["entrytag"] = G.gpstag //Name or 'tag' of the GPS - signal["coords"] = "[pos.x], [pos.y], [pos.z]" - if(pos.z == curr.z) //Distance/Direction calculations for same z-level only - signal["dist"] = max(get_dist(curr, pos), 0) //Distance between the src and remote GPS turfs - signal["degrees"] = round(Get_Angle(curr, pos)) //0-360 degree directional bearing, for more precision. - - signals += list(signal) //Add this signal to the list of signals - data["signals"] = signals - return data - - - -/obj/item/gps/ui_act(action, params) - if(..()) - return - switch(action) - if("rename") - var/a = input("Please enter desired tag.", name, gpstag) as text - a = copytext(sanitize(a), 1, 20) - gpstag = a - . = TRUE - name = "global positioning system ([gpstag])" - - if("power") - toggletracking(usr) - . = TRUE - if("updating") - updating = !updating - . = TRUE - if("globalmode") - global_mode = !global_mode - . = TRUE - + AddComponent(/datum/component/gps/item, gpstag) /obj/item/gps/science icon_state = "gps-s" diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index 0cf7ceb55b..e65325587c 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -29,7 +29,7 @@ /obj/item/electropack/Destroy() SSradio.remove_object(src, frequency) - . = ..() + return ..() /obj/item/electropack/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) if(iscarbon(user)) @@ -39,7 +39,7 @@ return return ..() -/obj/item/electropack/attackby(obj/item/W, mob/living/user, params) +/obj/item/electropack/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/clothing/head/helmet)) var/obj/item/assembly/shock_kit/A = new /obj/item/assembly/shock_kit(user) A.icon = 'icons/obj/assemblies.dmi' @@ -59,56 +59,12 @@ else return ..() -/obj/item/electropack/Topic(href, href_list) - var/mob/living/carbon/C = usr - if(usr.stat || usr.restrained() || C.back == src) - return - - if(!usr.canUseTopic(src, BE_CLOSE)) - usr << browse(null, "window=radio") - onclose(usr, "radio") - return - - if(href_list["set"]) - if(href_list["set"] == "freq") - var/new_freq = input(usr, "Input a new receiving frequency", "Electropack Frequency", format_frequency(frequency)) as num|null - if(!usr.canUseTopic(src, BE_CLOSE)) - return - new_freq = unformat_frequency(new_freq) - new_freq = sanitize_frequency(new_freq, TRUE) - set_frequency(new_freq) - - if(href_list["set"] == "code") - var/new_code = input(usr, "Input a new receiving code", "Electropack Code", code) as num|null - if(!usr.canUseTopic(src, BE_CLOSE)) - return - new_code = round(new_code) - new_code = clamp(new_code, 1, 100) - code = new_code - - if(href_list["set"] == "power") - if(!usr.canUseTopic(src, BE_CLOSE)) - return - on = !(on) - icon_state = "electropack[on]" - - if(usr) - attack_self(usr) - - return - -/obj/item/electropack/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - SSradio.add_object(src, frequency, RADIO_SIGNALER) - return - /obj/item/electropack/receive_signal(datum/signal/signal) if(!signal || signal.data["code"] != code) return if(isliving(loc) && on) - if(shock_cooldown == TRUE) + if(shock_cooldown) return shock_cooldown = TRUE addtimer(VARSET_CALLBACK(src, shock_cooldown, FALSE), 100) @@ -124,18 +80,59 @@ if(master) master.receive_signal() - return -/obj/item/electropack/ui_interact(mob/user) - if(!ishuman(user)) +/obj/item/electropack/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + SSradio.add_object(src, frequency, RADIO_SIGNALER) + +/obj/item/electropack/ui_state(mob/user) + return GLOB.hands_state + +/obj/item/electropack/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Electropack", name) + ui.open() + +/obj/item/electropack/ui_data(mob/user) + var/list/data = list() + data["power"] = on + data["frequency"] = frequency + data["code"] = code + data["minFrequency"] = MIN_FREE_FREQ + data["maxFrequency"] = MAX_FREE_FREQ + return data + +/obj/item/electropack/ui_act(action, params) + if(..()) return -/obj/item/electropack/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "Electropack", name, ui_x, ui_y, master_ui, state) - ui.open() + switch(action) + if("power") + on = !on + icon_state = "electropack[on]" + . = TRUE + if("freq") + var/value = unformat_frequency(params["freq"]) + if(value) + frequency = sanitize_frequency(value, TRUE) + set_frequency(frequency) + . = TRUE + if("code") + var/value = text2num(params["code"]) + if(value) + value = round(value) + code = clamp(value, 1, 100) + . = TRUE + if("reset") + if(params["reset"] == "freq") + frequency = initial(frequency) + . = TRUE + else if(params["reset"] == "code") + code = initial(code) + . = TRUE + /obj/item/electropack/shockcollar name = "shock collar" @@ -145,7 +142,7 @@ icon_state = "shockcollar" item_state = "shockcollar" body_parts_covered = NECK - slot_flags = ITEM_SLOT_NECK | ITEM_SLOT_DENYPOCKET //no more pocket shockers + slot_flags = ITEM_SLOT_NECK //no more pocket shockers. Now done without lazyness w_class = WEIGHT_CLASS_SMALL strip_delay = 60 equip_delay_other = 60 @@ -167,16 +164,18 @@ return return ..() -/obj/item/electropack/shockcollar/receive_signal(datum/signal/signal) +/obj/item/electropack/shockcollar/receive_signal(datum/signal/signal) //we have to override this because of text if(!signal || signal.data["code"] != code) return - if(isliving(loc) && on) + if(isliving(loc) && on) //the "on" arg is currently useless + var/mob/living/L = loc + if(!L.get_item_by_slot(SLOT_NECK)) //**properly** stops pocket shockers + return if(shock_cooldown == TRUE) return shock_cooldown = TRUE addtimer(VARSET_CALLBACK(src, shock_cooldown, FALSE), 100) - var/mob/living/L = loc step(L, pick(GLOB.cardinals)) to_chat(L, "You feel a sharp shock from the collar!") @@ -196,10 +195,13 @@ if(t) tagname = t name = "[initial(name)] - [t]" + return + if(istype(W, /obj/item/clothing/head/helmet)) //lazy method of denying this + return else return ..() -/obj/item/electropack/shockcollar/ui_interact(mob/user) //on_click calls this +/obj/item/electropack/shockcollar/ui_interact(mob/user) //note to src: use tgooey var/dat = {" Frequency/Code for shock collar:
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index e26b29289a..4623a405de 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -107,19 +107,15 @@ else ..() -/obj/item/radio/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state) - . = ..() - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/radio/ui_state(mob/user) + return GLOB.inventory_state + +/obj/item/radio/ui_interact(mob/user, datum/tgui/ui, datum/ui_state/state) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - var/ui_width = 360 - var/ui_height = 106 - if(subspace_transmission) - if(channels.len > 0) - ui_height += 6 + channels.len * 21 - else - ui_height += 24 - ui = new(user, src, ui_key, "Radio", name, ui_width, ui_height, master_ui, state) + ui = new(user, src, "Radio", name) + if(state) + ui.set_state(state) ui.open() /obj/item/radio/ui_data(mob/user) diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm index 4d255fd339..eb444cdb12 100644 --- a/code/game/objects/items/devices/traitordevices.dm +++ b/code/game/objects/items/devices/traitordevices.dm @@ -86,98 +86,102 @@ effective or pretty fucking useless. return if(!used) log_combat(user, M, "irradiated", src) - var/cooldown = GetCooldown() - used = 1 + var/cooldown = get_cooldown() + used = TRUE icon_state = "health1" - handle_cooldown(cooldown) // splits off to handle the cooldown while handling wavelength + addtimer(VARSET_CALLBACK(src, used, FALSE), cooldown) + addtimer(VARSET_CALLBACK(src, icon_state, "health"), cooldown) to_chat(user, "Successfully irradiated [M].") - spawn((wavelength+(intensity*4))*5) - if(M) - if(intensity >= 5) - M.apply_effect(round(intensity/0.075), EFFECT_UNCONSCIOUS) - M.rad_act(intensity*10) + addtimer(CALLBACK(src, .proc/radiation_aftereffect, M), (wavelength+(intensity*4))*5) else to_chat(user, "The radioactive microlaser is still recharging.") -/obj/item/healthanalyzer/rad_laser/proc/handle_cooldown(cooldown) - spawn(cooldown) - used = 0 - icon_state = "health" +/obj/item/healthanalyzer/rad_laser/proc/radiation_aftereffect(mob/living/M) + if(QDELETED(M)) + return + if(intensity >= 5) + M.apply_effect(round(intensity/0.075), EFFECT_UNCONSCIOUS) + M.rad_act(intensity*10) + +/obj/item/healthanalyzer/rad_laser/proc/get_cooldown() + return round(max(10, (stealth*30 + intensity*5 - wavelength/4))) /obj/item/healthanalyzer/rad_laser/attack_self(mob/user) interact(user) -/obj/item/healthanalyzer/rad_laser/proc/GetCooldown() - return round(max(10, (stealth*30 + intensity*5 - wavelength/4))) - /obj/item/healthanalyzer/rad_laser/interact(mob/user) ui_interact(user) -/obj/item/healthanalyzer/rad_laser/ui_interact(mob/user) -/obj/item/healthanalyzer/rad_laser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/healthanalyzer/rad_laser/ui_state(mob/user) + return GLOB.hands_state + +/obj/item/healthanalyzer/rad_laser/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "RadioactiveMicrolaser", "Radioactive Microlaser", ui_x, ui_y, master_ui, state) + ui = new(user, src, "RadioactiveMicrolaser") ui.open() - var/dat = "Irradiation: [irradiate ? "On" : "Off"]
" - dat += "Stealth Mode (NOTE: Deactivates automatically while Irradiation is off): [stealth ? "On" : "Off"]
" - dat += "Scan Mode: " - if(!scanmode) - dat += "Scan Health" - else if(scanmode == 1) - dat += "Scan Reagents" - else - dat += "Disabled" - dat += "

" +/obj/item/healthanalyzer/rad_laser/ui_data(mob/user) + var/list/data = list() + data["irradiate"] = irradiate + data["stealth"] = stealth + data["scanmode"] = scanmode + data["intensity"] = intensity + data["wavelength"] = wavelength + data["on_cooldown"] = used + data["cooldown"] = DisplayTimeText(get_cooldown()) + return data - dat += {" - Radiation Intensity: - -- - [intensity] - ++
+/obj/item/healthanalyzer/rad_laser/ui_act(action, params) + if(..()) + return - Radiation Wavelength: - -- - [(wavelength+(intensity*4))] - ++
- Laser Cooldown: [DisplayTimeText(GetCooldown())]
- "} - - var/datum/browser/popup = new(user, "radlaser", "Radioactive Microlaser Interface", 400, 240) - popup.set_content(dat) - popup.open() - -/obj/item/healthanalyzer/rad_laser/Topic(href, href_list) - if(!usr.canUseTopic(src)) - return 1 - - usr.set_machine(src) - if(href_list["rad"]) - irradiate = !irradiate - - else if(href_list["stealthy"]) - stealth = !stealth - - else if(href_list["mode"]) - scanmode += 1 - if(scanmode > 2) - scanmode = 0 - - else if(href_list["radint"]) - var/amount = text2num(href_list["radint"]) - amount += intensity - intensity = max(1,(min(20,amount))) - - else if(href_list["radwav"]) - var/amount = text2num(href_list["radwav"]) - amount += wavelength - wavelength = max(0,(min(120,amount))) - - attack_self(usr) - add_fingerprint(usr) - return + switch(action) + if("irradiate") + irradiate = !irradiate + . = TRUE + if("stealth") + stealth = !stealth + . = TRUE + if("scanmode") + scanmode = !scanmode + . = TRUE + if("radintensity") + var/target = params["target"] + var/adjust = text2num(params["adjust"]) + if(target == "min") + target = 1 + . = TRUE + else if(target == "max") + target = 20 + . = TRUE + else if(adjust) + target = intensity + adjust + . = TRUE + else if(text2num(target) != null) + target = text2num(target) + . = TRUE + if(.) + target = round(target) + intensity = clamp(target, 1, 20) + if("radwavelength") + var/target = params["target"] + var/adjust = text2num(params["adjust"]) + if(target == "min") + target = 0 + . = TRUE + else if(target == "max") + target = 120 + . = TRUE + else if(adjust) + target = wavelength + adjust + . = TRUE + else if(text2num(target) != null) + target = text2num(target) + . = TRUE + if(.) + target = round(target) + wavelength = clamp(target, 0, 120) /obj/item/shadowcloak name = "cloaker belt" diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index c791b7e323..32d9c02a27 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -188,11 +188,10 @@ tank_one.air_contents.merge(temp) tank_two.air_contents.set_volume(tank_two.air_contents.return_volume() - tank_one.air_contents.return_volume()) - /* +/* Exadv1: I know this isn't how it's going to work, but this was just to check it explodes properly when it gets a signal (and it does). - */ - +*/ /obj/item/transfer_valve/proc/toggle_valve() if(!valve_open && tank_one && tank_two) valve_open = TRUE @@ -233,16 +232,20 @@ valve_open = FALSE update_icon() -// this doesn't do anything but the timer etc. expects it to be here -// eventually maybe have it update icon to show state (timer, prox etc.) like old bombs +/* + This doesn't do anything but the timer etc. expects it to be here + eventually maybe have it update icon to show state (timer, prox etc.) like old bombs +*/ /obj/item/transfer_valve/proc/c_state() return -/obj/item/transfer_valve/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/transfer_valve/ui_state(mob/user) + return GLOB.hands_state + +/obj/item/transfer_valve/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "TransferValve", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "TransferValve", name) ui.open() /obj/item/transfer_valve/ui_data(mob/user) diff --git a/code/game/objects/items/eightball.dm b/code/game/objects/items/eightball.dm index dbc4c297ca..0d5c9a22aa 100644 --- a/code/game/objects/items/eightball.dm +++ b/code/game/objects/items/eightball.dm @@ -192,11 +192,13 @@ return top_vote -/obj/item/toy/eightball/haunted/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state = GLOB.always_state) +/obj/item/toy/eightball/haunted/ui_state(mob/user) + return GLOB.observer_state - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/toy/eightball/haunted/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "EightBallVote", name, 400, 600, master_ui, state) + ui = new(user, src, "EightBallVote", name) ui.open() /obj/item/toy/eightball/haunted/ui_data(mob/user) diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm index 1554142c8c..609cc3b719 100644 --- a/code/game/objects/items/granters.dm +++ b/code/game/objects/items/granters.dm @@ -79,7 +79,7 @@ ADD_TRAIT(user, granted_trait, BOOK_TRAIT) /obj/item/book/granter/trait/rifleman - name = "\proper the Neo-Russian Rifleman\'s Primer" + name = "The Neo-Russian Rifleman\'s Primer" desc = "A book with stains of vodka and...blood? The back is hard to read, but says something about bolt-actions. Or pump-actions. Both, maybe." oneuse = FALSE granted_trait = TRAIT_FAST_PUMP diff --git a/code/game/objects/items/implants/implant_misc.dm b/code/game/objects/items/implants/implant_misc.dm index 897cb9b750..08d6a586c8 100644 --- a/code/game/objects/items/implants/implant_misc.dm +++ b/code/game/objects/items/implants/implant_misc.dm @@ -47,7 +47,7 @@ /obj/item/implant/warp/activate() . = ..() uses-- - imp_in.do_adrenaline(150, TRUE, 0, 0, TRUE, list(/datum/reagent/fermi/eigenstate = 1.2), "You feel an internal prick as as the bluespace starts ramping up!") + imp_in.do_adrenaline(20, TRUE, 0, 0, TRUE, list(/datum/reagent/fermi/eigenstate = 1.2), "You feel an internal prick as as the bluespace starts ramping up!") to_chat(imp_in, "You feel an internal prick as as the bluespace starts ramping up!") if(!uses) qdel(src) @@ -86,4 +86,4 @@ healthstring = "Oxygen Deprivation Damage => [round(L.getOxyLoss())]
Fire Damage => [round(L.getFireLoss())]
Toxin Damage => [round(L.getToxLoss())]
Brute Force Damage => [round(L.getBruteLoss())]
" if (!healthstring) healthstring = "ERROR" - return healthstring \ No newline at end of file + return healthstring diff --git a/code/game/objects/items/implants/implant_radio.dm b/code/game/objects/items/implants/implant_radio.dm index 5d3d579a4e..6f42547004 100644 --- a/code/game/objects/items/implants/implant_radio.dm +++ b/code/game/objects/items/implants/implant_radio.dm @@ -10,7 +10,7 @@ /obj/item/implant/radio/activate() . = ..() // needs to be GLOB.deep_inventory_state otherwise it won't open - radio.ui_interact(usr, "main", null, FALSE, null, GLOB.deep_inventory_state) + radio.ui_interact(usr, state = GLOB.deep_inventory_state) /obj/item/implant/radio/implant(mob/living/target, mob/user, silent = FALSE) . = ..() diff --git a/code/game/objects/items/implants/implantchair.dm b/code/game/objects/items/implants/implantchair.dm index b3c1f79e94..7b05cf302e 100644 --- a/code/game/objects/items/implants/implantchair.dm +++ b/code/game/objects/items/implants/implantchair.dm @@ -26,14 +26,15 @@ open_machine() update_icon() +/obj/machinery/implantchair/ui_state(mob/user) + return GLOB.notcontained_state -/obj/machinery/implantchair/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/implantchair/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "implantchair", name, 375, 280, master_ui, state) + ui = new(user, src, "ImplantChair", name) ui.open() - /obj/machinery/implantchair/ui_data() var/list/data = list() data["occupied"] = occupant ? 1 : 0 diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index c50dc6e316..80fd177d84 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -6,21 +6,24 @@ desc = "Protected by FRM." icon = 'icons/obj/module.dmi' icon_state = "cyborg_upgrade" + w_class = WEIGHT_CLASS_SMALL var/locked = FALSE var/installed = 0 var/require_module = 0 var/list/module_type + /// Bitflags listing module compatibility. Used in the exosuit fabricator for creating sub-categories. + var/module_flags = NONE // if true, is not stored in the robot to be ejected // if module is reset var/one_use = FALSE /obj/item/borg/upgrade/proc/action(mob/living/silicon/robot/R, user = usr) if(R.stat == DEAD) - to_chat(user, "[src] will not function on a deceased cyborg.") + to_chat(user, "[src] will not function on a deceased cyborg.") return FALSE if(module_type && !is_type_in_list(R.module, module_type)) - to_chat(R, "Upgrade mounting error! No suitable hardpoint detected!") - to_chat(user, "There's no mounting point for the module!") + to_chat(R, "Upgrade mounting error! No suitable hardpoint detected.") + to_chat(user, "There's no mounting point for the module!") return FALSE return TRUE @@ -37,7 +40,7 @@ one_use = TRUE /obj/item/borg/upgrade/rename/attack_self(mob/user) - heldname = stripped_input(user, "Enter new robot name", "Cyborg Reclassification", heldname, MAX_NAME_LEN) + heldname = sanitize_name(stripped_input(user, "Enter new robot name", "Cyborg Reclassification", heldname, MAX_NAME_LEN)) /obj/item/borg/upgrade/rename/action(mob/living/silicon/robot/R) . = ..() @@ -95,6 +98,7 @@ desc = "Used to cool a mounted energy-based firearm, increasing the potential current in it and thus its recharge rate." icon_state = "cyborg_upgrade3" require_module = 1 + module_flags = BORG_MODULE_SECURITY /obj/item/borg/upgrade/disablercooler/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -147,6 +151,7 @@ icon_state = "cyborg_upgrade3" require_module = 1 module_type = list(/obj/item/robot_module/miner) + module_flags = BORG_MODULE_MINER /obj/item/borg/upgrade/ddrill/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -207,6 +212,7 @@ icon_state = "cyborg_upgrade3" require_module = 1 module_type = list(/obj/item/robot_module/butler) + module_flags = BORG_MODULE_JANITOR /obj/item/borg/upgrade/tboh/action(mob/living/silicon/robot/R) . = ..() @@ -234,6 +240,7 @@ icon_state = "cyborg_upgrade3" require_module = 1 module_type = list(/obj/item/robot_module/butler) + module_flags = BORG_MODULE_JANITOR /obj/item/borg/upgrade/amop/action(mob/living/silicon/robot/R) . = ..() @@ -283,6 +290,7 @@ resistance_flags = LAVA_PROOF | FIRE_PROOF require_module = 1 module_type = list(/obj/item/robot_module/miner) + module_flags = BORG_MODULE_MINER /obj/item/borg/upgrade/lavaproof/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -402,6 +410,7 @@ module_type = list(/obj/item/robot_module/medical, /obj/item/robot_module/syndicate_medical) var/list/additional_reagents = list() + module_flags = BORG_MODULE_MEDICAL /obj/item/borg/upgrade/hypospray/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -471,6 +480,7 @@ require_module = 1 module_type = list(/obj/item/robot_module/medical, /obj/item/robot_module/syndicate_medical) + module_flags = BORG_MODULE_MEDICAL /obj/item/borg/upgrade/processor/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -578,6 +588,7 @@ icon_state = "borg_BS_RPED" require_module = TRUE module_type = list(/obj/item/robot_module/engineering, /obj/item/robot_module/saboteur) + module_flags = BORG_MODULE_ENGINEERING /obj/item/borg/upgrade/rped/action(mob/living/silicon/robot/R, user = usr) . = ..() @@ -612,8 +623,8 @@ icon = 'icons/obj/device.dmi' icon_state = "pinpointer_crew" require_module = TRUE - module_type = list(/obj/item/robot_module/medical, - /obj/item/robot_module/syndicate_medical) + module_type = list(/obj/item/robot_module/medical, /obj/item/robot_module/syndicate_medical) + module_flags = BORG_MODULE_MEDICAL /obj/item/borg/upgrade/pinpointer/action(mob/living/silicon/robot/R, user = usr) . = ..() diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm index 992c173d3b..4952c93928 100644 --- a/code/game/objects/items/shields.dm +++ b/code/game/objects/items/shields.dm @@ -106,8 +106,8 @@ var/disarming = (target_downed && (shield_flags & SHIELD_BASH_GROUND_SLAM_DISARM)) || (shield_flags & SHIELD_BASH_ALWAYS_DISARM) || (wallhit && (shield_flags & SHIELD_BASH_WALL_DISARM)) var/knockdown = !target_downed && ((shield_flags & SHIELD_BASH_ALWAYS_KNOCKDOWN) || (wallhit && (shield_flags & SHIELD_BASH_WALL_KNOCKDOWN))) if(shieldbash_stagger_duration || knockdown) - target.visible_message("[target] is knocked [knockdown? "to the floor" : "off balanace"]!", - "You are knocked [knockdown? "to the floor" : "off balanace"]!") + target.visible_message("[target] is knocked [knockdown? "to the floor" : "off balance"]!", + "You are knocked [knockdown? "to the floor" : "off balance"]!") if(knockdown) target.KnockToFloor(disarming) else if(disarming) diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 6b6e9d7d65..9ea8c9c689 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -115,14 +115,15 @@ /obj/item/stack/medical/gauze name = "medical gauze" - desc = "A roll of elastic cloth, perfect for stabilizing all kinds of wounds, from cuts and burns, to broken bones. " + desc = "A roll of elastic cloth, perfect for stabilizing all kinds of wounds, from cuts and burns to broken bones." gender = PLURAL singular_name = "medical gauze" icon_state = "gauze" heal_brute = 5 self_delay = 50 other_delay = 20 - amount = 6 + amount = 10 + max_amount = 10 absorption_rate = 0.25 absorption_capacity = 5 splint_factor = 0.35 @@ -170,6 +171,14 @@ "You cut [src] into pieces of cloth with [I].", \ "You hear cutting.") use(2) + else if(I.is_drainable() && I.reagents.has_reagent(/datum/reagent/space_cleaner/sterilizine)) + if(!I.reagents.has_reagent(/datum/reagent/space_cleaner/sterilizine, 10)) + to_chat(user, "There's not enough sterilizine in [I] to sterilize [src]!") + return + user.visible_message("[user] pours the contents of [I] onto [src], sterilizing it.", "You pour the contents of [I] onto [src], sterilizing it.") + I.reagents.remove_reagent(/datum/reagent/space_cleaner/sterilizine, 10) + new /obj/item/stack/medical/gauze/adv/one(user.drop_location()) + use(1) else return ..() @@ -181,7 +190,7 @@ name = "improvised gauze" singular_name = "improvised gauze" heal_brute = 0 - desc = "A roll of cloth roughly cut from something that does a decent job of stabilizing wounds, but less efficiently so than real medical gauze." + desc = "A roll of cloth roughly cut from something that does a decent job of stabilizing wounds, but less efficiently than real medical gauze." self_delay = 60 other_delay = 30 absorption_rate = 0.15 @@ -189,9 +198,13 @@ /obj/item/stack/medical/gauze/adv name = "sterilized medical gauze" - desc = "A roll of elastic sterilized cloth that is extremely effective at stopping bleeding, heals minor wounds and cleans them." singular_name = "sterilized medical gauze" - self_delay = 5 + desc = "A roll of elastic sterilized cloth that is extremely effective at stopping bleeding and covering burns." + heal_brute = 6 + self_delay = 45 + other_delay = 15 + absorption_rate = 0.4 + absorption_capacity = 6 /obj/item/stack/medical/gauze/adv/one amount = 1 @@ -209,8 +222,8 @@ icon_state = "suture" self_delay = 30 other_delay = 10 - amount = 10 - max_amount = 10 + amount = 15 + max_amount = 15 repeating = TRUE heal_brute = 10 stop_bleeding = 0.6 @@ -226,6 +239,9 @@ /obj/item/stack/medical/suture/one amount = 1 +/obj/item/stack/medical/suture/five + amount = 5 + /obj/item/stack/medical/suture/medicated name = "medicated suture" icon_state = "suture_purp" @@ -264,8 +280,8 @@ icon_state = "ointment" lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' - amount = 10 - max_amount = 10 + amount = 12 + max_amount = 12 self_delay = 40 other_delay = 20 @@ -295,8 +311,8 @@ self_delay = 30 other_delay = 10 amount = 15 - heal_burn = 10 max_amount = 15 + heal_burn = 10 repeating = TRUE sanitization = 0.75 flesh_regeneration = 3 @@ -306,6 +322,9 @@ /obj/item/stack/medical/mesh/one amount = 1 +/obj/item/stack/medical/mesh/five + amount = 5 + /obj/item/stack/medical/mesh/advanced name = "advanced regenerative mesh" desc = "An advanced mesh made with aloe extracts and sterilizing chemicals, used to treat burns." diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index bad858aa21..442228ef31 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -335,6 +335,7 @@ new /obj/item/reagent_containers/glass/bottle/romerol(src) new /obj/item/reagent_containers/syringe(src) new /obj/item/reagent_containers/dropper(src) + new /obj/item/paper/guides/antag/romerol_instructions(src) /obj/item/storage/box/syndie_kit/ez_clean/PopulateContents() for(var/i in 1 to 3) @@ -409,7 +410,7 @@ /obj/item/storage/box/syndie_kit/revolver /obj/item/storage/box/syndie_kit/revolver/PopulateContents() - new /obj/item/gun/ballistic/revolver(src) + new /obj/item/gun/ballistic/revolver/syndicate(src) new /obj/item/ammo_box/a357(src) /obj/item/storage/box/syndie_kit/pistol diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm index 2de5860794..08b91332ef 100644 --- a/code/game/objects/items/tanks/tanks.dm +++ b/code/game/objects/items/tanks/tanks.dm @@ -164,11 +164,13 @@ else . = ..() -/obj/item/tank/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/tank/ui_state(mob/user) + return GLOB.hands_state + +/obj/item/tank/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Tank", name, 400, 120, master_ui, state) + ui = new(user, src, "Tank", name) ui.open() /obj/item/tank/ui_data(mob/user) @@ -205,10 +207,6 @@ else if(pressure == "max") pressure = TANK_MAX_RELEASE_PRESSURE . = TRUE - else if(pressure == "input") - pressure = input("New release pressure ([TANK_MIN_RELEASE_PRESSURE]-[TANK_MAX_RELEASE_PRESSURE] kPa):", name, distribute_pressure) as num|null - if(!isnull(pressure) && !..()) - . = TRUE else if(text2num(pressure) != null) pressure = text2num(pressure) . = TRUE diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm index 881e4d5f1a..f9ede407ca 100644 --- a/code/game/objects/items/teleportation.dm +++ b/code/game/objects/items/teleportation.dm @@ -1,4 +1,3 @@ - #define SOURCE_PORTAL 1 #define DESTINATION_PORTAL 2 @@ -25,90 +24,72 @@ throw_speed = 3 throw_range = 7 custom_materials = list(/datum/material/iron=400) + var/tracking_range = 20 -/obj/item/locator/attack_self(mob/user) - user.set_machine(src) - var/dat - if (temp) - dat = "[temp]

Clear" - else - dat = {" -Persistent Signal Locator
-Refresh"} - user << browse(dat, "window=radio") - onclose(user, "radio") - return +/obj/item/locator/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "BluespaceLocator", name) + ui.open() -/obj/item/locator/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained()) - return - var/turf/current_location = get_turf(usr)//What turf is the user on? - if(!current_location || is_centcom_level(current_location.z))//If turf was not found or they're on CentCom - to_chat(usr, "[src] is malfunctioning.") - return - if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc))) - usr.set_machine(src) - if (href_list["refresh"]) - temp = "Persistent Signal Locator
" - var/turf/sr = get_turf(src) +/obj/item/locator/ui_data(mob/user) + var/list/data = list() - if (sr) - temp += "Beacon Signals:
" - for(var/obj/item/beacon/W in GLOB.teleportbeacons) - if (!W.renamed) - continue - var/turf/tr = get_turf(W) - if (tr.z == sr.z && tr) - var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) - if (direct < 5) - direct = "very strong" - else - if (direct < 10) - direct = "strong" - else - if (direct < 20) - direct = "weak" - else - direct = "very weak" - temp += "[W.name]-[dir2text(get_dir(sr, tr))]-[direct]
" + data["trackingrange"] = tracking_range - temp += "Implant Signals:
" - for (var/obj/item/implant/tracking/W in GLOB.tracked_implants) - if (!isliving(W.imp_in)) - continue - var/mob/living/M = W.imp_in - if (M.stat == DEAD) - if (M.timeofdeath + W.lifespan_postmortem < world.time) - continue + // Get our current turf location. + var/turf/sr = get_turf(src) - var/turf/tr = get_turf(M) - if (tr.z == sr.z && tr) - var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) - if (direct < 20) - if (direct < 5) - direct = "very strong" - else - if (direct < 10) - direct = "strong" - else - direct = "weak" - temp += "[W.imp_in.name]-[dir2text(get_dir(sr, tr))]-[direct]
" + if (sr) + // Check every teleport beacon. + var/list/tele_beacons = list() + for(var/obj/item/beacon/W in GLOB.teleportbeacons) - temp += "You are at \[[sr.x],[sr.y],[sr.z]\] in orbital coordinates.

Refresh
" + // Get the tracking beacon's turf location. + var/turf/tr = get_turf(W) + + // Make sure it's on a turf and that its Z-level matches the tracker's Z-level + if (tr && tr.z == sr.z) + // Get the distance between the beacon's turf and our turf + var/distance = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) + + // If the target is too far away, skip over this beacon. + if(distance > tracking_range) + continue + + var/beacon_name + + if(W.renamed) + beacon_name = W.name + else + var/area/A = get_area(W) + beacon_name = A.name + + var/D = dir2text(get_dir(sr, tr)) + tele_beacons += list(list(name = beacon_name, direction = D, distance = distance)) + + data["telebeacons"] = tele_beacons + + var/list/track_implants = list() + + for (var/obj/item/implant/tracking/W in GLOB.tracked_implants) + if (!W.imp_in || !isliving(W.loc)) + continue else - temp += "Processing Error: Unable to locate orbital position.
" - else - if (href_list["temp"]) - temp = null - if (ismob(src.loc)) - attack_self(src.loc) - else - for(var/mob/M in fov_viewers(1, src)) - if (M.client) - src.attack_self(M) - return + var/mob/living/M = W.loc + if (M.stat == DEAD) + if (M.timeofdeath + W.lifespan_postmortem < world.time) + continue + var/turf/tr = get_turf(W) + var/distance = max(abs(tr.x - sr.x), abs(tr.y - sr.y)) + if(distance > tracking_range) + continue + + var/D = dir2text(get_dir(sr, tr)) + track_implants += list(list(name = W.imp_in.name, direction = D, distance = distance)) + data["trackimplants"] = track_implants + return data /* * Hand-tele @@ -190,13 +171,8 @@ user.show_message("\The [src] is recharging!") return var/atom/T = L[t1] - var/implantcheckmate = FALSE - if(isliving(T)) - var/mob/living/M = T - if(!locate(/obj/item/implant/tracking) in M.implants) //The user was too slow and let the target mob's tracking implant expire or get removed. - implantcheckmate = TRUE var/area/A = get_area(T) - if(A.noteleport || implantcheckmate) + if(A.noteleport) to_chat(user, "\The [src] is malfunctioning.") return current_location = get_turf(user) //Recheck. @@ -205,10 +181,12 @@ to_chat(user, "\The [src] is malfunctioning.") return user.show_message("Locked In.", MSG_AUDIBLE) - var/list/obj/effect/portal/created = create_portal_pair(current_location, get_teleport_turf(get_turf(T)), src, 300, 1, null, atmos_link_override) + var/list/obj/effect/portal/created = create_portal_pair(current_location, get_teleport_turf(get_turf(T)), 300, 1, null, atmos_link_override) if(!(LAZYLEN(created) == 2)) return - try_move_adjacent(created[1]) + RegisterSignal(created[1], COMSIG_PARENT_QDELETING, .proc/on_portal_destroy) //Gosh darn it kevinz. + RegisterSignal(created[2], COMSIG_PARENT_QDELETING, .proc/on_portal_destroy) + try_move_adjacent(created[1], user.dir) active_portal_pairs[created[1]] = created[2] var/obj/effect/portal/c1 = created[1] var/obj/effect/portal/c2 = created[2] diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index 4c6bcc08b4..2109d1038e 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -35,6 +35,10 @@ name = "syndi-cakes" icon_state = "syndi_cakes" +/obj/item/trash/energybar + name = "energybar wrapper" + icon_state = "energybar" + /obj/item/trash/waffles name = "waffles tray" icon_state = "waffles" diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index 82597615bc..44459eaedc 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -254,6 +254,20 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 playsound(src, 'sound/weapons/bladeslice.ogg', 50, 1) return(BRUTELOSS) +/obj/item/katana/timestop + name = "temporal katana" + desc = "Delicately balanced, this finely-crafted blade hums with barely-restrained potential." + block_chance = 0 // oops + force = 27.5 // oops + item_flags = ITEM_CAN_PARRY + block_parry_data = /datum/block_parry_data/bokken/quick_parry/proj + +/obj/item/katana/timestop/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time) + if(ishuman(owner)) + var/mob/living/carbon/human/flynn = owner + flynn.emote("smirk") + new /obj/effect/timestop/magic(get_turf(owner), 1, 50, list(owner)) // null roddies counter + /obj/item/melee/bokken // parrying stick name = "bokken" desc = "A space-Japanese training sword made of wood and shaped like a katana." @@ -295,7 +309,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 TEXT_ATTACK_TYPE_PROJECTILE = 30, ) parry_failed_stagger_duration = 3 SECONDS - parry_data = list(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5) // 7*2.5 = 17.5, 8*2.5 = 20, 9*2.5 = 22.5, 10*2.5 = 25 + parry_data = list( + PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5, // 7*2.5 = 17.5, 8*2.5 = 20, 9*2.5 = 22.5, 10*2.5 = 25 + ) /datum/block_parry_data/bokken/quick_parry // emphasizing REALLY SHORT PARRIES parry_stamina_cost = 6 // still more costly than most parries, but less than a full bokken parry @@ -306,6 +322,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 parry_failed_clickcd_duration = 1 SECONDS // more forgiving punishments for missed parries // still, don't fucking miss your parries or you're down stamina and staggered to shit +/datum/block_parry_data/bokken/quick_parry/proj + parry_efficiency_perfect_override = list() + /obj/item/melee/bokken/Initialize() . = ..() AddElement(/datum/element/sword_point) @@ -369,6 +388,40 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 if(burnt) . += " Burned into the \"blade\" is [burned_in]." +/obj/item/melee/bokken/debug + name = "funny debug parrying stick" + desc = "if you see this you've fucked up somewhere my good man" + block_parry_data = /datum/block_parry_data/bokken/debug + +/obj/item/melee/bokken/debug/AltClick(mob/user) + quick_parry = !quick_parry + if(quick_parry) + block_parry_data = /datum/block_parry_data/bokken/quick_parry/debug + else + block_parry_data = /datum/block_parry_data/bokken/debug + to_chat(user, "[src] is now [quick_parry ? "emphasizing shorter parries, forcing you to riposte or be staggered" : "emphasizing longer parries, with a shorter window to riposte but more forgiving parries"].") + +/datum/block_parry_data/bokken/debug + parry_efficiency_perfect_override = list() + parry_data = list( + PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5, // 7*2.5 = 17.5, 8*2.5 = 20, 9*2.5 = 22.5, 10*2.5 = 25 + PARRY_DISARM_ATTACKER = TRUE, + PARRY_KNOCKDOWN_ATTACKER = 10, + PARRY_STAGGER_ATTACKER = 10, + PARRY_DAZE_ATTACKER = 10, + ) + +/datum/block_parry_data/bokken/quick_parry/debug + parry_efficiency_perfect_override = list() + parry_data = list( + PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5, // 7*2.5 = 17.5, 8*2.5 = 20, 9*2.5 = 22.5, 10*2.5 = 25 + PARRY_DISARM_ATTACKER = TRUE, + PARRY_KNOCKDOWN_ATTACKER = 10, + PARRY_STAGGER_ATTACKER = 10, + PARRY_DAZE_ATTACKER = 10, + ) + + /obj/item/wirerod name = "wired rod" desc = "A rod with some wire wrapped around the top. It'd be easy to attach something to the top bit." diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 39f2d276a1..b758317fe2 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -9,8 +9,10 @@ var/mob/living/structureclimber var/broken = 0 //similar to machinery's stat BROKEN layer = BELOW_OBJ_LAYER - flags_ricochet = RICOCHET_HARD - ricochet_chance_mod = 0.5 + //ricochets on structures commented out for now because there's a lot of structures that /shouldnt/ be ricocheting and those need to be reviewed first + //flags_1 = DEFAULT_RICOCHET_1 + //flags_ricochet = RICOCHET_HARD + //ricochet_chance_mod = 0.5 /obj/structure/Initialize() if (!armor) diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm index 827e9a7b0a..47584a1775 100644 --- a/code/game/objects/structures/artstuff.dm +++ b/code/game/objects/structures/artstuff.dm @@ -45,8 +45,6 @@ var/height = 11 var/list/grid var/canvas_color = "#ffffff" //empty canvas color - var/ui_x = 400 - var/ui_y = 400 var/used = FALSE var/painting_name //Painting name, this is set after framing. var/finalized = FALSE //Blocks edits @@ -75,12 +73,16 @@ . = ..() ui_interact(user) -/obj/item/canvas/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) +/obj/item/canvas/ui_state(mob/user) + if(finalized) + return GLOB.physical_obscured_state + else + return GLOB.default_state - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/canvas/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Canvas", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "Canvas", name) ui.set_autoupdate(FALSE) ui.open() @@ -190,8 +192,6 @@ icon_state = "19x19" width = 19 height = 19 - ui_x = 600 - ui_y = 600 pixel_x = 6 pixel_y = 9 framed_offset_x = 8 @@ -201,8 +201,6 @@ icon_state = "23x19" width = 23 height = 19 - ui_x = 800 - ui_y = 600 pixel_x = 4 pixel_y = 10 framed_offset_x = 6 @@ -212,8 +210,6 @@ icon_state = "23x23" width = 23 height = 23 - ui_x = 800 - ui_y = 800 pixel_x = 5 pixel_y = 9 framed_offset_x = 5 @@ -262,7 +258,7 @@ /obj/structure/sign/painting/examine(mob/user) . = ..() if(C) - C.ui_interact(user,state = GLOB.physical_obscured_state) + C.ui_interact(user) /obj/structure/sign/painting/wirecutter_act(mob/living/user, obj/item/I) . = ..() diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index 299ba7b659..f3c4679d21 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -319,10 +319,43 @@ throwforce = 10 throw_range = 3 hitsound = 'sound/items/trayhit1.ogg' - block_chance = 50 custom_materials = list(/datum/material/iron = 2000) var/break_chance = 5 //Likely hood of smashing the chair. var/obj/structure/chair/origin_type = /obj/structure/chair + item_flags = ITEM_CAN_PARRY | ITEM_CAN_BLOCK + block_parry_data = /datum/block_parry_data/chair + +/obj/item/chair/ComponentInitialize() + . = ..() + AddComponent(/datum/component/two_handed, require_twohands = TRUE) + +/datum/block_parry_data/chair + block_damage_multiplier = 0.7 + block_stamina_efficiency = 2 + block_stamina_cost_per_second = 1.5 + block_slowdown = 0.5 + block_lock_attacking = FALSE + block_lock_sprinting = TRUE + block_start_delay = 1.5 + block_damage_absorption = 7 + block_damage_limit = 20 + block_resting_stamina_penalty_multiplier = 2 + block_projectile_mitigation = 20 + parry_stamina_cost = 5 + parry_time_windup = 1 + parry_time_active = 11 + parry_time_spindown = 2 + parry_time_perfect = 1.5 + parry_time_perfect_leeway = 1 + parry_imperfect_falloff_percent = 7.5 + parry_efficiency_to_counterattack = 100 + parry_efficiency_considered_successful = 50 + parry_efficiency_perfect = 120 + parry_efficiency_perfect_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 30, + ) + parry_failed_stagger_duration = 3.5 SECONDS + parry_data = list(PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5) /obj/item/chair/suicide_act(mob/living/carbon/user) user.visible_message("[user] begins hitting [user.p_them()]self with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!") diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index f0384ca96a..19c0f0aed1 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -345,3 +345,212 @@ name = initial(I.name) icon = initial(I.icon) icon_state = initial(I.icon_state) +/* Selling people in jars is currently disabled. +/obj/structure/displaycase/forsale + name = "vend-a-tray" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "laserbox0" + desc = "A display case with an ID-card swiper. Use your ID to purchase the contents." + density = FALSE + max_integrity = 100 + req_access = null + showpiece_type = /obj/item/reagent_containers/food + alert = FALSE //No, we're not calling the fire department because someone stole your cookie. + glass_fix = FALSE //Fixable with tools instead. + ///The price of the item being sold. Altered by grab intent ID use. + var/sale_price = 20 + ///The Account which will receive payment for purchases. Set by the first ID to swipe the tray. + var/datum/bank_account/payments_acc = null + ///We're using the same trick as paper does in order to cache the image, and only load the UI when messed with. + var/list/viewing_ui = list() + +/obj/structure/displaycase/forsale/update_icon() //remind me to fix my shitcode later + var/icon/I + if(open) + I = icon('icons/obj/stationobjs.dmi',"laserboxb0") + else + I = icon('icons/obj/stationobjs.dmi',"laserbox0") + if(!showpiece && !open) + I = icon('icons/obj/stationobjs.dmi',"laserbox_open") + if(broken) + I = icon('icons/obj/stationobjs.dmi',"laserbox_broken") + if(showpiece) + var/icon/S = getFlatIcon(showpiece) + S.Scale(17,17) + I.Blend(S,ICON_UNDERLAY,8,12) + src.icon = I + return + +/obj/structure/displaycase/forsale/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Vendatray", name) + ui.set_autoupdate(FALSE) + viewing_ui[user] = ui + ui.open() + +/obj/structure/displaycase/forsale/ui_data(mob/user) + var/list/data = list() + var/register = FALSE + if(payments_acc) + register = TRUE + data["owner_name"] = payments_acc.account_holder + if(showpiece) + data["product_name"] = capitalize(showpiece.name) + var/base64 = icon2base64(icon(showpiece.icon, showpiece.icon_state)) + data["product_icon"] = base64 + data["registered"] = register + data["product_cost"] = sale_price + data["tray_open"] = open + return data + +/obj/structure/displaycase/forsale/ui_act(action, params) + if(..()) + return + var/obj/item/card/id/potential_acc = usr.get_idcard(hand_first = TRUE) + switch(action) + if("Buy") + if(!showpiece) + to_chat(usr, "There's nothing for sale.") + return TRUE + if(broken) + to_chat(usr, "[src] appears to be broken.") + return TRUE + if(!payments_acc) + to_chat(usr, "[src] hasn't been registered yet.") + return TRUE + if(!usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + return TRUE + if(!potential_acc) + to_chat(usr, "No ID card detected.") + return + var/datum/bank_account/account = potential_acc.registered_account + if(!account) + to_chat(usr, "[potential_acc] has no account registered!") + return + if(!account.has_money(sale_price)) + to_chat(usr, "You do not possess the funds to purchase this.") + return TRUE + else + account.adjust_money(-sale_price) + if(payments_acc) + payments_acc.adjust_money(sale_price) + usr.put_in_hands(showpiece) + to_chat(usr, "You purchase [showpiece] for [sale_price] credits.") + playsound(src, 'sound/effects/cashregister.ogg', 40, TRUE) + icon = 'icons/obj/stationobjs.dmi' + flick("laserbox_vend", src) + showpiece = null + update_icon() + SStgui.update_uis(src) + return TRUE + if("Open") + if(!payments_acc) + to_chat(usr, "[src] hasn't been registered yet.") + return TRUE + if(!potential_acc || !potential_acc.registered_account) + return + if(!check_access(potential_acc)) + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE) + return + toggle_lock() + SStgui.update_uis(src) + if("Register") + if(payments_acc) + return + if(!potential_acc || !potential_acc.registered_account) + return + if(!check_access(potential_acc)) + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE) + return + payments_acc = potential_acc.registered_account + playsound(src, 'sound/machines/click.ogg', 20, TRUE) + if("Adjust") + if(!check_access(potential_acc) || potential_acc.registered_account != payments_acc) + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE) + return + + var/new_price_input = input(usr,"Set the sale price for this vend-a-tray.","new price",0) as num|null + if(isnull(new_price_input) || (payments_acc != potential_acc.registered_account)) + to_chat(usr, "[src] rejects your new price.") + return + if(!usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) ) + to_chat(usr, "You need to get closer!") + return + new_price_input = clamp(round(new_price_input, 1), 10, 1000) + sale_price = new_price_input + to_chat(usr, "The cost is now set to [sale_price].") + SStgui.update_uis(src) + return TRUE + . = TRUE +/obj/structure/displaycase/forsale/attackby(obj/item/I, mob/living/user, params) + if(isidcard(I)) + //Card Registration + var/obj/item/card/id/potential_acc = I + if(!potential_acc.registered_account) + to_chat(user, "This ID card has no account registered!") + return + if(payments_acc == potential_acc.registered_account) + playsound(src, 'sound/machines/click.ogg', 20, TRUE) + toggle_lock() + return + if(istype(I, /obj/item/pda)) + return TRUE + SStgui.update_uis(src) + . = ..() + + +/obj/structure/displaycase/forsale/multitool_act(mob/living/user, obj/item/I) + . = ..() + if(obj_integrity <= (integrity_failure * max_integrity)) + to_chat(user, "You start recalibrating [src]'s hover field...") + if(do_after(user, 20, target = src)) + broken = 0 + obj_integrity = max_integrity + update_icon() + return TRUE + +/obj/structure/displaycase/forsale/wrench_act(mob/living/user, obj/item/I) + . = ..() + if(open && user.a_intent == INTENT_HELP ) + if(anchored) + to_chat(user, "You start unsecuring [src]...") + else + to_chat(user, "You start securing [src]...") + if(I.use_tool(src, user, 16, volume=50)) + if(QDELETED(I)) + return + if(anchored) + to_chat(user, "You unsecure [src].") + else + to_chat(user, "You secure [src].") + anchored = !anchored + return + else if(!open && user.a_intent == INTENT_HELP) + to_chat(user, "[src] must be open to move it.") + return + +/obj/structure/displaycase/forsale/emag_act(mob/user) + . = ..() + payments_acc = null + req_access = list() + to_chat(user, "[src]'s card reader fizzles and smokes, and the account owner is reset.") + +/obj/structure/displaycase/forsale/examine(mob/user) + . = ..() + if(showpiece && !open) + . += "[showpiece] is for sale for [sale_price] credits." + if(broken) + . += "[src] is sparking and the hover field generator seems to be overloaded. Use a multitool to fix it." + +/obj/structure/displaycase/forsale/obj_break(damage_flag) + if(!broken && !(flags_1 & NODECONSTRUCT_1)) + broken = TRUE + playsound(src, "shatter", 70, TRUE) + update_icon() + trigger_alarm() //In case it's given an alarm anyway. + +/obj/structure/displaycase/forsale/kitchen + desc = "A display case with an ID-card swiper. Use your ID to purchase the contents. Meant for the bartender and chef." + req_one_access = list(ACCESS_KITCHEN, ACCESS_BAR) +*/ \ No newline at end of file diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index e2298ac6c5..2a441ff0f2 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -67,15 +67,14 @@ to_chat(user, "You put [I] in [src].") update_icon() -/obj/structure/tank_dispenser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "TankDispenser", name, 275, 103, master_ui, state) - ui.open() +/obj/structure/tank_dispenser/ui_state(mob/user) + return GLOB.physical_state -/obj/structure/tank_dispenser/attack_robot(mob/user) - ui_interact(user) +/obj/structure/tank_dispenser/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TankDispenser", name) + ui.open() /obj/structure/tank_dispenser/ui_data(mob/user) var/list/data = list() diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 6aae4955f1..3373278c0c 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -17,7 +17,6 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) layer = ABOVE_OBJ_LAYER //Just above doors pressure_resistance = 4*ONE_ATMOSPHERE anchored = TRUE //initially is 0 for tile smoothing - flags_1 = ON_BORDER_1 max_integrity = 25 var/ini_dir = null var/state = WINDOW_OUT_OF_FRAME @@ -38,7 +37,8 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) var/hitsound = 'sound/effects/Glasshit.ogg' rad_insulation = RAD_VERY_LIGHT_INSULATION rad_flags = RAD_PROTECT_CONTENTS - flags_ricochet = RICOCHET_HARD + flags_1 = ON_BORDER_1|DEFAULT_RICOCHET_1 + flags_ricochet = RICOCHET_HARD ricochet_chance_mod = 0.4 attack_hand_speed = CLICK_CD_MELEE attack_hand_is_action = TRUE diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm index f940761ff8..946401ba18 100644 --- a/code/game/turfs/simulated/floor/fancy_floor.dm +++ b/code/game/turfs/simulated/floor/fancy_floor.dm @@ -26,7 +26,8 @@ /turf/open/floor/wood/screwdriver_act(mob/living/user, obj/item/I) if(..()) return TRUE - return pry_tile(I, user) + . = STOP_ATTACK_PROC_CHAIN + pry_tile(I, user) /turf/open/floor/wood/try_replace_tile(obj/item/stack/tile/T, mob/user, params) if(T.turf_type == type) diff --git a/code/game/turfs/simulated/wall/mineral_walls.dm b/code/game/turfs/simulated/wall/mineral_walls.dm index 4466511acd..ed48c24462 100644 --- a/code/game/turfs/simulated/wall/mineral_walls.dm +++ b/code/game/turfs/simulated/wall/mineral_walls.dm @@ -192,6 +192,7 @@ icon_state = "map-shuttle" explosion_block = 3 flags_1 = CAN_BE_DIRTY_1 | DEFAULT_RICOCHET_1 + flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD sheet_type = /obj/item/stack/sheet/mineral/titanium smooth = SMOOTH_MORE|SMOOTH_DIAGONAL canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium) diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 0623f2b9d2..b020ab87c9 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -6,7 +6,8 @@ icon = 'icons/turf/walls/wall.dmi' icon_state = "wall" explosion_block = 1 - + flags_1 = DEFAULT_RICOCHET_1 + flags_ricochet = RICOCHET_HARD thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall attack_hand_speed = 8 diff --git a/code/modules/NTNet/relays.dm b/code/modules/NTNet/relays.dm index c64e2332cc..8c33dd1eba 100644 --- a/code/modules/NTNet/relays.dm +++ b/code/modules/NTNet/relays.dm @@ -9,8 +9,6 @@ icon_state = "bus" density = TRUE circuit = /obj/item/circuitboard/machine/ntnet_relay - ui_x = 400 - ui_y = 300 var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame. var/enabled = 1 // Set to 0 if the relay was turned off @@ -64,15 +62,12 @@ SSnetworks.station_network.add_log("Quantum relay switched from overload recovery mode to normal operation mode.") ..() -/obj/machinery/ntnet_relay/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - +/obj/machinery/ntnet_relay/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "NtnetRelay", "NTNet Quantum Relay", ui_x, ui_y, master_ui, state) + ui = new(user, src, "NtnetRelay") ui.open() - /obj/machinery/ntnet_relay/ui_data(mob/user) var/list/data = list() data["enabled"] = enabled @@ -81,7 +76,6 @@ data["dos_crashed"] = dos_failure return data - /obj/machinery/ntnet_relay/ui_act(action, params) if(..()) return diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm index 9953fe4bc5..cc59fe10ab 100644 --- a/code/modules/VR/vr_sleeper.dm +++ b/code/modules/VR/vr_sleeper.dm @@ -78,10 +78,15 @@ close_machine(target) ui_interact(user) -/obj/machinery/vr_sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_contained_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/vr_sleeper/ui_state(mob/user) + if(user == occupant) + return GLOB.contained_state + return GLOB.default_state + +/obj/machinery/vr_sleeper/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "VrSleeper", "VR Sleeper", 475, 340, master_ui, state) + ui = new(user, src, "VrSleeper", "VR Sleeper") ui.open() /obj/machinery/vr_sleeper/ui_act(action, params) diff --git a/code/modules/admin/chat_commands.dm b/code/modules/admin/chat_commands.dm index 7664d85b7d..bab74300f5 100644 --- a/code/modules/admin/chat_commands.dm +++ b/code/modules/admin/chat_commands.dm @@ -150,7 +150,7 @@ GLOBAL_LIST(round_end_notifiees) /datum/tgs_chat_command/wheelofsalt/Run(datum/tgs_chat_user/sender, params) var/saltresult = "The wheel of salt [pick("clatters","screams","vibrates","clanks","resonates","groans","moans","squeaks","emits a[pick(" god-forsaken"," lewd"," creepy"," generic","n orgasmic"," demonic")] [pick("airhorn","bike horn","trumpet","clown","latex","vore","dog","laughing")] noise")] as it spins violently... And it seems the salt of the day is the " - var/saltprimarysubject = "[pick("combat","medical","grab","furry","wall","orgasm","cat","ERP","lizard","dog","latex","vision cone","atmospherics","table","chem","vore","dogborg","Skylar Lineman","Mekhi Anderson","Peppermint","rework","cum","dick","cockvore","Medihound","sleeper","belly sleeper","door wires","flightsuit","coder privilege","Developer abuse","ban reason","github self merge","red panda","beret","male catgirl","powergame","hexacrocin","Discord server","Clitadel","Cargonia","Solarian Republic","Main and RP merger","bluespace","salt","chem dispenser theft","Botany","moth","BWOINK","anal vore","stamina","Mason Jakops","mining","noodle","milf","Lavaland","Necropolis","Ashwalker","Chase Redtail","Drew Mint","Pavel Marsk","Joker Amari","Durgit","chaplain","Antag","nanite","Syndicate","Nar-Sie","Ratvar","Cult","maint","Foam-Force","AI","cyborg","ghost","clockwork","cyberpunk","vaporwave","Clown","Leon Beech","Mime","security","research","Megafauna","Bubblegum","Ash Drake","Legion","Colossus","White Shuttle","Changeling","Cowboy","Space Ninja","Poly","Revolutionary","Skyrim","forbidden fruits","xenomorph","blob","Nuclear Operative","crossdressing")]" + var/saltprimarysubject = "[pick("combat","medical","grab","furry","wall","orgasm","cat","ERP","lizard","dog","latex","vision cone","atmospherics","table","chem","vore","dogborg","Skylar Lineman","Mekhi Anderson","Peppermint","rework","cum","dick","cockvore","Medihound","sleeper","belly sleeper","door wires","flightsuit","coder privilege","Developer abuse","ban reason","github self merge","red panda","beret","male catgirl","powergame","hexacrocin removal","Discord server","Clitadel","Cargonia","Solarian Republic","Main and RP merger","bluespace","salt","chem dispenser theft","Botany","moth","BWOINK","anal vore","stamina","Mason Jakops","mining","noodle","milf","Lavaland","Necropolis","Ashwalker","Chase Redtail","Drew Mint","Pavel Marsk","Joker Amari","Durgit","chaplain","Antag","nanite","Syndicate","Nar-Sie","Ratvar","Cult","maint","Foam-Force","AI","cyborg","ghost","clockwork","cyberpunk","vaporwave","Clown","Leon Beech","Mime","security","research","Megafauna","Bubblegum","Ash Drake","Legion","Colossus","White Shuttle","Changeling","Cowboy","Space Ninja","Poly","Revolutionary","Skyrim","forbidden fruits","xenomorph","blob","Nuclear Operative","crossdressing")]" var/saltsecondarysubject = "[pick("rework","changes","r34","ban","removal","addition","leak","proposal","fanart","introduction","tabling","ERP","bikeshedding","crossdressing","sprites","semen keg","argument","theft","nerf","screeching","salt","creampie","lewding","murder","kissing","marriage","replacement","fucking","ship","netflix adaptation","dance","remaster","system","voyeur","decoration","pre-order","bukkake","seduction","worship","gangbang","handholding")]" if(prob(10)) saltresult += "@here for your salt, all day every day" diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 41c3fe5105..13e8877440 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -2184,8 +2184,18 @@ if(!ishuman(H)) to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.") return - - var/obj/item/reagent_containers/food/snacks/cookie/cookie = new(H) + //let's keep it simple + //milk to plasmemes and skeletons, meat to lizards, electricity bars to ethereals, cookies to everyone else + var/cookiealt = /obj/item/reagent_containers/food/snacks/cookie + if(isskeleton(H)) + cookiealt = /obj/item/reagent_containers/food/condiment/milk + else if(isplasmaman(H)) + cookiealt = /obj/item/reagent_containers/food/condiment/milk + else if(isethereal(H)) + cookiealt = /obj/item/reagent_containers/food/snacks/energybar + else if(islizard(H)) + cookiealt = /obj/item/reagent_containers/food/snacks/meat/slab + var/obj/item/cookie = new cookiealt(H) if(H.put_in_hands(cookie)) H.update_inv_hands() else diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 14cf0d7ee2..b93c8dabf1 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -371,8 +371,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) return var/msg = "- AdminHelp marked as IC issue by [usr?.client?.holder?.fakekey? usr.client.holder.fakekey : "an administrator"]! -
" - msg += "Losing is part of the game!
" - msg += "It is also possible that your ahelp is unable to be answered properly, due to events occurring in the round." + msg += "Your ahelp is unable to be answered properly due to events occurring in the round. Your question probably has an IC answer, which means you should deal with it IC!" if(initiator) to_chat(initiator, msg) diff --git a/code/modules/antagonists/abductor/equipment/orderable_gear.dm b/code/modules/antagonists/abductor/equipment/orderable_gear.dm new file mode 100644 index 0000000000..d15e450c99 --- /dev/null +++ b/code/modules/antagonists/abductor/equipment/orderable_gear.dm @@ -0,0 +1,79 @@ +GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear)) + +/datum/abductor_gear + /// Name of the gear + var/name = "Generic Abductor Gear" + /// Description of the gear + var/description = "Generic description." + /// Unique ID of the gear + var/id = "abductor_generic" + /// Credit cost of the gear + var/cost = 1 + /// Build path of the gear itself + var/build_path = null + /// Category of the gear + var/category = "Basic Gear" + +/datum/abductor_gear/agent_helmet + name = "Agent Helmet" + description = "Abduct with style - spiky style. Prevents digital tracking." + id = "agent_helmet" + build_path = /obj/item/clothing/head/helmet/abductor + +/datum/abductor_gear/agent_vest + name = "Agent Vest" + description = "A vest outfitted with advanced stealth technology. It has two modes - combat and stealth." + id = "agent_vest" + build_path = /obj/item/clothing/suit/armor/abductor/vest + +/datum/abductor_gear/radio_silencer + name = "Radio Silencer" + description = "A compact device used to shut down communications equipment." + id = "radio_silencer" + build_path = /obj/item/abductor/silencer + +/datum/abductor_gear/science_tool + name = "Science Tool" + description = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras." + id = "science_tool" + build_path = /obj/item/abductor/gizmo +/* +/datum/abductor_gear/advanced_baton + name = "Advanced Baton" + description = "A quad-mode baton used for incapacitation and restraining of specimens." + id = "advanced_baton" + cost = 2 + build_path = /obj/item/melee/baton/abductor //does not exist? +*/ +/datum/abductor_gear/superlingual_matrix + name = "Superlingual Matrix" + description = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something." + id = "superlingual_matrix" + build_path = /obj/item/organ/tongue/abductor + category = "Advanced Gear" + +/datum/abductor_gear/mental_interface + name = "Mental Interface Device" + description = "A dual-mode tool for directly communicating with sentient brains. It can be used to send a direct message to a target, \ + or to send a command to a test subject with a charged gland." + id = "mental_interface" + cost = 2 + build_path = /obj/item/abductor/mind_device + category = "Advanced Gear" + +/datum/abductor_gear/reagent_synthesizer + name = "Reagent Synthesizer" + description = "Synthesizes a variety of reagents using proto-matter." + id = "reagent_synthesizer" + cost = 2 + build_path = /obj/item/abductor_machine_beacon/chem_dispenser + category = "Advanced Gear" + +/datum/abductor_gear/shrink_ray + name = "Shrink Ray Blaster" + description = "This is a piece of frightening alien tech that enhances the magnetic pull of atoms in a localized space to temporarily make an object shrink. \ + That or it's just space magic. Either way, it shrinks stuff." + id = "shrink_ray" + cost = 2 + build_path = /obj/item/gun/energy/shrink_ray + category = "Advanced Gear" diff --git a/code/modules/antagonists/abductor/machinery/console.dm b/code/modules/antagonists/abductor/machinery/console.dm index 6a981b36dc..f06f4fe68a 100644 --- a/code/modules/antagonists/abductor/machinery/console.dm +++ b/code/modules/antagonists/abductor/machinery/console.dm @@ -23,99 +23,105 @@ var/obj/machinery/abductor/pad/pad var/obj/machinery/computer/camera_advanced/abductor/camera var/list/datum/icon_snapshot/disguises = list() + /// Currently selected gear category + var/selected_cat + /// Dictates if the compact mode of the interface is on or off + var/compact_mode = FALSE + /// Possible gear to be dispensed + var/list/possible_gear /obj/machinery/abductor/console/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) if(!HAS_TRAIT(user, TRAIT_ABDUCTOR_TRAINING) && !HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_TRAINING)) to_chat(user, "You start mashing alien buttons at random!") if(do_after(user,100, target = src)) TeleporterSend() - return - user.set_machine(src) - var/dat = "" - dat += "

Abductsoft 3000

" +/obj/machinery/abductor/console/ui_status(mob/user) + if(!isabductor(user) && !isobserver(user)) + return UI_CLOSE + return ..() + +/obj/machinery/abductor/console/ui_state(mob/user) + return GLOB.physical_state + +/obj/machinery/abductor/console/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AbductorConsole", name) + ui.open() + +/obj/machinery/abductor/console/ui_static_data(mob/user) + var/list/data = list() + data["categories"] = list() + for(var/category in possible_gear) + var/list/cat = list( + "name" = category, + "items" = (category == selected_cat ? list() : null)) + for(var/gear in possible_gear[category]) + var/datum/abductor_gear/AG = possible_gear[category][gear] + cat["items"] += list(list( + "name" = AG.name, + "cost" = AG.cost, + "desc" = AG.description, + )) + data["categories"] += list(cat) + return data + +/obj/machinery/abductor/console/ui_data(mob/user) + var/list/data = list() + data["compactMode"] = compact_mode + data["experiment"] = experiment ? TRUE : FALSE if(experiment) - var/points = experiment.points - var/credits = experiment.credits - dat += "Collected Samples : [points]
" - dat += "Gear Credits: [credits]
" - dat += "Transfer data in exchange for supplies:
" - dat += "Advanced Baton (2 Credits)
" - dat += "Mental Interface Device (2 Credits)
" - dat += "Reagent Synthesizer (2 Credits)
" - dat += "Agent Helmet (1 Credit)
" - dat += "Agent Vest (1 Credit)
" - dat += "Radio Silencer (1 Credit)
" - dat += "Science Tool (1 Credit)
" - dat += "Superlingual Matrix (1 Credit)
" - else - dat += "NO EXPERIMENT MACHINE DETECTED
" - + data["points"] = experiment.points + data["credits"] = experiment.credits + data["pad"] = pad ? TRUE : FALSE if(pad) - dat += "Emergency Teleporter System." - dat += "Consider using primary observation console first." - dat += "Activate Teleporter
" - if(gizmo && gizmo.marked) - dat += "Retrieve Mark
" - else - dat += "Retrieve Mark
" - else - dat += "NO TELEPAD DETECTED
" - + data["gizmo"] = gizmo && gizmo.marked ? TRUE : FALSE + data["vest"] = vest ? TRUE : FALSE if(vest) - dat += "

Agent Vest Mode


" - var/mode = vest.mode - if(mode == VEST_STEALTH) - dat += "Combat" - dat += "Stealth" - else - dat += "Combat" - dat += "Stealth" + data["vest_mode"] = vest.mode + data["vest_lock"] = HAS_TRAIT_FROM(vest, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) + return data - dat+="
" - dat += "Select Agent Vest Disguise
" - dat += "[HAS_TRAIT_FROM(vest, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) ? "Unlock" : "Lock"] Vest
" - else - dat += "NO AGENT VEST DETECTED" - var/datum/browser/popup = new(user, "computer", "Abductor Console", 400, 500) - popup.set_content(dat) - popup.open() - -/obj/machinery/abductor/console/Topic(href, href_list) - if(..()) +/obj/machinery/abductor/console/ui_act(action, list/params) + . = ..() + if(.) return - usr.set_machine(src) - if(href_list["teleporter_send"]) - TeleporterSend() - else if(href_list["teleporter_retrieve"]) - TeleporterRetrieve() - else if(href_list["flip_vest"]) - FlipVest() - else if(href_list["toggle_vest"]) - if(vest) + switch(action) + if("buy") + var/item_name = params["name"] + var/list/buyable_items = list() + for(var/category in possible_gear) + buyable_items += possible_gear[category] + for(var/key in buyable_items) + var/datum/abductor_gear/AG = buyable_items[key] + if(AG.name == item_name) + Dispense(AG.build_path, AG.cost) + return TRUE + if("teleporter_send") + TeleporterSend() + return TRUE + if("teleporter_retrieve") + TeleporterRetrieve() + return TRUE + if("flip_vest") + FlipVest() + return TRUE + if("toggle_vest") + if(!vest) + return vest.toggle_nodrop() - else if(href_list["select_disguise"]) - SelectDisguise() - else if(href_list["dispense"]) - switch(href_list["dispense"]) - if("baton") - Dispense(/obj/item/abductor/baton,cost=2) - if("helmet") - Dispense(/obj/item/clothing/head/helmet/abductor) - if("silencer") - Dispense(/obj/item/abductor/silencer) - if("tool") - Dispense(/obj/item/abductor/gizmo) - if("vest") - Dispense(/obj/item/clothing/suit/armor/abductor/vest) - if("mind_device") - Dispense(/obj/item/abductor/mind_device,cost=2) - if("chem_dispenser") - Dispense(/obj/item/abductor_machine_beacon/chem_dispenser,cost=2) - if("tongue") - Dispense(/obj/item/organ/tongue/abductor) - updateUsrDialog() + return TRUE + if("select_disguise") + SelectDisguise() + return TRUE + if("select") + selected_cat = params["category"] + return TRUE + if("compact_toggle") + compact_mode = !compact_mode + return TRUE /obj/machinery/abductor/console/proc/TeleporterRetrieve() if(pad && gizmo && gizmo.marked) @@ -156,7 +162,6 @@ pad.teleport_target = location to_chat(user, "Location marked as test subject release point.") - /obj/machinery/abductor/console/Initialize(mapload) ..() return INITIALIZE_HINT_LATELOAD diff --git a/code/modules/antagonists/abductor/machinery/dispenser.dm b/code/modules/antagonists/abductor/machinery/dispenser.dm index 90dcf6626f..f5bc0946e6 100644 --- a/code/modules/antagonists/abductor/machinery/dispenser.dm +++ b/code/modules/antagonists/abductor/machinery/dispenser.dm @@ -22,38 +22,44 @@ gland_colors[i] = random_color() amounts[i] = rand(1,5) -/obj/machinery/abductor/gland_dispenser/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) - if(!isabductor(user)) +/obj/machinery/abductor/gland_dispenser/ui_status(mob/user) + if(!isabductor(user) && !isobserver(user)) + return UI_CLOSE + return ..() + +/obj/machinery/abductor/gland_dispenser/ui_state(mob/user) + return GLOB.physical_state + +/obj/machinery/abductor/gland_dispenser/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GlandDispenser", name) + ui.open() + +/obj/machinery/abductor/gland_dispenser/ui_data(mob/user) + var/list/data = list() + data["glands"] = list() + for(var/gland_number=1,gland_number<=gland_colors.len,gland_number++) + var/list/gland_information = list( + "color" = gland_colors[gland_number], + "amount" = amounts[gland_number], + "id" = gland_number, + ) + data["glands"] += list(gland_information) + return data + +/obj/machinery/abductor/gland_dispenser/ui_act(action, list/params) + . = ..() + if(.) return - user.set_machine(src) - var/box_css = {" - "} - var/dat = "" - var/item_count = 0 - for(var/i=1,i<=gland_colors.len,i++) - item_count++ - var/g_color = gland_colors[i] - var/amount = amounts[i] - dat += "[amount]" - if(item_count == 4) // Four boxes per line - dat +="

" - item_count = 0 - var/datum/browser/popup = new(user, "glands", "Gland Dispenser", 200, 200) - popup.add_head_content(box_css) - popup.set_content(dat) - popup.open() - return + + switch(action) + if("dispense") + var/gland_id = text2num(params["gland_id"]) + if(!gland_id) + return + Dispense(gland_id) + return TRUE /obj/machinery/abductor/gland_dispenser/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/organ/heart/gland)) @@ -65,15 +71,6 @@ else return ..() -/obj/machinery/abductor/gland_dispenser/Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - - if(href_list["dispense"]) - Dispense(text2num(href_list["dispense"])) - updateUsrDialog() - /obj/machinery/abductor/gland_dispenser/proc/Dispense(count) if(amounts[count]>0) amounts[count]-- diff --git a/code/modules/antagonists/abductor/machinery/experiment.dm b/code/modules/antagonists/abductor/machinery/experiment.dm index bc58cf45f0..604730cff2 100644 --- a/code/modules/antagonists/abductor/machinery/experiment.dm +++ b/code/modules/antagonists/abductor/machinery/experiment.dm @@ -15,16 +15,13 @@ var/breakout_time = 450 /obj/machinery/abductor/experiment/MouseDrop_T(mob/target, mob/user) - if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target)) + var/mob/living/L = user + if(user.stat || (isliving(user) && (!(L.mobility_flags & MOBILITY_STAND) || !(L.mobility_flags & MOBILITY_UI))) || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target)) return if(isabductor(target)) return close_machine(target) -/obj/machinery/abductor/experiment/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) - - experimentUI(user) - /obj/machinery/abductor/experiment/open_machine() if(!state_open && !panel_open) ..() @@ -46,7 +43,7 @@ /obj/machinery/abductor/experiment/container_resist(mob/living/user) user.visible_message("You see [user] kicking against the door of [src]!", \ "You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)", \ - "You hear a metallic creaking from [src].") + "You hear a metallic creaking from [src].") if(do_after(user,(breakout_time), target = src)) if(!user || user.stat != CONSCIOUS || user.loc != src || state_open) return @@ -54,122 +51,84 @@ "You successfully break out of [src]!") open_machine() -/obj/machinery/abductor/experiment/proc/dissection_icon(mob/living/carbon/human/H) - var/icon/photo = null - var/g = (H.dna.features["body_model"] == FEMALE) ? "f" : "m" - if(H.dna.species.use_skintones) - photo = icon("icon" = 'icons/mob/human.dmi', "icon_state" = "[H.skin_tone]_[g]") - else - photo = icon("icon" = 'icons/mob/human.dmi', "icon_state" = "[H.dna.species.id]_[g]") - photo.Blend("#[H.dna.features["mcolor"]]", ICON_MULTIPLY) +/obj/machinery/abductor/experiment/ui_status(mob/user) + if(user == occupant) + return UI_CLOSE + return ..() - var/icon/eyes - if(EYECOLOR in H.dna.species.species_traits) - eyes = icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "eyes") - eyes.Blend("#[H.eye_color]", ICON_MULTIPLY) +/obj/machinery/abductor/experiment/ui_state(mob/user) + return GLOB.physical_state - var/datum/sprite_accessory/S - S = GLOB.hair_styles_list[H.hair_style] - if(S && (HAIR in H.dna.species.species_traits)) - var/icon/hair = icon("icon" = S.icon, "icon_state" = "[S.icon_state]") - hair.Blend("#[H.hair_color]", ICON_MULTIPLY) - eyes.Blend(hair, ICON_OVERLAY) +/obj/machinery/abductor/experiment/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ProbingConsole", name) + ui.open() - S = GLOB.facial_hair_styles_list[H.facial_hair_style] - if(S && (FACEHAIR in H.dna.species.species_traits)) - var/icon/facial = icon("icon" = S.icon, "icon_state" = "[S.icon_state]") - facial.Blend("#[H.facial_hair_color]", ICON_MULTIPLY) - eyes.Blend(facial, ICON_OVERLAY) - - if(eyes) - photo.Blend(eyes, ICON_OVERLAY) - - var/icon/splat = icon("icon" = 'icons/mob/dam_mob.dmi',"icon_state" = "chest30") - photo.Blend(splat,ICON_OVERLAY) - - return photo - -/obj/machinery/abductor/experiment/proc/experimentUI(mob/user) - var/dat - dat += "

Experiment

" - if(occupant) - var/obj/item/photo/P = new - P.picture = new - P.picture.picture_image = icon(dissection_icon(occupant), dir = SOUTH) - user << browse_rsc(P.picture.picture_image, "dissection_img") - dat += "
" - dat += "" //Avert your eyes - dat += "" - dat += "Probe
" - dat += "Dissect
" - dat += "Analyze
" - dat += "
" - else - dat += "Experiment " - - if(!occupant) - dat += "

Machine Unoccupied

" - else - dat += "

Subject Status :

" - dat += "[occupant.name] => " - var/mob/living/mob_occupant = occupant - switch(mob_occupant.stat) - if(CONSCIOUS) - dat += "Conscious" - if(UNCONSCIOUS) - dat += "Unconscious" - else // DEAD - dat += "Deceased" - dat += "
" - dat += "[flash]" - dat += "
" - dat += "Scan" - dat += "Close" : "open=1'>Open"]" - var/datum/browser/popup = new(user, "experiment", "Probing Console", 300, 300) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.set_content(dat) - popup.open() - -/obj/machinery/abductor/experiment/Topic(href, href_list) - if(..() || usr == occupant) - return - usr.set_machine(src) - if(href_list["refresh"]) - updateUsrDialog() - return - if(href_list["open"]) - open_machine() - return - if(href_list["close"]) - close_machine() - return +/obj/machinery/abductor/experiment/ui_data(mob/user) + var/list/data = list() + data["open"] = state_open + data["feedback"] = flash + data["occupant"] = occupant ? TRUE : FALSE + data["occupant_name"] = null + data["occupant_status"] = null if(occupant) var/mob/living/mob_occupant = occupant - if(mob_occupant.stat != DEAD) - if(href_list["experiment"]) - flash = Experiment(occupant,href_list["experiment"],usr) - updateUsrDialog() - add_fingerprint(usr) + data["occupant_name"] = mob_occupant.name + data["occupant_status"] = mob_occupant.stat + return data -/obj/machinery/abductor/experiment/proc/Experiment(mob/occupant,type,mob/user) +/obj/machinery/abductor/experiment/ui_act(action, list/params) + . = ..() + if(.) + return + + switch(action) + if("door") + if(state_open) + close_machine() + return TRUE + else + open_machine() + return TRUE + if("experiment") + if(!occupant) + return + var/mob/living/mob_occupant = occupant + if(mob_occupant.stat == DEAD) + return + flash = experiment(occupant, params["experiment_type"], usr) + return TRUE + +/** + * experiment: Performs selected experiment on occupant mob, resulting in a point reward on success + * + * Arguments: + * * occupant The mob inside the machine + * * type The type of experiment to be performed + * * user The mob starting the experiment + */ +/obj/machinery/abductor/experiment/proc/experiment(mob/occupant, type, mob/user) LAZYINITLIST(history) var/mob/living/carbon/human/H = occupant var/datum/antagonist/abductor/user_abductor = user.mind.has_antag_datum(/datum/antagonist/abductor) if(!user_abductor) - return "Authorization failure. Contact mothership immidiately." + return "Authorization failure. Contact mothership immediately." var/point_reward = 0 + if(!H) + return "Invalid or missing specimen." if(H in history) - return "Specimen already in database." + return "Specimen already in database." if(H.stat == DEAD) say("Specimen deceased - please provide fresh sample.") - return "Specimen deceased." + return "Specimen deceased." var/obj/item/organ/heart/gland/GlandTest = locate() in H.internal_organs if(!GlandTest) say("Experimental dissection not detected!") - return "No glands detected!" - if(H.mind != null && (H.voluntary_ghosted || (H.ckey != null))) + return "No glands detected!" + if(H.mind != null && H.ckey != null) LAZYINITLIST(abductee_minds) LAZYADD(history, H) LAZYADD(abductee_minds, H.mind) @@ -191,22 +150,27 @@ point_reward++ if(point_reward > 0) open_machine() - SendBack(H) - playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) + send_back(H) + playsound(src.loc, 'sound/machines/ding.ogg', 50, TRUE) points += point_reward credits += point_reward - return "Experiment successful! [point_reward] new data-points collected." + return "Experiment successful! [point_reward] new data-points collected." else - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 1) - return "Experiment failed! No replacement organ detected." + playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, TRUE) + return "Experiment failed! No replacement organ detected." else say("Brain activity nonexistent - disposing sample...") open_machine() - SendBack(H) - return "Specimen braindead - disposed." + send_back(H) + return "Specimen braindead - disposed." - -/obj/machinery/abductor/experiment/proc/SendBack(mob/living/carbon/human/H) +/** + * send_back: Sends a mob back to a selected teleport location if safe + * + * Arguments: + * * H The human mob to be sent back + */ +/obj/machinery/abductor/experiment/proc/send_back(mob/living/carbon/human/H) H.Sleeping(160) H.uncuff() if(console && console.pad && console.pad.teleport_target) @@ -216,7 +180,6 @@ SSjob.SendToLateJoin(H, FALSE) return - /obj/machinery/abductor/experiment/update_icon_state() if(state_open) icon_state = "experiment-open" diff --git a/code/modules/antagonists/blob/blob/blobs/shield.dm b/code/modules/antagonists/blob/blob/blobs/shield.dm index a2a6ce94d3..a3a1403e58 100644 --- a/code/modules/antagonists/blob/blob/blobs/shield.dm +++ b/code/modules/antagonists/blob/blob/blobs/shield.dm @@ -45,11 +45,8 @@ desc = "A solid wall of slightly twitching tendrils with a reflective glow." damaged_desc = "A wall of twitching tendrils with a reflective glow." icon_state = "blob_glow" + flags_ricochet = RICOCHET_SHINY point_return = 8 max_integrity = 100 brute_resist = 1 explosion_block = 2 - -/obj/structure/blob/shield/reflective/check_projectile_ricochet(obj/item/projectile/P) - return PROJECTILE_RICOCHET_FORCE - diff --git a/code/modules/antagonists/changeling/cellular_emporium.dm b/code/modules/antagonists/changeling/cellular_emporium.dm index 6abefeefe7..2538394db7 100644 --- a/code/modules/antagonists/changeling/cellular_emporium.dm +++ b/code/modules/antagonists/changeling/cellular_emporium.dm @@ -13,10 +13,13 @@ changeling = null . = ..() -/datum/cellular_emporium/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/cellular_emporium/ui_state(mob/user) + return GLOB.always_state + +/datum/cellular_emporium/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "CellularEmporium", name, 900, 480, master_ui, state) + ui = new(user, src, "CellularEmporium", name) ui.open() /datum/cellular_emporium/ui_data(mob/user) diff --git a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm index e579023a7a..454870d1e1 100644 --- a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm +++ b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm @@ -405,3 +405,49 @@ animation_number = initial(animation_number) sigil_active = FALSE animate(src, alpha = initial(alpha), time = 10, flags = ANIMATION_END_NOW) + +/obj/effect/clockwork/sigil/rite + name = "radiant sigil" + desc = "A glowing sigil glowing with barely-contained power." + clockwork_desc = "A sigil that will allow you to perform certain rites on it, provided you have access to sufficient power and materials." + icon_state = "sigiltransmission" //am big lazy - recolored transmission sigil + sigil_name = "Sigil of Rites" + alpha = 255 + var/performing_rite = FALSE + color = "#ffe63a" + light_color = "#ffe63a" + light_range = 1 + light_power = 2 + +/obj/effect/clockwork/sigil/rite/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags) + . = ..() + if(.) + return + if(!is_servant_of_ratvar(user)) + return + if(!GLOB.all_clockwork_rites.len) //Did we already generate the list? + generate_all_rites() + if(performing_rite) + to_chat(user, "Someone is already performing a rite here!") + return + var/list/possible_rites = list() + for(var/datum/clockwork_rite/R in GLOB.all_clockwork_rites) + possible_rites[R] = R + var/input_key = input(user, "Choose a rite", "Choosing a rite") as null|anything in possible_rites + if(!input_key) + return + var/datum/clockwork_rite/CR = possible_rites[input_key] + if(!CR) + return + var/choice = alert(user, "What to do with this rite?", "What to do?", "Cast", "Show Info", "Cancel") + switch(choice) + if("Cast") + CR.try_cast(src, user) + if("Show Info") + var/infotext = CR.build_info() + to_chat(user, infotext) + +/obj/effect/clockwork/sigil/rite/proc/generate_all_rites() //The first time someone uses a sigil of rites, all the rites are actually generated. No need to have a bunch of random datums laying around all the time. + for(var/V in subtypesof(/datum/clockwork_rite)) + var/datum/clockwork_rite/R = new V + GLOB.all_clockwork_rites += R diff --git a/code/modules/antagonists/clockcult/clock_helpers/clock_rites.dm b/code/modules/antagonists/clockcult/clock_helpers/clock_rites.dm new file mode 100644 index 0000000000..7dabb18f03 --- /dev/null +++ b/code/modules/antagonists/clockcult/clock_helpers/clock_rites.dm @@ -0,0 +1,196 @@ +//This file is for clock rites, mainly used by the Sigil of Rites in clock_sigils.dm +//The rites themselves are in this file to prevent bloating the other file too much, aswell as for easier access + +#define INFINITE -1 + +//The base clockwork rite. This should never be visible +/datum/clockwork_rite + var/name = "Rite of THE frog" //The name of the rite + var/desc = "This rite is used to summon the legendary frog whose-name-shall-not-be-spoken, ender of many worlds." //What does this rite do? Shown to cultists if they choose 'Show Info' after selecting the rite. + var/list/required_ingredients = list(/obj/item/clockwork) //What does this rite require? + var/power_cost = 0 //How much power does this rite cost.. or does it even add power? + var/requires_human = FALSE //Does the rite require a ../carbon/human on the rune? + var/must_be_servant = TRUE //If the above is true, does the human need to be a servant? + var/target_can_be_invoker = TRUE //Does this rite work if the invoker is also the target? + var/cast_time = 0 //How long does the rite take to cast? + var/limit = INFINITE //How often can this rite be used per round? Set this to INFINITE for unlimited, 0 for disallowed, anything above 0 for a limit + var/times_used = 0 //How often has the rite already been used this shift? + var/rite_cast_sound = 'sound/items/bikehorn.ogg' //The sound played when successfully casting the rite. If it honks, the one adding the rite forgot to set one (or was just lazy). + +/datum/clockwork_rite/proc/try_cast(var/obj/effect/clockwork/sigil/rite/R, var/mob/living/invoker) //Performs a ton of checks to see if the invoker can cast the rite + if(!istype(R)) + return FALSE + if(!R || !R.loc) + return FALSE + var/turf/T = R.loc + if(!T) //Uh oh something is fucky + return FALSE + + if(limit != INFINITE && times_used >= limit) //Is the limit on casts exceeded? + to_chat(invoker, "There are no more uses left for this rite!") + return FALSE + + var/mob/living/carbon/human/H //This is only used if requires_human is TRUE + if(requires_human) //In case this requires a target + for(var/mob/living/carbon/human/possible_H in T) + if((!must_be_servant || is_servant_of_ratvar(possible_H)) && (target_can_be_invoker || invoker != possible_H)) + H = possible_H + break + if(!H) + to_chat(invoker, "There is no target for the rite on the sigil!") + return FALSE + + if(required_ingredients.len) //In case this requires materials + var/is_missing_materials = FALSE + for(var/I in required_ingredients) + var/obj/item/Material = locate(I) in T + if(!Material) + is_missing_materials = TRUE + break + if(is_missing_materials) + var/still_required_string = "" + for(var/i = 1 to required_ingredients.len) + var/obj/O = required_ingredients[i] + if(i != 1) + still_required_string += ", " + still_required_string += "a [initial(O.name)]" + to_chat(invoker, "There are still materials missing for this rite. You require [still_required_string].") + return FALSE + + if(power_cost) //If this costs power + if(!get_clockwork_power(power_cost)) + to_chat(invoker, "There is not enough power for this rite!") + return FALSE + R.performing_rite = TRUE + if(!do_after(invoker, cast_time, target = R)) + to_chat(invoker, "Your rite is disrupted.") + R.performing_rite = FALSE + return FALSE + . = cast(invoker, T, H) + if(!.) + to_chat(invoker, " You fail casting [name]") + post_cast(FALSE) + else + to_chat(invoker, "You successfully cast [name]") + post_cast(TRUE) + R.performing_rite = FALSE + return + +/datum/clockwork_rite/proc/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target) //Casts the rite and uses up ingredients. Doublechecks some things to prevent bypassing some restrictions via funky timing or badminnery. + if(!T || !invoker) + return FALSE + if(requires_human && !target) + return FALSE + if(power_cost && !get_clockwork_power(power_cost)) + return FALSE + adjust_clockwork_power(-power_cost) + if(limit != INFINITE && times_used >= limit) + return FALSE + if(required_ingredients.len) + var/is_missing_materials = FALSE + for(var/I in required_ingredients) + var/obj/item/Material = locate(I) in T + if(!Material) + is_missing_materials = TRUE + break + qdel(Material) + if(is_missing_materials) + return FALSE + playsound(T, rite_cast_sound, 50, 2) + return TRUE + +/datum/clockwork_rite/proc/post_cast(var/cast_succeeded) + if(cast_succeeded) + times_used++ + return TRUE + +/datum/clockwork_rite/proc/build_info() //Constructs the info text of a given rite, based on the vars of the rite + . = "" + . += "This is the [name].\n" + . += "[desc]\n" + . += "It requires: " + if(required_ingredients.len) + var/material_string = "" + for(var/i = 1 to required_ingredients.len) + var/obj/O = required_ingredients[i] + if(i != 1) + material_string += ", " + material_string += "a [initial(O.name)]" + . += "[material_string].\n" + else + . += "no materials.\n" + . += "It [power_cost >= 0 ? "costs" : "generates"] [power_cost ? "[power_cost]" : "no"] power.\n" + . += "It requires [requires_human ? " a human" : " no"] target.\n" + if(requires_human) + . += "The target [must_be_servant ? "cannot be" : "can be"] a nonservant.\n" + . += "The target [target_can_be_invoker ? "can be" : "cannot be"] the invoker.\n" + . += "It requires [cast_time/10] seconds to cast.\n" + . += "It has been used [times_used] time[times_used != 1 ? "s" : ""], out of [limit != INFINITE ? "[limit]" : "infinite"] available uses." + +//Adds a organ or cybernetic implant to a servant without the need for surgery. Cannot be used with brains for.. reasons. +/datum/clockwork_rite/advancement + name = "Rite of Advancement" + desc = "This rite is used to augment a servant with organs or cybernetic implants. The organ of choice, aswell as the servant and the required ingredients must be placed on the sigil for this rite to take place." + required_ingredients = list(/obj/item/assembly/prox_sensor, /obj/item/stock_parts/cell) + power_cost = 500 + requires_human = TRUE + cast_time = 40 + rite_cast_sound = 'sound/magic/blind.ogg' + +/datum/clockwork_rite/advancement/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target) + var/obj/item/organ/O = locate(/obj/item/organ) in T + if(!O) + return FALSE + if(istype(O, /obj/item/organ/brain)) //NOPE + return FALSE + . = ..() + if(!.) + return FALSE + O.Insert(target) + new /obj/effect/temp_visual/ratvar/sigil/transgression(T) + +//Heals all wounds (not damage) on the target, causing toxloss proportional to amount of wounds healed. 10 damage per wound. +/datum/clockwork_rite/treat_wounds + name = "Rite of Woundmending" + desc = "This rite is used to heal wounds of the servant on the rune. It causes toxins damage proportional to the amount of wounds healed. This can be lethal if performed on an critically injured target." + required_ingredients = list(/obj/item/stock_parts/cell, /obj/item/healthanalyzer, /obj/item/reagent_containers/food/drinks/bottle/holyoil) + power_cost = 300 + requires_human = TRUE + must_be_servant = FALSE + target_can_be_invoker = FALSE + cast_time = 80 + rite_cast_sound = 'sound/magic/staff_healing.ogg' + +/datum/clockwork_rite/treat_wounds/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target) + if(!target) + return FALSE + if(!target.all_wounds.len) + to_chat(invoker, "This one does not require mending.") + return FALSE + .= ..() + if(!.) + return FALSE + target.adjustToxLoss(10 * target.all_wounds.len) + QDEL_LIST(target.all_wounds) + to_chat(target, "You feel your wounds heal, but are overcome with deep nausea.") + new /obj/effect/temp_visual/ratvar/sigil/vitality(T) + +//Summons a brass claw implant on the sigil, which can extend a claw that benefits from repeatedly attacking a single target. Can only be cast a limited amount of times. +/datum/clockwork_rite/summon_claw + name = "Rite of the Claw" + desc = "Summons a special arm implant that, when added to a servant's limb, will allow them to extend and retract a claw at will. Don't leave any implants you want to keep on this rune when casting the rite." + required_ingredients = list(/obj/item/stock_parts/cell, /obj/item/organ/cyberimp, /obj/item/assembly/flash) + power_cost = 1000 + cast_time = 60 + limit = 4 + rite_cast_sound = 'sound/magic/clockwork/fellowship_armory.ogg' + +/datum/clockwork_rite/summon_claw/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target) + . = ..() + if(!.) + return FALSE + var/obj/item/organ/cyberimp/arm/clockwork/claw/CL = new /obj/item/organ/cyberimp/arm/clockwork/claw(T) + CL.visible_message("[CL] materialises out of thin air!") + new /obj/effect/temp_visual/ratvar/sigil/transmission(T,2) + +#undef INFINITE diff --git a/code/modules/antagonists/clockcult/clock_items/clock_augments.dm b/code/modules/antagonists/clockcult/clock_items/clock_augments.dm new file mode 100644 index 0000000000..2131aa7160 --- /dev/null +++ b/code/modules/antagonists/clockcult/clock_items/clock_augments.dm @@ -0,0 +1,32 @@ +//This file is for snowflakey clock augmentations and clock-themed cybernetic implants. + +//The base clockie arm implant, which only clock cultist can use unless it is emagged. THIS SHOULD NEVER ACTUALLY EXIST +/obj/item/organ/cyberimp/arm/clockwork + name = "clock-themed arm-mounted implant" + var/clockwork_desc = "According to Ratvar, this really shouldn't exist. Tell Him about this immediately." + syndicate_implant = TRUE + icon_state = "clock_arm_implant" + +/obj/item/organ/cyberimp/arm/clockwork/ui_action_click() + if(is_servant_of_ratvar(owner) || (obj_flags & EMAGGED)) //If you somehow manage to steal a clockie's implant AND have an emag AND manage to get it implanted for yourself, good on ya! + return ..() + to_chat(owner, "The implant refuses to activate..") + +/obj/item/organ/cyberimp/arm/clockwork/examine(mob/user) + if((is_servant_of_ratvar(user) || isobserver(user)) && clockwork_desc) + desc = clockwork_desc + . = ..() + desc = initial(desc) + +/obj/item/organ/cyberimp/arm/clockwork/emag_act() + if(obj_flags & EMAGGED) + return + obj_flags |= EMAGGED + to_chat(usr, "You emag [src], hoping it'll achieve something..") + +//Brass claw implant. Holds the brass claw from brass_claw.dm and can extend / retract it at will. +/obj/item/organ/cyberimp/arm/clockwork/claw + name = "brass claw implant" + desc = "Yikes, the claw attached to this looks pretty darn sharp." + clockwork_desc = "This implant, when added to a servant's arm, allows them to extend and retract a claw at will, though this is mildly painful to do. It will refuse to work for any non-servants." + contents = newlist(/obj/item/clockwork/brass_claw) diff --git a/code/modules/antagonists/clockcult/clock_items/clock_weapons/brass_claw.dm b/code/modules/antagonists/clockcult/clock_items/clock_weapons/brass_claw.dm new file mode 100644 index 0000000000..340f01f6f8 --- /dev/null +++ b/code/modules/antagonists/clockcult/clock_items/clock_weapons/brass_claw.dm @@ -0,0 +1,51 @@ +//Brass claw, an armblade-like weapon used by a clock implant. Stealthy if retracted, very obvious if active. +//Bit weaker than an armblade strength-wise but gains combo on consecutive attacks against the same target, which causes bonus damage + +/obj/item/clockwork/brass_claw + name = "brass claw" + desc = "A very sharp claw made out of brass." + clockwork_desc = "A incredibly sharp claw made out of brass. It is quite effective at crippling enemies, though very obvious when extended.\nGains combo on consecutive attacks against a target, causing bonus damage." + icon_state = "brass_claw" //Codersprite moment + item_state = "brass_claw" + lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi' + righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi' + w_class = WEIGHT_CLASS_HUGE + force = 15 //Doesn't generate vitality like the spear does / has somewhat less damage, but quite good at wounding and gets through armor pretty well. Also gains 2 bonus damage per consecutive attack on the same target + throwforce = 0 //haha yes lets be safe about this + throw_range = 0 + throw_speed = 0 + armour_penetration = 20 + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + sharpness = SHARP_EDGED + wound_bonus = 5 + bare_wound_bonus = 15 + total_mass = TOTAL_MASS_HAND_REPLACEMENT + var/mob/living/last_attacked + var/combo = 0 + var/damage_per_combo = 2 + var/maximum_combo_damage = 18 //33 damage on max stacks. Usually the target will already be dead by then but if they somehow aren't, better to have this capped + +/obj/item/clockwork/brass_claw/Initialize() + . = ..() + AddComponent(/datum/component/butchering, 60, 80) + +/obj/item/clockwork/brass_claw/examine(mob/user) + if(is_servant_of_ratvar(user)) + clockwork_desc += "\nIt has [combo] combo stacks built up against the current target, causing [min(maximum_combo_damage, combo * damage_per_combo)] bonus damage." + . = ..() + clockwork_desc = initial(clockwork_desc) + +/obj/item/clockwork/brass_claw/attack(mob/living/target, mob/living/carbon/human/user) + . = ..() + if(QDELETED(target) || target.anti_magic_check(chargecost = 0) || is_servant_of_ratvar(target)) + return + if(target != last_attacked) //Loses all combat on switching targets + last_attacked = target + combo = 0 + else + if(!iscultist(target)) //Hostile cultists being hit stacks up combo far faster than usual + combo++ + else + combo += 3 + target.adjustBruteLoss(min(maximum_combo_damage, combo * damage_per_combo)) diff --git a/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm b/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm index a2d597e27d..aa69478217 100644 --- a/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm +++ b/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_spear.dm @@ -12,6 +12,8 @@ attack_verb = list("stabbed", "poked", "slashed") hitsound = 'sound/weapons/bladeslice.ogg' w_class = WEIGHT_CLASS_BULKY + block_parry_data = /datum/block_parry_data/ratvarian_spear + item_flags = ITEM_CAN_PARRY var/bonus_burn = 5 /obj/item/clockwork/weapon/ratvarian_spear/ratvar_act() @@ -43,7 +45,7 @@ else if(iscultist(target) || isconstruct(target)) to_chat(target, "Your body flares with agony at [src]'s presence!") bonus_damage *= 3 //total 30 damage on cultists, 50 with ratvar - GLOB.clockwork_vitality += target.adjustFireLoss(bonus_damage) //adds the damage done to existing vitality + GLOB.clockwork_vitality += max(0, target.adjustFireLoss(bonus_damage)) //adds the damage done to existing vitality /obj/item/clockwork/weapon/ratvarian_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) var/turf/T = get_turf(hit_atom) @@ -80,3 +82,15 @@ new /obj/effect/temp_visual/ratvar/spearbreak(T) action.weapon_reset(RATVARIAN_WEAPON_COOLDOWN) +//A very short, very effective parry that counts on you predicting when the enemy will attack. +/datum/block_parry_data/ratvarian_spear + parry_time_windup = 0 //Very good for predicting + parry_time_active = 3 //Very short + parry_time_spindown = 1 + parry_time_perfect = 2 + parry_efficiency_perfect = 110 //Very low leeway for counterattacks... + parry_efficiency_considered_successful = 0.8 + parry_efficiency_to_counterattack = 1 + parry_cooldown = 15 //But also very low cooldown.. + parry_failed_stagger_duration = 2 SECONDS //And relatively small penalties for failing. + parry_failed_clickcd_duration = 1 SECONDS diff --git a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm index 04a8e92bb7..0bae7d3539 100644 --- a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm +++ b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm @@ -25,9 +25,6 @@ /datum/clockwork_scripture/ranged_ability/kindle, /datum/clockwork_scripture/ranged_ability/hateful_manacles) //quickbound scripture, accessed by index var/maximum_quickbound = 5 //how many quickbound scriptures we can have - var/ui_x = 800 - var/ui_z = 420 - var/obj/structure/destructible/clockwork/trap/linking //If we're linking traps together, which ones we're doing /obj/item/clockwork/slab/internal //an internal motor for mobs running scripture @@ -39,6 +36,11 @@ speed_multiplier = 0 no_cost = TRUE +/obj/item/clockwork/slab/debug/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags) + if(!is_servant_of_ratvar(user)) + add_servant_of_ratvar(user) + return ..() + /obj/item/clockwork/slab/traitor var/spent = FALSE @@ -57,11 +59,6 @@ to_chat(user, "[src] falls dark. It appears you weren't worthy.") return ..() -/obj/item/clockwork/slab/debug/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags) - if(!is_servant_of_ratvar(user)) - add_servant_of_ratvar(user) - return ..() - /obj/item/clockwork/slab/cyborg //three scriptures, plus a spear and fabricator clockwork_desc = "A divine link to the Celestial Derelict, allowing for limited recital of scripture." quickbound = list(/datum/clockwork_scripture/ranged_ability/judicial_marker, /datum/clockwork_scripture/ranged_ability/linked_vanguard, \ @@ -145,14 +142,15 @@ /obj/item/clockwork/slab/examine(mob/user) . = ..() - if(is_servant_of_ratvar(user) || isobserver(user)) - if(LAZYLEN(quickbound)) - for(var/i in 1 to quickbound.len) - if(!quickbound[i]) - continue - var/datum/clockwork_scripture/quickbind_slot = quickbound[i] - . += "Quickbind button: [initial(quickbind_slot.name)]." - . += "Available power: [DisplayPower(get_clockwork_power())]." + if(!is_servant_of_ratvar(user) || !isobserver(user)) + return + if(LAZYLEN(quickbound)) + for(var/i in 1 to quickbound.len) + if(!quickbound[i]) + continue + var/datum/clockwork_scripture/quickbind_slot = quickbound[i] + . += "Quickbind button: [initial(quickbind_slot.name)]." + . += "Available power: [DisplayPower(get_clockwork_power())]." //Slab actions; Hierophant, Quickbind /obj/item/clockwork/slab/ui_action_click(mob/user, action) @@ -198,12 +196,6 @@ ui_interact(user) return TRUE -/obj/item/clockwork/slab/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "ClockworkSlab", name, ui_x, ui_z, master_ui, state) - ui.open() - /obj/item/clockwork/slab/proc/recite_scripture(datum/clockwork_scripture/scripture, mob/living/user) if(!scripture || !user || !user.canUseTopic(src) || (!no_cost && !can_recite_scripture(user))) return FALSE @@ -223,27 +215,62 @@ scripture_to_recite.run_scripture() return TRUE +/* + * Gets text for a certain section. "Default" is used for when you first open Recollection. + * Current sections (make sure to update this if you add one: + * Basics + * Terminology + * Components + * Scripture + * Power + * Conversion + * * what - What section? + */ +/obj/item/clockwork/slab/proc/get_recollection(what) //Now DMDOC compliant!* + . = list() + switch(what) //need someone to rewrite info for this. + if("Default") + .["title"] = "Default" + .["info"] = "Hello servant! Currently these categories dosen't work!" + /* + if("Basics") + .["title"] = "Basics" + .["info"] = "# MARKDOWN WITH HTML?" + if("Terminology") + .["title"] = "Terminology" + .["info"] = "# MARKDOWN WITH HTML?" + if("Components") + .["title"] = "Default" + .["info"] = "# MARKDOWN WITH HTML?" + if("Scripture") + .["title"] = "Default" + .["info"] = "# MARKDOWN WITH HTML?" + if("Power") + .["title"] = "Power" + .["info"] = "# MARKDOWN WITH HTML?" + if("Conversion") + .["title"] = "Conversion" + .["info"] = "# MARKDOWN WITH HTML?" + */ + else + return null //error text handled tgui side. should not cause BSOD -//Gets text for a certain section. "Default" is used for when you first open Recollection. -//Current sections (make sure to update this if you add one: -//- Basics -//- Terminology -//- Components -//- Scripture -//- Power -//- Conversion +/obj/item/clockwork/slab/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ClockworkSlab", name) + ui.open() /obj/item/clockwork/slab/ui_data(mob/user) //we display a lot of data via TGUI . = list() .["recollection"] = recollecting .["power"] = DisplayPower(get_clockwork_power()) .["power_unformatted"] = get_clockwork_power() - // .["rec_text"] = recollection() handled TGUI side .["HONOR_RATVAR"] = GLOB.ratvar_awakens .["scripture"] = list() - for(var/s in GLOB.all_scripture) + for(var/s in GLOB.all_scripture) //don't block this, even when ratvar spawns for roundend griff. var/datum/clockwork_scripture/S = GLOB.all_scripture[s] - if(S.tier == SCRIPTURE_PERIPHERAL) //yes, tiers are the tabs. + if(S.tier == SCRIPTURE_PERIPHERAL) // This tier is skiped because this contains basetype stuff continue var/list/data = list() @@ -253,7 +280,7 @@ data["required"] = "([DisplayPower(S.power_cost)][S.special_power_text ? "+ [replacetext(S.special_power_text, "POWERCOST", "[DisplayPower(S.special_power_cost)]")]" : ""])" data["required_unformatted"] = S.power_cost data["type"] = "[S.type]" - data["quickbind"] = S.quickbind //this is if it cant quickbind + data["quickbind"] = S.quickbind //this is if it cant quickbind (bool) data["fontcolor"] = get_component_color_bright(S.primary_component) data["important"] = S.important //italic! @@ -265,10 +292,8 @@ .["rec_binds"] = list() for(var/i in 1 to maximum_quickbound) - if(GLOB.ratvar_awakens) - return if(LAZYLEN(quickbound) < i || !quickbound[i]) - .["rec_binds"] += list(list()) + .["rec_binds"] += list(list()) //a blank json. else var/datum/clockwork_scripture/quickbind_slot = quickbound[i] .["rec_binds"] += list(list( @@ -280,7 +305,11 @@ /obj/item/clockwork/slab/ui_static_data(mob/user) . = list() - .["tier_infos"] = list() + .["tier_infos"] = list() //HEY!! WHEN ADDING NEW TIER, ADD IT HERE + .["tier_infos"][SCRIPTURE_PERIPHERAL] = list( + "requirement" = "Breaking the code DM side. Report to coggerbus if this appears!!", + "ready" = FALSE //just in case. Should NOT exist at all + ) .["tier_infos"][SCRIPTURE_DRIVER] = list( "requirement" = "None, this is already unlocked", "ready" = TRUE //to bold it on JS side, and to say "These scriptures are permanently unlocked." @@ -297,10 +326,10 @@ "requirement" = "Unlock powerful equipment and structures by converting five servants or if [DisplayPower(JUDGEMENT_UNLOCK_THRESHOLD)] of power is reached..", "ready" = SSticker.scripture_states[SCRIPTURE_JUDGEMENT] ) - - // .["selected"] = selected_scripture - generate_all_scripture() - .["recollection_categories"] = GLOB.ratvar_awakens ? list() : list( + .["recollection_categories"] = list() + if(GLOB.ratvar_awakens) + return + .["recollection_categories"] = list( list("name" = "Getting Started", "desc" = "First-time servant? Read this first."), list("name" = "Basics", "desc" = "A primer on how to play as a servant."), list("name" = "Terminology", "desc" = "Common acronyms, words, and terms."), @@ -309,8 +338,9 @@ list("name" = "Power", "desc" = "The power system that certain objects use to function."), list("name" = "Conversion", "desc" = "Converting the crew, cyborgs, and very walls to your cause.") ) - // .["rec_section"]["title"] //this is here if ever we decided to return these back. - // .["rec_section"]["info"]// wall of info for the thing + .["rec_section"] = get_recollection(recollection_category) + generate_all_scripture() + //needs a new place to live, preferably when clockcult unlocks/downgrades a tier. Smart enough to earlyreturn. /obj/item/clockwork/slab/ui_act(action, params) switch(action) @@ -369,4 +399,4 @@ Q.button_icon_state = quickbind_slot.name Q.UpdateButtonIcon() if(isliving(loc)) - Q.Grant(loc) \ No newline at end of file + Q.Grant(loc) diff --git a/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm b/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm index c4b1913832..00c4d73428 100644 --- a/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm +++ b/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm @@ -97,7 +97,7 @@ desc = "Charges your slab with divine energy, allowing you to overwhelm a target with Ratvar's light." invocations = list("Divinity, show them your light!") whispered = TRUE - channel_time = 10 // I think making kindle channel a third of the time less is a good make up for the fact that it silences people for such a little amount of time. + channel_time = 15 // I think making kindle channel a third of the time less is a good make up for the fact that it silences people for such a little amount of time. power_cost = 125 usage_tip = "The light can be used from up to two tiles away. Damage taken will GREATLY REDUCE the stun's duration." tier = SCRIPTURE_DRIVER diff --git a/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm b/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm index d96765d536..8819544928 100644 --- a/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm +++ b/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm @@ -81,6 +81,25 @@ return /obj/effect/clockwork/sigil/vitality/neutered return ..() +//Sigil of Rites: Creates a sigil that allows to perform certain rites on it. More information on these can be found in clock_rites.dm, they usually require power, materials and sometimes a target. +/datum/clockwork_scripture/create_object/sigil_of_rites + descname = "Sigil, Access to rites" + name = "Sigil of Rites" + desc = "Places a sigil that, when interacted with, will allow for a variety of rites to be performed on the sigil. These usually require power cells, clockwork power, and some other components." + invocations = list("Engine, allow us..", ".. to be blessed with your rites.") + channel_time = 80 + power_cost = 1400 + invokers_required = 2 + multiple_invokers_used = TRUE + whispered = TRUE + object_path = /obj/effect/clockwork/sigil/rite + creator_message = "A sigil of Rites appears beneath you. It will allow you to perform certain rites, given sufficient materials and power." + usage_tip = "It may be useful to coordinate to acquire needed materials quickly." + tier = SCRIPTURE_SCRIPT + one_per_tile = TRUE + primary_component = HIEROPHANT_ANSIBLE + sort_priority = 4 + //Judicial Visor: Creates a judicial visor, which can smite an area. /datum/clockwork_scripture/create_object/judicial_visor descname = "Delayed Area Knockdown Glasses" @@ -96,7 +115,7 @@ tier = SCRIPTURE_SCRIPT space_allowed = TRUE primary_component = BELLIGERENT_EYE - sort_priority = 4 + sort_priority = 5 quickbind = TRUE quickbind_desc = "Creates a Judicial Visor, which can smite an area, applying Belligerent and briefly stunning." @@ -115,7 +134,7 @@ tier = SCRIPTURE_SCRIPT space_allowed = TRUE primary_component = VANGUARD_COGWHEEL - sort_priority = 6 + sort_priority = 7 quickbind = TRUE quickbind_desc = "Creates a Ratvarian shield, which can absorb energy from attacks for use in powerful bashes." @@ -131,7 +150,7 @@ usage_tip = "Throwing the spear at a mob will do massive damage and knock them down, but break the spear. You will need to wait for 30 seconds before resummoning it." tier = SCRIPTURE_SCRIPT primary_component = VANGUARD_COGWHEEL - sort_priority = 7 + sort_priority = 8 important = TRUE quickbind = TRUE quickbind_desc = "Permanently binds clockwork armor and a Ratvarian spear to you." @@ -229,7 +248,7 @@ usage_tip = "This is a very effective way to rapidly reinforce a base after an attack." tier = SCRIPTURE_SCRIPT primary_component = VANGUARD_COGWHEEL - sort_priority = 8 + sort_priority = 9 quickbind = TRUE quickbind_desc = "Repairs nearby structures and constructs. Servants wearing clockwork armor will also be healed.
Maximum 10 chants." var/heal_attempts = 4 @@ -342,7 +361,7 @@ usage_tip = "Though it requires you to stand still, this scripture can do massive damage." tier = SCRIPTURE_SCRIPT primary_component = BELLIGERENT_EYE - sort_priority = 5 + sort_priority = 6 quickbind = TRUE quickbind_desc = "Allows you to fire energy rays at target locations.
Maximum 5 chants." var/static/list/nzcrentr_insults = list("You're not very good at aiming.", "You hunt badly.", "What a waste of energy.", "Almost funny to watch.", @@ -391,7 +410,7 @@ usage_tip = "It may be useful to end channelling early if the burning becomes too much to handle.." tier = SCRIPTURE_SCRIPT primary_component = GEIS_CAPACITOR - sort_priority = 10 + sort_priority = 11 quickbind = TRUE quickbind_desc = "Quickly drains power in an area around the invoker, causing burns proportional to the amount of energy drained.
Maximum of 20 chants." diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm index 5c011b6318..a18906b70b 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm @@ -7,17 +7,13 @@ density = TRUE resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - ui_x = 350 - ui_y = 442 - var/timer_set = 90 - var/default_timer_set = 90 var/minimum_timer_set = 90 var/maximum_timer_set = 3600 - ui_style = "nanotrasen" var/numeric_input = "" var/ui_mode = NUKEUI_AWAIT_DISK + var/timing = FALSE var/exploding = FALSE var/exploded = FALSE @@ -34,7 +30,6 @@ var/interior = "" var/proper_bomb = TRUE //Please var/obj/effect/countdown/nuclearbomb/countdown - var/nuclear_cooldown //used to stop global spam. /obj/machinery/nuclearbomb/Initialize() . = ..() @@ -77,15 +72,16 @@ /obj/machinery/nuclearbomb/syndicate/get_cinematic_type(off_station) var/datum/game_mode/nuclear/NM = SSticker.mode switch(off_station) - if(FALSE) + if(0) if(istype(NM) && !NM.nuke_team.syndies_escaped()) return CINEMATIC_ANNIHILATION else return CINEMATIC_NUKE_WIN - if(NUKE_MISS_STATION) + if(1) return CINEMATIC_NUKE_MISS - else + if(2) return CINEMATIC_NUKE_FAR + return CINEMATIC_NUKE_FAR /obj/machinery/nuclearbomb/proc/disk_check(obj/item/disk/nuclear/D) if(D.fake) @@ -194,7 +190,7 @@ icon_state = "nuclearbomb_exploding" /obj/machinery/nuclearbomb/update_overlays() - . = ..() + . += ..() update_icon_interior() update_icon_lights() @@ -236,7 +232,7 @@ explode() else var/volume = (get_time_left() <= 20 ? 30 : 5) - playsound(loc, 'sound/items/timer.ogg', volume, 0) + playsound(loc, 'sound/items/timer.ogg', volume, FALSE) /obj/machinery/nuclearbomb/proc/update_ui_mode() if(exploded) @@ -261,17 +257,18 @@ ui_mode = NUKEUI_AWAIT_TIMER - -/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state=GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/nuclearbomb/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "NuclearBomb", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "NuclearBomb", name) ui.open() /obj/machinery/nuclearbomb/ui_data(mob/user) var/list/data = list() data["disk_present"] = auth + var/hidden_code = (ui_mode == NUKEUI_AWAIT_CODE && numeric_input != "ERROR") + var/current_code = "" if(hidden_code) while(length(current_code) < length(numeric_input)) @@ -388,14 +385,13 @@ if("anchor") if(auth && yes_code) playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE) - set_anchor(usr) + set_anchor() else playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE) - -/obj/machinery/nuclearbomb/proc/set_anchor(mob/user) - if((istype(get_area(src), /area/space) || isinspace()) && !anchored) - to_chat(user, "This is not a suitable platform for anchoring [src]!") +/obj/machinery/nuclearbomb/proc/set_anchor() + if(isinspace() && !anchored) + to_chat(usr, "There is nothing to anchor to!") else anchored = !anchored @@ -416,9 +412,6 @@ if(safety) to_chat(usr, "The safety is still on.") return - if(!timing && nuclear_cooldown > world.time) - to_chat(usr, "[src]'s timer protocols are currently on cooldown, please stand by.") - return timing = !timing if(timing) previous_level = NUM2SECLEVEL(GLOB.security_level) @@ -427,12 +420,6 @@ S.switch_mode_to(TRACK_INFILTRATOR) countdown.start() set_security_level("delta") - nuclear_cooldown = world.time + 15 SECONDS - - if(GLOB.war_declared) - var/area/A = get_area(src) - priority_announce("Alert: Unexpected increase in radiation levels near [A.name] ([src.x],[src.y],[src.z]). Please send an authorized radiation specialist to investigate.", "Sensory Nuclear Indexer Telemetry Calculation Helper") - else detonation_timer = null set_security_level(previous_level) @@ -483,12 +470,19 @@ var/off_station = FALSE var/turf/bomb_location = get_turf(src) - if(!bomb_location || !is_station_level(bomb_location.z)) - off_station = NUKE_MISS_STATION + var/area/A = get_area(bomb_location) + + if(bomb_location && is_station_level(bomb_location.z)) + if(istype(A, /area/space)) + off_station = NUKE_NEAR_MISS + if((bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE))) + off_station = NUKE_NEAR_MISS else if(bomb_location.onSyndieBase()) off_station = NUKE_SYNDICATE_BASE + else + off_station = NUKE_MISS_STATION - if(!off_station) + if(off_station < 2) //can only launch when nuke is on syndie base or space SSshuttle.registerHostileEnvironment(src) SSshuttle.lockdown = TRUE @@ -502,13 +496,13 @@ INVOKE_ASYNC(GLOBAL_PROC,.proc/KillEveryoneOnZLevel, z) /obj/machinery/nuclearbomb/proc/get_cinematic_type(off_station) - if(!off_station) + if(off_station < 2) return CINEMATIC_SELFDESTRUCT else return CINEMATIC_SELFDESTRUCT_MISS /obj/machinery/nuclearbomb/beer - name = "Nanotrasen-brand nuclear fission explosive" + name = "\improper Nanotrasen-brand nuclear fission explosive" desc = "One of the more successful achievements of the Nanotrasen Corporate Warfare Division, their nuclear fission explosives are renowned for being cheap to produce and devastatingly effective. Signs explain that though this particular device has been decommissioned, every Nanotrasen station is equipped with an equivalent one, just in case. All Captains carefully guard the disk needed to detonate them - at least, the sign says they do. There seems to be a tap on the back." proper_bomb = FALSE var/obj/structure/reagent_dispensers/beerkeg/keg @@ -521,9 +515,9 @@ /obj/machinery/nuclearbomb/beer/examine(mob/user) . = ..() if(keg.reagents.total_volume) - . += "It has [keg.reagents.total_volume] unit\s left." + to_chat(user, "It has [keg.reagents.total_volume] unit\s left.") else - . += "It's empty." + to_chat(user, "It's empty.") /obj/machinery/nuclearbomb/beer/attackby(obj/item/W, mob/user, params) if(W.is_refillable()) @@ -535,6 +529,8 @@ return ..() /obj/machinery/nuclearbomb/beer/actually_explode() + //Unblock roundend, we're not actually exploding. + SSticker.roundend_check_paused = FALSE var/turf/bomb_location = get_turf(src) if(!bomb_location) disarm() @@ -583,7 +579,7 @@ This is here to make the tiles around the station mininuke change when it's armed. */ -/obj/machinery/nuclearbomb/selfdestruct/set_anchor(mob/user) +/obj/machinery/nuclearbomb/selfdestruct/set_anchor() return /obj/machinery/nuclearbomb/selfdestruct/set_active() @@ -641,18 +637,19 @@ This is here to make the tiles around the station mininuke change when it's arme if(newturf && lastlocation == newturf) if(last_disk_move < world.time - 5000 && prob((world.time - 5000 - last_disk_move)*0.0001)) var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control - if(istype(loneop)) + if(istype(loneop) && loneop.occurrences < loneop.max_occurrences) loneop.weight += 1 - if(loneop.weight % 5 == 0) + if(loneop.weight % 5 == 0 && SSticker.totalPlayers > 1) //players count now message_admins("[src] is stationary in [ADMIN_VERBOSEJMP(newturf)]. The weight of Lone Operative is now [loneop.weight].") log_game("[src] is stationary for too long in [loc_name(newturf)], and has increased the weight of the Lone Operative event to [loneop.weight].") + else lastlocation = newturf last_disk_move = world.time var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control - if(istype(loneop) && prob(loneop.weight)) + if(istype(loneop) && loneop.occurrences < loneop.max_occurrences && prob(loneop.weight)) loneop.weight = max(loneop.weight - 1, 0) - if(loneop.weight % 5 == 0) + if(loneop.weight % 5 == 0 && SSticker.totalPlayers > 1) message_admins("[src] is on the move (currently in [ADMIN_VERBOSEJMP(newturf)]). The weight of Lone Operative is now [loneop.weight].") log_game("[src] being on the move has reduced the weight of the Lone Operative event to [loneop.weight].") @@ -661,9 +658,19 @@ This is here to make the tiles around the station mininuke change when it's arme if(!fake) return - if(isobserver(user) || HAS_TRAIT(user, TRAIT_DISK_VERIFIER) || (user.mind && HAS_TRAIT(user.mind, TRAIT_DISK_VERIFIER))) + if(isobserver(user) || HAS_TRAIT(user.mind, TRAIT_DISK_VERIFIER)) . += "The serial numbers on [src] are incorrect." +/* + * You can't accidentally eat the nuke disk, bro + */ + /* +/obj/item/disk/nuclear/on_accidental_consumption(mob/living/carbon/M, mob/living/carbon/user, obj/item/source_item, discover_after = TRUE) + M.visible_message("[M] looks like [M.p_theyve()] just bitten into something important.", \ + "Wait, is this the nuke disk?") + + return discover_after +*/ /obj/item/disk/nuclear/attackby(obj/item/I, mob/living/user, params) if(istype(I, /obj/item/claymore/highlander) && !fake) var/obj/item/claymore/highlander/H = I @@ -686,7 +693,7 @@ This is here to make the tiles around the station mininuke change when it's arme /obj/item/disk/nuclear/suicide_act(mob/user) user.visible_message("[user] is going delta! It looks like [user.p_theyre()] trying to commit suicide!") - playsound(src, 'sound/machines/alarm.ogg', 50, -1, 1) + playsound(src, 'sound/machines/alarm.ogg', 50, -1, TRUE) for(var/i in 1 to 100) addtimer(CALLBACK(user, /atom/proc/add_atom_colour, (i % 2)? "#00FF00" : "#FF0000", ADMIN_COLOUR_PRIORITY), i) addtimer(CALLBACK(src, .proc/manual_suicide, user), 101) @@ -694,7 +701,7 @@ This is here to make the tiles around the station mininuke change when it's arme /obj/item/disk/nuclear/proc/manual_suicide(mob/living/user) user.remove_atom_colour(ADMIN_COLOUR_PRIORITY) - user.visible_message("[user] was destroyed by the nuclear blast!") + user.visible_message("[user] is destroyed by the nuclear blast!") user.adjustOxyLoss(200) user.death(0) diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index 21ccd425a3..848fe124be 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -28,6 +28,7 @@ throwforce = 0 blood_volume = 0 has_field_of_vision = FALSE //we are a spoopy ghost + rad_flags = RAD_NO_CONTAMINATE | RAD_PROTECT_CONTENTS see_in_dark = 8 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm index 4c29c53a2e..d4d117f8ee 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook.dm @@ -294,6 +294,11 @@ dat += "[surplus] left.
" return dat +/datum/spellbook_entry/item/timestop_katana + name = "Temporal Katana" + desc = "An oddly-weighted katana, reinforced to allow parrying, with a temporal anomaly magically shoved into it. Successful ripostes prove devastating to those unprepared." + item_path = /obj/item/katana/timestop + /datum/spellbook_entry/item/staffchange name = "Staff of Change" desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself." diff --git a/code/modules/arousal/genitals.dm b/code/modules/arousal/genitals.dm index 4d2e5e6fef..8f88076af9 100644 --- a/code/modules/arousal/genitals.dm +++ b/code/modules/arousal/genitals.dm @@ -232,23 +232,6 @@ /obj/item/organ/genital/proc/get_features(mob/living/carbon/human/H) return - -//procs to handle sprite overlays being applied to humans - -/mob/living/carbon/human/equip_to_slot(obj/item/I, slot) - . = ..() - if(!. && I && slot && !(slot in GLOB.no_genitals_update_slots)) //the item was successfully equipped, and the chosen slot wasn't merely storage, hands or cuffs. - update_genitals() - -/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE) - var/no_update = FALSE - if(!I || I == l_store || I == r_store || I == s_store || I == handcuffed || I == legcuffed || get_held_index_of_item(I)) //stops storages, cuffs and held items from triggering it. - no_update = TRUE - . = ..() - if(!. || no_update) - return - update_genitals() - /mob/living/carbon/human/proc/update_genitals() if(QDELETED(src)) return diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index 6e8e2846a2..899eb12511 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -4,8 +4,6 @@ icon_state = "infrared" custom_materials = list(/datum/material/iron=1000, /datum/material/glass=500) is_position_sensitive = TRUE - var/ui_x = 225 - var/ui_y = 110 var/on = FALSE var/visible = FALSE var/maxlength = 8 @@ -134,7 +132,7 @@ . = ..() setDir(t) -/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE) +/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE) . = ..() olddir = dir @@ -186,11 +184,10 @@ return ..() return UI_CLOSE -/obj/item/assembly/infra/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/assembly/infra/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "InfraredEmitter", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "InfraredEmitter", name) ui.open() /obj/item/assembly/infra/ui_data(mob/user) diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index f0d8f28688..60de1b3e87 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -4,8 +4,6 @@ icon_state = "prox" custom_materials = list(/datum/material/iron=800, /datum/material/glass=200) attachable = TRUE - var/ui_x = 250 - var/ui_y = 185 var/scanning = FALSE var/timing = FALSE var/time = 10 @@ -113,11 +111,10 @@ return ..() return UI_CLOSE -/obj/item/assembly/prox_sensor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/assembly/prox_sensor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "ProximitySensor", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "ProximitySensor", name) ui.open() /obj/item/assembly/prox_sensor/ui_data(mob/user) diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index fe548d662d..e586f97a28 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -8,33 +8,46 @@ custom_materials = list(/datum/material/iron=400, /datum/material/glass=120) wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE attachable = TRUE - var/ui_x = 280 - var/ui_y = 132 + var/code = DEFAULT_SIGNALER_CODE var/frequency = FREQ_SIGNALER var/datum/radio_frequency/radio_connection - var/suicider = null + ///Holds the mind that commited suicide. + var/datum/mind/suicider + ///Holds a reference string to the mob, decides how much of a gamer you are. + var/suicide_mob var/hearing_range = 1 /obj/item/assembly/signaler/suicide_act(mob/living/carbon/user) user.visible_message("[user] eats \the [src]! If it is signaled, [user.p_they()] will die!") playsound(src, 'sound/items/eatfood.ogg', 50, TRUE) - user.transferItemToLoc(src, user, TRUE) - suicider = user + moveToNullspace() + suicider = user.mind + suicide_mob = REF(user) return MANUAL_SUICIDE -/obj/item/assembly/signaler/proc/manual_suicide(mob/living/carbon/user) - user.visible_message("[user]'s \the [src] receives a signal, killing [user.p_them()] instantly!") +/obj/item/assembly/signaler/proc/manual_suicide(datum/mind/suicidee) + var/mob/living/user = suicidee.current + if(!istype(user)) + return + if(suicide_mob == REF(user)) + user.visible_message("[user]'s [src] receives a signal, killing [user.p_them()] instantly!") + else + user.visible_message("[user]'s [src] receives a signal and [user.p_they()] die[user.p_s()] like a gamer!") user.adjustOxyLoss(200)//it sends an electrical pulse to their heart, killing them. or something. user.death(0) + //user.set_suicide(TRUE) + user.suicide_log() + playsound(user, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE) + qdel(src) /obj/item/assembly/signaler/Initialize() . = ..() set_frequency(frequency) - /obj/item/assembly/signaler/Destroy() SSradio.remove_object(src,frequency) + suicider = null . = ..() /obj/item/assembly/signaler/activate() @@ -53,11 +66,10 @@ return ..() return UI_CLOSE -/obj/item/assembly/signaler/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/assembly/signaler/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Signaler", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "Signaler", name) ui.open() /obj/item/assembly/signaler/ui_data(mob/user) @@ -66,12 +78,12 @@ data["code"] = code data["minFrequency"] = MIN_FREE_FREQ data["maxFrequency"] = MAX_FREE_FREQ - return data /obj/item/assembly/signaler/ui_act(action, params) if(..()) return + switch(action) if("signal") INVOKE_ASYNC(src, .proc/signal) @@ -115,9 +127,6 @@ if(usr) GLOB.lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") - - return - /obj/item/assembly/signaler/receive_signal(datum/signal/signal) . = FALSE if(!signal) @@ -128,6 +137,7 @@ return if(suicider) manual_suicide(suicider) + return pulse(TRUE) audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range) for(var/CHM in get_hearers_in_view(hearing_range, src)) @@ -136,7 +146,6 @@ LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE) return TRUE - /obj/item/assembly/signaler/proc/set_frequency(new_frequency) SSradio.remove_object(src, frequency) frequency = new_frequency @@ -165,69 +174,8 @@ return return ..(signal) -// Embedded signaller used in anomalies. -/obj/item/assembly/signaler/anomaly - name = "anomaly core" - desc = "The neutralized core of an anomaly. It'd probably be valuable for research." - icon_state = "anomaly core" - item_state = "electronic" - lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' - resistance_flags = FIRE_PROOF - var/anomaly_type = /obj/effect/anomaly - -/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal) - if(!signal) - return FALSE - if(signal.data["code"] != code) - return FALSE - if(suicider) - manual_suicide(suicider) - for(var/obj/effect/anomaly/A in get_turf(src)) - A.anomalyNeutralize() - return TRUE - -/obj/item/assembly/signaler/anomaly/manual_suicide(mob/living/carbon/user) - user.visible_message("[user]'s [src] is reacting to the radio signal, warping [user.p_their()] body!") - user.suiciding = TRUE - user.suicide_log() - user.gib() - -/obj/item/assembly/signaler/anomaly/attackby(obj/item/I, mob/user, params) - if(I.tool_behaviour == TOOL_ANALYZER) - to_chat(user, "Analyzing... [src]'s stabilized field is fluctuating along frequency [format_frequency(frequency)], code [code].") - ..() - -//Anomaly cores -/obj/item/assembly/signaler/anomaly/pyro - name = "\improper pyroclastic anomaly core" - desc = "The neutralized core of a pyroclastic anomaly. It feels warm to the touch. It'd probably be valuable for research." - icon_state = "pyro core" - anomaly_type = /obj/effect/anomaly/pyro - -/obj/item/assembly/signaler/anomaly/grav - name = "\improper gravitational anomaly core" - desc = "The neutralized core of a gravitational anomaly. It feels much heavier than it looks. It'd probably be valuable for research." - icon_state = "grav core" - anomaly_type = /obj/effect/anomaly/grav - -/obj/item/assembly/signaler/anomaly/flux - name = "\improper flux anomaly core" - desc = "The neutralized core of a flux anomaly. Touching it makes your skin tingle. It'd probably be valuable for research." - icon_state = "flux core" - anomaly_type = /obj/effect/anomaly/flux - -/obj/item/assembly/signaler/anomaly/bluespace - name = "\improper bluespace anomaly core" - desc = "The neutralized core of a bluespace anomaly. It keeps phasing in and out of view. It'd probably be valuable for research." - icon_state = "anomaly core" - anomaly_type = /obj/effect/anomaly/bluespace - -/obj/item/assembly/signaler/anomaly/vortex - name = "\improper vortex anomaly core" - desc = "The neutralized core of a vortex anomaly. It won't sit still, as if some invisible force is acting on it. It'd probably be valuable for research." - icon_state = "vortex core" - anomaly_type = /obj/effect/anomaly/bhole +/obj/item/assembly/signaler/anomaly/attack_self() + return /obj/item/assembly/signaler/cyborg diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm index d0f0e1e593..e06bb22b0b 100644 --- a/code/modules/assembly/timer.dm +++ b/code/modules/assembly/timer.dm @@ -4,8 +4,7 @@ icon_state = "timer" custom_materials = list(/datum/material/iron=500, /datum/material/glass=50) attachable = TRUE - var/ui_x = 275 - var/ui_y = 115 + var/timing = FALSE var/time = 5 var/saved_time = 5 @@ -88,11 +87,10 @@ return ..() return UI_CLOSE -/obj/item/assembly/timer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/assembly/timer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Timer", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "Timer", name) ui.open() /obj/item/assembly/timer/ui_data(mob/user) diff --git a/code/modules/asset_cache/asset_cache.dm b/code/modules/asset_cache/asset_cache.dm index f702bf714e..53a30d4299 100644 --- a/code/modules/asset_cache/asset_cache.dm +++ b/code/modules/asset_cache/asset_cache.dm @@ -94,6 +94,13 @@ Note: If your code uses output() with assets you will need to call asset_flush o var/list/stacktrace = gib_stack_trace() log_asset("WARNING: dupe asset added to the asset cache: [asset_name] existing asset md5: [OACI.md5] new asset md5:[ACI.md5]\n[stacktrace.Join("\n")]") SSassets.cache[asset_name] = ACI + return ACI + +/// Returns the url of the asset, currently this is just its name, here to allow further work cdn'ing assets. +/// Can be given an asset as well, this is just a work around for buggy edge cases where two assets may have the same name, doesn't matter now, but it will when the cdn comes. +/proc/get_asset_url(asset_name, asset = null) + var/datum/asset_cache_item/ACI = SSassets.cache[asset_name] + return ACI?.url //Generated names do not include file extention. //Used mainly for code that deals with assets in a generic way diff --git a/code/modules/asset_cache/asset_cache_item.dm b/code/modules/asset_cache/asset_cache_item.dm index 0e7d44a7ac..e74293c65e 100644 --- a/code/modules/asset_cache/asset_cache_item.dm +++ b/code/modules/asset_cache/asset_cache_item.dm @@ -5,6 +5,7 @@ **/ /datum/asset_cache_item var/name + var/url var/md5 var/resource @@ -18,4 +19,5 @@ CRASH("invalid asset sent to asset cache") debug_world_log("asset cache unexpected success of second fcopy_rsc") src.name = name + url = name resource = file diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm index 2e5881c67f..4ce9dcf6fc 100644 --- a/code/modules/asset_cache/asset_list.dm +++ b/code/modules/asset_cache/asset_list.dm @@ -16,6 +16,9 @@ GLOBAL_LIST_EMPTY(asset_datums) GLOB.asset_datums[type] = src register() +/datum/asset/proc/get_url_mappings() + return list() + /datum/asset/proc/register() return @@ -30,11 +33,19 @@ GLOBAL_LIST_EMPTY(asset_datums) /datum/asset/simple/register() for(var/asset_name in assets) - register_asset(asset_name, assets[asset_name]) + assets[asset_name] = register_asset(asset_name, assets[asset_name]) /datum/asset/simple/send(client) . = send_asset_list(client, assets) +/datum/asset/simple/get_url_mappings() + . = list() + for (var/asset_name in assets) + var/datum/asset_cache_item/ACI = assets[asset_name] + if (!ACI) + continue + .[asset_name] = ACI.url + // For registering or sending multiple others at once /datum/asset/group @@ -50,6 +61,11 @@ GLOBAL_LIST_EMPTY(asset_datums) var/datum/asset/A = get_asset_datum(type) . = A.send(C) || . +/datum/asset/group/get_url_mappings() + . = list() + for(var/type in children) + var/datum/asset/A = get_asset_datum(type) + . += A.get_url_mappings() // spritesheet implementation - coalesces various icons into a single .png file // and uses CSS to select icons out of that file - saves on transferring some @@ -70,7 +86,9 @@ GLOBAL_LIST_EMPTY(asset_datums) if (!name) CRASH("spritesheet [type] cannot register without a name") ensure_stripped() - + for(var/size_id in sizes) + var/size = sizes[size_id] + register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED]) var/res_name = "spritesheet_[name].css" var/fname = "data/spritesheets/[res_name]" fdel(fname) @@ -78,10 +96,6 @@ GLOBAL_LIST_EMPTY(asset_datums) register_asset(res_name, fcopy_rsc(fname)) fdel(fname) - for(var/size_id in sizes) - var/size = sizes[size_id] - register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED]) - /datum/asset/spritesheet/send(client/C) if (!name) return @@ -90,6 +104,15 @@ GLOBAL_LIST_EMPTY(asset_datums) all += "[name]_[size_id].png" . = send_asset_list(C, all) +/datum/asset/spritesheet/get_url_mappings() + if (!name) + return + . = list("spritesheet_[name].css" = get_asset_url("spritesheet_[name].css")) + for(var/size_id in sizes) + .["[name]_[size_id].png"] = get_asset_url("[name]_[size_id].png") + + + /datum/asset/spritesheet/proc/ensure_stripped(sizes_to_strip = sizes) for(var/size_id in sizes_to_strip) var/size = sizes[size_id] @@ -111,7 +134,7 @@ GLOBAL_LIST_EMPTY(asset_datums) for (var/size_id in sizes) var/size = sizes[size_id] var/icon/tiny = size[SPRSZ_ICON] - out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[name]_[size_id].png') no-repeat;}" + out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[get_asset_url("[name]_[size_id].png")]') no-repeat;}" for (var/sprite_id in sprites) var/sprite = sprites[sprite_id] @@ -162,7 +185,10 @@ GLOBAL_LIST_EMPTY(asset_datums) Insert("[prefix][prefix2][icon_state_name]", I, icon_state=icon_state_name, dir=direction) /datum/asset/spritesheet/proc/css_tag() - return {""} + return {""} + +/datum/asset/spritesheet/proc/css_filename() + return get_asset_url("spritesheet_[name].css") /datum/asset/spritesheet/proc/icon_tag(sprite_name) var/sprite = sprites[sprite_name] diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index 57d8e55e07..a8b4c93764 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -6,12 +6,6 @@ "tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css', ) -/datum/asset/group/tgui - children = list( - /datum/asset/simple/tgui, - /datum/asset/simple/fontawesome - ) - /datum/asset/simple/headers assets = list( "alarm_green.gif" = 'icons/program_icons/alarm_green.gif', @@ -78,6 +72,7 @@ "refresh" = 'icons/pda_icons/pda_refresh.png', "scanner" = 'icons/pda_icons/pda_scanner.png', "signaler" = 'icons/pda_icons/pda_signaler.png', + // "skills" = 'icons/pda_icons/pda_skills.png', "status" = 'icons/pda_icons/pda_status.png', "dronephone" = 'icons/pda_icons/pda_dronephone.png', "emoji" = 'icons/pda_icons/pda_emoji.png' @@ -97,6 +92,10 @@ "stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png', "stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png', "stamp-law" = 'icons/stamp_icons/large_stamp-law.png' + // "stamp-chap" = 'icons/stamp_icons/large_stamp-chap.png' + // "stamp-mime" = 'icons/stamp_icons/large_stamp-mime.png', + // "stamp-centcom" = 'icons/stamp_icons/large_stamp-centcom.png', + // "stamp-syndicate" = 'icons/stamp_icons/large_stamp-syndicate.png' ) @@ -147,7 +146,6 @@ "jquery.min.js" = 'code/modules/goonchat/browserassets/js/jquery.min.js', ) - /datum/asset/simple/goonchat assets = list( "json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js', @@ -375,7 +373,24 @@ "ghost.png" = 'html/ghost.png' ) +/datum/asset/simple/vv assets = list( - "ghost.png" = 'html/ghost.png' + "view_variables.css" = 'html/admin/view_variables.css' ) +/datum/asset/spritesheet/sheetmaterials + name = "sheetmaterials" + +/datum/asset/spritesheet/sheetmaterials/register() + InsertAll("", 'icons/obj/stack_objects.dmi') + + // Special case to handle Bluespace Crystals + Insert("polycrystal", 'icons/obj/telescience.dmi', "polycrystal") + ..() + +/datum/asset/spritesheet/mafia + name = "mafia" + +/datum/asset/spritesheet/mafia/register() + InsertAll("", 'icons/obj/mafia.dmi') + ..() diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm index 6b89deeb80..8316b29a24 100644 --- a/code/modules/atmospherics/machinery/airalarm.dm +++ b/code/modules/atmospherics/machinery/airalarm.dm @@ -237,11 +237,10 @@ return ..() return UI_CLOSE -/obj/machinery/airalarm/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/airalarm/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AirAlarm", name, 440, 650, master_ui, state) + ui = new(user, src, "AirAlarm", name) ui.open() /obj/machinery/airalarm/ui_data(mob/user) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm index 9e1908a500..00a085c31b 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm @@ -26,9 +26,6 @@ Passive gate is similar to the regular pump except: construction_type = /obj/item/pipe/directional pipe_state = "passivegate" - ui_x = 335 - ui_y = 115 - /obj/machinery/atmospherics/components/binary/passive_gate/CtrlClick(mob/user) if(can_interact(user)) on = !on @@ -102,11 +99,10 @@ Passive gate is similar to the regular pump except: )) radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) -/obj/machinery/atmospherics/components/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/components/binary/passive_gate/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "AtmosPump", name) ui.open() /obj/machinery/atmospherics/components/binary/passive_gate/ui_data() diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm index d41df83e12..eb00e432b7 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm @@ -107,11 +107,10 @@ )) radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) -/obj/machinery/atmospherics/components/binary/pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/components/binary/pump/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AtmosPump", name, 335, 115, master_ui, state) + ui = new(user, src, "AtmosPump", name) ui.open() /obj/machinery/atmospherics/components/binary/pump/ui_data() diff --git a/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm index 7bdd22cbd1..d0b663e4ad 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm @@ -60,15 +60,14 @@ else if(!opened && our_pressure >= open_pressure) open() -/obj/machinery/atmospherics/components/binary/relief_valve/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/components/binary/relief_valve/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "atmos_relief", name, 335, 115, master_ui, state) + ui = new(user, src, "AtmosRelief", name) ui.open() /obj/machinery/atmospherics/components/binary/relief_valve/ui_data() - var/data = list() + var/list/data = list() data["open_pressure"] = round(open_pressure) data["close_pressure"] = round(close_pressure) data["max_pressure"] = round(50*ONE_ATMOSPHERE) @@ -79,11 +78,11 @@ return switch(action) if("open_pressure") - var/pressure = params["open_pressure"] + var/pressure = params["pressure"] if(pressure == "max") pressure = 50*ONE_ATMOSPHERE . = TRUE - else if(pressure == "input") + else if(pressure == "input") // The manual expirience. pressure = input("New output pressure ([close_pressure]-[50*ONE_ATMOSPHERE] kPa):", name, open_pressure) as num|null if(!isnull(pressure) && !..()) . = TRUE @@ -94,7 +93,7 @@ open_pressure = clamp(pressure, close_pressure, 50*ONE_ATMOSPHERE) investigate_log("open pressure was set to [open_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS) if("close_pressure") - var/pressure = params["close_pressure"] + var/pressure = params["pressure"] if(pressure == "max") pressure = open_pressure . = TRUE diff --git a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm index d95e4ec123..1b049322a1 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm @@ -92,11 +92,10 @@ )) radio_connection.post_signal(src, signal) -/obj/machinery/atmospherics/components/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/components/binary/volume_pump/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AtmosPump", name, 310, 115, master_ui, state) + ui = new(user, src, "AtmosPump", name) ui.open() /obj/machinery/atmospherics/components/binary/volume_pump/ui_data() diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm index dd6952dfc6..ffab6a885c 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm @@ -133,11 +133,10 @@ set_frequency(frequency) return ..() -/obj/machinery/atmospherics/components/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/components/trinary/filter/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AtmosFilter", name, 475, 185, master_ui, state) + ui = new(user, src, "AtmosFilter", name) ui.open() /obj/machinery/atmospherics/components/trinary/filter/ui_data() diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm index 758d27ab61..7dac6d540e 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm @@ -14,9 +14,6 @@ construction_type = /obj/item/pipe/trinary/flippable pipe_state = "mixer" - ui_x = 370 - ui_y = 165 - //node 3 is the outlet, nodes 1 & 2 are intakes /obj/machinery/atmospherics/components/trinary/mixer/CtrlClick(mob/user) @@ -127,11 +124,10 @@ var/datum/pipeline/parent3 = parents[3] parent3.update = TRUE -/obj/machinery/atmospherics/components/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/components/trinary/mixer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AtmosMixer", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "AtmosMixer", name) ui.open() /obj/machinery/atmospherics/components/trinary/mixer/ui_data() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index 0f849af0e3..c08eaf2e8a 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -332,11 +332,13 @@ return return ..() -/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/components/unary/cryo_cell/ui_state(mob/user) + return GLOB.notcontained_state + +/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Cryo", name, 400, 550, master_ui, state) + ui = new(user, src, "Cryo", name) ui.open() /obj/machinery/atmospherics/components/unary/cryo_cell/ui_data() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm index 70672418a1..a07f131d62 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm @@ -20,9 +20,6 @@ pipe_state = "injector" - ui_x = 310 - ui_y = 115 - /obj/machinery/atmospherics/components/unary/outlet_injector/CtrlClick(mob/user) if(can_interact(user)) on = !on @@ -132,7 +129,7 @@ on = !on if("inject" in signal.data) - spawn inject() + INVOKE_ASYNC(src, .proc/inject) return if("set_volume_rate" in signal.data) @@ -140,22 +137,16 @@ var/datum/gas_mixture/air_contents = airs[1] volume_rate = clamp(number, 0, air_contents.return_volume()) - if("status" in signal.data) - spawn(2) - broadcast_status() - return //do not update_icon + addtimer(CALLBACK(src, .proc/broadcast_status), 2) - spawn(2) - broadcast_status() - - update_icon() + if(!("status" in signal.data)) //do not update_icon + update_icon() -/obj/machinery/atmospherics/components/unary/outlet_injector/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/components/unary/outlet_injector/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "AtmosPump", name) ui.open() /obj/machinery/atmospherics/components/unary/outlet_injector/ui_data() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm b/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm index 0893d2b9e6..f0d0d1d856 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm @@ -61,15 +61,14 @@ update_parents() -/obj/machinery/atmospherics/components/unary/relief_valve/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/components/unary/relief_valve/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "atmos_relief", name, 335, 115, master_ui, state) + ui = new(user, src, "AtmosRelief", name) ui.open() /obj/machinery/atmospherics/components/unary/relief_valve/ui_data() - var/data = list() + var/list/data = list() data["open_pressure"] = round(open_pressure) data["close_pressure"] = round(close_pressure) data["max_pressure"] = round(50*ONE_ATMOSPHERE) @@ -80,7 +79,7 @@ return switch(action) if("open_pressure") - var/pressure = params["open_pressure"] + var/pressure = params["pressure"] if(pressure == "max") pressure = 50*ONE_ATMOSPHERE . = TRUE @@ -95,7 +94,7 @@ open_pressure = clamp(pressure, close_pressure, 50*ONE_ATMOSPHERE) investigate_log("open pressure was set to [open_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS) if("close_pressure") - var/pressure = params["close_pressure"] + var/pressure = params["pressure"] if(pressure == "max") pressure = open_pressure . = TRUE diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm index c6168a6c9c..8456c0b346 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm @@ -11,8 +11,6 @@ layer = OBJ_LAYER plane = GAME_PLANE circuit = /obj/item/circuitboard/machine/thermomachine - ui_x = 300 - ui_y = 230 pipe_flags = PIPING_ONE_PER_TURF @@ -125,11 +123,10 @@ return ..() return UI_CLOSE -/obj/machinery/atmospherics/components/unary/thermomachine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/components/unary/thermomachine/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "ThermoMachine", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "ThermoMachine", name) ui.open() /obj/machinery/atmospherics/components/unary/thermomachine/ui_data(mob/user) diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index 12ba7e79b9..c66aabd57f 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -5,8 +5,6 @@ desc = "A canister for the storage of gas." icon_state = "yellow" density = TRUE - ui_x = 300 - ui_y = 232 var/valve_open = FALSE var/obj/machinery/atmospherics/components/binary/passive_gate/pump @@ -319,11 +317,13 @@ obj/machinery/portable_atmospherics/canister/welder_act(mob/living/user, obj/ite air_update_turf() // Update the environment if needed. update_icon() -/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/portable_atmospherics/canister/ui_state(mob/user) + return GLOB.physical_state + +/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Canister", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "Canister", name) ui.open() /obj/machinery/portable_atmospherics/canister/ui_data() diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm index 7f1dc4d20f..bdde8f0f22 100644 --- a/code/modules/atmospherics/machinery/portable/pump.dm +++ b/code/modules/atmospherics/machinery/portable/pump.dm @@ -8,8 +8,6 @@ name = "portable air pump" icon_state = "psiphon:0" density = TRUE - ui_x = 300 - ui_y = 315 var/on = FALSE var/direction = PUMP_OUT @@ -83,11 +81,10 @@ investigate_log("[key_name(user)] started a transfer into [holding].", INVESTIGATE_ATMOS) -/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "PortablePump", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "PortablePump", name) ui.open() /obj/machinery/portable_atmospherics/pump/ui_data() diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm index bc703b8bbd..7976ba641a 100644 --- a/code/modules/atmospherics/machinery/portable/scrubber.dm +++ b/code/modules/atmospherics/machinery/portable/scrubber.dm @@ -62,11 +62,10 @@ on = !on update_icon() -/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "PortableScrubber", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "PortableScrubber", name) ui.open() /obj/machinery/portable_atmospherics/scrubber/ui_data() diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index 157fdb7bc7..8476090f75 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -277,11 +277,10 @@ GLOBAL_LIST_EMPTY(gateway_destinations) . = ..() try_to_linkup() -/obj/machinery/computer/gateway_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui, force_open, datum/tgui/master_ui, datum/ui_state/state = GLOB.default_state) - . = ..() - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/gateway_control/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Gateway", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "Gateway", name) ui.open() /obj/machinery/computer/gateway_control/ui_data(mob/user) diff --git a/code/modules/cargo/bounty_console.dm b/code/modules/cargo/bounty_console.dm index f499c38090..8a29715016 100644 --- a/code/modules/cargo/bounty_console.dm +++ b/code/modules/cargo/bounty_console.dm @@ -1,18 +1,18 @@ #define PRINTER_TIMEOUT 10 - - /obj/machinery/computer/bounty - name = "Nanotrasen bounty console" + name = "\improper Nanotrasen bounty console" desc = "Used to check and claim bounties offered by Nanotrasen" icon_screen = "bounty" circuit = /obj/item/circuitboard/computer/bounty light_color = "#E2853D"//orange var/printer_ready = 0 //cooldown var + var/static/datum/bank_account/cargocash /obj/machinery/computer/bounty/Initialize() . = ..() printer_ready = world.time + PRINTER_TIMEOUT + cargocash = SSeconomy.get_dep_account(ACCOUNT_CAR) /obj/machinery/computer/bounty/proc/print_paper() new /obj/item/paper/bounty_printout(loc) @@ -23,70 +23,43 @@ /obj/item/paper/bounty_printout/Initialize() . = ..() info = "

Nanotrasen Cargo Bounties


" + update_icon() + for(var/datum/bounty/B in GLOB.bounties_list) if(B.claimed) continue info += {"

[B.name]

-
  • Reward: [B.reward_string()]
  • -
  • Completed: [B.completion_string()]
"} - -/obj/machinery/computer/bounty/ui_interact(mob/user) - . = ..() +
  • Reward: [B.reward_string()]
  • +
  • Completed: [B.completion_string()]
"} +/obj/machinery/computer/bounty/ui_interact(mob/user, datum/tgui/ui) if(!GLOB.bounties_list.len) setup_bounties() - var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR) - var/list/dat = list({"Refresh - Print Paper -

Credits: [D.account_balance]

- - "}) - for(var/datum/bounty/B in GLOB.bounties_list) - if(B.claimed) - dat += "" - else if(B.can_claim()) - dat += "" - else - dat += "" - if(B.high_priority) - dat += {" - - "} - else - dat += {" - - "} - dat += "" - if(B.claimed) - dat += "" - else if(B.can_claim()) - dat += "" - else - dat += "" - dat += "" - dat += "
NameDescriptionRewardCompletionStatus
[B.name]High Priority: [B.description][B.reward_string()][B.name][B.description][B.reward_string()][B.completion_string()]ClaimedClaimUnclaimed
" - dat = dat.Join() - var/datum/browser/popup = new(user, "bounties", "Nanotrasen Bounties", 700, 600) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "CargoBountyConsole", name) + ui.open() -/obj/machinery/computer/bounty/Topic(href, href_list) +/obj/machinery/computer/bounty/ui_data(mob/user) + var/list/data = list() + var/list/bountyinfo = list() + for(var/datum/bounty/B in GLOB.bounties_list) + bountyinfo += list(list("name" = B.name, "description" = B.description, "reward_string" = B.reward_string(), "completion_string" = B.completion_string() , "claimed" = B.claimed, "can_claim" = B.can_claim(), "priority" = B.high_priority, "bounty_ref" = REF(B))) + data["stored_cash"] = cargocash.account_balance + data["bountydata"] = bountyinfo + return data + +/obj/machinery/computer/bounty/ui_act(action,params) if(..()) return - - switch(href_list["choice"]) + switch(action) + if("ClaimBounty") + var/datum/bounty/cashmoney = locate(params["bounty"]) in GLOB.bounties_list + if(cashmoney) + cashmoney.claim() + return TRUE if("Print") if(printer_ready < world.time) printer_ready = world.time + PRINTER_TIMEOUT print_paper() - - if("Claim") - var/datum/bounty/B = locate(href_list["d_rec"]) - if(B in GLOB.bounties_list) - B.claim() - - if(href_list["refresh"]) - playsound(src, "terminal_type", 25, 0) - - updateUsrDialog() + return diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm index f33ea7059b..b7eac1e591 100644 --- a/code/modules/cargo/centcom_podlauncher.dm +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -11,7 +11,7 @@ /client/proc/centcom_podlauncher() //Creates a verb for admins to open up the ui set name = "Config/Launch Supplypod" - set desc = "Configure and launch a Centcom supplypod full of whatever your heart desires!" + set desc = "Configure and launch a CentCom supplypod full of whatever your heart desires!" set category = "Admin" var/datum/centcom_podlauncher/plaunch = new(usr)//create the datum plaunch.ui_interact(usr)//datum has a tgui component, here we open the window @@ -23,7 +23,10 @@ var/turf/oldTurf //Keeps track of where the user was at if they use the "teleport to centcom" button, so they can go back var/client/holder //client of whoever is using this datum var/area/bay //What bay we're using to launch shit from. + var/turf/dropoff_turf //If we're reversing, where the reverse pods go + var/picking_dropoff_turf var/launchClone = FALSE //If true, then we don't actually launch the thing in the bay. Instead we call duplicateObject() and send the result + var/launchRandomItem = FALSE //If true, lauches a single random item instead of everything on a turf. var/launchChoice = 1 //Determines if we launch all at once (0) , in order (1), or at random(2) var/explosionChoice = 0 //Determines if there is no explosion (0), custom explosion (1), or just do a maxcap (2) var/damageChoice = 0 //Determines if we do no damage (0), custom amnt of damage (1), or gib + 5000dmg (2) @@ -50,20 +53,25 @@ temp_pod = new(locate(/area/centcom/supplypod/podStorage) in GLOB.sortedAreas) //Create a new temp_pod in the podStorage area on centcom (so users are free to look at it and change other variables if needed) orderedArea = createOrderedArea(bay) //Order all the turfs in the selected bay (top left to bottom right) to a single list. Used for the "ordered" mode (launchChoice = 1) -/datum/centcom_podlauncher/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, \ -force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)//ui_interact is called when the client verb is called. +/datum/centcom_podlauncher/ui_state(mob/user) + return GLOB.admin_state - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/centcom_podlauncher/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "CentcomPodLauncher", "Config/Launch Supplypod", 700, 700, master_ui, state) + ui = new(user, src, "CentcomPodLauncher") ui.open() /datum/centcom_podlauncher/ui_data(mob/user) //Sends info about the pod to the UI. var/list/data = list() //*****NOTE*****: Many of these comments are similarly described in supplypod.dm. If you change them here, please consider doing so in the supplypod code as well! - var/B = (istype(bay, /area/centcom/supplypod/loading/one)) ? 1 : (istype(bay, /area/centcom/supplypod/loading/two)) ? 2 : (istype(bay, /area/centcom/supplypod/loading/three)) ? 3 : (istype(bay, /area/centcom/supplypod/loading/four)) ? 4 : 0 //top ten THICCEST FUCKING TERNARY CONDITIONALS OF 2036 - data["bay"] = B //Holds the current bay the user is launching objects from. Bays are specific rooms on the centcom map. + var/B = (istype(bay, /area/centcom/supplypod/loading/one)) ? 1 : (istype(bay, /area/centcom/supplypod/loading/two)) ? 2 : (istype(bay, /area/centcom/supplypod/loading/three)) ? 3 : (istype(bay, /area/centcom/supplypod/loading/four)) ? 4 : 0 //(istype(bay, /area/centcom/supplypod/loading/ert)) ? 5 : 0 //top ten THICCEST FUCKING TERNARY CONDITIONALS OF 2036 + data["bay"] = bay //Holds the current bay the user is launching objects from. Bays are specific rooms on the centcom map. + data["bayNumber"] = B //Holds the bay as a number. Useful for comparisons in centcom_podlauncher.ract data["oldArea"] = (oldTurf ? get_area(oldTurf) : null) //Holds the name of the area that the user was in before using the teleportCentcom action + data["picking_dropoff_turf"] = picking_dropoff_turf //If we're picking or have picked a dropoff turf. Only works when pod is in reverse mode + data["dropoff_turf"] = dropoff_turf //The turf that reverse pods will drop their newly acquired cargo off at data["launchClone"] = launchClone //Do we launch the actual items in the bay or just launch clones of them? + data["launchRandomItem"] = launchRandomItem //Do we launch a single random item instead of everything on the turf? data["launchChoice"] = launchChoice //Launch turfs all at once (0), ordered (1), or randomly(1) data["explosionChoice"] = explosionChoice //An explosion that occurs when landing. Can be no explosion (0), custom explosion (1), or maxcap (2) data["damageChoice"] = damageChoice //Damage that occurs to any mob under the pod when it lands. Can be no damage (0), custom damage (1), or gib+5000dmg (2) @@ -72,11 +80,12 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm data["openingDelay"] = temp_pod.openingDelay //How long the pod takes to open after landing data["departureDelay"] = temp_pod.departureDelay //How long the pod takes to leave after opening (if bluespace=true, it deletes. if reversing=true, it flies back to centcom) data["styleChoice"] = temp_pod.style //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the POD_STYLES list in cargo.dm defines to get the proper icon/name/desc for the pod. + data["effectShrapnel"] = FALSE //temp_pod.effectShrapnel //If true, creates a cloud of shrapnel of a decided type and magnitude on landing data["effectStun"] = temp_pod.effectStun //If true, stuns anyone under the pod when it launches until it lands, forcing them to get hit by the pod. Devilish! data["effectLimb"] = temp_pod.effectLimb //If true, pops off a limb (if applicable) from anyone caught under the pod when it lands data["effectOrgans"] = temp_pod.effectOrgans //If true, yeets the organs out of any bodies caught under the pod when it lands data["effectBluespace"] = temp_pod.bluespace //If true, the pod deletes (in a shower of sparks) after landing - data["effectStealth"] = temp_pod.effectStealth //If true, a target icon isnt displayed on the turf where the pod will land + data["effectStealth"] = temp_pod.effectStealth //If true, a target icon isn't displayed on the turf where the pod will land data["effectQuiet"] = temp_pod.effectQuiet //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc) data["effectMissile"] = temp_pod.effectMissile //If true, the pod deletes the second it lands. If you give it an explosion, it will act like a missile exploding as it hits the ground data["effectCircle"] = temp_pod.effectCircle //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here @@ -115,20 +124,41 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm bay = locate(/area/centcom/supplypod/loading/four) in GLOB.sortedAreas refreshBay() . = TRUE + if("bay5") + to_chat(usr, "LetterN is lazy and didin't bother porting this new cc area!") + return + // bay = locate(/area/centcom/supplypod/loading/ert) in GLOB.sortedAreas + // refreshBay() + // . = TRUE + if("pickDropoffTurf") //Enters a mode that lets you pick the dropoff location for reverse pods + if (picking_dropoff_turf) + picking_dropoff_turf = FALSE + updateCursor(FALSE, FALSE) //Update the cursor of the user to a cool looking target icon + return + if (launcherActivated) + launcherActivated = FALSE //We don't want to have launch mode enabled while we're picking a turf + picking_dropoff_turf = TRUE + updateCursor(FALSE, TRUE) //Update the cursor of the user to a cool looking target icon + . = TRUE + if("clearDropoffTurf") + picking_dropoff_turf = FALSE + dropoff_turf = null + updateCursor(FALSE, FALSE) + . = TRUE if("teleportCentcom") //Teleports the user to the centcom supply loading facility. var/mob/M = holder.mob //We teleport whatever mob the client is attached to at the point of clicking oldTurf = get_turf(M) //Used for the "teleportBack" action - var/area/A = locate(/area/centcom/supplypod/loading) in GLOB.sortedAreas + var/area/A = locate(bay) in GLOB.sortedAreas var/list/turfs = list() for(var/turf/T in A) turfs.Add(T) //Fill a list with turfs in the area - var/turf/T = safepick(turfs) //Only teleport if the list isn't empty - if(!T) //If the list is empty, error and cancel + if (!length(turfs)) //If the list is empty, error and cancel to_chat(M, "Nowhere to jump to!") - return + return //Only teleport if the list isn't empty + var/turf/T = pick(turfs) M.forceMove(T) //Perform the actual teleport - log_admin("[key_name(usr)] jumped to [AREACOORD(A)]") - message_admins("[key_name_admin(usr)] jumped to [AREACOORD(A)]") + log_admin("[key_name(usr)] jumped to [AREACOORD(T)]") + message_admins("[key_name_admin(usr)] jumped to [AREACOORD(T)]") . = TRUE if("teleportBack") //After teleporting to centcom, this button allows the user to teleport to the last spot they were at. var/mob/M = holder.mob @@ -144,6 +174,9 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if("launchClone") //Toggles the launchClone var. See variable declarations above for what this specifically means launchClone = !launchClone . = TRUE + if("launchRandomItem") //Pick random turfs from the supplypod bay at centcom to launch + launchRandomItem = !launchRandomItem + . = TRUE if("launchOrdered") //Launch turfs (from the orderedArea list) one at a time in order, from the supplypod bay at centcom if (launchChoice == 1) //launchChoice 1 represents ordered. If we push "ordered" and it already is, then we go to default value launchChoice = 0 @@ -152,7 +185,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm launchChoice = 1 updateSelector() . = TRUE - if("launchRandom") //Pick random turfs from the supplypod bay at centcom to launch + if("launchRandomTurf") //Pick random turfs from the supplypod bay at centcom to launch if (launchChoice == 2) launchChoice = 0 updateSelector() @@ -170,11 +203,11 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm var/list/expNames = list("Devastation", "Heavy Damage", "Light Damage", "Flame") //Explosions have a range of different types of damage var/list/boomInput = list() for (var/i=1 to expNames.len) //Gather input from the user for the value of each type of damage - boomInput.Add(input("[expNames[i]] Range", "Enter the [expNames[i]] range of the explosion. WARNING: This ignores the bomb cap!", 0) as null|num) + boomInput.Add(input("Enter the [expNames[i]] range of the explosion. WARNING: This ignores the bomb cap!", "[expNames[i]] Range", 0) as null|num) if (isnull(boomInput[i])) return if (!isnum(boomInput[i])) //If the user doesn't input a number, set that specific explosion value to zero - alert(usr, "That wasnt a number! Value set to default (zero) instead.") + alert(usr, "That wasn't a number! Value set to default (zero) instead.") boomInput = 0 explosionChoice = 1 temp_pod.explosionSize = boomInput @@ -192,11 +225,11 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm damageChoice = 0 temp_pod.damage = 0 return - var/damageInput = input("How much damage to deal", "Enter the amount of brute damage dealt by getting hit", 0) as null|num + var/damageInput = input("Enter the amount of brute damage dealt by getting hit","How much damage to deal", 0) as null|num if (isnull(damageInput)) return if (!isnum(damageInput)) //Sanitize the input for damage to deal.s - alert(usr, "That wasnt a number! Value set to default (zero) instead.") + alert(usr, "That wasn't a number! Value set to default (zero) instead.") damageInput = 0 damageChoice = 1 temp_pod.damage = damageInput @@ -226,13 +259,32 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm temp_pod.desc = descInput temp_pod.adminNamed = TRUE //This variable is checked in the supplypod/setStyle() proc . = TRUE + /* + if("effectShrapnel") //Creates a cloud of shrapnel on landing + if (temp_pod.effectShrapnel == TRUE) //If already doing custom damage, set back to default (no shrapnel) + temp_pod.effectShrapnel = FALSE + return + var/shrapnelInput = input("Please enter the type of pellet cloud you'd like to create on landing (Can be any projectile!)", "Projectile Typepath", 0) in sortList(subtypesof(/obj/item/projectile), /proc/cmp_typepaths_asc) + if (isnull(shrapnelInput)) + return + var/shrapnelMagnitude = input("Enter the magnitude of the pellet cloud. This is usually a value around 1-5. Please note that Ryll-Ryll has asked me to tell you that if you go too crazy with the projectiles you might crash the server. So uh, be gentle!", "Shrapnel Magnitude", 0) as null|num + if (isnull(shrapnelMagnitude)) + return + if (!isnum(shrapnelMagnitude)) + alert(usr, "That wasn't a number! Value set to 3 instead.") + shrapnelMagnitude = 3 + temp_pod.shrapnel_type = shrapnelInput + temp_pod.shrapnel_magnitude = shrapnelMagnitude + temp_pod.effectShrapnel = TRUE + . = TRUE + */ if("effectStun") //Toggle: Any mob under the pod is stunned (cant move) until the pod lands, hitting them! temp_pod.effectStun = !temp_pod.effectStun . = TRUE if("effectLimb") //Toggle: Anyone carbon mob under the pod loses a limb when it lands temp_pod.effectLimb = !temp_pod.effectLimb . = TRUE - if("effectOrgans") //Toggle: Any carbon mob under the pod loses every limb and organ + if("effectOrgans") //Toggle: Anyone carbon mob under the pod loses a limb when it lands temp_pod.effectOrgans = !temp_pod.effectOrgans . = TRUE if("effectBluespace") //Toggle: Deletes the pod after landing @@ -253,7 +305,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if("effectBurst") //Toggle: Launch 5 pods (with a very slight delay between) in a 3x3 area centered around the target effectBurst = !effectBurst . = TRUE - if("effectAnnounce") //Toggle: Sends a ghost announcement. + if("effectAnnounce") //Toggle: Launch 5 pods (with a very slight delay between) in a 3x3 area centered around the target effectAnnounce = !effectAnnounce . = TRUE if("effectReverse") //Toggle: Don't send any items. Instead, after landing, close (taking any objects inside) and go back to the centcom bay it came from @@ -272,15 +324,15 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm . = TRUE ////////////////////////////TIMER DELAYS////////////////// - if("fallDuration") //Change the falling animation duration - if (temp_pod.fallDuration != initial(temp_pod.fallDuration)) //If the fall duration has already been changed when we push the "change value" button, then set it to default + if("fallDuration") //Change the time it takes the pod to land, after firing + if (temp_pod.fallDuration != initial(temp_pod.fallDuration)) //If the landing delay has already been changed when we push the "change value" button, then set it to default temp_pod.fallDuration = initial(temp_pod.fallDuration) return - var/timeInput = input("Enter the duration of the pod's falling animation, in seconds", "Delay Time", initial(temp_pod.fallDuration) * 0.1) as null|num + var/timeInput = input("Enter the duration of the pod's falling animation, in seconds", "Delay Time", initial(temp_pod.fallDuration) * 0.1) as null|num if (isnull(timeInput)) return if (!isnum(timeInput)) //Sanitize input, if it doesnt check out, error and set to default - alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.fallDuration)*0.1]) instead.") + alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.fallDuration)*0.1]) instead.") timeInput = initial(temp_pod.fallDuration) temp_pod.fallDuration = 10 * timeInput . = TRUE @@ -292,7 +344,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if (isnull(timeInput)) return if (!isnum(timeInput)) //Sanitize input, if it doesnt check out, error and set to default - alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.landingDelay)*0.1]) instead.") + alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.landingDelay)*0.1]) instead.") timeInput = initial(temp_pod.landingDelay) temp_pod.landingDelay = 10 * timeInput . = TRUE @@ -304,7 +356,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if (isnull(timeInput)) return if (!isnum(timeInput)) //Sanitize input - alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.openingDelay)*0.1]) instead.") + alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.openingDelay)*0.1]) instead.") timeInput = initial(temp_pod.openingDelay) temp_pod.openingDelay = 10 * timeInput . = TRUE @@ -316,13 +368,13 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if (isnull(timeInput)) return if (!isnum(timeInput)) - alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.departureDelay)*0.1]) instead.") + alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.departureDelay)*0.1]) instead.") timeInput = initial(temp_pod.departureDelay) temp_pod.departureDelay = 10 * timeInput . = TRUE ////////////////////////////ADMIN SOUNDS////////////////// - if("fallingSound") //Admin sound from a local file that plays when the pod falls + if("fallSound") //Admin sound from a local file that plays when the pod lands if ((temp_pod.fallingSound) != initial(temp_pod.fallingSound)) temp_pod.fallingSound = initial(temp_pod.fallingSound) temp_pod.fallingSoundLength = initial(temp_pod.fallingSoundLength) @@ -334,7 +386,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if (isnull(timeInput)) return if (!isnum(timeInput)) - alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.fallingSoundLength)*0.1]) instead.") + alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.fallingSoundLength)*0.1]) instead.") temp_pod.fallingSound = soundInput temp_pod.fallingSoundLength = 10 * timeInput . = TRUE @@ -369,7 +421,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if (temp_pod.soundVolume != initial(temp_pod.soundVolume)) temp_pod.soundVolume = initial(temp_pod.soundVolume) return - var/soundInput = input(holder, "Please pick a volume. Default is between 1 and 100 with 80 being average, but pick whatever. I'm a notification, not a cop. If you still cant hear your sound, consider turning on the Quiet effect. It will silence all pod sounds except for the custom admin ones set by the previous three buttons.", "Pick Admin Sound Volume") as null|num + var/soundInput = input(holder, "Please pick a volume. Default is between 1 and 100 with 50 being average, but pick whatever. I'm a notification, not a cop. If you still cant hear your sound, consider turning on the Quiet effect. It will silence all pod sounds except for the custom admin ones set by the previous three buttons.", "Pick Admin Sound Volume") as null|num if (isnull(soundInput)) return temp_pod.soundVolume = soundInput @@ -421,26 +473,36 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm . = TRUE if("giveLauncher") //Enters the "Launch Mode". When the launcher is activated, temp_pod is cloned, and the result it filled and launched anywhere the user clicks (unless specificTarget is true) launcherActivated = !launcherActivated - updateCursor(launcherActivated) //Update the cursor of the user to a cool looking target icon + updateCursor(launcherActivated, FALSE) //Update the cursor of the user to a cool looking target icon + . = TRUE + if("clearBay") //Delete all mobs and objs in the selected bay + if(alert(usr, "This will delete all objs and mobs in [bay]. Are you sure?", "Confirmation", "Delete that shit", "No") == "Delete that shit") + clearBay() + refreshBay() . = TRUE /datum/centcom_podlauncher/ui_close() //Uses the destroy() proc. When the user closes the UI, we clean up the temp_pod and supplypod_selector variables. qdel(src) -/datum/centcom_podlauncher/proc/updateCursor(var/launching) //Update the moues of the user - if (holder) //Check to see if we have a client - if (launching) //If the launching param is true, we give the user new mouse icons. - holder.mouse_up_icon = 'icons/effects/supplypod_target.dmi' //Icon for when mouse is released - holder.mouse_down_icon = 'icons/effects/supplypod_down_target.dmi' //Icon for when mouse is pressed - holder.mouse_pointer_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released) - holder.click_intercept = src //Create a click_intercept so we know where the user is clicking - else - var/mob/M = holder.mob - holder.mouse_up_icon = null - holder.mouse_down_icon = null - holder.click_intercept = null - if (M) - M.update_mouse_pointer() //set the moues icons to null, then call update_moues_pointer() which resets them to the correct values based on what the mob is doing (in a mech, holding a spell, etc)() +/datum/centcom_podlauncher/proc/updateCursor(var/launching, var/turf_picking) //Update the mouse of the user + if (!holder) //Can't update the mouse icon if the client doesnt exist! + return + if (launching || turf_picking) //If the launching param is true, we give the user new mouse icons. + if(launching) + holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_target.dmi' //Icon for when mouse is released + holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_down_target.dmi' //Icon for when mouse is pressed + if(turf_picking) + holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_pickturf.dmi' //Icon for when mouse is released + holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_pickturf_down.dmi' //Icon for when mouse is pressed + holder.mouse_pointer_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released) + holder.click_intercept = src //Create a click_intercept so we know where the user is clicking + else + var/mob/M = holder.mob + holder.mouse_up_icon = null + holder.mouse_down_icon = null + holder.click_intercept = null + if (M) + M.update_mouse_pointer() //set the moues icons to null, then call update_moues_pointer() which resets them to the correct values based on what the mob is doing (in a mech, holding a spell, etc)() /datum/centcom_podlauncher/proc/InterceptClickOn(user,params,atom/target) //Click Intercept so we know where to send pods where the user clicks var/list/pa = params2list(params) @@ -461,7 +523,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm else return //if target is null and we don't have a specific target, cancel if (effectAnnounce) - deadchat_broadcast("A special package is being launched at the station!", turf_target = target) + deadchat_broadcast("A special package is being launched at the station!", turf_target = target) //, message_type=DEADCHAT_ANNOUNCEMENT) var/list/bouttaDie = list() for (var/mob/living/M in target) bouttaDie.Add(M) @@ -479,6 +541,15 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm else launch(target) //If we couldn't locate an adjacent turf, just launch at the normal target sleep(rand()*2) //looks cooler than them all appearing at once. Gives the impression of burst fire. + else if (picking_dropoff_turf) + //Clicking on UI elements shouldn't pick a dropoff turf + if(istype(target,/obj/screen)) + return FALSE + + . = TRUE + if(left_click) //When we left click: + dropoff_turf = get_turf(target) + to_chat(user, " You've selected [dropoff_turf] at [COORD(dropoff_turf)] as your dropoff location.") /datum/centcom_podlauncher/proc/refreshBay() //Called whenever the bay is switched, as well as wheneber a pod is launched orderedArea = createOrderedArea(bay) //Create an ordered list full of turfs form the bay @@ -489,7 +560,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm to_chat(holder.mob, "No /area/centcom/supplypod/loading/one (or /two or /three or /four) in the world! You can make one yourself (then refresh) for now, but yell at a mapper to fix this, today!") CRASH("No /area/centcom/supplypod/loading/one (or /two or /three or /four) has been mapped into the centcom z-level!") orderedArea = list() - if (!isemptylist(A.contents)) //Go through the area passed into the proc, and figure out the top left and bottom right corners by calculating max and min values + if (length(A.contents)) //Go through the area passed into the proc, and figure out the top left and bottom right corners by calculating max and min values var/startX = A.contents[1].x //Create the four values (we do it off a.contents[1] so they have some sort of arbitrary initial value. They should be overwritten in a few moments) var/endX = A.contents[1].x var/startY = A.contents[1].y @@ -512,12 +583,12 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm numTurfs = 0 //Counts the number of turfs that can be launched (remember, supplypods either launch all at once or one turf-worth of items at a time) acceptableTurfs = list() for (var/turf/T in orderedArea) //Go through the orderedArea list - if (typecache_filter_list_reverse(T.contents, ignored_atoms).len != 0) //if there is something in this turf that isnt in the blacklist, we consider this turf "acceptable" and add it to the acceptableTurfs list + if (typecache_filter_list_reverse(T.contents, ignored_atoms).len != 0) //if there is something in this turf that isn't in the blacklist, we consider this turf "acceptable" and add it to the acceptableTurfs list acceptableTurfs.Add(T) //Because orderedArea was an ordered linear list, acceptableTurfs will be as well. numTurfs ++ launchList = list() //Anything in launchList will go into the supplypod when it is launched - if (!isemptylist(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We dont fill the supplypod if acceptableTurfs is empty, if the pod is going in reverse (effectReverse=true), or if the pod is acitng like a missile (effectMissile=true) + if (length(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We dont fill the supplypod if acceptableTurfs is empty, if the pod is going in reverse (effectReverse=true), or if the pod is acitng like a missile (effectMissile=true) switch(launchChoice) if(0) //If we are launching all the turfs at once for (var/turf/T in acceptableTurfs) @@ -536,22 +607,36 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm if (isnull(A)) return var/obj/structure/closet/supplypod/centcompod/toLaunch = DuplicateObject(temp_pod) //Duplicate the temp_pod (which we have been varediting or configuring with the UI) and store the result - toLaunch.bay = bay //Bay is currently a nonstatic expression, so it cant go into toLaunch using DuplicateObject - toLaunch.update_icon()//we update_icon() here so that the door doesnt "flicker on" right after it lands - if (launchClone) //We arent launching the actual items from the bay, rather we are creating clones and launching those - for (var/atom/movable/O in launchList) - DuplicateObject(O).forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod - new /obj/effect/abstract/DPtarget(A, toLaunch) //Create the DPTarget, which will eventually forceMove the temp_pod to it's location + /* + if(dropoff_turf) + toLaunch.reverse_dropoff_turf = dropoff_turf else - for (var/atom/movable/O in launchList) //If we aren't cloning the objects, just go through the launchList + toLaunch.reverse_dropoff_turf = bay //Bay is currently a nonstatic expression, so it cant go into toLaunch using DuplicateObject + */ + toLaunch.update_icon()//we update_icon() here so that the door doesnt "flicker on" right after it lands + // var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/fly_me_to_the_moon] + // toLaunch.forceMove(shippingLane) The shipping lane is temporarily closed due to ratvarian blockades + if (launchClone) //We arent launching the actual items from the bay, rather we are creating clones and launching those + if(launchRandomItem) + var/atom/movable/O = pick_n_take(launchList) + DuplicateObject(O).forceMove(toLaunch) //Duplicate a single atom/movable from launchList and forceMove it into the supplypod + else + for (var/atom/movable/O in launchList) + DuplicateObject(O).forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod + else + if(launchRandomItem) + var/atom/movable/O = pick_n_take(launchList) O.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod - new /obj/effect/abstract/DPtarget(A, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to it's location + else + for (var/atom/movable/O in launchList) //If we aren't cloning the objects, just go through the launchList + O.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod + new /obj/effect/abstract/DPtarget(A, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to it's location if (launchClone) launchCounter++ //We only need to increment launchCounter if we are cloning objects. //If we aren't cloning objects, taking and removing the first item each time from the acceptableTurfs list will inherently iterate through the list in order /datum/centcom_podlauncher/proc/updateSelector() //Ensures that the selector effect will showcase the next item if needed - if (launchChoice == 1 && !isemptylist(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We only show the selector if we are taking items from the bay + if (launchChoice == 1 && length(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We only show the selector if we are taking items from the bay var/index = launchCounter + 1 //launchCounter acts as an index to the ordered acceptableTurfs list, so adding one will show the next item in the list if (index > acceptableTurfs.len) //out of bounds check index = 1 @@ -559,8 +644,14 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm else selector.moveToNullspace() //Otherwise, we move the selector to nullspace until it is needed again +/datum/centcom_podlauncher/proc/clearBay() //Clear all objs and mobs from the selected bay + for (var/obj/O in bay.GetAllContents()) + qdel(O) + for (var/mob/M in bay.GetAllContents()) + qdel(M) + /datum/centcom_podlauncher/Destroy() //The Destroy() proc. This is called by ui_close proc, or whenever the user leaves the game - updateCursor(FALSE) //Make sure our moues cursor resets to default. False means we are not in launch mode + updateCursor(FALSE, FALSE) //Make sure our moues cursor resets to default. False means we are not in launch mode qdel(temp_pod) //Delete the temp_pod qdel(selector) //Delete the selector effect . = ..() @@ -581,8 +672,8 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm for (var/X in temp_pod.explosionSize) explosionString += "[X]|" - var/msg = "launched [podString][whomString].[delayString][damageString][explosionString]]" - message_admins("[key_name_admin(usr)] [msg] in [AREACOORD(specificTarget)].") - if (!isemptylist(whoDyin)) + var/msg = "launched [podString] towards [whomString] [delayString][damageString][explosionString]" + message_admins("[key_name_admin(usr)] [msg] in [ADMIN_VERBOSEJMP(specificTarget)].") + if (length(whoDyin)) for (var/mob/living/M in whoDyin) admin_ticket_log(M, "[key_name_admin(usr)] [msg]") diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm index 8a438a1342..f5a8d21278 100644 --- a/code/modules/cargo/console.dm +++ b/code/modules/cargo/console.dm @@ -3,9 +3,6 @@ desc = "Used to order supplies, approve requests, and control the shuttle." icon_screen = "supply" circuit = /obj/item/circuitboard/computer/cargo - req_access = list(ACCESS_CARGO) - ui_x = 780 - ui_y = 750 var/requestonly = FALSE var/contraband = FALSE @@ -27,7 +24,6 @@ desc = "Used to request supplies from cargo." icon_screen = "request" circuit = /obj/item/circuitboard/computer/cargo/request - req_access = list() requestonly = TRUE /obj/machinery/computer/cargo/Initialize() @@ -66,15 +62,12 @@ var/obj/item/circuitboard/computer/cargo/board = circuit board.contraband = TRUE board.obj_flags |= EMAGGED - req_access = list() update_static_data(user) - return ..() -/obj/machinery/computer/cargo/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/cargo/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Cargo", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "Cargo", name) ui.open() /obj/machinery/computer/cargo/ui_data() @@ -120,7 +113,6 @@ var/list/data = list() data["requestonly"] = requestonly data["supplies"] = list() - data["emagged"] = obj_flags & EMAGGED for(var/pack in SSshuttle.supply_packs) var/datum/supply_pack/P = SSshuttle.supply_packs[pack] if(!data["supplies"][P.group]) @@ -135,8 +127,8 @@ "cost" = P.cost, "id" = pack, "desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name. + "goody" = P.goody, "private_goody" = P.goody == PACK_GOODY_PRIVATE, - "goody" = P.goody == PACK_GOODY_PUBLIC, "access" = P.access, "can_private_buy" = P.can_private_buy )) @@ -145,9 +137,6 @@ /obj/machinery/computer/cargo/ui_act(action, params, datum/tgui/ui) if(..()) return - if(!allowed(usr)) - to_chat(usr, "Access denied.") - return switch(action) if("send") if(!SSshuttle.supply.canMove()) @@ -179,6 +168,8 @@ else SSshuttle.shuttle_loan.loan_shuttle() say("The supply shuttle has been loaned to CentCom.") + investigate_log("[key_name(usr)] accepted a shuttle loan event.", INVESTIGATE_CARGO) + log_game("[key_name(usr)] accepted a shuttle loan event.") . = TRUE if("add") var/id = text2path(params["id"]) @@ -200,13 +191,15 @@ rank = "Silicon" var/datum/bank_account/account - if(self_paid) - if(!pack.can_private_buy && !(obj_flags & EMAGGED)) - return - var/obj/item/card/id/id_card = usr.get_idcard(TRUE) + if(self_paid && ishuman(usr)) + var/mob/living/carbon/human/H = usr + var/obj/item/card/id/id_card = H.get_idcard(TRUE) if(!istype(id_card)) say("No ID card detected.") return + if(istype(id_card, /obj/item/card/id/departmental_budget)) + say("The [src] rejects [id_card].") + return account = id_card.registered_account if(!istype(account)) say("Invalid bank account.") @@ -241,6 +234,9 @@ SSshuttle.shoppinglist += SO if(self_paid) say("Order processed. The price will be charged to [account.account_holder]'s bank account on delivery.") + if(requestonly && message_cooldown < world.time) + radio.talk_into(src, "A new order has been requested.", RADIO_CHANNEL_SUPPLY) + message_cooldown = world.time + 30 SECONDS . = TRUE if("remove") var/id = text2num(params["id"]) diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm index dc7e4b5a06..4ca97a13a5 100644 --- a/code/modules/cargo/expressconsole.dm +++ b/code/modules/cargo/expressconsole.dm @@ -1,5 +1,5 @@ #define MAX_EMAG_ROCKETS 8 -#define BEACON_COST 5000 +#define BEACON_COST 500 #define SP_LINKED 1 #define SP_READY 2 #define SP_LAUNCH 3 @@ -13,10 +13,9 @@ All sales are near instantaneous - please choose carefully" icon_screen = "supply_express" circuit = /obj/item/circuitboard/computer/cargo/express - ui_x = 600 - ui_y = 700 blockade_warning = "Bluespace instability detected. Delivery impossible." req_access = list(ACCESS_QM) + var/message var/printed_beacons = 0 //number of beacons printed. Used to determine beacon names. var/list/meme_pack_data @@ -42,7 +41,7 @@ to_chat(user, "You [locked ? "lock" : "unlock"] the interface.") return else if(istype(W, /obj/item/disk/cargo/bluespace_pod)) - podType = /obj/structure/closet/supplypod/bluespacepod + podType = /obj/structure/closet/supplypod/bluespacepod //doesnt effect circuit board, making reversal possible to_chat(user, "You insert the disk into [src], allowing for advanced supply delivery vehicles.") qdel(W) return TRUE @@ -52,22 +51,20 @@ sb.link_console(src, user) return TRUE else - to_chat(user, "[src] is already linked to [sb].") + to_chat(user, "[src] is already linked to [sb].") ..() /obj/machinery/computer/cargo/express/emag_act(mob/living/user) - . = SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT) if(obj_flags & EMAGGED) return - user.visible_message("[user] swipes a suspicious card through [src]!", - "You change the routing protocols, allowing the Supply Pod to land anywhere on the station.") + if(user) + user.visible_message("[user] swipes a suspicious card through [src]!", + "You change the routing protocols, allowing the Supply Pod to land anywhere on the station.") obj_flags |= EMAGGED // This also sets this on the circuit board var/obj/item/circuitboard/computer/cargo/board = circuit board.obj_flags |= EMAGGED packin_up() - req_access = list() - return TRUE /obj/machinery/computer/cargo/express/proc/packin_up() // oh shit, I'm sorry meme_pack_data = list() // sorry for what? @@ -89,10 +86,10 @@ "desc" = P.desc || P.name // If there is a description, use it. Otherwise use the pack's name. )) -/obj/machinery/computer/cargo/express/ui_interact(mob/living/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state. - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/cargo/express/ui_interact(mob/living/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "CargoExpress", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "CargoExpress", name) ui.open() /obj/machinery/computer/cargo/express/ui_data(mob/user) @@ -131,9 +128,6 @@ return data /obj/machinery/computer/cargo/express/ui_act(action, params, datum/tgui/ui) - if(!allowed(usr)) - to_chat(usr, "Access denied.") - return switch(action) if("LZCargo") usingBeacon = FALSE @@ -153,6 +147,7 @@ printed_beacons++//printed_beacons starts at 0, so the first one out will be called beacon # 1 beacon.name = "Supply Pod Beacon #[printed_beacons]" + if("add")//Generate Supply Order first var/id = text2path(params["id"]) var/datum/supply_pack/pack = SSshuttle.supply_packs[id] @@ -195,7 +190,6 @@ LZ = pick(empty_turfs) if (SO.pack.cost <= points_to_check && LZ)//we need to call the cost check again because of the CHECK_TICK call D.adjust_money(-SO.pack.cost) - SSblackbox.record_feedback("nested tally", "cargo_imports", 1, list("[SO.pack.cost]", "[SO.pack.name]")) new /obj/effect/abstract/DPtarget(LZ, podType, SO) . = TRUE update_icon() @@ -209,7 +203,7 @@ CHECK_TICK if(empty_turfs && empty_turfs.len) D.adjust_money(-(SO.pack.cost * (0.72*MAX_EMAG_ROCKETS))) - SSblackbox.record_feedback("nested tally", "cargo_imports", MAX_EMAG_ROCKETS, list("[SO.pack.cost * 0.72]", "[SO.pack.name]")) + SO.generateRequisition(get_turf(src)) for(var/i in 1 to MAX_EMAG_ROCKETS) var/LZ = pick(empty_turfs) diff --git a/code/modules/cargo/packs/armory.dm b/code/modules/cargo/packs/armory.dm index 835457536f..2ef0abb000 100644 --- a/code/modules/cargo/packs/armory.dm +++ b/code/modules/cargo/packs/armory.dm @@ -223,3 +223,10 @@ /obj/item/ammo_box/magazine/wt550m9/wtrubber, /obj/item/ammo_box/magazine/wt550m9/wtrubber) crate_name = "auto rifle ammo crate" + +/datum/supply_pack/security/armory/hell_single + name = "Hellgun Single-Pack" + crate_name = "hellgun crate" + desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids." + cost = 1500 + contains = list(/obj/item/gun/energy/laser/hellgun) diff --git a/code/modules/cargo/packs/goodies.dm b/code/modules/cargo/packs/goodies.dm index 86a7c73a34..5d07e85bac 100644 --- a/code/modules/cargo/packs/goodies.dm +++ b/code/modules/cargo/packs/goodies.dm @@ -76,12 +76,6 @@ cost = 200 contains = list(/obj/item/toy/beach_ball) -/datum/supply_pack/goody/hell_single - name = "Hellgun Single-Pack" - desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids." - cost = 1500 - contains = list(/obj/item/gun/energy/laser/hellgun) - /datum/supply_pack/goody/medipen_twopak name = "Medipen Two-Pak" desc = "Contains one standard epinephrine medipen and one standard emergency first-aid kit medipen. For when you want to prepare for the worst." diff --git a/code/modules/cargo/packs/misc.dm b/code/modules/cargo/packs/misc.dm index a84e22f6f9..c6728831eb 100644 --- a/code/modules/cargo/packs/misc.dm +++ b/code/modules/cargo/packs/misc.dm @@ -194,9 +194,9 @@ /datum/supply_pack/misc/dirtymags name = "Dirty Magazines" - desc = "Get your mind out of the gutter operative, you have work to do. Three items per order. Possible Results: .357 Speedloaders, Kitchen Gun Mags, Stetchkin Mags." + desc = "Get your mind out of the gutter operative, you have work to do. Three items per order. Possible Results: .357 Speedloaders, Kitchen Gun patented magazines, or Stetchkin magazines." hidden = TRUE - cost = 12000 + cost = 4000 var/num_contained = 3 contains = list(/obj/item/ammo_box/a357, /obj/item/ammo_box/magazine/pistolm9mm, @@ -415,21 +415,10 @@ /obj/item/restraints/handcuffs/fake/kinky, /obj/item/clothing/head/kitty/genuine, // Why its illegal /obj/item/clothing/head/kitty/genuine, - /obj/item/storage/pill_bottle/penis_enlargement, - /obj/structure/reagent_dispensers/keg/aphro) + /obj/item/storage/pill_bottle/penis_enlargement) crate_name = "lewd kit" crate_type = /obj/structure/closet/crate -/datum/supply_pack/misc/lewdkeg - name = "Lewd Deluxe Keg" - desc = "That other stuff not getting you ready? Well I have a Chemslut making tons of the good stuff." - cost = 7500 //It can be a weapon - contraband = TRUE - contains = list(/obj/structure/reagent_dispensers/keg/aphro/strong) - crate_name = "deluxe keg" - crate_type = /obj/structure/closet/crate - - ///Special supply crate that generates random syndicate gear up to a determined TC value /datum/supply_pack/misc/syndicate @@ -466,4 +455,4 @@ if(crate_value < I.cost) continue crate_value -= I.cost - new I.item(C) \ No newline at end of file + new I.item(C) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 5c9b1eec2e..9b7e928852 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -41,6 +41,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if(!asset_cache_job) return + // Rate limiting var/mtl = CONFIG_GET(number/minute_topic_limit) if (!holder && mtl) var/minute = round(world.time, 600) @@ -98,6 +99,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( keyUp(keycode) return + // Tgui Topic middleware + if(!tgui_Topic(href_list)) + return + // Admin PM if(href_list["priv_msg"]) cmd_admin_pm(href_list["priv_msg"],null) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 739a5fa243..1b6e1f9748 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -40,7 +40,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) //If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were, //autocorrected this round, not that you'd need to check that. - var/UI_style = null var/buttons_locked = FALSE var/hotkeys = FALSE @@ -230,7 +229,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/gear_points = 10 var/list/gear_categories var/list/chosen_gear = list() - var/gear_tab + var/gear_category + var/gear_subcategory var/screenshake = 100 var/damagescreenshake = 2 @@ -1059,58 +1059,83 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "
" if(3) - if(!gear_tab) - gear_tab = GLOB.loadout_items[1] dat += "" dat += "" dat += "" dat += "" + dat += "" + + dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - for(var/j in GLOB.loadout_items[gear_tab]) - var/datum/gear/gear = GLOB.loadout_items[gear_tab][j] - var/donoritem = gear.donoritem - if(donoritem && !gear.donator_ckey_check(user.ckey)) - continue - var/class_link = "" - if(gear.type in chosen_gear) - class_link = "style='white-space:normal;' class='linkOn' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(j)];toggle_gear=0'" - else if(gear_points <= 0) - class_link = "style='white-space:normal;' class='linkOff'" - else if(donoritem) - class_link = "style='white-space:normal;background:#ebc42e;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(j)];toggle_gear=1'" - else - class_link = "style='white-space:normal;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(j)];toggle_gear=1'" - dat += "" - dat += "" - dat += "
[gear_points] loadout points remaining. \[Clear Loadout\]
You can only choose one item per category, unless it's an item that spawns in your backpack or hands.
" - var/firstcat = TRUE - for(var/i in GLOB.loadout_items) - if(firstcat) - firstcat = FALSE + + if(!length(GLOB.loadout_items)) + dat += "
ERROR: No loadout categories - something is horribly wrong!" + else + if(!GLOB.loadout_categories[gear_category]) + gear_category = GLOB.loadout_categories[1] + var/firstcat = TRUE + for(var/category in GLOB.loadout_categories) + if(firstcat) + firstcat = FALSE + else + dat += " |" + if(category == gear_category) + dat += " [category] " + else + dat += " [category] " + + dat += "

" + + if(!length(GLOB.loadout_categories[gear_category])) + dat += "No subcategories detected. Something is horribly wrong!" else - dat += " |" - if(i == gear_tab) - dat += " [i] " - else - dat += " [i] " - dat += "

[gear_tab]

NameCostRestrictionsDescription
[j][gear.cost]" - if(islist(gear.restricted_roles)) - if(gear.restricted_roles.len) - if(gear.restricted_desc) - dat += "" - dat += gear.restricted_desc - dat += "" + var/list/subcategories = GLOB.loadout_categories[gear_category] + if(!subcategories.Find(gear_subcategory)) + gear_subcategory = subcategories[1] + + var/firstsubcat = FALSE + for(var/subcategory in subcategories) + if(firstsubcat) + firstsubcat = FALSE else - dat += "" - dat += gear.restricted_roles.Join(";") - dat += "" - dat += "[gear.description]
" + dat += " |" + if(gear_subcategory == subcategory) + dat += " [subcategory] " + else + dat += " [subcategory] " + dat += "
" + + dat += "Name" + dat += "Cost" + dat += "Restrictions" + dat += "Description" + for(var/name in GLOB.loadout_items[gear_category][gear_subcategory]) + var/datum/gear/gear = GLOB.loadout_items[gear_category][gear_subcategory][name] + var/donoritem = gear.donoritem + if(donoritem && !gear.donator_ckey_check(user.ckey)) + continue + var/class_link = "" + if(gear.type in chosen_gear) + class_link = "style='white-space:normal;' class='linkOn' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=0'" + else if(gear_points <= 0) + class_link = "style='white-space:normal;' class='linkOff'" + else if(donoritem) + class_link = "style='white-space:normal;background:#ebc42e;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'" + else + class_link = "style='white-space:normal;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'" + dat += "[name]" + dat += "[gear.cost]" + if(islist(gear.restricted_roles)) + if(gear.restricted_roles.len) + if(gear.restricted_desc) + dat += "" + dat += gear.restricted_desc + dat += "" + else + dat += "" + dat += gear.restricted_roles.Join(";") + dat += "" + dat += "[gear.description]" + dat += "" if(4) // Content preferences dat += "
" dat += "

Fetish content prefs

" @@ -1128,7 +1153,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Breast Enlargement: [(cit_toggles & BREAST_ENLARGEMENT) ? "Allowed" : "Disallowed"]
" dat += "Penis Enlargement: [(cit_toggles & PENIS_ENLARGEMENT) ? "Allowed" : "Disallowed"]
" dat += "Hypno: [(cit_toggles & NEVER_HYPNO) ? "Disallowed" : "Allowed"]
" - dat += "Aphrodisiacs: [(cit_toggles & NO_APHRO) ? "Disallowed" : "Allowed"]
" dat += "Ass Slapping: [(cit_toggles & NO_ASS_SLAP) ? "Disallowed" : "Allowed"]
" dat += "Automatic Wagging: [(cit_toggles & NO_AUTO_WAG) ? "Disabled" : "Enabled"]
" dat += "
" @@ -2691,11 +2715,13 @@ GLOBAL_LIST_EMPTY(preferences_datums) gear_points = CONFIG_GET(number/initial_gear_points) save_preferences() if(href_list["select_category"]) - for(var/i in GLOB.loadout_items) - if(i == href_list["select_category"]) - gear_tab = i + gear_category = html_decode(href_list["select_category"]) + gear_subcategory = GLOB.loadout_categories[gear_category][1] + if(href_list["select_subcategory"]) + gear_subcategory = html_decode(href_list["select_subcategory"]) if(href_list["toggle_gear_path"]) - var/datum/gear/G = GLOB.loadout_items[gear_tab][html_decode(href_list["toggle_gear_path"])] + var/name = html_decode(href_list["toggle_gear_path"]) + var/datum/gear/G = GLOB.loadout_items[gear_category][gear_subcategory][name] if(!G) return var/toggle = text2num(href_list["toggle_gear"]) @@ -2875,17 +2901,17 @@ GLOBAL_LIST_EMPTY(preferences_datums) LAZYINITLIST(L) for(var/i in chosen_gear) var/datum/gear/G = i - var/occupied_slots = L[slot_to_string(initial(G.category))] ? L[slot_to_string(initial(G.category))] + 1 : 1 - LAZYSET(L, slot_to_string(initial(G.category)), occupied_slots) + var/occupied_slots = L[initial(G.category)] ? L[initial(G.category)] + 1 : 1 + LAZYSET(L, initial(G.category), occupied_slots) switch(slot) if(SLOT_IN_BACKPACK) - if(L[slot_to_string(SLOT_IN_BACKPACK)] < BACKPACK_SLOT_AMT) + if(L[LOADOUT_CATEGORY_BACKPACK] < BACKPACK_SLOT_AMT) return TRUE if(SLOT_HANDS) - if(L[slot_to_string(SLOT_HANDS)] < HANDS_SLOT_AMT) + if(L[LOADOUT_CATEGORY_HANDS] < HANDS_SLOT_AMT) return TRUE else - if(L[slot_to_string(slot)] < DEFAULT_SLOT_AMT) + if(L[slot] < DEFAULT_SLOT_AMT) return TRUE #undef DEFAULT_SLOT_AMT diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 59d99674eb..dffab645a4 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -224,7 +224,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car var/needs_update = savefile_needs_update(S) if(needs_update == -2) //fatal, can't load any data return 0 - + . = TRUE //general preferences @@ -443,7 +443,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car return 0 . = TRUE - + //Species var/species_id S["species"] >> species_id diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 350a577fb1..3f6d21bcd2 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -149,6 +149,30 @@ icon_state = "eyepatch" item_state = "eyepatch" +/obj/item/clothing/glasses/eyepatch/syndicate + name = "cybernetic eyepatch" + desc = "An eyepatch used to enhance one's aim with guns." + icon_state = "syndicatepatch" + item_state = "syndicatepatch" + resistance_flags = ACID_PROOF + +/obj/item/clothing/glasses/eyepatch/syndicate/equipped(mob/living/carbon/human/user, slot) + . = ..() + if(slot == SLOT_GLASSES) + user.visible_message("Circuitry from the eyepatch links itself to your brain as you put on the eyepatch.") + if(HAS_TRAIT(user, TRAIT_POOR_AIM)) + user.visible_message("You hear a fizzing noise from the circuit. That can't be good.") + ADD_TRAIT(user, TRAIT_INSANE_AIM, "SYNDICATE_EYEPATCH_AIM") + ADD_TRAIT(src, TRAIT_NODROP, "SYNDICATE_EYEPATCH_NODROP") + +/obj/item/clothing/glasses/eyepatch/syndicate/dropped(mob/living/carbon/human/user) + . = ..() + REMOVE_TRAIT(user, TRAIT_INSANE_AIM, "SYNDICATE_EYEPATCH_AIM") + var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES) + if(eyes) + eyes.applyOrganDamage(30) + user.visible_message("Your eye stings as the circuitry is removed from your eye!") + /obj/item/clothing/glasses/monocle name = "monocle" desc = "Such a dapper eyepiece!" diff --git a/code/modules/clothing/glasses/engine_goggles.dm b/code/modules/clothing/glasses/engine_goggles.dm index 2a64445776..d7e7ae3669 100644 --- a/code/modules/clothing/glasses/engine_goggles.dm +++ b/code/modules/clothing/glasses/engine_goggles.dm @@ -97,14 +97,14 @@ if(get_dist(user, place) >= range*8) //Rads are easier to see than wires under the floor continue var/strength = round(rad_places[i] / 1000, 0.1) - var/image/pic = new(loc = place) + var/image/pic = image(loc = place) var/mutable_appearance/MA = new() - MA.alpha = 180 - MA.maptext = "[strength]k" - MA.color = "#64C864" - MA.layer = FLY_LAYER + MA.maptext = "[strength]k" + MA.color = "#04e604" + MA.layer = RAD_TEXT_LAYER + MA.plane = GAME_PLANE pic.appearance = MA - flick_overlay(pic, list(user.client), 8) + flick_overlay(pic, list(user.client), 10) /obj/item/clothing/glasses/meson/engine/proc/show_shuttle() var/mob/living/carbon/human/user = loc diff --git a/code/modules/clothing/gloves/ring.dm b/code/modules/clothing/gloves/ring.dm index 8354f13bd2..daca843c45 100644 --- a/code/modules/clothing/gloves/ring.dm +++ b/code/modules/clothing/gloves/ring.dm @@ -21,7 +21,7 @@ desc = "An expensive ring, studded with a diamond. Cultures have used these rings in courtship for a millenia." icon_state = "ringdiamond" item_state = "dring" - + /obj/item/clothing/gloves/ring/diamond/attack_self(mob/user) user.visible_message("\The [user] gets down on one knee, presenting \the [src].","You get down on one knee, presenting \the [src].") @@ -30,3 +30,12 @@ desc = "A tiny silver ring, sized to wrap around a finger." icon_state = "ringsilver" item_state = "sring" + +/obj/item/clothing/gloves/ring/custom + name = "ring" + desc = "A ring." + gender = NEUTER + w_class = WEIGHT_CLASS_TINY + obj_flags = UNIQUE_RENAME + icon_state = "ringsilver" + item_state = "sring" diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index 72d0f68c18..124ece8fdc 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -469,3 +469,16 @@ icon_state = "maid" item_state = "maid" dynamic_hair_suffix = "" + +/obj/item/clothing/head/widered + name = "Wide red hat" + desc = "It is both wide, and red. Stylish!" + icon_state = "widehat_red" + item_state = "widehat_red" + +/obj/item/clothing/head/kabuto + name = "Kabuto helmet" + desc = "A traditional kabuto helmet." + icon_state = "kabuto" + item_state = "kabuto" + flags_inv = HIDEHAIR|HIDEEARS diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 40797b97ed..12829ed0e7 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -214,7 +214,7 @@ name = "advanced hardsuit" desc = "An advanced suit that protects against hazardous, low pressure environments. Shines with a high polish." item_state = "ce_hardsuit" - armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 95, "fire" = 100, "acid" = 90, "wound" = 10) + armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 90, "wound" = 10) heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 0b1121b22b..607a8ecb55 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -1057,3 +1057,9 @@ desc = "Reminds you of someone, but you just can't put your finger on it..." icon_state = "waldo_shirt" item_state = "waldo_shirt" + +/obj/item/clothing/suit/samurai + name = "Samurai outfit" + desc = "An outfit used by traditional japanese warriors." + icon_state = "samurai" + item_state = "samurai" \ No newline at end of file diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm index cb173f3bde..ee7e4c48e1 100644 --- a/code/modules/clothing/under/accessories.dm +++ b/code/modules/clothing/under/accessories.dm @@ -369,3 +369,12 @@ icon_state = "plastics" armor = list("melee" = 0, "bullet" = 0, "laser" = 20, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = -40) flags_inv = HIDEACCESSORY + +//necklace +/obj/item/clothing/accessory/necklace + name = "necklace" + desc = "A necklace." + icon_state = "locket" + obj_flags = UNIQUE_RENAME + custom_materials = list(/datum/material/iron=100) + resistance_flags = FIRE_PROOF diff --git a/code/modules/clothing/under/costume.dm b/code/modules/clothing/under/costume.dm index 3e7bc755cb..18cd104ff8 100644 --- a/code/modules/clothing/under/costume.dm +++ b/code/modules/clothing/under/costume.dm @@ -329,3 +329,29 @@ desc = "cloud" icon_state = "cloud" can_adjust = FALSE + +/obj/item/clothing/under/costume/kimono + name = "Kimono" + desc = "A traditional piece of clothing from japan" + icon_state = "kimono" + item_state = "kimono" + +/obj/item/clothing/under/costume/kimono/black + name = "Black Kimono" + icon_state = "kimono_a" + item_state = "kimono_a" + +/obj/item/clothing/under/costume/kimono/kamishimo + name = "Kamishimo" + icon_state = "kamishimo" + item_state = "kamishimo" + +/obj/item/clothing/under/costume/kimono/fancy + name = "Fancy Kimono" + icon_state = "fancy_kimono" + item_state = "fancy_kimono" + +/obj/item/clothing/under/costume/kimono/sakura + name = "Sakura Kimono'" + icon_state = "sakura_kimono" + item_state = "sakura_kimono" diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index bc38cb6664..27fb0cc00d 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -308,3 +308,21 @@ icon_state = "polyfemtankpantsu" poly_states = 2 poly_colors = list("#808080", "#FF3535") + +/obj/item/clothing/under/misc/black_dress + name = "little black dress" + desc = "A small black dress" + icon_state = "littleblackdress_s" + item_state = "littleblackdress_s" + +/obj/item/clothing/under/misc/pinktutu + name = "pink tutu" + desc = "A pink tutu" + icon_state = "pinktutu_s" + item_state = "pinktutu_s" + +/obj/item/clothing/under/misc/bathrobe + name = "bathrobe" + desc = "A blue bathrobe." + icon_state = "bathrobe" + item_state = "bathrobe" diff --git a/code/modules/emoji/emoji_parse.dm b/code/modules/emoji/emoji_parse.dm index 33d32227e1..3fd83899c9 100644 --- a/code/modules/emoji/emoji_parse.dm +++ b/code/modules/emoji/emoji_parse.dm @@ -16,11 +16,16 @@ search = findtext(text, ":", pos + length(text[pos])) if(search) emoji = lowertext(copytext(text, pos + length(text[pos]), search)) + var/isthisapath = (emoji[1] == "/") && text2path(emoji) var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat) var/tag = sheet.icon_tag("emoji-[emoji]") if(tag) parsed += "[tag]" //evil way of enforcing 16x16 pos = search + length(text[pos]) + else if(ispath(isthisapath, /atom)) //path + var/atom/thisisanatom = isthisapath + parsed += "[icon2html(initial(thisisanatom.icon), world, initial(thisisanatom.icon_state))]" + pos = search + length(text[pos]) else parsed += copytext(text, pos, search) pos = search diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index 89511a7b15..06318df4f5 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -42,6 +42,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 name = "immovable rod" desc = "What the fuck is that?" icon = 'icons/obj/objects.dmi' + movement_type = FLOATING icon_state = "immrod" throwforce = 100 move_force = INFINITY diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm index f1803b03ee..af44b54ed0 100644 --- a/code/modules/events/pirates.dm +++ b/code/modules/events/pirates.dm @@ -95,14 +95,11 @@ icon_state = "dominator" density = TRUE var/active = FALSE - var/obj/item/gps/gps var/credits_stored = 0 var/siphon_per_tick = 5 /obj/machinery/shuttle_scrambler/Initialize(mapload) . = ..() - gps = new/obj/item/gps/internal/pirate(src) - gps.tracking = FALSE update_icon() /obj/machinery/shuttle_scrambler/process() @@ -112,6 +109,7 @@ if(D) var/siphoned = min(D.account_balance,siphon_per_tick) D.adjust_money(-siphoned) + credits_stored += siphoned interrupt_research() else return @@ -120,7 +118,7 @@ /obj/machinery/shuttle_scrambler/proc/toggle_on(mob/user) SSshuttle.registerTradeBlockade(src) - gps.tracking = TRUE + AddComponent(/datum/component/gps, "Nautical Signal") active = TRUE to_chat(user,"You toggle [src] [active ? "on":"off"].") to_chat(user,"The scrambling signal can be now tracked by GPS.") @@ -130,7 +128,7 @@ if(!active) if(alert(user, "Turning the scrambler on will make the shuttle trackable by GPS. Are you sure you want to do it?", "Scrambler", "Yes", "Cancel") == "Cancel") return - if(active || !user.canUseTopic(src)) + if(active || !user.canUseTopic(src, BE_CLOSE)) return toggle_on(user) update_icon() @@ -147,35 +145,31 @@ new /obj/effect/temp_visual/emp(get_turf(S)) /obj/machinery/shuttle_scrambler/proc/dump_loot(mob/user) - new /obj/item/holochip(drop_location(), credits_stored) - to_chat(user,"You retrieve the siphoned credits!") - credits_stored = 0 + if(credits_stored) // Prevents spamming empty holochips + new /obj/item/holochip(drop_location(), credits_stored) + to_chat(user,"You retrieve the siphoned credits!") + credits_stored = 0 + else + to_chat(user,"There's nothing to withdraw.") /obj/machinery/shuttle_scrambler/proc/send_notification() priority_announce("Data theft signal detected, source registered on local gps units.") /obj/machinery/shuttle_scrambler/proc/toggle_off(mob/user) SSshuttle.clearTradeBlockade(src) - gps.tracking = FALSE active = FALSE STOP_PROCESSING(SSobj,src) -/obj/machinery/shuttle_scrambler/update_overlays() - . = ..() +/obj/machinery/shuttle_scrambler/update_icon_state() if(active) - var/mutable_appearance/M = mutable_appearance(icon, "dominator-overlay") - M.color = "#00FFFF" - . += M + icon_state = "dominator-blue" + else + icon_state = "dominator" /obj/machinery/shuttle_scrambler/Destroy() toggle_off() - QDEL_NULL(gps) return ..() -/obj/item/gps/internal/pirate - gpstag = "Nautical Signal" - desc = "You can hear shanties over the static." - /obj/machinery/computer/shuttle/pirate name = "pirate shuttle console" shuttleId = "pirateship" @@ -312,11 +306,10 @@ else pad = locate() in range(4,src) -/obj/machinery/computer/piratepad_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/piratepad_control/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "CargoHoldTerminal", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "CargoHoldTerminal", name) ui.open() /obj/machinery/computer/piratepad_control/ui_data(mob/user) diff --git a/code/modules/food_and_drinks/food/snacks/meat.dm b/code/modules/food_and_drinks/food/snacks/meat.dm index fe70739ab5..4d287fdb86 100644 --- a/code/modules/food_and_drinks/food/snacks/meat.dm +++ b/code/modules/food_and_drinks/food/snacks/meat.dm @@ -162,6 +162,14 @@ tastes = list("brains" = 1, "meat" = 1) foodtype = RAW | MEAT | TOXIC +/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ethereal + icon_state = "etherealmeat" + desc = "So shiny you feel like ingesting it might make you shine too" + filling_color = "#97ee63" + list_reagents = list(/datum/reagent/consumable/liquidelectricity = 3) + tastes = list("pure electricity" = 2, "meat" = 1) + foodtype = RAW | MEAT | TOXIC + /obj/item/reagent_containers/food/snacks/carpmeat/aquatic name = "fillet" desc = "A fillet of one of the local water dwelling species." diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm index 2a4139dbb9..05a0da2793 100644 --- a/code/modules/food_and_drinks/food/snacks_meat.dm +++ b/code/modules/food_and_drinks/food/snacks_meat.dm @@ -139,11 +139,22 @@ tastes = list("meat" = 1, "salmon" = 1) foodtype = MEAT | ALCOHOL +/obj/item/reagent_containers/food/snacks/rawmeatball + name = "raw meatball" + desc = "Raw mushy meat. Better cook this!" + icon_state = "rawmeatball" + cooked_type = /obj/item/reagent_containers/food/snacks/meatball + list_reagents = list(/datum/reagent/consumable/nutriment = 3) + filling_color = "#bd2020" + tastes = list("meat" = 1, "slime" = 1) + foodtype = MEAT | RAW + /obj/item/reagent_containers/food/snacks/meatball name = "meatball" desc = "MAMA MIA DAS A SPICY" icon_state = "meatball" list_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/nutriment/vitamin = 1) + bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 1) filling_color = "#800000" tastes = list("meat" = 1) foodtype = MEAT diff --git a/code/modules/food_and_drinks/food/snacks_pie.dm b/code/modules/food_and_drinks/food/snacks_pie.dm index 03f4640718..81805f5529 100644 --- a/code/modules/food_and_drinks/food/snacks_pie.dm +++ b/code/modules/food_and_drinks/food/snacks_pie.dm @@ -113,6 +113,27 @@ tastes = list("pie" = 1, "meat" = 1) foodtype = GRAIN | MEAT +/obj/item/reagent_containers/food/snacks/pie/burek + name = "Burek" + icon = 'icons/obj/food/piecake.dmi' + icon_state = "burek" + desc = "If you know, you know." + slice_path = /obj/item/reagent_containers/food/snacks/pie/burekslice + slices_num = 4 + bonus_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/nutriment/vitamin = 6) + list_reagents = list(/datum/reagent/consumable/nutriment= 20, /datum/reagent/consumable/nutriment/vitamin = 6) + bitesize = 12 + tastes = list("meat" = 1, "oil" = 1) + foodtype = GRAIN | MEAT + +/obj/item/reagent_containers/food/snacks/pie/burekslice + name = "Burek Slice" + icon = 'icons/obj/food/piecake.dmi' + icon_state = "burekslice" + desc = "A slice of Burek, watch out for oil stains!" + tastes = list("meat" = 1, "oil" = 1) + foodtype = GRAIN | MEAT + /obj/item/reagent_containers/food/snacks/pie/tofupie name = "tofu-pie" diff --git a/code/modules/food_and_drinks/food/snacks_vend.dm b/code/modules/food_and_drinks/food/snacks_vend.dm index 38f7ecf5b1..b4c7c89b74 100644 --- a/code/modules/food_and_drinks/food/snacks_vend.dm +++ b/code/modules/food_and_drinks/food/snacks_vend.dm @@ -91,3 +91,13 @@ tastes = list("sweetness" = 3, "cake" = 1) foodtype = GRAIN | FRUIT | VEGETABLES custom_price = PRICE_CHEAP + +/obj/item/reagent_containers/food/snacks/energybar + name = "High-power energy bars" + icon_state = "energybar" + desc = "An energy bar with a lot of punch, you probably shouldn't eat this if you're not an Ethereal." + trash = /obj/item/trash/energybar + list_reagents = list(/datum/reagent/consumable/nutriment = 3, /datum/reagent/consumable/liquidelectricity = 3) + filling_color = "#97ee63" + tastes = list("pure electricity" = 3, "fitness" = 2) + foodtype = TOXIC diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index 36e3b64487..35fa40e15d 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -12,6 +12,7 @@ idle_power_usage = 5 active_power_usage = 100 circuit = /obj/item/circuitboard/machine/smartfridge + var/max_n_of_items = 1500 var/allow_ai_retrieve = FALSE var/list/initial_contents @@ -38,12 +39,10 @@ if(in_range(user, src) || isobserver(user)) . += "The status display reads: This unit can hold a maximum of [max_n_of_items] items." -/obj/machinery/smartfridge/power_change() - ..() - update_icon() - /obj/machinery/smartfridge/update_icon_state() + SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) if(!stat) + SSvis_overlays.add_vis_overlay(src, icon, "smartfridge-light-mask", EMISSIVE_LAYER, EMISSIVE_PLANE, dir, alpha) if(visible_contents) switch(contents.len) if(0) @@ -66,9 +65,6 @@ ********************/ /obj/machinery/smartfridge/attackby(obj/item/O, mob/user, params) - if(user.a_intent == INTENT_HARM) - return ..() - if(default_deconstruction_screwdriver(user, icon_state, icon_state, O)) cut_overlays() if(panel_open) @@ -87,46 +83,53 @@ updateUsrDialog() return - if(stat) - updateUsrDialog() - return FALSE + if(!stat) - if(contents.len >= max_n_of_items) - to_chat(user, "\The [src] is full!") - return FALSE - - if(accept_check(O)) - load(O) - user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") - updateUsrDialog() - if (visible_contents) - update_icon() - return TRUE - - if(istype(O, /obj/item/storage/bag)) - var/obj/item/storage/P = O - var/loaded = 0 - for(var/obj/G in P.contents) - if(contents.len >= max_n_of_items) - break - if(accept_check(G)) - load(G) - loaded++ - updateUsrDialog() - - if(loaded) - user.visible_message("[user] loads \the [src] with \the [O].", \ - "You [contents.len >= max_n_of_items ? "fill" : "load"] \the [src] with \the [O].") - if(O.contents.len > 0) - to_chat(user, "Some items are refused.") - return TRUE - else - to_chat(user, "There is nothing in [O] to put in [src]!") + if(contents.len >= max_n_of_items) + to_chat(user, "\The [src] is full!") return FALSE - to_chat(user, "\The [src] smartly refuses [O].") - updateUsrDialog() - return FALSE + if(accept_check(O)) + load(O) + user.visible_message("[user] adds \the [O] to \the [src].", "You add \the [O] to \the [src].") + updateUsrDialog() + if (visible_contents) + update_icon() + return TRUE + + if(istype(O, /obj/item/storage/bag)) + var/obj/item/storage/P = O + var/loaded = 0 + for(var/obj/G in P.contents) + if(contents.len >= max_n_of_items) + break + if(accept_check(G)) + load(G) + loaded++ + updateUsrDialog() + + if(loaded) + if(contents.len >= max_n_of_items) + user.visible_message("[user] loads \the [src] with \the [O].", \ + "You fill \the [src] with \the [O].") + else + user.visible_message("[user] loads \the [src] with \the [O].", \ + "You load \the [src] with \the [O].") + if(O.contents.len > 0) + to_chat(user, "Some items are refused.") + if (visible_contents) + update_icon() + return TRUE + else + to_chat(user, "There is nothing in [O] to put in [src]!") + return FALSE + + if(user.a_intent != INTENT_HARM) + to_chat(user, "\The [src] smartly refuses [O].") + updateUsrDialog() + return FALSE + else + return ..() @@ -151,16 +154,16 @@ return TRUE ///Really simple proc, just moves the object "O" into the hands of mob "M" if able, done so I could modify the proc a little for the organ fridge -/obj/machinery/smartfridge/proc/dispense(obj/item/O, var/mob/M) +/obj/machinery/smartfridge/proc/dispense(obj/item/O, mob/M) if(!M.put_in_hands(O)) O.forceMove(drop_location()) adjust_item_drop_location(O) -/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/smartfridge/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "SmartVend", name, 440, 550, master_ui, state) + ui = new(user, src, "SmartVend", name) ui.set_autoupdate(FALSE) ui.open() @@ -232,7 +235,7 @@ // ---------------------------- /obj/machinery/smartfridge/drying_rack name = "drying rack" - desc = "A wooden contraption, used to dry plant products, food and leather." + desc = "A wooden contraption, used to dry plant products, food and hide." icon = 'icons/obj/hydroponics/equipment.dmi' icon_state = "drying_rack" use_power = IDLE_POWER_USE @@ -277,6 +280,11 @@ return TRUE return FALSE +// /obj/machinery/smartfridge/drying_rack/powered() do we have this? no. +// if(!anchored) +// return FALSE +// return ..() + /obj/machinery/smartfridge/drying_rack/power_change() if(powered() && anchored) stat &= ~NOPOWER @@ -285,6 +293,10 @@ toggle_drying(TRUE) update_icon() + // . = ..() + // if(!powered()) + // toggle_drying(TRUE) + /obj/machinery/smartfridge/drying_rack/load() //For updating the filled overlay ..() update_icon() @@ -308,7 +320,7 @@ var/obj/item/reagent_containers/food/snacks/S = O if(S.dried_type) return TRUE - if(istype(O, /obj/item/stack/sheet/wetleather/)) + if(istype(O, /obj/item/stack/sheet/wetleather/)) //no wethide return TRUE return FALSE @@ -386,19 +398,19 @@ /obj/machinery/smartfridge/extract/preloaded initial_contents = list(/obj/item/slime_scanner = 2) -// ------------------------- You think you're better than Chem, huh? +// ------------------------- // Organ Surgery Smartfridge -// ------------------------- Just wait till Tamiorgans +// ------------------------- /obj/machinery/smartfridge/organ name = "smart organ storage" desc = "A refrigerated storage unit for organ storage." - max_n_of_items = 25 //vastly lower to prevent processing too long + max_n_of_items = 20 //vastly lower to prevent processing too long var/repair_rate = 0 /obj/machinery/smartfridge/organ/accept_check(obj/item/O) - if(istype(O, /obj/item/organ)) + if(isorgan(O) || isbodypart(O)) return TRUE - if(istype(O, /obj/item/reagent_containers/syringe)) + if(istype(O, /obj/item/reagent_containers/syringe)) //other medical things. return TRUE if(istype(O, /obj/item/reagent_containers/glass/bottle)) return TRUE @@ -410,7 +422,7 @@ . = ..() if(!.) //if the item loads, clear can_decompose return - if(istype(O, /obj/item/organ)) + if(isorgan(O)) var/obj/item/organ/organ = O organ.organ_flags |= ORGAN_FROZEN @@ -426,12 +438,13 @@ return O.applyOrganDamage(-repair_rate) -/obj/machinery/smartfridge/organ/Exited(obj/item/organ/AM, atom/newLoc) +/obj/machinery/smartfridge/organ/Exited(atom/movable/AM, atom/newLoc) . = ..() - if(istype(AM)) - AM.organ_flags &= ~ORGAN_FROZEN + if(isorgan(AM)) + var/obj/item/organ/O = AM + O.organ_flags &= ~ORGAN_FROZEN -/obj/machinery/smartfridge/organ/preloaded +/obj/machinery/smartfridge/organ/preloaded //cit specific?????? initial_contents = list( /obj/item/reagent_containers/medspray/synthtissue = 1, /obj/item/reagent_containers/medspray/sterilizine = 1) @@ -450,6 +463,15 @@ desc = "A refrigerated storage unit for medicine storage." /obj/machinery/smartfridge/chemistry/accept_check(obj/item/O) + var/static/list/chemfridge_typecache = typecacheof(list( + /obj/item/reagent_containers/syringe, + /obj/item/reagent_containers/glass/bottle, + /obj/item/reagent_containers/glass/beaker, + /obj/item/reagent_containers/spray, + // /obj/item/reagent_containers/medigel, + /obj/item/reagent_containers/chem_pack + )) + if(istype(O, /obj/item/storage/pill_bottle)) if(O.contents.len) for(var/obj/item/I in O) @@ -463,7 +485,7 @@ return TRUE if(!O.reagents || !O.reagents.reagent_list.len) // other empty containers not accepted return FALSE - if(istype(O, /obj/item/reagent_containers/syringe) || istype(O, /obj/item/reagent_containers/glass/bottle) || istype(O, /obj/item/reagent_containers/glass/beaker) || istype(O, /obj/item/reagent_containers/spray) || istype(O, /obj/item/reagent_containers/medspray) || istype(O, /obj/item/reagent_containers/chem_pack)) + if(is_type_in_typecache(O, chemfridge_typecache)) return TRUE return FALSE @@ -487,6 +509,7 @@ /obj/item/reagent_containers/glass/bottle/cold = 1, /obj/item/reagent_containers/glass/bottle/flu_virion = 1, /obj/item/reagent_containers/glass/bottle/mutagen = 1, + /obj/item/reagent_containers/glass/bottle/sugar = 1, /obj/item/reagent_containers/glass/bottle/plasma = 1, /obj/item/reagent_containers/glass/bottle/synaptizine = 1, /obj/item/reagent_containers/glass/bottle/formaldehyde = 1) @@ -498,8 +521,8 @@ name = "disk compartmentalizer" desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)." icon_state = "disktoaster" - visible_contents = FALSE pass_flags = PASSTABLE + visible_contents = FALSE /obj/machinery/smartfridge/disks/accept_check(obj/item/O) if(istype(O, /obj/item/disk/)) diff --git a/code/modules/food_and_drinks/recipes/processor_recipes.dm b/code/modules/food_and_drinks/recipes/processor_recipes.dm index 1e3afd1cf5..f75cf6ef3a 100644 --- a/code/modules/food_and_drinks/recipes/processor_recipes.dm +++ b/code/modules/food_and_drinks/recipes/processor_recipes.dm @@ -6,7 +6,7 @@ /datum/food_processor_process/meat input = /obj/item/reagent_containers/food/snacks/meat/slab - output = /obj/item/reagent_containers/food/snacks/meatball + output = /obj/item/reagent_containers/food/snacks/rawmeatball /datum/food_processor_process/bacon input = /obj/item/reagent_containers/food/snacks/meat/rawcutlet diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pies_sweets.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pies_sweets.dm index 52becf81df..289c698b9a 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pies_sweets.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pies_sweets.dm @@ -130,6 +130,18 @@ result = /obj/item/reagent_containers/food/snacks/pie/dulcedebatata subcategory = CAT_PIE +/datum/crafting_recipe/food/burek + name = "Burek" + reqs = list( + /datum/reagent/consumable/blackpepper = 3, + /datum/reagent/consumable/sodiumchloride = 3, + /obj/item/reagent_containers/food/snacks/pizzabread = 2, + /obj/item/reagent_containers/food/snacks/meat/cutlet/plain = 6, + /obj/item/reagent_containers/food/snacks/butter = 1, + ) + result = /obj/item/reagent_containers/food/snacks/pie/burek + subcategory = CAT_PIE + /datum/crafting_recipe/food/meatpie name = "Meat pie" reqs = list( @@ -302,4 +314,4 @@ /obj/item/reagent_containers/food/snacks/spiderling = 1 ) result = /obj/item/reagent_containers/food/snacks/spiderlollipop - subcategory = CAT_PIE \ No newline at end of file + subcategory = CAT_PIE diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm index d72e1b1fc8..e5cd36cad6 100644 --- a/code/modules/holodeck/computer.dm +++ b/code/modules/holodeck/computer.dm @@ -2,10 +2,7 @@ Holodeck Update The on-station holodeck area is of type [holodeck_type]. - All types found in GLOB.holodeck_areas_per_comp_type[src.type], generated on make_datum_references_lists(), - are loaded into the program cache or emag programs list. - Paths with their abstract_type variable equal to themselves will be skipped. - + All subtypes of [program_type] are loaded into the program cache or emag programs list. If init_program is null, a random program will be loaded on startup. If you don't wish this, set it to the offline program or another of your choosing. @@ -15,6 +12,7 @@ 3) Create a new control console that uses those areas Non-mapped areas should be skipped but you should probably comment them out anyway. + The base of program_type will always be ignored; only subtypes will be loaded. */ #define HOLODECK_CD 25 @@ -26,18 +24,20 @@ icon_screen = "holocontrol" idle_power_usage = 10 active_power_usage = 50 + var/area/holodeck/linked var/area/holodeck/program var/area/holodeck/last_program var/area/offline_program = /area/holodeck/rec_center/offline - // Splitting this up allows two holodecks of the same size - // to use the same source patterns. Y'know, if you want to. - var/holodeck_type = /area/holodeck/rec_center - var/list/program_cache var/list/emag_programs + // Splitting this up allows two holodecks of the same size + // to use the same source patterns. Y'know, if you want to. + var/holodeck_type = /area/holodeck/rec_center // locate(this) to get the target holodeck + var/program_type = /area/holodeck/rec_center // subtypes of this (but not this itself) are loadable programs + var/active = FALSE var/damaged = FALSE var/list/spawned = list() @@ -49,41 +49,47 @@ return INITIALIZE_HINT_LATELOAD /obj/machinery/computer/holodeck/LateInitialize() - linked = SSholodeck.target_holodeck_area[type] - offline_program = SSholodeck.offline_programs[type] + if(ispath(holodeck_type, /area)) + linked = pop(get_areas(holodeck_type, FALSE)) + if(ispath(offline_program, /area)) + offline_program = pop(get_areas(offline_program), FALSE) + // the following is necessary for power reasons if(!linked || !offline_program) log_world("No matching holodeck area found") qdel(src) return - - program_cache = SSholodeck.program_cache[type] - emag_programs = SSholodeck.emag_program_cache[type] - - // the following is necessary for power reasons - var/area/AS = get_base_area(src) + var/area/AS = get_area(src) if(istype(AS, /area/holodeck)) log_mapping("Holodeck computer cannot be in a holodeck, This would cause circular power dependency.") qdel(src) return else linked.linked = src - + /* + var/area/my_area = get_area(src) + if(my_area) + linked.power_usage = my_area.power_usage + else + linked.power_usage = new /list(AREA_USAGE_LEN) + */ + generate_program_list() load_program(offline_program, FALSE, FALSE) /obj/machinery/computer/holodeck/Destroy() emergency_shutdown() if(linked) linked.linked = null + //linked.power_usage = new /list(AREA_USAGE_LEN) return ..() /obj/machinery/computer/holodeck/power_change() . = ..() toggle_power(!stat) -/obj/machinery/computer/holodeck/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/holodeck/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Holodeck", name, 400, 500, master_ui, state) + ui = new(user, src, "Holodeck", name) ui.open() /obj/machinery/computer/holodeck/ui_data(mob/user) @@ -107,19 +113,27 @@ var/program_to_load = text2path(params["type"]) if(!ispath(program_to_load)) return FALSE + var/valid = FALSE + var/list/checked = program_cache.Copy() + if(obj_flags & EMAGGED) + checked |= emag_programs + for(var/prog in checked) + var/list/P = prog + if(P["type"] == program_to_load) + valid = TRUE + break + if(!valid) + return FALSE + var/area/A = locate(program_to_load) in GLOB.sortedAreas if(A) load_program(A) if("safety") - if(!hasSiliconAccessInArea(usr) && !IsAdminGhost(usr)) - var/msg = "[key_name(usr)] attempted to emag the holodeck using a href they shouldn't have!" - message_admins(msg) - log_admin(msg) - return - obj_flags ^= EMAGGED - if((obj_flags & EMAGGED) && program && emag_programs[program.name]) + if((obj_flags & EMAGGED) && program) emergency_shutdown() nerf(obj_flags & EMAGGED) + obj_flags ^= EMAGGED + say("Safeties restored. Restarting...") /obj/machinery/computer/holodeck/process() if(damaged && prob(10)) @@ -160,13 +174,12 @@ if(!LAZYLEN(emag_programs)) to_chat(user, "[src] does not seem to have a card swipe port. It must be an inferior model.") return - playsound(src, "sparks", 75, 1) + playsound(src, "sparks", 75, TRUE) obj_flags |= EMAGGED to_chat(user, "You vastly increase projector power and override the safety and security protocols.") - to_chat(user, "Warning. Automatic shutoff and derezing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator.") + say("Warning. Automatic shutoff and derezzing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator.") log_game("[key_name(user)] emagged the Holodeck Control Console") nerf(!(obj_flags & EMAGGED)) - return TRUE /obj/machinery/computer/holodeck/emp_act(severity) . = ..() @@ -182,6 +195,19 @@ emergency_shutdown() return ..() +/obj/machinery/computer/holodeck/proc/generate_program_list() + for(var/typekey in subtypesof(program_type)) + var/area/holodeck/A = GLOB.areas_by_type[typekey] + if(!A || !A.contents.len) + continue + var/list/info_this = list() + info_this["name"] = A.name + info_this["type"] = A.type + if(A.restricted) + LAZYADD(emag_programs, list(info_this)) + else + LAZYADD(program_cache, list(info_this)) + /obj/machinery/computer/holodeck/proc/toggle_power(toggleOn = FALSE) if(active == toggleOn) return @@ -281,7 +307,7 @@ silent = FALSE // otherwise make sure they are dropped if(!silent) - visible_message("[O] fades away!") + visible_message("[O] fades away!") qdel(O) #undef HOLODECK_CD diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm index 06d2b3efda..b61d9451ef 100644 --- a/code/modules/hydroponics/biogenerator.dm +++ b/code/modules/hydroponics/biogenerator.dm @@ -10,13 +10,13 @@ var/processing = FALSE var/obj/item/reagent_containers/glass/beaker = null var/points = 0 - var/menustat = "menu" var/efficiency = 0 var/productivity = 0 var/max_items = 40 var/datum/techweb/stored_research var/list/show_categories = list("Food", "Botany Chemicals", "Organic Materials") - var/list/timesFiveCategories = list("Food", "Botany Chemicals") + /// Currently selected category in the UI + var/selected_cat /obj/machinery/biogenerator/Initialize() . = ..() @@ -37,22 +37,20 @@ if(A == beaker) beaker = null update_icon() - updateUsrDialog() /obj/machinery/biogenerator/RefreshParts() - var/E = 0.5 - var/P = 0.5 - var/max_storage = 20 + var/E = 0 + var/P = 0 + var/max_storage = 40 for(var/obj/item/stock_parts/matter_bin/B in component_parts) - P += B.rating * 0.5 - max_storage = max(20 * B.rating, max_storage) + P += B.rating + max_storage = 40 * B.rating for(var/obj/item/stock_parts/manipulator/M in component_parts) - E += M.rating * 0.5 + E += M.rating efficiency = E productivity = P max_items = max_storage - /obj/machinery/biogenerator/examine(mob/user) . = ..() if(in_range(user, src) || isobserver(user)) @@ -70,7 +68,6 @@ icon_state = "biogen-stand" else icon_state = "biogen-work" - return /obj/machinery/biogenerator/attackby(obj/item/O, mob/user, params) if(user.a_intent == INTENT_HARM) @@ -102,7 +99,6 @@ beaker = O to_chat(user, "You add the container to the machine.") update_icon() - updateUsrDialog() else to_chat(user, "Close the maintenance panel first.") return @@ -139,9 +135,9 @@ to_chat(user, "You put [O.name] in [src.name]") return TRUE //no afterattack else if (istype(O, /obj/item/disk/design_disk)) - user.visible_message("[user] begins to load \the [O] in \the [src]...", - "You begin to load a design from \the [O]...", - "You hear the chatter of a floppy drive.") + user.visible_message("[user] begins to load \the [O] in \the [src]...", + "You begin to load a design from \the [O]...", + "You hear the chatter of a floppy drive.") processing = TRUE var/obj/item/disk/design_disk/D = O if(do_after(user, 10, target = src)) @@ -153,106 +149,53 @@ else to_chat(user, "You cannot put this in [src.name]!") -/obj/machinery/biogenerator/ui_interact(mob/user) - if(stat & BROKEN || panel_open) - return - . = ..() - var/dat - if(processing) - dat += "
Biogenerator is processing! Please wait...

" - else - switch(menustat) - if("nopoints") - dat += "
You do not have enough biomass to create products.
Please, put growns into reactor and activate it.
" - menustat = "menu" - if("complete") - dat += "
Operation complete.
" - menustat = "menu" - if("void") - dat += "
Error: No growns inside.
Please, put growns into reactor.
" - menustat = "menu" - if("nobeakerspace") - dat += "
Not enough space left in container. Unable to create product.
" - menustat = "menu" - if(beaker) - var/categories = show_categories.Copy() - for(var/V in categories) - categories[V] = list() - for(var/V in stored_research.researched_designs) - var/datum/design/D = SSresearch.techweb_design_by_id(V) - for(var/C in categories) - if(C in D.category) - categories[C] += D - - dat += "
Biomass: [points] units.

" - dat += "ActivateDetach Container" - for(var/cat in categories) - dat += "

[cat]:

" - dat += "
" - for(var/V in categories[cat]) - var/datum/design/D = V - dat += "[D.name]: Make" - if(cat in timesFiveCategories) - dat += "x5" - if(ispath(D.build_path, /obj/item/stack)) - dat += "x10" - dat += "([CEILING(D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency, 1)])
" - dat += "
" - else - dat += "
No container inside, please insert container.
" - - var/datum/browser/popup = new(user, "biogen", name, 350, 520) - popup.set_content(dat) - popup.open() - /obj/machinery/biogenerator/AltClick(mob/living/user) . = ..() - if(istype(user) && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && can_interact(user)) detach(user) -/obj/machinery/biogenerator/proc/activate() - if (usr.stat != CONSCIOUS) +/** + * activate: Activates biomass processing and converts all inserted grown products into biomass + * + * Arguments: + * * user The mob starting the biomass processing + */ +/obj/machinery/biogenerator/proc/activate(mob/user) + if(user.stat != CONSCIOUS) return - if (src.stat != NONE) //NOPOWER etc + if(stat != NONE) return if(processing) - to_chat(usr, "The biogenerator is in the process of working.") + to_chat(user, "The biogenerator is in the process of working.") return var/S = 0 - var/total = 0 for(var/obj/item/reagent_containers/food/snacks/grown/I in contents) S += 5 - var/nutri_amount = I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) - if(nutri_amount < 0.1) - total += 1*productivity + if(I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) < 0.1) + points += 1 * productivity else - total += nutri_amount*10*productivity + points += I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) * 10 * productivity qdel(I) - points += round(total) if(S) processing = TRUE update_icon() - updateUsrDialog() - playsound(src.loc, 'sound/machines/blender.ogg', 50, 1) - use_power(S*30) - sleep(S+15/productivity) + playsound(loc, 'sound/machines/blender.ogg', 50, TRUE) + use_power(S * 30) + sleep(S + 15 / productivity) + if(QDELETED(src)) //let's not. + return processing = FALSE update_icon() - else - menustat = "void" /obj/machinery/biogenerator/proc/check_cost(list/materials, multiplier = 1, remove_points = TRUE) if(materials.len != 1 || materials[1] != SSmaterials.GetMaterialRef(/datum/material/biomass)) return FALSE - var/cost = CEILING(materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency, 1) - if (cost > points) - menustat = "nopoints" + if (materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency > points) return FALSE else if(remove_points) - points -= cost + points -= materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency update_icon() - updateUsrDialog() return TRUE /obj/machinery/biogenerator/proc/check_container_volume(list/reagents, multiplier = 1) @@ -262,7 +205,6 @@ sum_reagents *= multiplier if(beaker.reagents.total_volume + sum_reagents > beaker.reagents.maximum_volume) - menustat = "nobeakerspace" return FALSE return TRUE @@ -284,6 +226,7 @@ var/i = amount while(i > 0) if(!check_container_volume(D.make_reagents)) + say("Warning: Attached container does not have enough free capacity!") return . if(!check_cost(D.materials)) return . @@ -293,51 +236,100 @@ beaker.reagents.add_reagent(R, D.make_reagents[R]) . = 1 --i - - menustat = "complete" update_icon() return . /obj/machinery/biogenerator/proc/detach(mob/living/user) if(beaker) - user.put_in_hands(beaker) + if(can_interact(user)) + user.put_in_hands(beaker) + else + beaker.drop_location(get_turf(src)) beaker = null update_icon() -/obj/machinery/biogenerator/Topic(href, href_list) - if(..() || panel_open) +/obj/machinery/biogenerator/ui_status(mob/user) + if(stat & BROKEN || panel_open) + return UI_CLOSE + return ..() + +/obj/machinery/biogenerator/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/research_designs), + ) + +/obj/machinery/biogenerator/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Biogenerator", name) + ui.open() + +/obj/machinery/biogenerator/ui_data(mob/user) + var/list/data = list() + data["beaker"] = beaker ? TRUE : FALSE + data["biomass"] = points + data["processing"] = processing + if(locate(/obj/item/reagent_containers/food/snacks/grown) in contents) + data["can_process"] = TRUE + else + data["can_process"] = FALSE + return data + +/obj/machinery/biogenerator/ui_static_data(mob/user) + var/list/data = list() + data["categories"] = list() + + var/categories = show_categories.Copy() + for(var/V in categories) + categories[V] = list() + for(var/V in stored_research.researched_designs) + var/datum/design/D = SSresearch.techweb_design_by_id(V) + for(var/C in categories) + if(C in D.category) + categories[C] += D + + for(var/category in categories) + var/list/cat = list( + "name" = category, + "items" = (category == selected_cat ? list() : null)) + for(var/item in categories[category]) + var/datum/design/D = item + cat["items"] += list(list( + "id" = D.id, + "name" = D.name, + "cost" = D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency, + )) + data["categories"] += list(cat) + + return data + +/obj/machinery/biogenerator/ui_act(action, list/params) + if(..()) return - usr.set_machine(src) - - if(href_list["activate"]) - activate() - updateUsrDialog() - - else if(href_list["detach"]) - detach(usr) - updateUsrDialog() - - else if(href_list["create"]) - var/amount = (text2num(href_list["amount"])) - //Can't be outside these (if you change this keep a sane limit) - amount = clamp(amount, 1, 50) - var/id = href_list["create"] - if(!stored_research.researched_designs.Find(id)) - //naughty naughty - stack_trace("ID did not map to a researched datum [id]") - return - - //Get design by id (or may return error design) - var/datum/design/D = SSresearch.techweb_design_by_id(id) - //Valid design datum, amount and the datum is not the error design, lets proceed - if(D && amount && !istype(D, /datum/design/error_design)) - create_product(D, amount) - //This shouldnt happen normally but href forgery is real - else - stack_trace("ID could not be turned into a valid techweb design datum [id]") - updateUsrDialog() - - else if(href_list["menu"]) - menustat = "menu" - updateUsrDialog() + switch(action) + if("activate") + activate(usr) + return TRUE + if("detach") + detach(usr) + return TRUE + if("create") + var/amount = text2num(params["amount"]) + amount = clamp(amount, 1, 10) + if(!amount) + return + var/id = params["id"] + if(!stored_research.researched_designs.Find(id)) + stack_trace("ID did not map to a researched datum [id]") + return + var/datum/design/D = SSresearch.techweb_design_by_id(id) + if(D && !istype(D, /datum/design/error_design)) + create_product(D, amount) + else + stack_trace("ID could not be turned into a valid techweb design datum [id]") + return + return TRUE + if("select") + selected_cat = params["category"] + return TRUE diff --git a/code/modules/hydroponics/seed_extractor.dm b/code/modules/hydroponics/seed_extractor.dm index 63b96632e6..71701d9637 100644 --- a/code/modules/hydroponics/seed_extractor.dm +++ b/code/modules/hydroponics/seed_extractor.dm @@ -1,3 +1,18 @@ +/** + * Finds and extracts seeds from an object + * + * Checks if the object is such that creates a seed when extracted. Used by seed + * extractors or posably anything that would create seeds in some way. The seeds + * are dropped either at the extractor, if it exists, or where the original object + * was and it qdel's the object + * + * Arguments: + * * O - Object containing the seed, can be the loc of the dumping of seeds + * * t_max - Amount of seed copies to dump, -1 is ranomized + * * extractor - Seed Extractor, used as the dumping loc for the seeds and seed multiplier + * * user - checks if we can remove the object from the inventory + * * + */ /proc/seedify(obj/item/O, t_max, obj/machinery/seed_extractor/extractor, mob/living/user) var/t_amount = 0 var/list/seeds = list() @@ -46,20 +61,22 @@ icon_state = "sextractor" density = TRUE circuit = /obj/item/circuitboard/machine/seed_extractor - var/piles = list() + /// Associated list of seeds, they are all weak refs. We check the len to see how many refs we have for each + // seed + var/list/piles = list() var/max_seeds = 1000 var/seed_multiplier = 1 /obj/machinery/seed_extractor/RefreshParts() for(var/obj/item/stock_parts/matter_bin/B in component_parts) - max_seeds = 1000 * B.rating + max_seeds = initial(max_seeds) * B.rating for(var/obj/item/stock_parts/manipulator/M in component_parts) - seed_multiplier = M.rating + seed_multiplier = initial(seed_multiplier) * M.rating /obj/machinery/seed_extractor/examine(mob/user) . = ..() if(in_range(user, src) || isobserver(user)) - . += "The status display reads: Extracting [seed_multiplier] seed(s) per piece of produce.
Machine can store up to [max_seeds] seeds.
" + . += "The status display reads: Extracting [seed_multiplier] seed(s) per piece of produce.
Machine can store up to [max_seeds]% seeds.
" /obj/machinery/seed_extractor/attackby(obj/item/O, mob/user, params) @@ -102,78 +119,26 @@ else return ..() -/datum/seed_pile - var/name = "" - var/lifespan = 0 //Saved stats - var/endurance = 0 - var/maturation = 0 - var/production = 0 - var/yield = 0 - var/potency = 0 - var/amount = 0 +/** + * Generate seed string + * + * Creates a string based of the traits of a seed. We use this string as a bucket for all + * seeds that match as well as the key the ui uses to get the seed. We also use the key + * for the data shown in the ui. Javascript parses this string to display + * + * Arguments: + * * O - seed to generate the string from + */ +/obj/machinery/seed_extractor/proc/generate_seed_string(obj/item/seeds/O) + return "name=[O.name];lifespan=[O.lifespan];endurance=[O.endurance];maturation=[O.maturation];production=[O.production];yield=[O.yield];potency=[O.potency];instability=0" -/datum/seed_pile/New(var/name, var/life, var/endur, var/matur, var/prod, var/yie, var/poten, var/am = 1) - src.name = name - src.lifespan = life - src.endurance = endur - src.maturation = matur - src.production = prod - src.yield = yie - src.potency = poten - src.amount = am - -/obj/machinery/seed_extractor/ui_interact(mob/user) - . = ..() - if (stat) - return FALSE - - var/dat = "Stored seeds:
" - - if (contents.len == 0) - dat += "No seeds" - else - dat += "" - for (var/datum/seed_pile/O in piles) - dat += "" - dat += "" - dat += "
NameLifespanEnduranceMaturationProductionYieldPotencyStock
[O.name][O.lifespan][O.endurance][O.maturation][O.production][O.yield][O.potency]" - dat += "Vend ([O.amount] left)
" - var/datum/browser/popup = new(user, "seed_ext", name, 700, 400) - popup.set_content(dat) - popup.open() - return - -/obj/machinery/seed_extractor/Topic(var/href, var/list/href_list) - if(..()) - return - usr.set_machine(src) - - href_list["li"] = text2num(href_list["li"]) - href_list["en"] = text2num(href_list["en"]) - href_list["ma"] = text2num(href_list["ma"]) - href_list["pr"] = text2num(href_list["pr"]) - href_list["yi"] = text2num(href_list["yi"]) - href_list["pot"] = text2num(href_list["pot"]) - - for (var/datum/seed_pile/N in piles)//Find the pile we need to reduce... - if (href_list["name"] == N.name && href_list["li"] == N.lifespan && href_list["en"] == N.endurance && href_list["ma"] == N.maturation && href_list["pr"] == N.production && href_list["yi"] == N.yield && href_list["pot"] == N.potency) - if(N.amount <= 0) - return - N.amount = max(N.amount - 1, 0) - if (N.amount <= 0) - piles -= N - qdel(N) - break - - for (var/obj/T in contents)//Now we find the seed we need to vend - var/obj/item/seeds/O = T - if (O.plantname == href_list["name"] && O.lifespan == href_list["li"] && O.endurance == href_list["en"] && O.maturation == href_list["ma"] && O.production == href_list["pr"] && O.yield == href_list["yi"] && O.potency == href_list["pot"]) - O.forceMove(drop_location()) - break - - src.updateUsrDialog() - return +/** Add Seeds Proc. + * + * Adds the seeds to the contents and to an associated list that pregenerates the data + * needed to go to the ui handler + * + **/ /obj/machinery/seed_extractor/proc/add_seed(obj/item/seeds/O) if(contents.len >= 999) to_chat(usr, "\The [src] is full.") @@ -188,10 +153,47 @@ if(!M.transferItemToLoc(O, src)) return FALSE - . = TRUE - for (var/datum/seed_pile/N in piles) - if (O.plantname == N.name && O.lifespan == N.lifespan && O.endurance == N.endurance && O.maturation == N.maturation && O.production == N.production && O.yield == N.yield && O.potency == N.potency) - ++N.amount - return + var/seed_string = generate_seed_string(O) + if(piles[seed_string]) + piles[seed_string] += WEAKREF(O) + else + piles[seed_string] = list(WEAKREF(O)) + + . = TRUE + +/obj/machinery/seed_extractor/ui_state(mob/user) + return GLOB.notcontained_state + +/obj/machinery/seed_extractor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SeedExtractor", name) + ui.open() + +/obj/machinery/seed_extractor/ui_data() + var/list/V = list() + for(var/key in piles) + if(piles[key]) + var/len = length(piles[key]) + if(len) + V[key] = len + + . = list() + .["seeds"] = V + +/obj/machinery/seed_extractor/ui_act(action, params) + if(..()) + return + + switch(action) + if("select") + var/item = params["item"] + if(piles[item] && length(piles[item]) > 0) + var/datum/weakref/WO = piles[item][1] + var/obj/item/seeds/O = WO.resolve() + if(O) + piles[item] -= WO + O.forceMove(drop_location()) + . = TRUE + //to_chat(usr, "[src] clanks to life briefly before vending [prize.equipment_name]!") - piles += new /datum/seed_pile(O.plantname, O.lifespan, O.endurance, O.maturation, O.production, O.yield, O.potency) diff --git a/code/modules/language/language_holder.dm b/code/modules/language/language_holder.dm index 55e9352843..c1677117e9 100644 --- a/code/modules/language/language_holder.dm +++ b/code/modules/language/language_holder.dm @@ -324,6 +324,12 @@ Key procs /datum/language/sylvan = list(LANGUAGE_ATOM)) spoken_languages = list(/datum/language/sylvan = list(LANGUAGE_ATOM)) +/datum/language_holder/ethereal + understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM), + /datum/language/voltaic = list(LANGUAGE_ATOM)) + spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM), + /datum/language/voltaic = list(LANGUAGE_ATOM)) + /datum/language_holder/empty understood_languages = list() spoken_languages = list() diff --git a/code/modules/language/language_menu.dm b/code/modules/language/language_menu.dm index 0df7c01fca..bffd3d59af 100644 --- a/code/modules/language/language_menu.dm +++ b/code/modules/language/language_menu.dm @@ -8,10 +8,13 @@ language_holder = null . = ..() -/datum/language_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.language_menu_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/language_menu/ui_state(mob/user) + return GLOB.language_menu_state + +/datum/language_menu/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "LanguageMenu", "Language Menu", 700, 600, master_ui, state) + ui = new(user, src, "LanguageMenu") ui.open() /datum/language_menu/ui_data(mob/user) diff --git a/code/modules/language/voltaic.dm b/code/modules/language/voltaic.dm new file mode 100644 index 0000000000..ead7fe7c7f --- /dev/null +++ b/code/modules/language/voltaic.dm @@ -0,0 +1,14 @@ +// One of these languages will actually work, I'm certain of it. +/datum/language/voltaic + name = "Voltaic" + desc = "A sparky language made by manipulating electrical discharge." + key = "v" + space_chance = 20 + syllables = list( + "bzzt", "skrrt", "zzp", "mmm", "hzz", "tk", "shz", "k", "z", + "bzt", "zzt", "skzt", "skzz", "hmmt", "zrrt", "hzzt", "hz", + "vzt", "zt", "vz", "zip", "tzp", "lzzt", "dzzt", "zdt", "kzt", + "zzzz", "mzz" + ) + icon_state = "volt" + default_priority = 90 diff --git a/code/modules/library/lib_codex_gigas.dm b/code/modules/library/lib_codex_gigas.dm index 146c4221f8..26fa5b6f3d 100644 --- a/code/modules/library/lib_codex_gigas.dm +++ b/code/modules/library/lib_codex_gigas.dm @@ -34,13 +34,13 @@ if(U.check_acedia()) to_chat(user, "None of this matters, why are you reading this? You put [title] down.") return - user.visible_message("[user] opens [title] and begins reading intently.") + user.visible_message("[user] opens [title] and begins reading intently.") ask_name(user) /obj/item/book/codex_gigas/proc/perform_research(mob/user, devilName) if(!devilName) - user.visible_message("[user] closes [title] without looking anything up.") + user.visible_message("[user] closes [title] without looking anything up.") return inUse = TRUE var/speed = 300 @@ -50,7 +50,7 @@ if(U.job in list("Curator")) // the curator is both faster, and more accurate than normal crew members at research speed = 100 correctness = 100 - correctness -= U.getOrganLoss(ORGAN_SLOT_BRAIN) *0.5 //Brain damage makes researching hard. + correctness -= U.getOrganLoss(ORGAN_SLOT_BRAIN) * 0.5 //Brain damage makes researching hard. speed += U.getOrganLoss(ORGAN_SLOT_BRAIN) * 3 if(do_after(user, speed, 0, user)) var/usedName = devilName @@ -95,11 +95,10 @@ currentSection = SUFFIX return currentSection != oldSection -/obj/item/book/codex_gigas/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/book/codex_gigas/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "CodexGigas", name, 450, 450, master_ui, state) + ui = new(user, src, "CodexGigas", name) ui.open() /obj/item/book/codex_gigas/ui_data(mob/user) diff --git a/code/modules/library/soapstone.dm b/code/modules/library/soapstone.dm index 50f984c44d..f17040a938 100644 --- a/code/modules/library/soapstone.dm +++ b/code/modules/library/soapstone.dm @@ -35,13 +35,13 @@ return if(existing_message) - user.visible_message("[user] starts erasing [existing_message].", "You start erasing [existing_message].", "You hear a chipping sound.") - playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1) + user.visible_message("[user] starts erasing [existing_message].", "You start erasing [existing_message].", "You hear a chipping sound.") + playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1) if(do_after(user, tool_speed, target = existing_message)) user.visible_message("[user] erases [existing_message].", "You erase [existing_message][existing_message.creator_key == user.ckey ? ", refunding a use" : ""].") existing_message.persists = FALSE qdel(existing_message) - playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1) + playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1) if(existing_message.creator_key == user.ckey) refund_use() return @@ -54,12 +54,12 @@ if(!target.Adjacent(user) && locate(/obj/structure/chisel_message) in T) to_chat(user, "Someone wrote here before you chose! Find another spot.") return - playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1) - user.visible_message("[user] starts engraving a message into [T]...", "You start engraving a message into [T]...", "You hear a chipping sound.") + playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1) + user.visible_message("[user] starts engraving a message into [T]...", "You start engraving a message into [T]...", "You hear a chipping sound.") if(can_use() && do_after(user, tool_speed, target = T) && can_use()) //This looks messy but it's actually really clever! if(!locate(/obj/structure/chisel_message) in T) - user.visible_message("[user] leaves a message for future spacemen!", "You engrave a message into [T]!", "You hear a chipping sound.") - playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1) + user.visible_message("[user] leaves a message for future spacemen!", "You engrave a message into [T]!", "You hear a chipping sound.") + playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1) var/obj/structure/chisel_message/M = new(T) M.register(user, message) remove_use() @@ -112,12 +112,10 @@ desc = "A message from a past traveler." icon = 'icons/obj/stationobjs.dmi' icon_state = "soapstone_message" - layer = HIGH_OBJ_LAYER + layer = LATTICE_LAYER density = FALSE anchored = TRUE max_integrity = 30 - layer = LATTICE_LAYER - light_power = 0.3 var/hidden_message var/creator_key @@ -206,10 +204,13 @@ /obj/structure/chisel_message/interact() return -/obj/structure/chisel_message/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/structure/chisel_message/ui_state(mob/user) + return GLOB.always_state + +/obj/structure/chisel_message/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "EngravedMessage", name, 600, 300, master_ui, state) + ui = new(user, src, "EngravedMessage", name) ui.open() /obj/structure/chisel_message/ui_data(mob/user) diff --git a/code/modules/mafia/_defines.dm b/code/modules/mafia/_defines.dm new file mode 100644 index 0000000000..194851beed --- /dev/null +++ b/code/modules/mafia/_defines.dm @@ -0,0 +1,65 @@ +///how many people can play mafia without issues (running out of spawns, procs not expecting more than this amount of people, etc) +#define MAFIA_MAX_PLAYER_COUNT 12 + +#define MAFIA_TEAM_TOWN "town" +#define MAFIA_TEAM_MAFIA "mafia" +#define MAFIA_TEAM_SOLO "solo" + +//types of town roles for random setup gen +/// assistants it's just assistants filling up the rest of the roles +#define TOWN_OVERFLOW "overflow" +/// roles that learn info about others in the game (chaplain, detective, psych) +#define TOWN_INVEST "invest" +/// roles that keep other roles safe (doctor, and weirdly enough lawyer counts) +#define TOWN_PROTECT "protect" +/// roles that don't fit into anything else (hop) +#define TOWN_MISC "misc" + +//other types (mafia team, neutrals) +/// normal vote kill changelings +#define MAFIA_REGULAR "regular" +/// every other changeling role that has extra abilities +#define MAFIA_SPECIAL "special" +/// role that wins solo that nobody likes +#define NEUTRAL_KILL "kill" +/// role that upsets the game aka obsessed, usually worse for town than mafia but they can vote against mafia +#define NEUTRAL_DISRUPT "disrupt" + +#define MAFIA_PHASE_SETUP 1 +#define MAFIA_PHASE_DAY 2 +#define MAFIA_PHASE_VOTING 3 +#define MAFIA_PHASE_JUDGEMENT 4 +#define MAFIA_PHASE_NIGHT 5 +#define MAFIA_PHASE_VICTORY_LAP 6 + +#define MAFIA_ALIVE 1 +#define MAFIA_DEAD 2 + +#define COMSIG_MAFIA_ON_KILL "mafia_onkill" +#define MAFIA_PREVENT_KILL 1 + +#define COMSIG_MAFIA_CAN_PERFORM_ACTION "mafia_can_perform_action" +#define MAFIA_PREVENT_ACTION 1 + +//in order of events + game end + +/// when the shutters fall, before the 45 second wait and night event resolution +#define COMSIG_MAFIA_SUNDOWN "sundown" +/// after the 45 second wait, for actions that must go first +#define COMSIG_MAFIA_NIGHT_START "night_start" +/// most night actions now resolve +#define COMSIG_MAFIA_NIGHT_ACTION_PHASE "night_actions" +/// now killing happens from the roles that do that. the reason this is post action phase is to ensure doctors can protect and lawyers can block +#define COMSIG_MAFIA_NIGHT_KILL_PHASE "night_kill" +/// now undoing states like protection, actions that must happen last, etc. right before shutters raise and the day begins +#define COMSIG_MAFIA_NIGHT_END "night_end" + +/// signal sent to roles when the game is confirmed ending +#define COMSIG_MAFIA_GAME_END "game_end" + +/// list of ghosts who want to play mafia, every time someone enters the list it checks to see if enough are in +GLOBAL_LIST_EMPTY(mafia_signup) +/// list of ghosts who want to play mafia that have since disconnected. They are kept in the lobby, but not counted for starting a game. +GLOBAL_LIST_EMPTY(mafia_bad_signup) +/// the current global mafia game running. +GLOBAL_VAR(mafia_game) diff --git a/code/modules/mafia/controller.dm b/code/modules/mafia/controller.dm new file mode 100644 index 0000000000..5873decb8f --- /dev/null +++ b/code/modules/mafia/controller.dm @@ -0,0 +1,947 @@ + + +/** + * The mafia controller handles the mafia minigame in progress. + * It is first created when the first ghost signs up to play. + */ +/datum/mafia_controller + ///list of observers that should get game updates. + var/list/spectators = list() + ///all roles in the game, dead or alive. check their game status if you only want living or dead. + var/list/all_roles = list() + ///exists to speed up role retrieval, it's a dict. player_role_lookup[player ckey] will give you the role they play + var/list/player_role_lookup = list() + ///what part of the game you're playing in. day phases, night phases, judgement phases, etc. + var/phase = MAFIA_PHASE_SETUP + ///how long the game has gone on for, changes with every sunrise. day one, night one, day two, etc. + var/turn = 0 + ///for debugging and testing a full game, or adminbuse. If this is not null, it will use this as a setup. clears when game is over + var/list/custom_setup = list() + ///first day has no voting, and thus is shorter + var/first_day_phase_period = 20 SECONDS + ///talk with others about the last night + var/day_phase_period = 1 MINUTES + ///vote someone to get put on trial + var/voting_phase_period = 30 SECONDS + ///defend yourself! don't get lynched! sometimes skipped if nobody votes. + var/judgement_phase_period = 30 SECONDS + ///guilty or innocent, we want a bit of time for players to process the outcome of the vote + var/judgement_lynch_period = 5 SECONDS + ///mafia talk at night and pick someone to kill, some town roles use their actions, etc etc. + var/night_phase_period = 45 SECONDS + ///like the lynch period, players need to see what the other players in the game's roles were + var/victory_lap_period = 20 SECONDS + + ///template picked when the game starts. used for the name and desc reading + var/datum/map_template/mafia/current_map + ///map generation tool that deletes the current map after the game finishes + var/datum/mapGenerator/massdelete/map_deleter + + ///Readable list of roles in current game, sent to the tgui panel for roles list > list("Psychologist x1", "Clown x2") + var/list/current_setup_text + + ///starting outfit for all mafia players. it's just a grey jumpsuit. + var/player_outfit = /datum/outfit/mafia + + ///spawn points for players, each one has a house + var/list/landmarks = list() + ///town center for when people get put on trial + var/town_center_landmark + + ///group voting on one person, like putting people to trial or choosing who to kill as mafia + var/list/votes = list() + ///and these (judgement_innocent_votes, judgement_abstain_votes and judgement_guilty_votes) are the judgement phase votes, aka people sorting themselves into guilty and innocent, and "eh, i don't really care" lists. whichever has more inno or guilty wins! + var/list/judgement_abstain_votes = list() + var/list/judgement_innocent_votes = list() + var/list/judgement_guilty_votes = list() + ///current role on trial for the judgement phase, will die if guilty is greater than innocent + var/datum/mafia_role/on_trial + + ///current timer for phase + var/next_phase_timer + + ///used for debugging in testing (doesn't put people out of the game, some other shit i forgot, who knows just don't set this in live) honestly kinda deprecated + var/debug = FALSE + + ///Max player count + var/max_player = MAFIA_MAX_PLAYER_COUNT + ///Required player count + var/required_player = 5 + +/datum/mafia_controller/New() + . = ..() + GLOB.mafia_game = src + map_deleter = new + +/datum/mafia_controller/Destroy(force, ...) + . = ..() + GLOB.mafia_game = null + end_game() + qdel(map_deleter) + +/** + * Triggers at beginning of the game when there is a confirmed list of valid, ready players. + * Creates a 100% ready game that has NOT started (no players in bodies) + * Followed by start game + * + * Does the following: + * * Picks map, and loads it + * * Grabs landmarks if it is the first time it's loading + * * Sets up the role list + * * Puts players in each role randomly + * Arguments: + * * setup_list: list of all the datum setups (fancy list of roles) that would work for the game + * * ready_players: list of filtered, sane players (so not playing or disconnected) for the game to put into roles + */ +/datum/mafia_controller/proc/prepare_game(setup_list,ready_players) + + var/list/possible_maps = subtypesof(/datum/map_template/mafia) + var/turf/spawn_area = get_turf(locate(/obj/effect/landmark/mafia_game_area) in GLOB.landmarks_list) + + current_map = pick(possible_maps) + current_map = new current_map + + if(!spawn_area) + CRASH("No spawn area detected for Mafia!") + var/list/bounds = current_map.load(spawn_area) + if(!bounds) + CRASH("Loading mafia map failed!") + map_deleter.defineRegion(spawn_area, locate(spawn_area.x + 23,spawn_area.y + 23,spawn_area.z), replace = TRUE) //so we're ready to mass delete when round ends + + if(!landmarks.len)//we grab town center when we grab landmarks, if there is none (the first game signed up for let's grab them post load) + for(var/obj/effect/landmark/mafia/possible_spawn in GLOB.landmarks_list) + if(istype(possible_spawn, /obj/effect/landmark/mafia/town_center)) + town_center_landmark = possible_spawn + else + landmarks += possible_spawn + + current_setup_text = list() + for(var/rtype in setup_list) + for(var/i in 1 to setup_list[rtype]) + all_roles += new rtype(src) + var/datum/mafia_role/rp = rtype + current_setup_text += "[initial(rp.name)] x[setup_list[rtype]]" + var/list/spawnpoints = landmarks.Copy() + for(var/datum/mafia_role/role in all_roles) + role.assigned_landmark = pick_n_take(spawnpoints) + if(!debug) + role.player_key = pick_n_take(ready_players) + else + role.player_key = pop(ready_players) + +/datum/mafia_controller/proc/send_message(msg,team) + for(var/datum/mafia_role/R in all_roles) + if(team && R.team != team) + continue + to_chat(R.body,msg) + var/team_suffix = team ? "([uppertext(team)] CHAT)" : "" + for(var/M in GLOB.dead_mob_list) + var/mob/spectator = M + if(spectator.ckey in spectators) //was in current game, or spectatin' (won't send to living) + var/link = FOLLOW_LINK(M, town_center_landmark) + to_chat(M, "[link] MAFIA: [msg] [team_suffix]") + +/** + * The game by this point is now all set up, and so we can put people in their bodies and start the first phase. + * + * Does the following: + * * Creates bodies for all of the roles with the first proc + * * Starts the first day manually (so no timer) with the second proc + */ +/datum/mafia_controller/proc/start_game() + create_bodies() + start_day() + +/** + * How every day starts. + * + * What players do in this phase: + * * If day one, just a small starting period to see who is in the game and check role, leading to the night phase. + * * Otherwise, it's a longer period used to discuss events that happened during the night, leading to the voting phase. + */ +/datum/mafia_controller/proc/start_day() + turn += 1 + phase = MAFIA_PHASE_DAY + if(!check_victory()) + if(turn == 1) + send_message("The selected map is [current_map.name]!
[current_map.description]
") + send_message("Day [turn] started! There is no voting on the first day. Say hello to everybody!") + next_phase_timer = addtimer(CALLBACK(src,.proc/check_trial, FALSE),first_day_phase_period,TIMER_STOPPABLE) //no voting period = no votes = instant night + else + send_message("Day [turn] started! Voting will start in 1 minute.") + next_phase_timer = addtimer(CALLBACK(src,.proc/start_voting_phase),day_phase_period,TIMER_STOPPABLE) + + SStgui.update_uis(src) + +/** + * Players have finished the discussion period, and now must put up someone to the chopping block. + * + * What players do in this phase: + * * Vote on which player to put up for lynching, leading to the judgement phase. + * * If no votes are case, the judgement phase is skipped, leading to the night phase. + */ +/datum/mafia_controller/proc/start_voting_phase() + phase = MAFIA_PHASE_VOTING + next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, TRUE),voting_phase_period,TIMER_STOPPABLE) //be verbose! + send_message("Voting started! Vote for who you want to see on trial today.") + SStgui.update_uis(src) + +/** + * Players have voted someone up, and now the person must defend themselves while the town votes innocent or guilty. + * + * What players do in this phase: + * * Vote innocent or guilty, if they are not on trial. + * * Defend themselves and wait for judgement, if they are. + * * Leads to the lynch phase. + * Arguments: + * * verbose: boolean, announces whether there were votes or not. after judgement it goes back here with no voting period to end the day. + */ +/datum/mafia_controller/proc/check_trial(verbose = TRUE) + var/datum/mafia_role/loser = get_vote_winner("Day")//, majority_of_town = TRUE) + // var/loser_votes = get_vote_count(loser,"Day") + if(loser) + // if(loser_votes > 12) + // loser.body.client?.give_award(/datum/award/achievement/mafia/universally_hated, loser.body) + send_message("[loser.body.real_name] wins the day vote, Listen to their defense and vote \"INNOCENT\" or \"GUILTY\"!") + //refresh the lists + judgement_abstain_votes = list() + judgement_innocent_votes = list() + judgement_guilty_votes = list() + for(var/i in all_roles) + var/datum/mafia_role/abstainee = i + if(abstainee.game_status == MAFIA_ALIVE && abstainee != loser) + judgement_abstain_votes += abstainee + on_trial = loser + on_trial.body.forceMove(get_turf(town_center_landmark)) + phase = MAFIA_PHASE_JUDGEMENT + next_phase_timer = addtimer(CALLBACK(src, .proc/lynch),judgement_phase_period,TIMER_STOPPABLE) + reset_votes("Day") + else + if(verbose) + send_message("Not enough people have voted to put someone on trial, nobody will be lynched today.") + if(!check_victory()) + lockdown() + SStgui.update_uis(src) + +/** + * Players have voted innocent or guilty on the person on trial, and that person is now killed or returned home. + * + * What players do in this phase: + * * r/watchpeopledie + * * If the accused is killed, their true role is revealed to the rest of the players. + */ +/datum/mafia_controller/proc/lynch() + for(var/i in judgement_innocent_votes) + var/datum/mafia_role/role = i + send_message("[role.body.real_name] voted innocent.") + for(var/ii in judgement_abstain_votes) + var/datum/mafia_role/role = ii + send_message("[role.body.real_name] abstained.") + for(var/iii in judgement_guilty_votes) + var/datum/mafia_role/role = iii + send_message("[role.body.real_name] voted guilty.") + if(judgement_guilty_votes.len > judgement_innocent_votes.len) //strictly need majority guilty to lynch + send_message("Guilty wins majority, [on_trial.body.real_name] has been lynched.") + on_trial.kill(src, lynch = TRUE) + addtimer(CALLBACK(src, .proc/send_home, on_trial),judgement_lynch_period) + else + send_message("Innocent wins majority, [on_trial.body.real_name] has been spared.") + on_trial.body.forceMove(get_turf(on_trial.assigned_landmark)) + on_trial = null + //day votes are already cleared, so this will skip the trial and check victory/lockdown/whatever else + next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, FALSE),judgement_lynch_period,TIMER_STOPPABLE)// small pause to see the guy dead, no verbosity since we already did this + +/** + * Teenie helper proc to move players back to their home. + * Used in the above, but also used in the debug button "send all players home" + * Arguments: + * * role: mafia role that is getting sent back to the game. + */ +/datum/mafia_controller/proc/send_home(datum/mafia_role/role) + role.body.forceMove(get_turf(role.assigned_landmark)) + +/** + * Checks to see if a faction (or solo antagonist) has won. + * + * Calculates in this order: + * * counts up town, mafia, and solo + * * solos can count as town members for the purposes of mafia winning + * * sends the amount of living people to the solo antagonists, and see if they won OR block the victory of the teams + * * checks if solos won from above, then if town, then if mafia + * * starts the end of the game if a faction won + * * returns TRUE if someone won the game, halting other procs from continuing in the case of a victory + */ +/datum/mafia_controller/proc/check_victory() + //needed for achievements + var/list/total_town = list() + var/list/total_mafia = list() + + var/alive_town = 0 + var/alive_mafia = 0 + var/list/solos_to_ask = list() //need to ask after because first round is counting team sizes + var/list/total_victors = list() //if this list gets filled with anyone, they win. list because side antags can with with people + var/blocked_victory = FALSE //if a solo antagonist is stopping the town or mafia from finishing the game. + + ///PHASE ONE: TALLY UP ALL NUMBERS OF PEOPLE STILL ALIVE + + for(var/datum/mafia_role/R in all_roles) + switch(R.team) + if(MAFIA_TEAM_MAFIA) + total_mafia += R + if(R.game_status == MAFIA_ALIVE) + alive_mafia += R.vote_power + if(MAFIA_TEAM_TOWN) + total_town += R + if(R.game_status == MAFIA_ALIVE) + alive_town += R.vote_power + if(MAFIA_TEAM_SOLO) + if(R.game_status == MAFIA_ALIVE) + if(R.solo_counts_as_town) + alive_town += R.vote_power + solos_to_ask += R + + ///PHASE TWO: SEND STATS TO SOLO ANTAGS, SEE IF THEY WON OR TEAMS CANNOT WIN + + for(var/datum/mafia_role/solo in solos_to_ask) + if(solo.check_total_victory(alive_town, alive_mafia)) + total_victors += solo + if(solo.block_team_victory(alive_town, alive_mafia)) + blocked_victory = TRUE + + //solo victories! + var/solo_end = FALSE + for(var/datum/mafia_role/winner in total_victors) + send_message("!! [uppertext(winner.name)] VICTORY !!") + // var/client/winner_client = GLOB.directory[winner.player_key] + // winner_client?.give_award(winner.winner_award, winner.body) + solo_end = TRUE + if(solo_end) + start_the_end() + return TRUE + if(blocked_victory) + return FALSE + if(alive_mafia == 0) + // for(var/datum/mafia_role/townie in total_town) + // var/client/townie_client = GLOB.directory[townie.player_key] + // townie_client?.give_award(townie.winner_award, townie.body) + start_the_end("!! TOWN VICTORY !!") + return TRUE + else if(alive_mafia >= alive_town) //guess could change if town nightkill is added + start_the_end("!! MAFIA VICTORY !!") + // for(var/datum/mafia_role/changeling in total_mafia) + // var/client/changeling_client = GLOB.directory[changeling.player_key] + // changeling_client?.give_award(changeling.winner_award, changeling.body) + return TRUE + +/** + * The end of the game is in two procs, because we want a bit of time for players to see eachothers roles. + * Because of how check_victory works, the game is halted in other places by this point. + * + * What players do in this phase: + * * See everyone's role postgame + * * See who won the game + * Arguments: + * * message: string, if non-null it sends it to all players. used to announce team victories while solos are handled in check victory + */ +/datum/mafia_controller/proc/start_the_end(message) + SEND_SIGNAL(src,COMSIG_MAFIA_GAME_END) + if(message) + send_message(message) + for(var/datum/mafia_role/R in all_roles) + R.reveal_role(src) + phase = MAFIA_PHASE_VICTORY_LAP + next_phase_timer = addtimer(CALLBACK(src,.proc/end_game),victory_lap_period,TIMER_STOPPABLE) + +/** + * Cleans up the game, resetting variables back to the beginning and removing the map with the generator. + */ +/datum/mafia_controller/proc/end_game() + map_deleter.generate() //remove the map, it will be loaded at the start of the next one + QDEL_LIST(all_roles) + current_setup_text = null + custom_setup = list() + turn = 0 + votes = list() + //map gen does not deal with landmarks + QDEL_LIST(landmarks) + QDEL_NULL(town_center_landmark) + phase = MAFIA_PHASE_SETUP + +/** + * After the voting and judgement phases, the game goes to night shutting the windows and beginning night with a proc. + */ +/datum/mafia_controller/proc/lockdown() + toggle_night_curtains(close=TRUE) + start_night() + +/** + * Shuts poddoors attached to mafia. + * Arguments: + * * close: boolean, the state you want the curtains in. + */ +/datum/mafia_controller/proc/toggle_night_curtains(close) + for(var/obj/machinery/door/poddoor/D in GLOB.machines) //I really dislike pathing of these + if(D.id != "mafia") //so as to not trigger shutters on station, lol + continue + if(close) + INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/close) + else + INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/open) + +/** + * The actual start of night for players. Mostly info is given at the start of the night as the end of the night is when votes and actions are submitted and tried. + * + * What players do in this phase: + * * Mafia are told to begin voting on who to kill + * * Powers that are picked during the day announce themselves right now + */ +/datum/mafia_controller/proc/start_night() + phase = MAFIA_PHASE_NIGHT + send_message("Night [turn] started! Lockdown will end in 45 seconds.") + SEND_SIGNAL(src,COMSIG_MAFIA_SUNDOWN) + next_phase_timer = addtimer(CALLBACK(src, .proc/resolve_night),night_phase_period,TIMER_STOPPABLE) + SStgui.update_uis(src) + +/** + * The end of the night, and a series of signals for the order of events on a night. + * + * Order of events, and what they mean: + * * Start of resolve (NIGHT_START) is for activating night abilities that MUST go first + * * Action phase (NIGHT_ACTION_PHASE) is for non-lethal day abilities + * * Mafia then tallies votes and kills the highest voted person (note: one random voter visits that person for the purposes of roleblocking) + * * Killing phase (NIGHT_KILL_PHASE) is for lethal night abilities + * * End of resolve (NIGHT_END) is for cleaning up abilities that went off and i guess doing some that must go last + * * Finally opens the curtains and calls the start of day phase, completing the cycle until check victory returns TRUE + */ +/datum/mafia_controller/proc/resolve_night() + SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_START) + SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_ACTION_PHASE) + //resolve mafia kill, todo unsnowflake this + var/datum/mafia_role/R = get_vote_winner("Mafia") + if(R) + var/datum/mafia_role/killer = get_random_voter("Mafia") + if(SEND_SIGNAL(killer,COMSIG_MAFIA_CAN_PERFORM_ACTION,src,"mafia killing",R) & MAFIA_PREVENT_ACTION) + send_message("[killer.body.real_name] was unable to attack [R.body.real_name] tonight!",MAFIA_TEAM_MAFIA) + else + send_message("[killer.body.real_name] has attacked [R.body.real_name]!",MAFIA_TEAM_MAFIA) + R.kill(src) + reset_votes("Mafia") + SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_KILL_PHASE) + SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_END) + toggle_night_curtains(close=FALSE) + start_day() + SStgui.update_uis(src) + +/** + * Proc that goes off when players vote for something with their mafia panel. + * + * If teams, it hides the tally overlay and only sends the vote messages to the team that is voting + * Arguments: + * * voter: the mafia role that is trying to vote for... + * * target: the mafia role that is getting voted for + * * vote_type: type of vote submitted (is this the day vote? is this the mafia night vote?) + * * teams: see mafia team defines for what to put in, makes the messages only send to a specific team (so mafia night votes only sending messages to mafia at night) + */ +/datum/mafia_controller/proc/vote_for(datum/mafia_role/voter,datum/mafia_role/target,vote_type, teams) + if(!votes[vote_type]) + votes[vote_type] = list() + var/old_vote = votes[vote_type][voter] + if(old_vote && old_vote == target) + votes[vote_type] -= voter + else + votes[vote_type][voter] = target + if(old_vote && old_vote == target) + send_message("[voter.body.real_name] retracts their vote for [target.body.real_name]!", team = teams) + else + send_message("[voter.body.real_name] voted for [target.body.real_name]!",team = teams) + if(!teams) + target.body.update_icon() //Update the vote display if it's a public vote + var/datum/mafia_role/old = old_vote + if(old) + old.body.update_icon() + +/** + * Clears out the votes of a certain type (day votes, mafia kill votes) while leaving others untouched + */ +/datum/mafia_controller/proc/reset_votes(vote_type) + var/list/bodies_to_update = list() + for(var/vote in votes[vote_type]) + var/datum/mafia_role/R = votes[vote_type][vote] + bodies_to_update += R.body + votes[vote_type] = list() + for(var/mob/M in bodies_to_update) + M.update_icon() + +/** + * Returns how many people voted for the role, in whatever vote (day vote, night kill vote) + * Arguments: + * * role: the mafia role the proc tries to get the amount of votes for + * * vote_type: the vote type (getting how many day votes were for the role, or mafia night votes for the role) + */ +/datum/mafia_controller/proc/get_vote_count(role,vote_type) + . = 0 + for(var/v in votes[vote_type]) + var/datum/mafia_role/votee = v + if(votes[vote_type][votee] == role) + . += votee.vote_power + +/** + * Returns whichever role got the most votes, in whatever vote (day vote, night kill vote) + * returns null if no votes + * Arguments: + * * vote_type: the vote type (getting the role that got the most day votes, or the role that got the most mafia votes) + */ +/datum/mafia_controller/proc/get_vote_winner(vote_type) + var/list/tally = list() + for(var/votee in votes[vote_type]) + if(!tally[votes[vote_type][votee]]) + tally[votes[vote_type][votee]] = 1 + else + tally[votes[vote_type][votee]] += 1 + sortTim(tally,/proc/cmp_numeric_dsc,associative=TRUE) + return length(tally) ? tally[1] : null + +/** + * Returns a random person who voted for whatever vote (day vote, night kill vote) + * Arguments: + * * vote_type: vote type (getting a random day voter, or mafia night voter) + */ +/datum/mafia_controller/proc/get_random_voter(vote_type) + if(length(votes[vote_type])) + return pick(votes[vote_type]) + +/** + * Adds mutable appearances to people who get publicly voted on (so not night votes) showing how many people are picking them + * Arguments: + * * source: the body of the role getting the overlays + * * overlay_list: signal var passing the overlay list of the mob + */ +/datum/mafia_controller/proc/display_votes(atom/source, list/overlay_list) + if(phase != MAFIA_PHASE_VOTING) + return + var/v = get_vote_count(player_role_lookup[source],"Day") + var/mutable_appearance/MA = mutable_appearance('icons/obj/mafia.dmi',"vote_[v > 12 ? "over_12" : v]") + overlay_list += MA + +/** + * Called when the game is setting up, AFTER map is loaded but BEFORE the phase timers start. Creates and places each role's body and gives the correct player key + * + * Notably: + * * Toggles godmode so the mafia players cannot kill themselves + * * Adds signals for voting overlays, see display_votes proc + * * gives mafia panel + * * sends the greeting text (goals, role name, etc) + */ +/datum/mafia_controller/proc/create_bodies() + for(var/datum/mafia_role/role in all_roles) + var/mob/living/carbon/human/H = new(get_turf(role.assigned_landmark)) + H.equipOutfit(player_outfit) + H.status_flags |= GODMODE + RegisterSignal(H,COMSIG_ATOM_UPDATE_OVERLAYS,.proc/display_votes) + var/datum/action/innate/mafia_panel/mafia_panel = new(null,src) + mafia_panel.Grant(H) + var/client/player_client = GLOB.directory[role.player_key] + if(player_client) + player_client.prefs.copy_to(H) + if(H.dna.species.outfit_important_for_life) //plasmamen + H.set_species(/datum/species/human) + role.body = H + player_role_lookup[H] = role + H.key = role.player_key + role.greet() + +/datum/mafia_controller/ui_data(mob/user) + . = ..() + switch(phase) + if(MAFIA_PHASE_DAY,MAFIA_PHASE_VOTING,MAFIA_PHASE_JUDGEMENT) + .["phase"] = "Day [turn]" + if(MAFIA_PHASE_NIGHT) + .["phase"] = "Night [turn]" + else + .["phase"] = "No Game" + if(user.client?.holder) + .["admin_controls"] = TRUE //show admin buttons to start/setup/stop + if(phase == MAFIA_PHASE_JUDGEMENT) + .["judgement_phase"] = TRUE //show judgement section + else + .["judgement_phase"] = FALSE + var/datum/mafia_role/user_role = player_role_lookup[user] + if(user_role) + .["roleinfo"] = list("role" = user_role.name,"desc" = user_role.desc, "action_log" = user_role.role_notes, "hud_icon" = user_role.hud_icon, "revealed_icon" = user_role.revealed_icon) + var/actions = list() + for(var/action in user_role.actions) + if(user_role.validate_action_target(src,action,null)) + actions += action + .["actions"] = actions + .["role_theme"] = user_role.special_theme + else + var/list/lobby_data = list() + for(var/key in GLOB.mafia_signup + GLOB.mafia_bad_signup) + var/list/lobby_member = list() + lobby_member["name"] = key + lobby_member["status"] = "Ready" + if(key in GLOB.mafia_bad_signup) + lobby_member["status"] = "Disconnected" + lobby_member["spectating"] = "Ghost" + if(key in spectators) + lobby_member["spectating"] = "Spectator" + lobby_data += list(lobby_member) + .["lobbydata"] = lobby_data + var/list/player_data = list() + for(var/datum/mafia_role/R in all_roles) + var/list/player_info = list() + var/list/actions = list() + if(user_role) //not observer + for(var/action in user_role.targeted_actions) + if(user_role.validate_action_target(src,action,R)) + actions += action + //Awful snowflake, could use generalizing + if(phase == MAFIA_PHASE_VOTING) + player_info["votes"] = get_vote_count(R,"Day") + if(R.game_status == MAFIA_ALIVE && R != user_role) + actions += "Vote" + if(phase == MAFIA_PHASE_NIGHT && user_role.team == MAFIA_TEAM_MAFIA && R.game_status == MAFIA_ALIVE && R.team != MAFIA_TEAM_MAFIA) + actions += "Kill Vote" + player_info["name"] = R.body.real_name + player_info["ref"] = REF(R) + player_info["actions"] = actions + player_info["alive"] = R.game_status == MAFIA_ALIVE + player_data += list(player_info) + .["players"] = player_data + .["timeleft"] = next_phase_timer ? timeleft(next_phase_timer) : 0 + + //Not sure on this, should this info be visible + .["all_roles"] = current_setup_text + +/datum/mafia_controller/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/mafia), + ) + +/datum/mafia_controller/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + var/datum/mafia_role/user_role = player_role_lookup[usr] + //Admin actions + if(usr.client?.holder) + switch(action) + if("new_game") + end_game() + basic_setup() + if("nuke") + end_game() + qdel(src) + if("next_phase") + var/datum/timedevent/timer = SStimer.timer_id_dict[next_phase_timer] + if(!timer.spent) + var/datum/callback/tc = timer.callBack + deltimer(next_phase_timer) + tc.InvokeAsync() + return TRUE + if("players_home") + var/list/failed = list() + for(var/datum/mafia_role/player in all_roles) + if(!player.body) + failed += player + continue + player.body.forceMove(get_turf(player.assigned_landmark)) + if(failed.len) + to_chat(usr, "List of players who no longer had a body (if you see this, the game is runtiming anyway so just hit \"New Game\" to end it)") + for(var/i in failed) + var/datum/mafia_role/fail = i + to_chat(usr, fail.player_key) + if("debug_setup") + var/list/debug_setup = list() + var/list/rolelist_dict = list() + var/done = FALSE + for(var/p in typesof(/datum/mafia_role)) + var/datum/mafia_role/path = p + rolelist_dict[initial(path.name) + " ([uppertext(initial(path.team))])"] = path + rolelist_dict = list("CANCEL", "FINISH") + rolelist_dict + while(!done) + to_chat(usr, "You have a total player count of [assoc_value_sum(debug_setup)] in this setup.") + var/chosen_role_name = input(usr,"Select a role!","Custom Setup Creation",rolelist_dict[1]) as null|anything in rolelist_dict + if(chosen_role_name == "CANCEL") + return + if(chosen_role_name == "FINISH") + break + var/found_path = rolelist_dict[chosen_role_name] + var/role_count = input(usr,"How many? Zero to cancel.","Custom Setup Creation",0) as null|num + if(role_count > 0) + debug_setup[found_path] = role_count + custom_setup = debug_setup + if("cancel_setup") + custom_setup = list() + switch(action) //both living and dead + if("mf_lookup") + var/role_lookup = params["atype"] + var/datum/mafia_role/helper + for(var/datum/mafia_role/role in all_roles) + if(role_lookup == role.name) + helper = role + break + helper.show_help(usr) + if(!user_role)//just the dead + var/client/C = ui.user.client + switch(action) + if("mf_signup") + if(!SSticker.HasRoundStarted()) + to_chat(usr, "Wait for the round to start.") + return + if(GLOB.mafia_signup[C.ckey]) + GLOB.mafia_signup -= C.ckey + to_chat(usr, "You unregister from Mafia.") + return + else + GLOB.mafia_signup[C.ckey] = C + to_chat(usr, "You sign up for Mafia.") + if(phase == MAFIA_PHASE_SETUP) + check_signups() + try_autostart() + if("mf_spectate") + if(C.ckey in spectators) + to_chat(usr, "You will no longer get messages from the game.") + spectators -= C.ckey + else + to_chat(usr, "You will now get messages from the game.") + spectators += C.ckey + if(user_role.game_status == MAFIA_DEAD) + return + //User actions (just living) + switch(action) + if("mf_action") + if(!user_role.actions.Find(params["atype"])) + return + user_role.handle_action(src,params["atype"],null) + return TRUE //vals for self-ui update + if("mf_targ_action") + var/datum/mafia_role/target = locate(params["target"]) in all_roles + if(!istype(target)) + return + switch(params["atype"]) + if("Vote") + if(phase != MAFIA_PHASE_VOTING) + return + vote_for(user_role,target,vote_type="Day") + if("Kill Vote") + if(phase != MAFIA_PHASE_NIGHT || user_role.team != MAFIA_TEAM_MAFIA) + return + vote_for(user_role,target,"Mafia", MAFIA_TEAM_MAFIA) + to_chat(user_role.body,"You will vote for [target.body.real_name] for tonights killing.") + else + if(!user_role.targeted_actions.Find(params["atype"])) + return + if(!user_role.validate_action_target(src,params["atype"],target)) + return + user_role.handle_action(src,params["atype"],target) + return TRUE + if(user_role != on_trial) + switch(action) + if("vote_abstain") + if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_abstain_votes)) + return + to_chat(user_role.body,"You have decided to abstain.") + judgement_innocent_votes -= user_role + judgement_guilty_votes -= user_role + judgement_abstain_votes += user_role + if("vote_innocent") + if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_innocent_votes)) + return + to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as INNOCENT!") + judgement_abstain_votes -= user_role//no fakers, and... + judgement_guilty_votes -= user_role//no radical centrism + judgement_innocent_votes += user_role + if("vote_guilty") + if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_guilty_votes)) + return + to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as GUILTY!") + judgement_abstain_votes -= user_role//no fakers, and... + judgement_innocent_votes -= user_role//no radical centrism + judgement_guilty_votes += user_role + +/datum/mafia_controller/ui_state(mob/user) + return GLOB.always_state + +/datum/mafia_controller/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, null) + if(!ui) + ui = new(user, src, "MafiaPanel") + ui.set_autoupdate(FALSE) + ui.open() + +/proc/assoc_value_sum(list/L) + . = 0 + for(var/key in L) + . += L[key] + +/** + * Returns a semirandom setup, with... + * Town, Two invest roles, one protect role, sometimes a misc role, and the rest assistants for town. + * Mafia, 2 normal mafia and one special. + * Neutral, two disruption roles, sometimes one is a killing. + * + * See _defines.dm in the mafia folder for a rundown on what these groups of roles include. + */ +/datum/mafia_controller/proc/generate_random_setup() + var/invests_left = 2 + var/protects_left = 1 + var/miscs_left = prob(35) + var/mafiareg_left = 2 + var/mafiaspe_left = 1 + var/killing_role = prob(50) + var/disruptors = killing_role ? 1 : 2 //still required to calculate overflow + var/overflow_left = max_player - (invests_left + protects_left + miscs_left + mafiareg_left + mafiaspe_left + killing_role + disruptors) + + var/list/random_setup = list() + for(var/i in 1 to max_player) //should match the number of roles to add + if(overflow_left) + add_setup_role(random_setup, TOWN_OVERFLOW) + overflow_left-- + else if(invests_left) + add_setup_role(random_setup, TOWN_INVEST) + invests_left-- + else if(protects_left) + add_setup_role(random_setup, TOWN_PROTECT) + protects_left-- + else if(miscs_left) + add_setup_role(random_setup, TOWN_MISC) + miscs_left-- + else if(mafiareg_left) + add_setup_role(random_setup, MAFIA_REGULAR) + mafiareg_left-- + else if(mafiaspe_left) + add_setup_role(random_setup, MAFIA_SPECIAL) + mafiaspe_left-- + else if(killing_role) + add_setup_role(random_setup, NEUTRAL_KILL) + killing_role-- + else + add_setup_role(random_setup, NEUTRAL_DISRUPT) + return random_setup + +/** + * Helper proc that adds a random role of a type to a setup. if it doesn't exist in the setup, it adds the path to the list and otherwise bumps the path in the list up one + */ +/datum/mafia_controller/proc/add_setup_role(setup_list, wanted_role_type) + var/list/role_type_paths = list() + for(var/path in typesof(/datum/mafia_role)) + var/datum/mafia_role/instance = path + if(initial(instance.role_type) == wanted_role_type) + role_type_paths += instance + + var/mafia_path = pick(role_type_paths) + var/datum/mafia_role/mafia_path_type = mafia_path + var/found_role + for(var/searched_path in setup_list) + var/datum/mafia_role/searched_path_type = searched_path + if(initial(mafia_path_type.name) == initial(searched_path_type.name)) + found_role = searched_path + break + if(found_role) + setup_list[found_role] += 1 + return + setup_list[mafia_path] = 1 + +/** + * Called when enough players have signed up to fill a setup. DOESN'T NECESSARILY MEAN THE GAME WILL START. + * + * Checks for a custom setup, if so gets the required players from that and if not it sets the player requirement to required_player(max_player) and generates one IF basic setup starts a game. + * Checks if everyone signed up is an observer, and is still connected. If people aren't, they're removed from the list. + * If there aren't enough players post sanity, it aborts. otherwise, it selects enough people for the game and starts preparing the game for real. + */ +/datum/mafia_controller/proc/basic_setup() + var/req_players + var/list/setup = custom_setup + if(!setup.len) + req_players = required_player //max_player + else + req_players = assoc_value_sum(setup) + + //final list for all the players who will be in this game + var/list/filtered_keys = list() + //cuts invalid players from signups (disconnected/not a ghost) + var/list/possible_keys = list() + for(var/key in GLOB.mafia_signup) + if(GLOB.directory[key]) + var/client/C = GLOB.directory[key] + if(isobserver(C.mob)) + possible_keys += key + continue + GLOB.mafia_signup -= key //not valid to play when we checked so remove them from signups + + //if there were not enough players, don't start. we already trimmed the list to now hold only valid signups + if(length(possible_keys) < req_players) + return + else //hacky implementation of max players + req_players = clamp(length(possible_keys), 1, max_player) + + //if there were too many players, still start but only make filtered keys as big as it needs to be (cut excess) + //also removes people who do get into final player list from the signup so they have to sign up again when game ends + for(var/i in 1 to req_players) + var/chosen_key = pick_n_take(possible_keys) + filtered_keys += chosen_key + GLOB.mafia_signup -= chosen_key + //small message about not getting into this game for clarity on why they didn't get in + for(var/unpicked in possible_keys) + var/client/unpicked_client = GLOB.directory[unpicked] + to_chat(unpicked_client, "Sorry, the starting mafia game has too many players and you were not picked.") + to_chat(unpicked_client, "You're still signed up, getting messages from the current round, and have another chance to join when the one starting now finishes.") + + if(!setup.len) //don't actually have one yet, so generate a max player random setup. it's good to do this here instead of above so it doesn't generate one every time a game could possibly start. + setup = generate_random_setup() + prepare_game(setup,filtered_keys) + start_game() + +/** + * Called when someone signs up, and sees if there are enough people in the signup list to begin. + * + * Only checks if everyone is actually valid to start (still connected and an observer) if there are enough players (basic_setup) + */ +/datum/mafia_controller/proc/try_autostart() + if(phase != MAFIA_PHASE_SETUP) // || !(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME)) + return + if(GLOB.mafia_signup.len >= max_player || GLOB.mafia_signup.len >= required_player|| custom_setup.len)//enough people to try and make something (or debug mode) + basic_setup() + +/** + * Filters inactive player into a different list until they reconnect, and removes players who are no longer ghosts. + * + * If a disconnected player gets a non-ghost mob and reconnects, they will be first put back into mafia_signup then filtered by that. + */ +/datum/mafia_controller/proc/check_signups() + for(var/bad_key in GLOB.mafia_bad_signup) + if(GLOB.directory[bad_key])//they have reconnected if we can search their key and get a client + GLOB.mafia_bad_signup -= bad_key + GLOB.mafia_signup += bad_key + for(var/key in GLOB.mafia_signup) + var/client/C = GLOB.directory[key] + if(!C)//vice versa but in a variable we use later + GLOB.mafia_signup -= key + GLOB.mafia_bad_signup += key + if(!isobserver(C.mob)) + //they are back to playing the game, remove them from the signups + GLOB.mafia_signup -= key + +/datum/action/innate/mafia_panel + name = "Mafia Panel" + desc = "Use this to play." + icon_icon = 'icons/obj/mafia.dmi' + button_icon_state = "board" + var/datum/mafia_controller/parent + +/datum/action/innate/mafia_panel/New(Target,mf) + . = ..() + parent = mf + +/datum/action/innate/mafia_panel/Activate() + parent.ui_interact(owner) + +/** + * Creates the global datum for playing mafia games, destroys the last if that's required and returns the new. + */ +/proc/create_mafia_game() + if(GLOB.mafia_game) + QDEL_NULL(GLOB.mafia_game) + var/datum/mafia_controller/MF = new() + return MF diff --git a/code/modules/mafia/map_pieces.dm b/code/modules/mafia/map_pieces.dm new file mode 100644 index 0000000000..3459603f3c --- /dev/null +++ b/code/modules/mafia/map_pieces.dm @@ -0,0 +1,79 @@ +/obj/effect/landmark/mafia_game_area //locations where mafia will be loaded by the datum + name = "Mafia Area Spawn" + var/game_id = "mafia" + +/obj/effect/landmark/mafia + name = "Mafia Player Spawn" + var/game_id = "mafia" + +/obj/effect/landmark/mafia/town_center + name = "Mafia Town Center" + +//for ghosts/admins +/obj/mafia_game_board + name = "Mafia Game Board" + icon = 'icons/obj/mafia.dmi' + icon_state = "board" + anchored = TRUE + var/game_id = "mafia" + var/datum/mafia_controller/MF + +/obj/mafia_game_board/attack_ghost(mob/user) + . = ..() + if(!MF) + MF = GLOB.mafia_game + if(!MF) + MF = create_mafia_game() + MF.ui_interact(user) + +/area/mafia + name = "Mafia Minigame" + icon_state = "mafia" + dynamic_lighting = DYNAMIC_LIGHTING_DISABLED + requires_power = FALSE + has_gravity = STANDARD_GRAVITY + flags_1 = NONE + // block_suicide = TRUE + +/datum/map_template/mafia + var/description = "" + +/datum/map_template/mafia/summerball + name = "Summerball 2020" + description = "The original, the OG. The 2020 Summer ball was where mafia came from, with this map." + mappath = "_maps/map_files/Mafia/mafia_ball.dmm" + +/datum/map_template/mafia/syndicate + name = "Syndicate Megastation" + description = "Yes, it's a very confusing day at the Megastation. Will the syndicate conflict resolution operatives succeed?" + mappath = "_maps/map_files/Mafia/mafia_syndie.dmm" + +/datum/map_template/mafia/lavaland + name = "Lavaland Excursion" + description = "The station has no idea what's going down on lavaland right now, we got changelings... traitors, and worst of all... lawyers roleblocking you every night." + mappath = "_maps/map_files/Mafia/mafia_lavaland.dmm" + +/datum/map_template/mafia/ufo + name = "Alien Mothership" + description = "The haunted ghost UFO tour has gone south and now it's up to our fine townies and scare seekers to kill the actual real alien changelings..." + mappath = "_maps/map_files/Mafia/mafia_ayylmao.dmm" + +/datum/map_template/mafia/spider_clan + name = "Spider Clan Kidnapping" + description = "New and improved spider clan kidnappings are a lot less boring and have a lot more lynching. Damn westaboos!" + mappath = "_maps/map_files/Mafia/mafia_spiderclan.dmm" + +/datum/map_template/mafia/snowy + name = "Snowdin" + description = "Based off of the icey moon map of the same name, the guy who reworked it pretty much did it for nothing since away missions are disabled but at least he'll get this...?" + mappath = "_maps/map_files/Mafia/mafia_snow.dmm" + +/datum/map_template/mafia/gothic + name = "Vampire's Castle" + description = "Vampires and changelings clash to find out who's the superior bloodsucking monster in this creepy castle map." + mappath = "_maps/map_files/Mafia/mafia_gothic.dmm" + +/datum/map_template/mafia/gothic + name = "Reebe" + description = "Trouble in Reebe station! Copypaste guranteed by ClockCo™" + mappath = "_maps/map_files/Mafia/mafia_reebe.dmm" \ No newline at end of file diff --git a/code/modules/mafia/outfits.dm b/code/modules/mafia/outfits.dm new file mode 100644 index 0000000000..bbc72bd120 --- /dev/null +++ b/code/modules/mafia/outfits.dm @@ -0,0 +1,108 @@ + +//what people wear unrevealed + +/datum/outfit/mafia + name = "Mafia Game Outfit" + uniform = /obj/item/clothing/under/color/grey + shoes = /obj/item/clothing/shoes/sneakers/black + +//town + +/datum/outfit/mafia/assistant + name = "Mafia Assistant" + + uniform = /obj/item/clothing/under/color/rainbow + +/datum/outfit/mafia/detective + name = "Mafia Detective" + + uniform = /obj/item/clothing/under/rank/security/detective + // neck = /obj/item/clothing/neck/tie/detective + shoes = /obj/item/clothing/shoes/sneakers/brown + suit = /obj/item/clothing/suit/det_suit + gloves = /obj/item/clothing/gloves/color/black + head = /obj/item/clothing/head/fedora/det_hat + mask = /obj/item/clothing/mask/cigarette + +/datum/outfit/mafia/psychologist + name = "Mafia Psychologist" + + uniform = /obj/item/clothing/under/suit/black + shoes = /obj/item/clothing/shoes/laceup + +/datum/outfit/mafia/md + name = "Mafia Medical Doctor" + + uniform = /obj/item/clothing/under/rank/medical/doctor + shoes = /obj/item/clothing/shoes/sneakers/white + suit = /obj/item/clothing/suit/toggle/labcoat + +/datum/outfit/mafia/chaplain + name = "Mafia Chaplain" + + uniform = /obj/item/clothing/under/rank/civilian/chaplain + +/datum/outfit/mafia/lawyer + name = "Mafia Lawyer" + + uniform = /obj/item/clothing/under/rank/civilian/lawyer/bluesuit + suit = /obj/item/clothing/suit/toggle/lawyer + shoes = /obj/item/clothing/shoes/laceup + +/datum/outfit/mafia/hop + name = "Mafia Head of Personnel" + + uniform = /obj/item/clothing/under/rank/civilian/head_of_personnel + suit = /obj/item/clothing/suit/armor/vest/alt + shoes = /obj/item/clothing/shoes/sneakers/brown + head = /obj/item/clothing/head/hopcap + glasses = /obj/item/clothing/glasses/sunglasses + +//mafia + +/datum/outfit/mafia/changeling + name = "Mafia Changeling" + + head = /obj/item/clothing/head/helmet/changeling + suit = /obj/item/clothing/suit/armor/changeling + +//solo + +/datum/outfit/mafia/fugitive + name = "Mafia Fugitive" + + uniform = /obj/item/clothing/under/rank/prisoner + shoes = /obj/item/clothing/shoes/sneakers/orange + +/datum/outfit/mafia/obsessed + name = "Mafia Obsessed" + uniform = /obj/item/clothing/under/misc/overalls + shoes = /obj/item/clothing/shoes/sneakers/white + gloves = /obj/item/clothing/gloves/color/latex + mask = /obj/item/clothing/mask/surgical + suit = /obj/item/clothing/suit/apron + +/datum/outfit/mafia/obsessed/post_equip(mob/living/carbon/human/H) + for(var/obj/item/carried_item in H.get_equipped_items(TRUE)) + carried_item.add_mob_blood(H)//Oh yes, there will be blood... + H.regenerate_icons() + +/datum/outfit/mafia/clown + name = "Mafia Clown" + + uniform = /obj/item/clothing/under/rank/civilian/clown + shoes = /obj/item/clothing/shoes/clown_shoes + mask = /obj/item/clothing/mask/gas/clown_hat + +/datum/outfit/mafia/traitor + name = "Mafia Traitor" + + mask = /obj/item/clothing/mask/gas/syndicate + uniform = /obj/item/clothing/under/syndicate/tacticool + shoes = /obj/item/clothing/shoes/jackboots + +/datum/outfit/mafia/nightmare + name = "Mafia Nightmare" + + uniform = null + shoes = null diff --git a/code/modules/mafia/roles.dm b/code/modules/mafia/roles.dm new file mode 100644 index 0000000000..2461a93976 --- /dev/null +++ b/code/modules/mafia/roles.dm @@ -0,0 +1,705 @@ +/datum/mafia_role + var/name = "Assistant" + var/desc = "You are a crewmember without any special abilities." + var/win_condition = "kill all mafia and solo killing roles." + var/team = MAFIA_TEAM_TOWN + ///how the random setup chooses which roles get put in + var/role_type = TOWN_OVERFLOW + + var/player_key + var/mob/living/carbon/human/body + var/obj/effect/landmark/mafia/assigned_landmark + + ///how many votes submitted when you vote. + var/vote_power = 1 + var/detect_immune = FALSE + var/revealed = FALSE + var/datum/outfit/revealed_outfit = /datum/outfit/mafia/assistant //the assistants need a special path to call out they were in fact assistant, everything else can just use job equipment + //action = uses + var/list/actions = list() + var/list/targeted_actions = list() + //what the role gets when it wins a game + // var/winner_award = /datum/award/achievement/mafia/assistant + + //so mafia have to also kill them to have a majority + var/solo_counts_as_town = FALSE //(don't set this for town) + var/game_status = MAFIA_ALIVE + + ///icon state in the mafia dmi of the hud of the role, used in the mafia ui + var/hud_icon = "hudassistant" + ///icon state in the mafia dmi of the hud of the role, used in the mafia ui + var/revealed_icon = "assistant" + ///set this to something cool for antagonists and their window will look different + var/special_theme + + var/list/role_notes = list() + + +/datum/mafia_role/New(datum/mafia_controller/game) + . = ..() + +/datum/mafia_role/proc/kill(datum/mafia_controller/game,lynch=FALSE) + if(SEND_SIGNAL(src,COMSIG_MAFIA_ON_KILL,game,lynch) & MAFIA_PREVENT_KILL) + return FALSE + game_status = MAFIA_DEAD + body.death() + if(lynch) + reveal_role(game, verbose = TRUE) + if(!(player_key in game.spectators)) //people who played will want to see the end of the game more often than not + game.spectators += player_key + return TRUE + +/datum/mafia_role/Destroy(force, ...) + QDEL_NULL(body) + . = ..() + +/datum/mafia_role/proc/greet() + SEND_SOUND(body, 'sound/ambience/ambifailure.ogg') + to_chat(body,"You are the [name].") + to_chat(body,"[desc]") + switch(team) + if(MAFIA_TEAM_MAFIA) + to_chat(body,"You and your co-conspirators win if you outnumber crewmembers.") + if(MAFIA_TEAM_TOWN) + to_chat(body,"You are a crewmember. Find out and lynch the changelings!") + if(MAFIA_TEAM_SOLO) + to_chat(body,"You are not aligned to town or mafia. Accomplish your own objectives!") + to_chat(body, "Be sure to read the wiki page to learn more, if you have no idea what's going on.") + +/datum/mafia_role/proc/reveal_role(datum/mafia_controller/game, verbose = FALSE) + if(revealed) + return + if(verbose) + game.send_message("It is revealed that the true role of [body] [game_status == MAFIA_ALIVE ? "is" : "was"] [name]!") + var/list/oldoutfit = body.get_equipped_items() + for(var/thing in oldoutfit) + qdel(thing) + special_reveal_equip(game) + body.equipOutfit(revealed_outfit) + revealed = TRUE + +/datum/mafia_role/proc/special_reveal_equip(datum/mafia_controller/game) + return + +/datum/mafia_role/proc/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target) + return + +/datum/mafia_role/proc/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target) + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,action,target) & MAFIA_PREVENT_ACTION) + return FALSE + return TRUE + +/datum/mafia_role/proc/add_note(note) + role_notes += note + +/datum/mafia_role/proc/check_total_victory(alive_town, alive_mafia) //solo antags can win... solo. + return FALSE + +/datum/mafia_role/proc/block_team_victory(alive_town, alive_mafia) //solo antags can also block team wins. + return FALSE + +/datum/mafia_role/proc/show_help(clueless) + var/list/result = list() + var/team_desc = "" + var/team_span = "" + var/the = TRUE + switch(team) + if(MAFIA_TEAM_TOWN) + team_desc = "Town" + team_span = "nicegreen" + if(MAFIA_TEAM_MAFIA) + team_desc = "Mafia" + team_span = "red" + if(MAFIA_TEAM_SOLO) + team_desc = "Nobody" + team_span = "comradio" + the = FALSE + result += "The [name] is aligned with [the ? "the " : ""][team_desc]" + result += "\"[desc]\"" + result += "[name] wins when they [win_condition]" + to_chat(clueless, result.Join("
")) + +/datum/mafia_role/detective + name = "Detective" + desc = "You can investigate a single person each night to learn their team." + revealed_outfit = /datum/outfit/mafia/detective + role_type = TOWN_INVEST + // winner_award = /datum/award/achievement/mafia/detective + + hud_icon = "huddetective" + revealed_icon = "detective" + + targeted_actions = list("Investigate") + + var/datum/mafia_role/current_investigation + +/datum/mafia_role/detective/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/investigate) + +/datum/mafia_role/detective/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target) + . = ..() + if(!.) + return + return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src + +/datum/mafia_role/detective/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target) + if(!target || target.game_status != MAFIA_ALIVE) + to_chat(body,"You can only investigate alive people.") + return + to_chat(body,"You will investigate [target.body.real_name] tonight.") + current_investigation = target + +/datum/mafia_role/detective/proc/investigate(datum/mafia_controller/game) + var/datum/mafia_role/target = current_investigation + if(target) + if(target.detect_immune) + to_chat(body,"Your investigations reveal that [target.body.real_name] is a true member of the station.") + add_note("N[game.turn] - [target.body.real_name] - Town") + else + var/team_text + var/fluff + switch(target.team) + if(MAFIA_TEAM_TOWN) + team_text = "Town" + fluff = "a true member of the station." + if(MAFIA_TEAM_MAFIA) + team_text = "Mafia" + fluff = "an unfeeling, hideous changeling!" + if(MAFIA_TEAM_SOLO) + team_text = "Solo" + fluff = "a rogue, with their own objectives..." + to_chat(body,"Your investigations reveal that [target.body.real_name] is [fluff]") + add_note("N[game.turn] - [target.body.real_name] - [team_text]") + current_investigation = null + +/datum/mafia_role/psychologist + name = "Psychologist" + desc = "You can visit someone ONCE PER GAME to reveal their true role in the morning!" + revealed_outfit = /datum/outfit/mafia/psychologist + role_type = TOWN_INVEST + // winner_award = /datum/award/achievement/mafia/psychologist + + hud_icon = "hudpsychologist" + revealed_icon = "psychologist" + + targeted_actions = list("Reveal") + var/datum/mafia_role/current_target + var/can_use = TRUE + +/datum/mafia_role/psychologist/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/therapy_reveal) + +/datum/mafia_role/psychologist/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!. || !can_use || game.phase == MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || target.revealed || target == src) + return FALSE + +/datum/mafia_role/psychologist/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + to_chat(body,"You will reveal [target.body.real_name] tonight.") + current_target = target + +/datum/mafia_role/psychologist/proc/therapy_reveal(datum/mafia_controller/game) + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"reveal",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by a lawyer. + current_target = null + if(current_target) + add_note("N[game.turn] - [current_target.body.real_name] - Revealed true identity") + to_chat(body,"You have revealed the true nature of the [current_target]!") + current_target.reveal_role(game, verbose = TRUE) + current_target = null + can_use = FALSE + +/datum/mafia_role/chaplain + name = "Chaplain" + desc = "You can communicate with spirits of the dead each night to discover dead crewmember roles." + revealed_outfit = /datum/outfit/mafia/chaplain + role_type = TOWN_INVEST + hud_icon = "hudchaplain" + revealed_icon = "chaplain" + // winner_award = /datum/award/achievement/mafia/chaplain + + targeted_actions = list("Pray") + var/current_target + +/datum/mafia_role/chaplain/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/commune) + +/datum/mafia_role/chaplain/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!.) + return + return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_DEAD && target != src && !target.revealed + +/datum/mafia_role/chaplain/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + to_chat(body,"You will commune with the spirit of [target.body.real_name] tonight.") + current_target = target + +/datum/mafia_role/chaplain/proc/commune(datum/mafia_controller/game) + var/datum/mafia_role/target = current_target + if(target) + to_chat(body,"You invoke spirit of [target.body.real_name] and learn their role was [target.name].") + add_note("N[game.turn] - [target.body.real_name] - [target.name]") + current_target = null + +/datum/mafia_role/md + name = "Medical Doctor" + desc = "You can protect a single person each night from killing." + revealed_outfit = /datum/outfit/mafia/md // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these) + role_type = TOWN_PROTECT + hud_icon = "hudmedicaldoctor" + revealed_icon = "medicaldoctor" + // winner_award = /datum/award/achievement/mafia/md + + targeted_actions = list("Protect") + var/datum/mafia_role/current_protected + +/datum/mafia_role/md/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/protect) + RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/end_protection) + +/datum/mafia_role/md/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target) + . = ..() + if(!.) + return + if(target.name == "Head of Personnel" && target.revealed) + return FALSE + return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src + +/datum/mafia_role/md/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target) + if(!target || target.game_status != MAFIA_ALIVE) + to_chat(body,"You can only protect alive people.") + return + to_chat(body,"You will protect [target.body.real_name] tonight.") + current_protected = target + +/datum/mafia_role/md/proc/protect(datum/mafia_controller/game) + if(current_protected) + RegisterSignal(current_protected,COMSIG_MAFIA_ON_KILL,.proc/prevent_kill) + add_note("N[game.turn] - Protected [current_protected.body.real_name]") + +/datum/mafia_role/md/proc/prevent_kill(datum/source) + to_chat(body,"The person you protected tonight was attacked!") + to_chat(current_protected.body,"You were attacked last night, but someone nursed you back to life!") + return MAFIA_PREVENT_KILL + +/datum/mafia_role/md/proc/end_protection(datum/mafia_controller/game) + if(current_protected) + UnregisterSignal(current_protected,COMSIG_MAFIA_ON_KILL) + current_protected = null + +/datum/mafia_role/lawyer + name = "Lawyer" + desc = "You can choose a person during the day to provide extensive legal advice to during the night, preventing night actions." + revealed_outfit = /datum/outfit/mafia/lawyer + role_type = TOWN_PROTECT + hud_icon = "hudlawyer" + revealed_icon = "lawyer" + // winner_award = /datum/award/achievement/mafia/lawyer + + targeted_actions = list("Advise") + var/datum/mafia_role/current_target + +/datum/mafia_role/lawyer/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/roleblock_text) + RegisterSignal(game,COMSIG_MAFIA_NIGHT_START,.proc/try_to_roleblock) + RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/release) + +/datum/mafia_role/lawyer/proc/roleblock_text(datum/mafia_controller/game) + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"roleblock",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by another lawyer. + current_target = null + if(current_target) + to_chat(current_target.body,"YOU HAVE BEEN BLOCKED! YOU CANNOT PERFORM ANY ACTIONS TONIGHT.") + add_note("N[game.turn] - [current_target.body.real_name] - Blocked") + +/datum/mafia_role/lawyer/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!.) + return FALSE + if(game.phase == MAFIA_PHASE_NIGHT) + return FALSE + if(target.game_status != MAFIA_ALIVE) + return FALSE + +/datum/mafia_role/lawyer/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(target == current_target) + current_target = null + to_chat(body,"You have decided against blocking anyone tonight.") + else + current_target = target + to_chat(body,"You will block [target.body.real_name] tonight.") + +/datum/mafia_role/lawyer/proc/try_to_roleblock(datum/mafia_controller/game) + if(current_target) + RegisterSignal(current_target,COMSIG_MAFIA_CAN_PERFORM_ACTION, .proc/prevent_action) + +/datum/mafia_role/lawyer/proc/release(datum/mafia_controller/game) + . = ..() + if(current_target) + UnregisterSignal(current_target, COMSIG_MAFIA_CAN_PERFORM_ACTION) + current_target = null + +/datum/mafia_role/lawyer/proc/prevent_action(datum/source) + if(game_status == MAFIA_ALIVE) //in case we got killed while imprisoning sk - bad luck edge + return MAFIA_PREVENT_ACTION + +/datum/mafia_role/hop + name = "Head of Personnel" + desc = "You can reveal yourself once per game, tripling your vote power but becoming unable to be protected!" + revealed_outfit = /datum/outfit/mafia/hop + role_type = TOWN_MISC + hud_icon = "hudheadofpersonnel" + revealed_icon = "headofpersonnel" + // winner_award = /datum/award/achievement/mafia/hop + + targeted_actions = list("Reveal") + +/datum/mafia_role/hop/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!. || game.phase == MAFIA_PHASE_NIGHT || game.turn == 1 || target.game_status != MAFIA_ALIVE || target != src || revealed) + return FALSE + +/datum/mafia_role/hop/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + reveal_role(game, TRUE) + vote_power = 2 + +///MAFIA ROLES/// only one until i rework this to allow more, they're the "anti-town" working to kill off townies to win + +/datum/mafia_role/mafia + name = "Changeling" + desc = "You're a member of the changeling hive. Use ':j' talk prefix to talk to your fellow lings." + team = MAFIA_TEAM_MAFIA + role_type = MAFIA_REGULAR + hud_icon = "hudchangeling" + revealed_icon = "changeling" + // winner_award = /datum/award/achievement/mafia/changeling + + revealed_outfit = /datum/outfit/mafia/changeling + special_theme = "syndicate" + win_condition = "become majority over the town and no solo killing role can stop them." + +/datum/mafia_role/mafia/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/mafia_text) + +/datum/mafia_role/mafia/proc/mafia_text(datum/mafia_controller/source) + to_chat(body,"Vote for who to kill tonight. The killer will be chosen randomly from voters.") + +//better detective for mafia +/datum/mafia_role/mafia/thoughtfeeder + name = "Thoughtfeeder" + desc = "You're a changeling variant that feeds on the memories of others. Use ':j' talk prefix to talk to your fellow lings, and visit people at night to learn their role." + role_type = MAFIA_SPECIAL + hud_icon = "hudthoughtfeeder" + revealed_icon = "thoughtfeeder" + // winner_award = /datum/award/achievement/mafia/thoughtfeeder + + targeted_actions = list("Learn Role") + var/datum/mafia_role/current_investigation + +/datum/mafia_role/mafia/thoughtfeeder/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/investigate) + +/datum/mafia_role/mafia/thoughtfeeder/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target) + . = ..() + if(!.) + return + return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src + +/datum/mafia_role/mafia/thoughtfeeder/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target) + to_chat(body,"You will feast on the memories of [target.body.real_name] tonight.") + current_investigation = target + +/datum/mafia_role/mafia/thoughtfeeder/proc/investigate(datum/mafia_controller/game) + var/datum/mafia_role/target = current_investigation + current_investigation = null + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"thoughtfeed",target) & MAFIA_PREVENT_ACTION) + to_chat(body,"You were unable to investigate [target.body.real_name].") + add_note("N[game.turn] - [target.body.real_name] - Unable to investigate") + return + if(target) + if(target.detect_immune) + to_chat(body,"[target.body.real_name]'s memories reveal that they are the Assistant.") + add_note("N[game.turn] - [target.body.real_name] - Assistant") + else + to_chat(body,"[target.body.real_name]'s memories reveal that they are the [target.name].") + add_note("N[game.turn] - [target.body.real_name] - [target.name]") + + +///SOLO ROLES/// they range from anomalous factors to deranged killers that try to win alone. + +/datum/mafia_role/traitor + name = "Traitor" + desc = "You're a solo traitor. You are immune to night kills, can kill every night and you win by outnumbering everyone else." + win_condition = "kill everyone." + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_KILL + // winner_award = /datum/award/achievement/mafia/traitor + + targeted_actions = list("Night Kill") + revealed_outfit = /datum/outfit/mafia/traitor + + hud_icon = "hudtraitor" + revealed_icon = "traitor" + special_theme = "neutral" + + var/datum/mafia_role/current_victim + +/datum/mafia_role/traitor/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/nightkill_immunity) + RegisterSignal(game,COMSIG_MAFIA_NIGHT_KILL_PHASE,.proc/try_to_kill) + +/datum/mafia_role/traitor/check_total_victory(alive_town, alive_mafia) //serial killers just want teams dead + return alive_town + alive_mafia <= 1 + +/datum/mafia_role/traitor/block_team_victory(alive_town, alive_mafia) //no team can win until they're dead + return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point) + +/datum/mafia_role/traitor/proc/nightkill_immunity(datum/source,datum/mafia_controller/game,lynch) + if(game.phase == MAFIA_PHASE_NIGHT && !lynch) + to_chat(body,"You were attacked, but they'll have to try harder than that to put you down.") + return MAFIA_PREVENT_KILL + +/datum/mafia_role/traitor/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!.) + return FALSE + if(game.phase != MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || target == src) + return FALSE + +/datum/mafia_role/traitor/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + current_victim = target + to_chat(body,"You will attempt to kill [target.body.real_name] tonight.") + +/datum/mafia_role/traitor/proc/try_to_kill(datum/mafia_controller/source) + var/datum/mafia_role/target = current_victim + current_victim = null + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"traitor kill",target) & MAFIA_PREVENT_ACTION) + return + if(game_status == MAFIA_ALIVE && target && target.game_status == MAFIA_ALIVE) + if(!target.kill(source)) + to_chat(body,"Your attempt at killing [target.body] was prevented!") + +/datum/mafia_role/nightmare + name = "Nightmare" + desc = "You're a solo monster that cannot be detected by detective roles. You can flicker lights of another room each night. You can instead decide to hunt, killing everyone in a flickering room. Kill everyone to win." + win_condition = "kill everyone." + revealed_outfit = /datum/outfit/mafia/nightmare + detect_immune = TRUE + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_KILL + special_theme = "neutral" + hud_icon = "hudnightmare" + revealed_icon = "nightmare" + // winner_award = /datum/award/achievement/mafia/nightmare + + targeted_actions = list("Flicker", "Hunt") + var/list/flickering = list() + var/datum/mafia_role/flicker_target + +/datum/mafia_role/nightmare/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_KILL_PHASE,.proc/flicker_or_hunt) + +/datum/mafia_role/nightmare/check_total_victory(alive_town, alive_mafia) //nightmares just want teams dead + return alive_town + alive_mafia <= 1 + +/datum/mafia_role/nightmare/block_team_victory(alive_town, alive_mafia) //no team can win until they're dead + return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point) + +/datum/mafia_role/nightmare/special_reveal_equip() + body.underwear = "Nude" + body.undershirt = "Nude" + body.socks = "Nude" + body.set_species(/datum/species/shadow) + body.update_body() + +/datum/mafia_role/nightmare/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!. || game.phase != MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE) + return FALSE + if(action == "Flicker") + return target != src && !(target in flickering) + return target == src + +/datum/mafia_role/nightmare/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(target == flicker_target) + to_chat(body,"You will do nothing tonight.") + flicker_target = null + flicker_target = target + if(action == "Flicker") + to_chat(body,"You will attempt to flicker [target.body.real_name]'s room tonight.") + else + to_chat(body,"You will hunt everyone in a flickering room down tonight.") + +/datum/mafia_role/nightmare/proc/flicker_or_hunt(datum/mafia_controller/source) + if(game_status != MAFIA_ALIVE || !flicker_target) + return + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"nightmare actions",flicker_target) & MAFIA_PREVENT_ACTION) + to_chat(flicker_target.body, "Your actions were prevented!") + return + var/datum/mafia_role/target = flicker_target + flicker_target = null + if(target != src) //flicker instead of hunt + to_chat(target.body, "The lights begin to flicker and dim. You're in danger.") + flickering += target + return + for(var/r in flickering) + var/datum/mafia_role/role = r + if(role && role.game_status == MAFIA_ALIVE) + to_chat(role.body, "A shadowy monster appears out of the darkness!") + role.kill(source) + flickering -= role + +//just helps read better +#define FUGITIVE_NOT_PRESERVING 0//will not become night immune tonight +#define FUGITIVE_WILL_PRESERVE 1 //will become night immune tonight + +/datum/mafia_role/fugitive + name = "Fugitive" + desc = "You're on the run. You can become immune to night kills exactly twice, and you win by surviving to the end of the game with anyone." + win_condition = "survive to the end of the game, with anyone" + solo_counts_as_town = TRUE //should not count towards mafia victory, they should have the option to work with town + revealed_outfit = /datum/outfit/mafia/fugitive + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_DISRUPT + special_theme = "neutral" + hud_icon = "hudfugitive" + revealed_icon = "fugitive" + // winner_award = /datum/award/achievement/mafia/fugitive + + actions = list("Self Preservation") + var/charges = 2 + var/protection_status = FUGITIVE_NOT_PRESERVING + + +/datum/mafia_role/fugitive/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/night_start) + RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/night_end) + RegisterSignal(game,COMSIG_MAFIA_GAME_END,.proc/survived) + +/datum/mafia_role/fugitive/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!charges) + to_chat(body,"You're out of supplies and cannot protect yourself anymore.") + return + if(game.phase == MAFIA_PHASE_NIGHT) + to_chat(body,"You don't have time to prepare, night has already arrived.") + return + if(protection_status == FUGITIVE_WILL_PRESERVE) + to_chat(body,"You decide to not prepare tonight.") + else + to_chat(body,"You decide to prepare for a horrible night.") + protection_status = !protection_status + +/datum/mafia_role/fugitive/proc/night_start(datum/mafia_controller/game) + if(protection_status == FUGITIVE_WILL_PRESERVE) + to_chat(body,"Your preparations are complete. Nothing could kill you tonight!") + RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prevent_death) + +/datum/mafia_role/fugitive/proc/night_end(datum/mafia_controller/game) + if(protection_status == FUGITIVE_WILL_PRESERVE) + charges-- + UnregisterSignal(src,COMSIG_MAFIA_ON_KILL) + to_chat(body,"You are no longer protected. You have [charges] use[charges == 1 ? "" : "s"] left of your power.") + protection_status = FUGITIVE_NOT_PRESERVING + +/datum/mafia_role/fugitive/proc/prevent_death(datum/mafia_controller/game) + to_chat(body,"You were attacked! Luckily, you were ready for this!") + return MAFIA_PREVENT_KILL + +/datum/mafia_role/fugitive/proc/survived(datum/mafia_controller/game) + if(game_status == MAFIA_ALIVE) + // var/client/winner_client = GLOB.directory[player_key] + // winner_client?.give_award(winner_award, body) + game.send_message("!! FUGITIVE VICTORY !!") + +#undef FUGITIVE_NOT_PRESERVING +#undef FUGITIVE_WILL_PRESERVE + +/datum/mafia_role/obsessed + name = "Obsessed" + desc = "You're completely lost in your own mind. You win by lynching your obsession before you get killed in this mess. Obsession assigned on the first night!" + win_condition = "lynch their obsession." + revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these) + solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective! + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_DISRUPT + special_theme = "neutral" + hud_icon = "hudobsessed" + revealed_icon = "obsessed" + + // winner_award = /datum/award/achievement/mafia/obsessed + + revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these) + solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective! + var/datum/mafia_role/obsession + var/lynched_target = FALSE + +/datum/mafia_role/obsessed/New(datum/mafia_controller/game) //note: obsession is always a townie + . = ..() + RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/find_obsession) + +/datum/mafia_role/obsessed/proc/find_obsession(datum/mafia_controller/game) + var/list/all_roles_shuffle = shuffle(game.all_roles) + for(var/role in all_roles_shuffle) + var/datum/mafia_role/possible = role + if(possible.team == MAFIA_TEAM_TOWN && possible.game_status != MAFIA_DEAD) + obsession = possible + break + if(!obsession) + obsession = pick(all_roles_shuffle) //okay no town just pick anyone here + //if you still don't have an obsession you're playing a single player game like i can't help your dumb ass + to_chat(body, "Your obsession is [obsession.body.real_name]! Get them lynched to win!") + add_note("N[game.turn] - I vowed to watch my obsession, [obsession.body.real_name], hang!") //it'll always be N1 but whatever + RegisterSignal(obsession,COMSIG_MAFIA_ON_KILL,.proc/check_victory) + UnregisterSignal(game,COMSIG_MAFIA_SUNDOWN) + +/datum/mafia_role/obsessed/proc/check_victory(datum/source,datum/mafia_controller/game,lynch) + UnregisterSignal(source,COMSIG_MAFIA_ON_KILL) + if(game_status == MAFIA_DEAD) + return + if(lynch) + game.send_message("!! OBSESSED VICTORY !!") + // var/client/winner_client = GLOB.directory[player_key] + // winner_client?.give_award(winner_award, body) + reveal_role(game, FALSE) + else + to_chat(body, "You have failed your objective to lynch [obsession.body]!") + +/datum/mafia_role/clown + name = "Clown" + desc = "If you are lynched you take down one of your voters (guilty or abstain) with you and win. HONK!" + win_condition = "get themselves lynched!" + revealed_outfit = /datum/outfit/mafia/clown + solo_counts_as_town = TRUE + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_DISRUPT + special_theme = "neutral" + hud_icon = "hudclown" + revealed_icon = "clown" + // winner_award = /datum/award/achievement/mafia/clown + +/datum/mafia_role/clown/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prank) + +/datum/mafia_role/clown/proc/prank(datum/source,datum/mafia_controller/game,lynch) + if(lynch) + var/datum/mafia_role/victim = pick(game.judgement_guilty_votes + game.judgement_abstain_votes) + game.send_message("[body.real_name] WAS A CLOWN! HONK! They take down [victim.body.real_name] with their last prank.") + game.send_message("!! CLOWN VICTORY !!") + // var/client/winner_client = GLOB.directory[player_key] + // winner_client?.give_award(winner_award, body) + victim.kill(game,FALSE) diff --git a/code/modules/mapping/map_config.dm b/code/modules/mapping/map_config.dm index efa2655325..c03ef65f43 100644 --- a/code/modules/mapping/map_config.dm +++ b/code/modules/mapping/map_config.dm @@ -20,7 +20,7 @@ var/map_file = "BoxStation.dmm" var/traits = null - var/space_ruin_levels = 2 + var/space_ruin_levels = 4 var/space_empty_levels = 1 var/station_ruin_budget = -1 // can be set to manually override the station ruins budget on maps that don't support station ruins, stopping the error from being unable to place the ruins. diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 7a755265dc..a09d890f69 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -183,9 +183,40 @@ attack_verb = list("stabbed", "diced", "sliced", "cleaved", "chopped", "lacerated", "cut", "jabbed", "punctured") icon_state = "crusher-glaive" item_state = "crusher0-glaive" + item_flags = ITEM_CAN_PARRY + block_parry_data = /datum/block_parry_data/crusherglaive //ideas: altclick that lets you pummel people with the handguard/handle? //parrying functionality? +/datum/block_parry_data/crusherglaive // small perfect window, active for a fair while, time it right or use the Forbidden Technique + parry_time_windup = 0 + parry_time_active = 8 + parry_time_spindown = 0 + parry_time_perfect = 1 + parry_time_perfect_leeway = 2 + parry_imperfect_falloff_percent = 20 + parry_efficiency_to_counterattack = 100 // perfect parry or you're cringe + parry_failed_stagger_duration = 1.5 SECONDS // a good time to reconsider your actions... + parry_failed_clickcd_duration = 1.5 SECONDS // or your failures + +/obj/item/kinetic_crusher/glaive/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time) // if you're dumb enough to go for a parry... + var/turf/proj_turf = owner.loc // destabilizer bolt, ignoring cooldown + if(!isturf(proj_turf)) + return + var/obj/item/projectile/destabilizer/D = new /obj/item/projectile/destabilizer(proj_turf) + for(var/t in trophies) + var/obj/item/crusher_trophy/T = t + T.on_projectile_fire(D, owner) + D.preparePixelProjectile(attacker, owner) + D.firer = owner + D.hammer_synced = src + playsound(owner, 'sound/weapons/plasma_cutter.ogg', 100, 1) + D.fire() + +/obj/item/kinetic_crusher/glaive/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency, list/effect_text) + if(owner.Adjacent(attacker) && (!attacker.anchored || ismegafauna(attacker))) // free backstab, if you perfect parry + attacker.dir = get_dir(owner,attacker) + /obj/item/kinetic_crusher/glaive/update_icon_state() item_state = "crusher[wielded]-glaive" // this is not icon_state and not supported by 2hcomponent diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index 4d0b7dff21..429dc98e8a 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -8,8 +8,6 @@ GLOBAL_LIST(labor_sheet_values) icon = 'icons/obj/machines/mining_machines.dmi' icon_state = "console" density = FALSE - ui_x = 315 - ui_y = 430 var/obj/machinery/mineral/stacking_machine/laborstacker/stacking_machine = null var/machinedir = SOUTH @@ -35,11 +33,10 @@ GLOBAL_LIST(labor_sheet_values) /proc/cmp_sheet_list(list/a, list/b) return a["value"] - b["value"] -/obj/machinery/mineral/labor_claim_console/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/mineral/labor_claim_console/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "LaborClaimConsole", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "LaborClaimConsole", name) ui.open() /obj/machinery/mineral/labor_claim_console/ui_data(mob/user) @@ -121,7 +118,7 @@ GLOBAL_LIST(labor_sheet_values) /obj/machinery/mineral/stacking_machine/laborstacker force_connect = TRUE var/points = 0 //The unclaimed value of ore stacked. - + //damage_deflection = 21 /obj/machinery/mineral/stacking_machine/laborstacker/process_sheet(obj/item/stack/sheet/inp) points += inp.point_value * inp.amount ..() diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index bae0e94032..0268d32a6b 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -195,10 +195,10 @@ to_chat(user, "You change [src]'s I/O settings, setting the input to [dir2text(input_dir)] and the output to [dir2text(output_dir)].") return TRUE -/obj/machinery/mineral/ore_redemption/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/mineral/ore_redemption/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "OreRedemptionMachine", "Ore Redemption Machine", 440, 550, master_ui, state) + ui = new(user, src, "OreRedemptionMachine") ui.open() /obj/machinery/mineral/ore_redemption/ui_data(mob/user) diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm index 564d554319..2cbb965ef5 100644 --- a/code/modules/mining/machine_vending.dm +++ b/code/modules/mining/machine_vending.dm @@ -7,8 +7,6 @@ icon_state = "mining" density = TRUE circuit = /obj/item/circuitboard/machine/mining_equipment_vendor - ui_x = 425 - ui_y = 600 var/icon_deny = "mining-deny" var/obj/item/card/id/inserted_id var/list/prize_list = list( //if you add something to this, please, for the love of god, sort it by price/type. use tabs and not spaces. @@ -68,13 +66,13 @@ new /datum/data/mining_equipment("KA Adjustable Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer/adjustable, 150), new /datum/data/mining_equipment("KA Super Chassis", /obj/item/borg/upgrade/modkit/chassis_mod, 250), new /datum/data/mining_equipment("KA Hyper Chassis", /obj/item/borg/upgrade/modkit/chassis_mod/orange, 300), - new /datum/data/mining_equipment("Kinetic Glaive", /obj/item/kinetic_crusher/glaive, 1500), new /datum/data/mining_equipment("KA Range Increase", /obj/item/borg/upgrade/modkit/range, 1000), new /datum/data/mining_equipment("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000), new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000), new /datum/data/mining_equipment("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000), new /datum/data/mining_equipment("Miner Full Replacement", /obj/item/storage/backpack/duffelbag/mining_cloned, 3000), - new /datum/data/mining_equipment("Premium Accelerator", /obj/item/gun/energy/kinetic_accelerator/premiumka, 8000) + new /datum/data/mining_equipment("Premium Accelerator", /obj/item/gun/energy/kinetic_accelerator/premiumka, 8000), + new /datum/data/mining_equipment("Kinetic Glaive", /obj/item/kinetic_crusher/glaive, 2250), ) /datum/data/mining_equipment @@ -102,17 +100,15 @@ else icon_state = "[initial(icon_state)]-off" -/obj/machinery/mineral/equipment_vendor/ui_base_html(html) - var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/vending) - . = replacetext(html, "", assets.css_tag()) +/obj/machinery/mineral/equipment_vendor/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/vending), + ) -/obj/machinery/mineral/equipment_vendor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/mineral/equipment_vendor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/vending) - assets.send(user) - ui = new(user, src, ui_key, "MiningVendor", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "MiningVendor", name) ui.open() /obj/machinery/mineral/equipment_vendor/ui_static_data(mob/user) @@ -137,10 +133,12 @@ if(C) .["user"] = list() .["user"]["points"] = C.mining_points - if(C.assignment) - .["user"]["job"] = C.assignment - else - .["user"]["job"] = "No Job" + if(C.registered_account) + .["user"]["name"] = C.registered_account.account_holder + if(C.registered_account.account_job) + .["user"]["job"] = C.registered_account.account_job.title + else + .["user"]["job"] = "No Job" /obj/machinery/mineral/equipment_vendor/ui_act(action, params) if(..()) diff --git a/code/modules/mining/satchel_ore_boxdm.dm b/code/modules/mining/satchel_ore_boxdm.dm index ae42ca4745..36da9d5db9 100644 --- a/code/modules/mining/satchel_ore_boxdm.dm +++ b/code/modules/mining/satchel_ore_boxdm.dm @@ -9,9 +9,6 @@ density = TRUE pressure_resistance = 5*ONE_ATMOSPHERE - var/ui_x = 335 - var/ui_y = 415 - /obj/structure/ore_box/attackby(obj/item/W, mob/user, params) if (istype(W, /obj/item/stack/ore)) user.transferItemToLoc(W, src) @@ -58,11 +55,10 @@ stoplag() drop = drop_location() -/obj/structure/ore_box/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/structure/ore_box/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "OreBox", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "OreBox", name) ui.open() /obj/structure/ore_box/ui_data() diff --git a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm index 89f2dd5370..138704067d 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/hair_head.dm @@ -791,6 +791,10 @@ name = "Volaju" icon_state = "hair_volaju" +/datum/sprite_accessory/hair/volajupompless + name = "Volaju (Alt)" + icon_state = "hair_volajupompless" + /datum/sprite_accessory/hair/wisp name = "Wisp" icon_state = "hair_wisp" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm b/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm index 60e8ed1007..2c0db56ce4 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm @@ -184,6 +184,12 @@ extra = TRUE extra_color_src = MUTCOLORS3 +/datum/sprite_accessory/mam_snouts/skulldog + name = "Skulldog" + icon_state = "skulldog" + extra = TRUE + extra_color_src = MATRIXED + /datum/sprite_accessory/mam_snouts/lcanid name = "Mammal, Long" icon_state = "lcanid" diff --git a/code/modules/mob/dead/observer/notificationprefs.dm b/code/modules/mob/dead/observer/notificationprefs.dm index 160abd57e1..524ff80d5d 100644 --- a/code/modules/mob/dead/observer/notificationprefs.dm +++ b/code/modules/mob/dead/observer/notificationprefs.dm @@ -3,12 +3,10 @@ set name = "Notification preferences" set desc = "Notification preferences" - var/datum/notificationpanel/panel = new(usr) + var/datum/notificationpanel/panel = new(usr) panel.ui_interact(usr) - - /datum/notificationpanel var/client/user @@ -21,10 +19,13 @@ else src.user = user -/datum/notificationpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/notificationpanel/ui_state(mob/user) + return GLOB.observer_state + +/datum/notificationpanel/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "NotificationPreferences", "Notification Preferences", 270, 360, master_ui, state) + ui = new(user, src, "NotificationPreferences") ui.open() /datum/notificationpanel/ui_data(mob/user) @@ -35,8 +36,7 @@ "key" = key, "enabled" = (user.ckey in GLOB.poll_ignore[key]), "desc" = GLOB.poll_ignore_desc[key] - )) - + )) /datum/notificationpanel/ui_act(action, params) if(..()) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index bb39639ec1..a0df1ee938 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -905,6 +905,22 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp else to_chat(usr, "Can't become a pAI candidate while not dead!") +/mob/dead/observer/verb/mafia_game_signup() + set category = "Ghost" + set name = "Signup for Mafia" + set desc = "Sign up for a game of Mafia to pass the time while dead." + mafia_signup() +/mob/dead/observer/proc/mafia_signup() + if(!client) + return + if(!isobserver(src)) + to_chat(usr, "You must be a ghost to join mafia!") + return + var/datum/mafia_controller/game = GLOB.mafia_game //this needs to change if you want multiple mafia games up at once. + if(!game) + game = create_mafia_game("mafia") + game.ui_interact(usr) + /mob/dead/observer/CtrlShiftClick(mob/user) if(isobserver(user) && check_rights(R_SPAWN)) change_mob_type( /mob/living/carbon/human , null, null, TRUE) //always delmob, ghosts shouldn't be left lingering diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm index eb23a5fb67..b81172afad 100644 --- a/code/modules/mob/dead/observer/orbit.dm +++ b/code/modules/mob/dead/observer/orbit.dm @@ -6,9 +6,12 @@ qdel(src) owner = new_owner -/datum/orbit_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state) +/datum/orbit_menu/ui_state(mob/user) + return GLOB.observer_state + +/datum/orbit_menu/ui_interact(mob/user, datum/tgui/ui) if (!ui) - ui = new(user, src, ui_key, "Orbit", "Orbit", 350, 700, master_ui, state) + ui = new(user, src, "Orbit") ui.open() /datum/orbit_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) @@ -16,11 +19,10 @@ return if (action == "orbit") - var/list/pois = getpois(skip_mindless = 1) - var/atom/movable/poi = pois[params["name"]] + var/ref = params["ref"] + var/atom/movable/poi = (locate(ref) in GLOB.mob_list) || (locate(ref) in GLOB.poi_list) if (poi != null) owner.ManualFollow(poi) - ui.close() /datum/orbit_menu/ui_data(mob/user) var/list/data = list() @@ -39,6 +41,8 @@ var/poi = pois[name] + serialized["ref"] = REF(poi) + var/mob/M = poi if (istype(M)) if (isobserver(M)) diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 36a6f6296b..cf1a158fc5 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -62,10 +62,6 @@ //Effects of bloodloss var/word = pick("dizzy","woozy","faint") switch(blood_volume) - if(BLOOD_VOLUME_EXCESS to BLOOD_VOLUME_MAX_LETHAL) - if(prob(15)) - to_chat(src, "Blood starts to tear your skin apart. You're going to burst!") - gib() if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS) if(prob(10)) to_chat(src, "You feel terribly bloated.") diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index b402260611..38b420aaba 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1042,7 +1042,7 @@ /mob/living/carbon/human/updatehealth() . = ..() - + dna?.species.spec_updatehealth(src) if(HAS_TRAIT(src, TRAIT_IGNORESLOWDOWN)) //if we want to ignore slowdown from damage and equipment remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown) remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying) @@ -1199,6 +1199,9 @@ /mob/living/carbon/human/species/lizard race = /datum/species/lizard +/mob/living/carbon/human/species/ethereal + race = /datum/species/ethereal + /mob/living/carbon/human/species/lizard/ashwalker race = /datum/species/lizard/ashwalker diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 1673c57a80..de4b7a659f 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -336,7 +336,8 @@ if (!istype(ears, /obj/item/clothing/ears/earmuffs)) adjustEarDamage(30, 120) Unconscious(20) //short amount of time for follow up attacks against elusive enemies like wizards - Knockdown(200 - (bomb_armor * 1.6)) //between ~4 and ~20 seconds of knockdown depending on bomb armor + Knockdown((200 - (bomb_armor * 1.6)) / 4) //between ~1 and ~5 seconds of knockdown depending on bomb armor + adjustStaminaLoss(brute_loss) if(EXPLODE_LIGHT) brute_loss = 30 @@ -345,7 +346,8 @@ damage_clothes(max(50 - bomb_armor, 0), BRUTE, "bomb") if (!istype(ears, /obj/item/clothing/ears/earmuffs)) adjustEarDamage(15,60) - Knockdown(160 - (bomb_armor * 1.6)) //100 bomb armor will prevent knockdown altogether + Knockdown((160 - (bomb_armor * 1.6)) / 4) //100 bomb armor will prevent knockdown altogether + adjustStaminaLoss(brute_loss) take_overall_damage(brute_loss,burn_loss) diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index a7593ca0cb..1c776427cf 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -169,7 +169,7 @@ //Item is handled and in slot, valid to call callback, for this proc should always be true if(!not_handled) I.equipped(src, slot) - + update_genitals() return not_handled //For future deeper overrides /mob/living/carbon/human/equipped_speed_mods() @@ -257,6 +257,7 @@ s_store = null if(!QDELETED(src)) update_inv_s_store() + update_genitals() /mob/living/carbon/human/wear_mask_update(obj/item/clothing/C, toggle_off = 1) if((C.flags_inv & (HIDEHAIR|HIDEFACIALHAIR)) || (initial(C.flags_inv) & (HIDEHAIR|HIDEFACIALHAIR))) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index dc7fe86aca..54c3b314b9 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -56,6 +56,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/list/mutant_organs = list() //Internal organs that are unique to this race. var/speedmod = 0 // this affects the race's speed. positive numbers make it move slower, negative numbers make it move faster var/armor = 0 // overall defense for the race... or less defense, if it's negative. + var/attack_type = BRUTE // the type of damage unarmed attacks from this species do var/brutemod = 1 // multiplier for brute damage var/burnmod = 1 // multiplier for burn damage var/coldmod = 1 // multiplier for cold damage @@ -1364,6 +1365,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/hungry = (500 - H.nutrition) / 5 //So overeat would be 100 and default level would be 80 if(hungry >= 70) H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/hunger, multiplicative_slowdown = (hungry / 50)) + else if(isethereal(H)) + var/datum/species/ethereal/E = H.dna.species + if(E.get_charge(H) <= ETHEREAL_CHARGE_NORMAL) + H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/hunger, multiplicative_slowdown = (1.5 * (1 - E.get_charge(H) / 100))) else H.remove_movespeed_modifier(/datum/movespeed_modifier/hunger) @@ -1420,6 +1425,12 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) // ATTACK PROCS // ////////////////// +/datum/species/proc/spec_updatehealth(mob/living/carbon/human/H) + return + +/datum/species/proc/spec_fully_heal(mob/living/carbon/human/H) + return + /datum/species/proc/help(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style) if(target.health >= 0 && !HAS_TRAIT(target, TRAIT_FAKEDEATH)) target.help_shake_act(user) @@ -1538,11 +1549,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) target.dismembering_strike(user, affecting.body_zone) if(atk_verb == ATTACK_EFFECT_KICK)//kicks deal 1.5x raw damage + 0.5x stamina damage - target.apply_damage(damage*1.5, BRUTE, affecting, armor_block) + target.apply_damage(damage*1.5, attack_type, affecting, armor_block) target.apply_damage(damage*0.5, STAMINA, affecting, armor_block) log_combat(user, target, "kicked") else//other attacks deal full raw damage + 2x in stamina damage - target.apply_damage(damage, BRUTE, affecting, armor_block) + target.apply_damage(damage, attack_type, affecting, armor_block) target.apply_damage(damage*2, STAMINA, affecting, armor_block) log_combat(user, target, "punched") diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm new file mode 100644 index 0000000000..27338f598f --- /dev/null +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -0,0 +1,182 @@ +#define ETHEREAL_COLORS list("#00ffff", "#ffc0cb", "#9400D3", "#4B0082", "#0000FF", "#00FF00", "#FFFF00", "#FF7F00", "#FF0000") + +/datum/species/ethereal + name = "Ethereal" + id = "ethereal" + attack_verb = "burn" + attack_sound = 'sound/weapons/etherealhit.ogg' + miss_sound = 'sound/weapons/etherealmiss.ogg' + meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ethereal + mutantstomach = /obj/item/organ/stomach/ethereal + mutanttongue = /obj/item/organ/tongue/ethereal + exotic_blood = /datum/reagent/consumable/liquidelectricity //Liquid Electricity. fuck you think of something better gamer + siemens_coeff = 0.5 //They thrive on energy + brutemod = 1.25 //They're weak to punches + attack_type = BURN //burn bish + damage_overlay_type = "" //We are too cool for regular damage overlays + species_traits = list(MUTCOLORS, NO_UNDERWEAR, HAIR, HAS_FLESH, HAS_BONE) // i mean i guess they have blood so they can have wounds too + species_language_holder = /datum/language_holder/ethereal + inherent_traits = list(TRAIT_NOHUNGER) + sexes = FALSE + toxic_food = NONE + /* + citadel doesn't have per-species temperatures, yet + // Body temperature for ethereals is much higher then humans as they like hotter environments + bodytemp_normal = (BODYTEMP_NORMAL + 50) + bodytemp_heat_damage_limit = FIRE_MINIMUM_TEMPERATURE_TO_SPREAD // about 150C + // Cold temperatures hurt faster as it is harder to move with out the heat energy + bodytemp_cold_damage_limit = (T20C - 10) // about 10c + */ + hair_color = "fixedmutcolor" + hair_alpha = 140 + var/current_color + var/EMPeffect = FALSE + var/emageffect = FALSE + var/r1 + var/g1 + var/b1 + var/static/r2 = 237 + var/static/g2 = 164 + var/static/b2 = 149 + //this is shit but how do i fix it? no clue. + var/drain_time = 0 //used to keep ethereals from spam draining power sources + +/datum/species/ethereal/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load) + .=..() + if(ishuman(C)) + var/mob/living/carbon/human/H = C + default_color = "#" + H.dna.features["mcolor"] + r1 = GETREDPART(default_color) + g1 = GETGREENPART(default_color) + b1 = GETBLUEPART(default_color) + spec_updatehealth(H) + RegisterSignal(C, COMSIG_ATOM_EMAG_ACT, .proc/on_emag_act) + RegisterSignal(C, COMSIG_ATOM_EMP_ACT, .proc/on_emp_act) + +/datum/species/ethereal/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load) + .=..() + C.set_light(0) + UnregisterSignal(C, COMSIG_ATOM_EMAG_ACT) + UnregisterSignal(C, COMSIG_ATOM_EMP_ACT) + +/datum/species/ethereal/random_name(gender,unique,lastname) + if(unique) + return random_unique_ethereal_name() + + var/randname = ethereal_name() + + return randname + +/datum/species/ethereal/spec_updatehealth(mob/living/carbon/human/H) + .=..() + if(H.stat != DEAD && !EMPeffect) + var/healthpercent = max(H.health, 0) / 100 + if(!emageffect) + current_color = rgb(r2 + ((r1-r2)*healthpercent), g2 + ((g1-g2)*healthpercent), b2 + ((b1-b2)*healthpercent)) + H.set_light(1 + (2 * healthpercent), 1 + (1 * healthpercent), current_color) + fixed_mut_color = copytext_char(current_color, 2) + else + H.set_light(0) + fixed_mut_color = rgb(128,128,128) + H.update_body() + +/datum/species/ethereal/proc/on_emp_act(mob/living/carbon/human/H, severity) + EMPeffect = TRUE + spec_updatehealth(H) + to_chat(H, "You feel the light of your body leave you.") + switch(severity) + if(EMP_LIGHT) + addtimer(CALLBACK(src, .proc/stop_emp, H), 10 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) //We're out for 10 seconds + if(EMP_HEAVY) + addtimer(CALLBACK(src, .proc/stop_emp, H), 20 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) //We're out for 20 seconds + +/datum/species/ethereal/proc/on_emag_act(mob/living/carbon/human/H, mob/user) + if(emageffect) + return + emageffect = TRUE + if(user) + to_chat(user, "You tap [H] on the back with your card.") + H.visible_message("[H] starts flickering in an array of colors!") + handle_emag(H) + addtimer(CALLBACK(src, .proc/stop_emag, H), 30 SECONDS) //Disco mode for 30 seconds! This doesn't affect the ethereal at all besides either annoying some players, or making someone look badass. + + +/datum/species/ethereal/spec_life(mob/living/carbon/human/H) + .=..() + handle_charge(H) + + +/datum/species/ethereal/proc/stop_emp(mob/living/carbon/human/H) + EMPeffect = FALSE + spec_updatehealth(H) + to_chat(H, "You feel more energized as your shine comes back.") + + +/datum/species/ethereal/proc/handle_emag(mob/living/carbon/human/H) + if(!emageffect) + return + current_color = pick(ETHEREAL_COLORS) + spec_updatehealth(H) + addtimer(CALLBACK(src, .proc/handle_emag, H), 5) //Call ourselves every 0.5 seconds to change color + +/datum/species/ethereal/proc/stop_emag(mob/living/carbon/human/H) + emageffect = FALSE + spec_updatehealth(H) + H.visible_message("[H] stops flickering and goes back to their normal state!") + +/datum/species/ethereal/proc/handle_charge(mob/living/carbon/human/H) + brutemod = 1.25 + switch(get_charge(H)) + if(ETHEREAL_CHARGE_NONE) + H.throw_alert("ethereal_charge", /obj/screen/alert/etherealcharge, 3) + if(ETHEREAL_CHARGE_NONE to ETHEREAL_CHARGE_LOWPOWER) + H.throw_alert("ethereal_charge", /obj/screen/alert/etherealcharge, 2) + if(H.health > 10.5) + apply_damage(0.65, TOX, null, null, H) + brutemod = 1.75 + if(ETHEREAL_CHARGE_LOWPOWER to ETHEREAL_CHARGE_NORMAL) + H.throw_alert("ethereal_charge", /obj/screen/alert/etherealcharge, 1) + brutemod = 1.5 + if(ETHEREAL_CHARGE_FULL to ETHEREAL_CHARGE_OVERLOAD) + H.throw_alert("ethereal_overcharge", /obj/screen/alert/ethereal_overcharge, 1) + apply_damage(0.2, TOX, null, null, H) + brutemod = 1.5 + if(ETHEREAL_CHARGE_OVERLOAD to ETHEREAL_CHARGE_DANGEROUS) + H.throw_alert("ethereal_overcharge", /obj/screen/alert/ethereal_overcharge, 2) + apply_damage(0.65, TOX, null, null, H) + brutemod = 1.75 + if(prob(10)) //10% each tick for ethereals to explosively release excess energy if it reaches dangerous levels + discharge_process(H) + else + H.clear_alert("ethereal_charge") + H.clear_alert("ethereal_overcharge") + +/datum/species/ethereal/proc/discharge_process(mob/living/carbon/human/H) + to_chat(H, "You begin to lose control over your charge!") + H.visible_message("[H] begins to spark violently!") + var/static/mutable_appearance/overcharge //shameless copycode from lightning spell + overcharge = overcharge || mutable_appearance('icons/effects/effects.dmi', "electricity", EFFECTS_LAYER) + H.add_overlay(overcharge) + if(do_mob(H, H, 50, 1)) + H.flash_lighting_fx(5, 7, current_color) + var/obj/item/organ/stomach/ethereal/stomach = H.getorganslot(ORGAN_SLOT_STOMACH) + playsound(H, 'sound/magic/lightningshock.ogg', 100, TRUE, extrarange = 5) + H.cut_overlay(overcharge) + tesla_zap(H, 2, stomach.crystal_charge*50, ZAP_OBJ_DAMAGE | ZAP_ALLOW_DUPLICATES) + if(istype(stomach)) + stomach.adjust_charge(100 - stomach.crystal_charge) + to_chat(H, "You violently discharge energy!") + H.visible_message("[H] violently discharges energy!") + if(prob(10)) //chance of developing heart disease to dissuade overcharging oneself + var/datum/disease/D = new /datum/disease/heart_failure + H.ForceContractDisease(D) + to_chat(H, "You're pretty sure you just felt your heart stop for a second there..") + H.playsound_local(H, 'sound/effects/singlebeat.ogg', 100, 0) + H.Paralyze(100) + return + +/datum/species/ethereal/proc/get_charge(mob/living/carbon/H) //this feels like it should be somewhere else. Eh? + var/obj/item/organ/stomach/ethereal/stomach = H.getorganslot(ORGAN_SLOT_STOMACH) + if(istype(stomach)) + return stomach.crystal_charge + return ETHEREAL_CHARGE_NONE diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index 4498306b89..c762a52ffc 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -4,7 +4,7 @@ id = "jelly" default_color = "00FF90" say_mod = "chirps" - species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,WINGCOLOR,HAS_FLESH,HAS_BONE) + species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,WINGCOLOR,HAS_FLESH) mutantlungs = /obj/item/organ/lungs/slime mutant_heart = /obj/item/organ/heart/slime mutant_bodyparts = list("mcolor" = "FFFFFF", "mam_tail" = "None", "mam_ears" = "None", "mam_snouts" = "None", "taur" = "None", "deco_wings" = "None") @@ -287,11 +287,16 @@ else ui_interact(owner) -/datum/action/innate/swap_body/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state) +/datum/action/innate/swap_body/ui_host(mob/user) + return owner - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/action/innate/swap_body/ui_state(mob/user) + return GLOB.not_incapacitated_state + +/datum/action/innate/swap_body/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "SlimeBodySwapper", name, 400, 400, master_ui, state) + ui = new(user, src, "SlimeBodySwapper", name) ui.open() /datum/action/innate/swap_body/ui_data(mob/user) @@ -361,7 +366,8 @@ return switch(action) if("swap") - var/mob/living/carbon/human/selected = locate(params["ref"]) + var/datum/species/jelly/slime/SS = H.dna.species + var/mob/living/carbon/human/selected = locate(params["ref"]) in SS.bodies if(!can_swap(selected)) return SStgui.close_uis(src) diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index b9864bce16..b760af9850 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -185,6 +185,8 @@ T.ScrapeAway(flags = CHANGETURF_INHERIT_AIR) else if(isliving(AM)) var/mob/living/L = AM + if(isethereal(AM)) + AM.emp_act(EMP_LIGHT) if(iscyborg(AM)) var/mob/living/silicon/robot/borg = AM if(borg.lamp_intensity) diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm index 81a7f6e2e7..8d86ad2fa9 100644 --- a/code/modules/mob/living/carbon/human/species_types/zombies.dm +++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm @@ -32,6 +32,7 @@ name = "Infectious Zombie" id = "memezombies" limbs_id = "zombie" + inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NOBREATH,TRAIT_NODEATH,TRAIT_NOSOFTCRIT, TRAIT_FAKEDEATH) mutanthands = /obj/item/zombie_hand armor = 20 // 120 damage to KO a zombie, which kills it speedmod = 1.6 // they're very slow @@ -89,8 +90,8 @@ infection = new() infection.Insert(C) - //make their bodyparts stamina-resistant - var/incoming_stam_mult = 0.7 + //make their bodyparts stamina-immune, its a corpse. + var/incoming_stam_mult = 0 for(var/obj/item/bodypart/part in C.bodyparts) part.incoming_stam_mult = incoming_stam_mult //todo: add negative wound resistance to all parts when wounds is merged (zombies are physically weak in terms of limbs) diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index a5de3a2fdc..97de8bfd6c 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -326,6 +326,11 @@ key_third_person = "smiles" message = "smiles." +/datum/emote/living/smirk + key = "smirk" + key_third_person = "smirks" + message = "smirks." + /datum/emote/living/sneeze key = "sneeze" key_third_person = "sneezes" @@ -441,7 +446,7 @@ to_chat(user, "You cannot send IC messages (muted).") return FALSE else if(!params) - var/custom_emote = stripped_multiline_input(user, "Choose an emote to display.", "Custom Emote", null, MAX_MESSAGE_LEN) + var/custom_emote = stripped_multiline_input_or_reflect(user, "Choose an emote to display.", "Custom Emote", null, MAX_MESSAGE_LEN) if(custom_emote && !check_invalid(user, custom_emote)) var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable") switch(type) @@ -531,3 +536,29 @@ to_chat(user, "You ready your slapping hand.") else to_chat(user, "You're incapable of slapping in your current state.") + +/datum/emote/living/audio_emote/blorble + key = "blorble" + key_third_person = "blorbles" + message = "blorbles." + message_param = "blorbles at %t." + +/datum/emote/living/audio_emote/blorble/run_emote(mob/user, params) + . = ..() + if(. && iscarbon(user)) + var/mob/living/carbon/C = user + if(isjellyperson(C)) + pick(playsound(C, 'sound/effects/attackblob.ogg', 50, 1),playsound(C, 'sound/effects/blobattack.ogg', 50, 1)) + +/datum/emote/living/audio_emote/blurp + key = "blurp" + key_third_person = "blurps" + message = "blurps." + message_param = "blurps at %t." + +/datum/emote/living/audio_emote/blurp/run_emote(mob/user, params) + . = ..() + if(. && iscarbon(user)) + var/mob/living/carbon/C = user + if(isjellyperson(C)) + pick(playsound(C, 'sound/effects/meatslap.ogg', 50, 1),playsound(C, 'sound/effects/gib_step.ogg', 50, 1)) diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm index 4a9c4e6311..b6706d0548 100644 --- a/code/modules/mob/living/living_active_parry.dm +++ b/code/modules/mob/living/living_active_parry.dm @@ -245,7 +245,7 @@ . |= BLOCK_SUCCESS var/list/effect_text if(efficiency >= data.parry_efficiency_to_counterattack) - run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) + effect_text = run_parry_countereffects(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency) if(data.parry_flags & PARRY_DEFAULT_HANDLE_FEEDBACK) handle_parry_feedback(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list, efficiency, effect_text) successful_parries += efficiency @@ -254,9 +254,12 @@ /mob/living/proc/handle_parry_feedback(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency, list/effect_text) var/datum/block_parry_data/data = get_parry_data() + var/knockdown_check = FALSE + if(data.parry_data[PARRY_KNOCKDOWN_ATTACKER] && parry_efficiency >= data.parry_efficiency_to_counterattack) + knockdown_check = TRUE if(data.parry_sounds) playsound(src, pick(data.parry_sounds), 75) - visible_message("[src] parries [attack_text][length(effect_text)? ", [english_list(effect_text)] [attacker]" : ""]!") + visible_message("[src] parries [attack_text][length(effect_text)? ", [english_list(effect_text)] [attacker]" : ""][length(effect_text) && knockdown_check? " and" : ""][knockdown_check? " knocking them to the ground" : ""]!") /// Run counterattack if any /mob/living/proc/run_parry_countereffects(atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list = list(), parry_efficiency) @@ -288,15 +291,15 @@ if(data.parry_data[PARRY_DISARM_ATTACKER]) L.drop_all_held_items() effect_text += "disarming" - if(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) - L.DefaultCombatKnockdown(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) - effect_text += "knocking them to the ground" if(data.parry_data[PARRY_STAGGER_ATTACKER]) L.Stagger(data.parry_data[PARRY_STAGGER_ATTACKER]) effect_text += "staggering" if(data.parry_data[PARRY_DAZE_ATTACKER]) L.Daze(data.parry_data[PARRY_DAZE_ATTACKER]) effect_text += "dazing" + if(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) + L.DefaultCombatKnockdown(data.parry_data[PARRY_KNOCKDOWN_ATTACKER]) + // effect_text += "knocking them to the ground" - snowflaked above return effect_text /// Gets the datum/block_parry_data we're going to use to parry. diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 4f2332e99d..2cbd1f35ca 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -151,7 +151,7 @@ to_chat(src, "Receiving control information from APC.") sleep(2) apc_override = 1 - theAPC.ui_interact(src, state = GLOB.conscious_state) + theAPC.ui_interact(src) apc_override = 0 aiRestorePowerRoutine = POWER_RESTORATION_APC_FOUND sleep(50) diff --git a/code/modules/mob/living/silicon/ai/robot_control.dm b/code/modules/mob/living/silicon/ai/robot_control.dm index 0eaea103f2..bbfb7604ba 100644 --- a/code/modules/mob/living/silicon/ai/robot_control.dm +++ b/code/modules/mob/living/silicon/ai/robot_control.dm @@ -19,11 +19,13 @@ return ..() return UI_CLOSE -/datum/robot_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/robot_control/ui_state(mob/user) + return GLOB.always_state + +/datum/robot_control/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "RemoteRobotControl", "Remote Robot Control", 500, 500, master_ui, state) + ui = new(user, src, "RemoteRobotControl") ui.open() /datum/robot_control/ui_data(mob/user) diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index a063d090db..9b964ef188 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -79,15 +79,15 @@ if(activated(O)) to_chat(src, "That module is already activated.") return - if(!held_items[1]) + if(!held_items[1] && health >= -maxHealth*0.5) held_items[1] = O O.screen_loc = inv1.screen_loc . = TRUE - else if(!held_items[2]) + else if(!held_items[2] && health >= 0) held_items[2] = O O.screen_loc = inv2.screen_loc . = TRUE - else if(!held_items[3]) + else if(!held_items[3] && health >= maxHealth*0.5) held_items[3] = O O.screen_loc = inv3.screen_loc . = TRUE diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 4fcd6d1dbd..9018c49b2c 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -16,6 +16,8 @@ wires = new /datum/wires/robot(src) AddElement(/datum/element/empprotection, EMP_PROTECT_WIRES) + RegisterSignal(src, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, .proc/charge) + robot_modules_background = new() robot_modules_background.icon_state = "block" robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer ABOVE_HUD_LAYER, UI should be just below it. @@ -1097,6 +1099,15 @@ for(var/i in connected_ai.aicamera.stored) aicamera.stored[i] = TRUE +/mob/living/silicon/robot/proc/charge(datum/source, amount, repairs) + if(module) + var/coeff = amount * 0.005 + module.respawn_consumable(src, coeff) + if(repairs) + heal_bodypart_damage(repairs, repairs - 1) + if(cell) + cell.charge = min(cell.charge + amount, cell.maxcharge) + /mob/living/silicon/robot/proc/rest_style() set name = "Switch Rest Style" set category = "Robot Commands" diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 5e96818766..d9bf8cc553 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -29,9 +29,6 @@ model = "MULE" bot_core_type = /obj/machinery/bot_core/mulebot - var/ui_x = 350 - var/ui_y = 425 - var/id path_image_color = "#7F5200" @@ -170,11 +167,10 @@ return ui_interact(user) -/mob/living/simple_animal/bot/mulebot/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/mob/living/simple_animal/bot/mulebot/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Mule", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "Mule", name) ui.open() /mob/living/simple_animal/bot/mulebot/ui_data(mob/user) @@ -191,8 +187,7 @@ data["modeStatus"] = "average" if(BOT_NO_ROUTE) data["modeStatus"] = "bad" - else - data["load"] = load ? load.name : null + data["load"] = load ? load.name : null //IF YOU CHANGE THE NAME OF THIS, UPDATE MULEBOT/PARANORMAL/UI_DATA. data["destination"] = destination ? destination : null data["home"] = home_destination data["destinations"] = GLOB.deliverybeacontags @@ -206,18 +201,20 @@ return data /mob/living/simple_animal/bot/mulebot/ui_act(action, params) - var/silicon_access = hasSiliconAccessInArea(usr) - if(..() || (locked && silicon_access)) + if(..() || (locked && hasSiliconAccessInArea(usr))) return switch(action) if("lock") - if(silicon_access) + if(hasSiliconAccessInArea(usr)) locked = !locked . = TRUE if("power") if(on) turn_off() - else if(cell && !open) + else if(open) + to_chat(usr, "[name]'s maintenance panel is open!") + return + else if(cell) if(!turn_on()) to_chat(usr, "You can't switch on [src]!") return diff --git a/code/modules/mob/living/simple_animal/friendly/possum.dm b/code/modules/mob/living/simple_animal/friendly/possum.dm new file mode 100644 index 0000000000..71fdbd1465 --- /dev/null +++ b/code/modules/mob/living/simple_animal/friendly/possum.dm @@ -0,0 +1,37 @@ +/mob/living/simple_animal/opossum + name = "opossum" + desc = "It's an opossum, a small scavenging marsupial." + icon_state = "possum" + icon_living = "possum" + icon_dead = "possum_dead" + speak = list("Hiss!","HISS!","Hissss?") + speak_emote = list("hisses") + emote_hear = list("hisses.") + emote_see = list("runs in a circle.", "shakes.") + speak_chance = 1 + turns_per_move = 3 + blood_volume = 250 + see_in_dark = 5 + maxHealth = 15 + health = 15 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 1) + response_help_continuous = "pets" + response_help_simple = "pet" + response_disarm_continuous = "gently pushes aside" + response_disarm_simple = "gently push aside" + response_harm_continuous = "stamps on" + response_harm_simple = "stamp" + density = FALSE + ventcrawler = VENTCRAWLER_ALWAYS + pass_flags = PASSTABLE | PASSMOB + mob_size = MOB_SIZE_TINY + mob_biotypes = MOB_ORGANIC|MOB_BEAST + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/opossum/poppy + name = "Poppy the Safety Possum" + desc = "Safety first!" + icon_state = "poppypossum" + icon_living = "poppypossum" + icon_dead = "poppypossum_dead" + butcher_results = list(/obj/item/clothing/head/hardhat = 1) diff --git a/code/modules/mob/living/simple_animal/hostile/regalrat.dm b/code/modules/mob/living/simple_animal/hostile/regalrat.dm index e21514b37e..77b2d4268a 100644 --- a/code/modules/mob/living/simple_animal/hostile/regalrat.dm +++ b/code/modules/mob/living/simple_animal/hostile/regalrat.dm @@ -184,6 +184,7 @@ /mob/living/simple_animal/hostile/rat/Initialize() . = ..() SSmobs.cheeserats += src + AddComponent(/datum/component/swarming) /mob/living/simple_animal/hostile/rat/Destroy() SSmobs.cheeserats -= src diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 3ceafe4a11..7d97270808 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -43,6 +43,11 @@ if(GLOB.say_disabled) //This is here to try to identify lag problems to_chat(usr, "Speech is currently admin-disabled.") return + + if(length(message) > MAX_MESSAGE_LEN) + to_chat(usr, message) + to_chat(usr, "^^^----- The preceeding message has been DISCARDED for being over the maximum length of [MAX_MESSAGE_LEN]. It has NOT been sent! -----^^^") + return message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN)) clear_typing_indicator() // clear it immediately! diff --git a/code/modules/mob/say_vr.dm b/code/modules/mob/say_vr.dm index 5ed24af726..5db3ccf216 100644 --- a/code/modules/mob/say_vr.dm +++ b/code/modules/mob/say_vr.dm @@ -37,7 +37,7 @@ proc/get_top_level_mob(var/mob/S) to_chat(user, "You cannot send IC messages (muted).") return FALSE else if(!params) - var/subtle_emote = stripped_multiline_input(user, "Choose an emote to display.", "Subtle", null, MAX_MESSAGE_LEN) + var/subtle_emote = stripped_multiline_input_or_reflect(user, "Choose an emote to display.", "Subtle", null, MAX_MESSAGE_LEN) if(subtle_emote && !check_invalid(user, subtle_emote)) var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable") switch(type) @@ -98,7 +98,7 @@ proc/get_top_level_mob(var/mob/S) to_chat(user, "You cannot send IC messages (muted).") return FALSE else if(!params) - var/subtle_emote = stripped_multiline_input(user, "Choose an emote to display.", "Subtler" , null, MAX_MESSAGE_LEN) + var/subtle_emote = stripped_multiline_input_or_reflect(user, "Choose an emote to display.", "Subtler" , null, MAX_MESSAGE_LEN) if(subtle_emote && !check_invalid(user, subtle_emote)) var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable") switch(type) diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm index eeb5212aeb..b5f3bae53d 100644 --- a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm +++ b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm @@ -73,4 +73,4 @@ add_status_message("[client.username] has changed channel title from [title] to [newtitle]") title = newtitle -#undef MAX_CHANNELS \ No newline at end of file +#undef MAX_CHANNELS diff --git a/code/modules/modular_computers/computers/_modular_computer_shared.dm b/code/modules/modular_computers/computers/_modular_computer_shared.dm new file mode 100644 index 0000000000..8ca93e8347 --- /dev/null +++ b/code/modules/modular_computers/computers/_modular_computer_shared.dm @@ -0,0 +1,64 @@ + +/obj/proc/is_modular_computer() + return + +/obj/proc/get_modular_computer_part(part_type) + return null + +/obj/item/modular_computer/is_modular_computer() + return TRUE + +/obj/item/modular_computer/get_modular_computer_part(part_type) + if(!part_type) + stack_trace("get_modular_computer_part() called without a valid part_type") + return null + return all_components[part_type] + + +/obj/machinery/modular_computer/is_modular_computer() + return TRUE + +/obj/machinery/modular_computer/get_modular_computer_part(part_type) + if(!part_type) + stack_trace("get_modular_computer_part() called without a valid part_type") + return null + return cpu?.all_components[part_type] + + +/obj/proc/get_modular_computer_parts_examine(mob/user) + . = list() + if(!is_modular_computer()) + return + + var/user_is_adjacent = Adjacent(user) //don't reveal full details unless they're close enough to see it on the screen anyway. + + var/obj/item/computer_hardware/ai_slot/ai_slot = get_modular_computer_part(MC_AI) + if(ai_slot) + if(ai_slot.stored_card) + if(user_is_adjacent) + . += "It has a slot installed for an intelliCard which contains: [ai_slot.stored_card.name]" + else + . += "It has a slot installed for an intelliCard, which appears to be occupied." + . += "Alt-click to eject the intelliCard." + else + . += "It has a slot installed for an intelliCard." + + var/obj/item/computer_hardware/card_slot/card_slot = get_modular_computer_part(MC_CARD) + if(card_slot) + if(card_slot.stored_card || card_slot.stored_card2) + var/obj/item/card/id/first_ID = card_slot.stored_card + var/obj/item/card/id/second_ID = card_slot.stored_card2 + var/multiple_cards = istype(first_ID) && istype(second_ID) + if(user_is_adjacent) + . += "It has two slots for identification cards installed[multiple_cards ? " which contain [first_ID] and [second_ID]" : ", one of which contains [first_ID ? first_ID : second_ID]"]." + else + . += "It has two slots for identification cards installed, [multiple_cards ? "both of which appear" : "and one of them appears"] to be occupied." + . += "Alt-click [src] to eject the identification card[multiple_cards ? "s":""]." + else + . += "It has two slots installed for identification cards." + + var/obj/item/computer_hardware/printer/printer_slot = get_modular_computer_part(MC_PRINT) + if(printer_slot) + . += "It has a printer installed." + if(user_is_adjacent) + . += "The printer's paper levels are at: [printer_slot.stored_paper]/[printer_slot.max_paper].
]" diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index d722197bd9..67c04de13f 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -7,6 +7,7 @@ var/enabled = 0 // Whether the computer is turned on. var/screen_on = 1 // Whether the computer is active/opened/it's screen is on. + var/device_theme = "ntos" // Sets the theme for the main menu, hardware config, and file browser apps. Overridden by certain non-NT devices. var/datum/computer_file/program/active_program = null // A currently active program running on the computer. var/hardware_flag = 0 // A flag that describes this device type var/last_power_usage = 0 @@ -98,7 +99,7 @@ if(issilicon(usr)) return var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - if(usr.canUseTopic(src)) + if(usr.canUseTopic(src, BE_CLOSE)) card_slot.try_eject(null, usr) // Eject ID card from computer, if it has ID slot with card inside. @@ -109,7 +110,7 @@ if(issilicon(usr)) return var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI] - if(usr.canUseTopic(src)) + if(usr.canUseTopic(src, BE_CLOSE)) ai_slot.try_eject(null, usr,1) @@ -121,17 +122,17 @@ if(issilicon(usr)) return - if(usr.canUseTopic(src)) + if(usr.canUseTopic(src, BE_CLOSE)) var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD] if(uninstall_component(portable_drive, usr)) portable_drive.verb_pickup() /obj/item/modular_computer/AltClick(mob/user) - . = ..() + ..() if(issilicon(user)) return - if(user.canUseTopic(src)) + if(user.canUseTopic(src, BE_CLOSE)) var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI] var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD] @@ -143,7 +144,7 @@ return if(ai_slot) ai_slot.try_eject(null, user) - return TRUE + // Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs. /obj/item/modular_computer/GetAccess() @@ -175,7 +176,7 @@ /obj/item/modular_computer/MouseDrop(obj/over_object, src_location, over_location) var/mob/M = usr - if((!istype(over_object, /obj/screen)) && usr.canUseTopic(src)) + if((!istype(over_object, /obj/screen)) && usr.canUseTopic(src, BE_CLOSE)) return attack_self(M) return ..() @@ -195,12 +196,22 @@ /obj/item/modular_computer/emag_act(mob/user) . = ..() - if(obj_flags & EMAGGED) - to_chat(user, "\The [src] was already emagged.") - return - obj_flags |= EMAGGED - to_chat(user, "You emag \the [src]. It's screen briefly shows a \"OVERRIDE ACCEPTED: New software downloads available.\" message.") - return TRUE + if(!enabled) + to_chat(user, "You'd need to turn the [src] on first.") + return FALSE + obj_flags |= EMAGGED //Mostly for consistancy purposes; the programs will do their own emag handling + var/newemag = FALSE + var/obj/item/computer_hardware/hard_drive/drive = all_components[MC_HDD] + for(var/datum/computer_file/program/app in drive.stored_files) + if(!istype(app)) + continue + if(app.run_emag()) + newemag = TRUE + if(newemag) + to_chat(user, "You swipe \the [src]. A console window momentarily fills the screen, with white text rapidly scrolling past.") + return TRUE + to_chat(user, "You swipe \the [src]. A console window fills the screen, but it quickly closes itself after only a few lines are written to it.") + return FALSE /obj/item/modular_computer/examine(mob/user) . = ..() @@ -209,13 +220,14 @@ else if(obj_integrity < max_integrity) . += "It is damaged." + . += get_modular_computer_parts_examine(user) + /obj/item/modular_computer/update_icon_state() if(!enabled) icon_state = icon_state_unpowered else icon_state = icon_state_powered - /obj/item/modular_computer/update_overlays() . = ..() if(!display_overlays) @@ -306,6 +318,8 @@ /obj/item/modular_computer/proc/get_header_data() var/list/data = list() + data["PC_device_theme"] = device_theme + var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL] var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE] @@ -407,17 +421,17 @@ if(install_component(W, user)) return - if(istype(W, /obj/item/wrench)) + if(W.tool_behaviour == TOOL_WRENCH) if(all_components.len) to_chat(user, "Remove all components from \the [src] before disassembling it.") return new /obj/item/stack/sheet/metal( get_turf(src.loc), steel_sheet_cost ) - physical.visible_message("\The [src] has been disassembled by [user].") + physical.visible_message("\The [src] is disassembled by [user].") relay_qdel() qdel(src) return - if(istype(W, /obj/item/weldingtool)) + if(W.tool_behaviour == TOOL_WELDER) if(obj_integrity == max_integrity) to_chat(user, "\The [src] does not require repairs.") return @@ -431,7 +445,7 @@ to_chat(user, "You repair \the [src].") return - if(istype(W, /obj/item/screwdriver)) + if(W.tool_behaviour == TOOL_SCREWDRIVER) if(!all_components.len) to_chat(user, "This device doesn't have any components installed.") return @@ -440,7 +454,7 @@ var/obj/item/computer_hardware/H = all_components[h] component_names.Add(H.name) - var/choice = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in component_names + var/choice = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in sortList(component_names) if(!choice) return diff --git a/code/modules/modular_computers/computers/item/computer_damage.dm b/code/modules/modular_computers/computers/item/computer_damage.dm index 6664b449bd..b510f8aded 100644 --- a/code/modules/modular_computers/computers/item/computer_damage.dm +++ b/code/modules/modular_computers/computers/item/computer_damage.dm @@ -18,13 +18,13 @@ /obj/item/modular_computer/proc/break_apart() if(!(flags_1 & NODECONSTRUCT_1)) - physical.visible_message("\The [src] breaks apart!") + physical.visible_message("\The [src] breaks apart!") var/turf/newloc = get_turf(src) new /obj/item/stack/sheet/metal(newloc, round(steel_sheet_cost/2)) for(var/C in all_components) var/obj/item/computer_hardware/H = all_components[C] if(QDELETED(H)) - return + continue uninstall_component(H) H.forceMove(newloc) if(prob(25)) diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm index d3c65f86ec..b5188f43d9 100644 --- a/code/modules/modular_computers/computers/item/computer_power.dm +++ b/code/modules/modular_computers/computers/item/computer_power.dm @@ -28,8 +28,7 @@ /obj/item/modular_computer/get_cell() var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL] - if(battery_module && battery_module.battery) - return battery_module.battery + return battery_module?.get_cell() // Used in following function to reduce copypaste /obj/item/modular_computer/proc/power_failure() diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index 6266daad2b..fd017e2b0f 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -3,7 +3,7 @@ ui_interact(user) // Operates TGUI -/obj/item/modular_computer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) +/obj/item/modular_computer/ui_interact(mob/user, datum/tgui/ui) if(!enabled) if(ui) ui.close() @@ -14,7 +14,7 @@ return 0 // Robots don't really need to see the screen, their wireless connection works as long as computer is on. - if(!screen_on && !hasSiliconAccessInArea(user)) + if(!screen_on && !issilicon(user)) if(ui) ui.close() return 0 @@ -33,19 +33,17 @@ to_chat(user, "\The [src] beeps three times, it's screen displaying a \"DISK ERROR\" warning.") return // No HDD, No HDD files list or no stored files. Something is very broken. - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui) if (!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - assets = get_asset_datum(/datum/asset/simple/arcade) - assets.send(user) - ui = new(user, src, ui_key, "NtosMain", "NtOS Main menu", 400, 500, master_ui, state) + ui = new(user, src, "NtosMain") + ui.set_autoupdate(TRUE) ui.open() - ui.set_autoupdate(state = 1) + ui.send_asset(get_asset_datum(/datum/asset/simple/headers)) /obj/item/modular_computer/ui_data(mob/user) var/list/data = get_header_data() + data["device_theme"] = device_theme data["programs"] = list() var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] for(var/datum/computer_file/program/P in hard_drive.stored_files) @@ -143,6 +141,7 @@ set_light(comp_light_luminosity, 1, comp_light_color) else set_light(0) + return TRUE if("PC_light_color") var/mob/user = usr diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm index 4781cbd8bc..5927d57a0b 100644 --- a/code/modules/modular_computers/computers/item/laptop.dm +++ b/code/modules/modular_computers/computers/item/laptop.dm @@ -7,6 +7,7 @@ icon_state_powered = "laptop" icon_state_unpowered = "laptop-off" icon_state_menu = "menu" + display_overlays = FALSE hardware_flag = PROGRAM_LAPTOP max_hardware_size = 2 @@ -18,8 +19,8 @@ screen_on = 0 // Starts closed var/start_open = TRUE // unless this var is set to 1 var/icon_state_closed = "laptop-closed" - display_overlays = FALSE var/w_class_open = WEIGHT_CLASS_BULKY + var/slowdown_open = TRUE /obj/item/modular_computer/laptop/examine(mob/user) . = ..() @@ -38,6 +39,13 @@ else . = ..() +/obj/item/modular_computer/laptop/update_overlays() + if(screen_on) + return ..() + else + cut_overlays() + icon_state = icon_state_closed + /obj/item/modular_computer/laptop/attack_self(mob/user) if(!screen_on) try_toggle_open(user) @@ -65,6 +73,7 @@ M.put_in_hand(src, H.held_index) /obj/item/modular_computer/laptop/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) + . = ..() if(screen_on && isturf(loc)) return attack_self(user) @@ -73,7 +82,7 @@ return if(!isturf(loc) && !ismob(loc)) // No opening it in backpack. return - if(!user.canUseTopic(src)) + if(!user.canUseTopic(src, BE_CLOSE)) return toggle_open(user) @@ -82,15 +91,17 @@ /obj/item/modular_computer/laptop/AltClick(mob/user) if(screen_on) // Close it. try_toggle_open(user) - return TRUE - return ..() + else + return ..() /obj/item/modular_computer/laptop/proc/toggle_open(mob/living/user=null) if(screen_on) to_chat(user, "You close \the [src].") + slowdown = initial(slowdown) w_class = initial(w_class) else to_chat(user, "You open \the [src].") + slowdown = slowdown_open w_class = w_class_open screen_on = !screen_on diff --git a/code/modules/modular_computers/computers/item/laptop_presets.dm b/code/modules/modular_computers/computers/item/laptop_presets.dm index e50392e3b3..6bc2919bea 100644 --- a/code/modules/modular_computers/computers/item/laptop_presets.dm +++ b/code/modules/modular_computers/computers/item/laptop_presets.dm @@ -20,4 +20,3 @@ /obj/item/modular_computer/laptop/preset/civilian/install_programs() var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] hard_drive.store_file(new/datum/computer_file/program/chatclient()) - hard_drive.store_file(new/datum/computer_file/program/nttransfer()) diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm index 81e2cb95e6..c79d7a9361 100644 --- a/code/modules/modular_computers/computers/item/processor.dm +++ b/code/modules/modular_computers/computers/item/processor.dm @@ -1,4 +1,5 @@ // Held by /obj/machinery/modular_computer to reduce amount of copy-pasted code. +//TODO: REFACTOR THIS SPAGHETTI CODE, MAKE IT A COMPUTER_HARDWARE COMPONENT OR REMOVE IT /obj/item/modular_computer/processor name = "processing unit" desc = "You shouldn't see this. If you do, report it." @@ -11,19 +12,22 @@ var/obj/machinery/modular_computer/machinery_computer = null /obj/item/modular_computer/processor/Destroy() - . = ..() if(machinery_computer && (machinery_computer.cpu == src)) machinery_computer.cpu = null + machinery_computer.UnregisterSignal(src, COMSIG_ATOM_UPDATED_ICON) machinery_computer = null - -/obj/item/modular_computer/processor/Initialize(mapload) . = ..() - if(!loc || !istype(loc, /obj/machinery/modular_computer)) + +/obj/item/modular_computer/processor/New(comp) //intentional new probably + ..() + STOP_PROCESSING(SSobj, src) // Processed by its machine + + if(!comp || !istype(comp, /obj/machinery/modular_computer)) CRASH("Inapropriate type passed to obj/item/modular_computer/processor/New()! Aborting.") // Obtain reference to machinery computer all_components = list() idle_threads = list() - machinery_computer = loc + machinery_computer = comp machinery_computer.cpu = src hardware_flag = machinery_computer.hardware_flag max_hardware_size = machinery_computer.max_hardware_size @@ -39,7 +43,7 @@ qdel(machinery_computer) // This thing is not meant to be used on it's own, get topic data from our machinery owner. -//obj/item/modular_computer/processor/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE) +//obj/item/modular_computer/processor/canUseTopic(atom/movable/M, be_close=FALSE, no_dexterity=FALSE, no_tk=FALSE) // if(!machinery_computer) // return 0 @@ -69,3 +73,6 @@ machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_disk if(MC_AI) machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_card + +/obj/item/modular_computer/processor/attack_ghost(mob/user) + ui_interact(user) diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm index a371e97ec6..41a256467f 100644 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -5,6 +5,7 @@ icon_state_unpowered = "tablet" icon_state_powered = "tablet" icon_state_menu = "menu" + //worn_icon_state = "tablet" hardware_flag = PROGRAM_TABLET max_hardware_size = 1 w_class = WEIGHT_CLASS_SMALL @@ -32,3 +33,17 @@ slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT comp_light_luminosity = 6.3 has_variants = FALSE + +/// Given to Nuke Ops members. +/obj/item/modular_computer/tablet/nukeops + icon_state = "tablet-syndicate" + comp_light_luminosity = 6.3 + has_variants = FALSE + device_theme = "syndicate" + +/obj/item/modular_computer/tablet/nukeops/emag_act(mob/user) + if(!enabled) + to_chat(user, "You'd need to turn the [src] on first.") + return FALSE + to_chat(user, "You swipe \the [src]. It's screen briefly shows a message reading \"MEMORY CODE INJECTION DETECTED AND SUCCESSFULLY QUARANTINED\".") + return FALSE diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm index f516d3802f..7cca8ea5b4 100644 --- a/code/modules/modular_computers/computers/item/tablet_presets.dm +++ b/code/modules/modular_computers/computers/item/tablet_presets.dm @@ -22,23 +22,38 @@ /obj/item/modular_computer/tablet/preset/cargo/Initialize() . = ..() + var/obj/item/computer_hardware/hard_drive/small/hard_drive = new install_component(new /obj/item/computer_hardware/processor_unit/small) install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer)) - install_component(new /obj/item/computer_hardware/hard_drive/small) + install_component(hard_drive) install_component(new /obj/item/computer_hardware/network_card) install_component(new /obj/item/computer_hardware/printer/mini) + hard_drive.store_file(new /datum/computer_file/program/bounty) + //hard_drive.store_file(new /datum/computer_file/program/shipping) -/obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink/Initialize() // Given by the syndicate as part of the contract uplink bundle - loads in the Contractor Uplink. +/// Given by the syndicate as part of the contract uplink bundle - loads in the Contractor Uplink. +/obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink/Initialize() . = ..() var/obj/item/computer_hardware/hard_drive/small/syndicate/hard_drive = new var/datum/computer_file/program/contract_uplink/uplink = new + active_program = uplink uplink.program_state = PROGRAM_STATE_ACTIVE uplink.computer = src + hard_drive.store_file(uplink) + install_component(new /obj/item/computer_hardware/processor_unit/small) install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer)) install_component(hard_drive) install_component(new /obj/item/computer_hardware/network_card) install_component(new /obj/item/computer_hardware/card_slot) - install_component(new /obj/item/computer_hardware/printer/mini) \ No newline at end of file + install_component(new /obj/item/computer_hardware/printer/mini) + +/// Given to Nuke Ops members. +/obj/item/modular_computer/tablet/nukeops/Initialize() + . = ..() + install_component(new /obj/item/computer_hardware/processor_unit/small) + install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer)) + install_component(new /obj/item/computer_hardware/hard_drive/small/nukeops) + install_component(new /obj/item/computer_hardware/network_card) diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm index 066f1fb98b..9d29b23e76 100644 --- a/code/modules/modular_computers/computers/machinery/console_presets.dm +++ b/code/modules/modular_computers/computers/machinery/console_presets.dm @@ -46,16 +46,12 @@ desc = "A stationary computer. This one comes preloaded with research programs." _has_ai = TRUE -/obj/machinery/modular_computer/console/preset/research/examine(mob/user) - . = ..() - . += "Alt-click to eject the intelliCard." - /obj/machinery/modular_computer/console/preset/research/install_programs() var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] hard_drive.store_file(new/datum/computer_file/program/ntnetmonitor()) - hard_drive.store_file(new/datum/computer_file/program/nttransfer()) hard_drive.store_file(new/datum/computer_file/program/chatclient()) hard_drive.store_file(new/datum/computer_file/program/aidiag()) + hard_drive.store_file(new/datum/computer_file/program/robocontrol()) // ===== COMMAND CONSOLE ===== @@ -66,15 +62,27 @@ _has_id_slot = TRUE _has_printer = TRUE -/obj/machinery/modular_computer/console/preset/command/examine(mob/user) - . = ..() - . += "Alt-click [src] to eject the identification card." - /obj/machinery/modular_computer/console/preset/command/install_programs() var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] hard_drive.store_file(new/datum/computer_file/program/chatclient()) hard_drive.store_file(new/datum/computer_file/program/card_mod()) + +// ===== IDENTIFICATION CONSOLE ===== +/obj/machinery/modular_computer/console/preset/id + console_department = "Identification" + name = "identification console" + desc = "A stationary computer. This one comes preloaded with identification modification programs." + _has_id_slot = TRUE + _has_printer = TRUE + +/obj/machinery/modular_computer/console/preset/id/install_programs() + var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] + hard_drive.store_file(new/datum/computer_file/program/chatclient()) + hard_drive.store_file(new/datum/computer_file/program/card_mod()) + hard_drive.store_file(new/datum/computer_file/program/job_management()) + hard_drive.store_file(new/datum/computer_file/program/crew_manifest()) + // ===== CIVILIAN CONSOLE ===== /obj/machinery/modular_computer/console/preset/civilian console_department = "Civilian" @@ -84,5 +92,4 @@ /obj/machinery/modular_computer/console/preset/civilian/install_programs() var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] hard_drive.store_file(new/datum/computer_file/program/chatclient()) - hard_drive.store_file(new/datum/computer_file/program/nttransfer()) hard_drive.store_file(new/datum/computer_file/program/arcade()) diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm index 7e384c3ddd..6f016ad147 100644 --- a/code/modules/modular_computers/computers/machinery/modular_computer.dm +++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm @@ -36,6 +36,10 @@ QDEL_NULL(cpu) return ..() +/obj/machinery/modular_computer/examine(mob/user) + . = ..() + . += get_modular_computer_parts_examine(user) + /obj/machinery/modular_computer/attack_ghost(mob/dead/observer/user) . = ..() if(.) @@ -45,31 +49,31 @@ /obj/machinery/modular_computer/emag_act(mob/user) . = ..() - if(cpu) - . |= cpu.emag_act(user) + if(!cpu) + to_chat(user, "You'd need to turn the [src] on first.") + return FALSE + return (cpu.emag_act(user)) -/obj/machinery/modular_computer/update_icon_state() - if(cpu?.enabled) - icon_state = icon_state_powered - else if(stat & NOPOWER || !(cpu?.use_power())) - icon_state = icon_state_unpowered +/obj/machinery/modular_computer/update_icon() + cut_overlays() + icon_state = icon_state_powered -/obj/machinery/modular_computer/update_overlays() - . = ..() if(!cpu || !cpu.enabled) if (!(stat & NOPOWER) && (cpu && cpu.use_power())) - . += screen_icon_screensaver + add_overlay(screen_icon_screensaver) + else + icon_state = icon_state_unpowered set_light(0) else set_light(light_strength) if(cpu.active_program) - . += cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu + add_overlay(cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu) else - . += screen_icon_state_menu + add_overlay(screen_icon_state_menu) if(cpu && cpu.obj_integrity <= cpu.integrity_failure * cpu.max_integrity) - . += "bsod" - . += "broken" + add_overlay("bsod") + add_overlay("broken") // Eject ID card from computer, if it has ID slot with card inside. /obj/machinery/modular_computer/proc/eject_id() @@ -96,10 +100,10 @@ cpu.eject_card() /obj/machinery/modular_computer/AltClick(mob/user) - . = ..() if(cpu) - return cpu.AltClick(user) + cpu.AltClick(user) +//ATTACK HAND IGNORING PARENT RETURN VALUE // On-click handling. Turns on the computer if it's off and opens the GUI. /obj/machinery/modular_computer/interact(mob/user) if(cpu) @@ -130,8 +134,7 @@ stat &= ~NOPOWER update_icon() return - ..() - update_icon() + . = ..() /obj/machinery/modular_computer/attackby(var/obj/item/W as obj, mob/user) if(cpu && !(flags_1 & NODECONSTRUCT_1)) @@ -144,6 +147,13 @@ /obj/machinery/modular_computer/ex_act(severity) if(cpu) cpu.ex_act(severity) + // switch(severity) + // if(EXPLODE_DEVASTATE) + // SSexplosions.highobj += cpu + // if(EXPLODE_HEAVY) + // SSexplosions.medobj += cpu + // if(EXPLODE_LIGHT) + // SSexplosions.lowobj += cpu ..() // EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components diff --git a/code/modules/modular_computers/computers/machinery/modular_console.dm b/code/modules/modular_computers/computers/machinery/modular_console.dm index 3d4ec22e89..5d596f98e4 100644 --- a/code/modules/modular_computers/computers/machinery/modular_console.dm +++ b/code/modules/modular_computers/computers/machinery/modular_console.dm @@ -52,4 +52,4 @@ network_card.identification_string = "Unknown Console" if(cpu) cpu.screen_on = 1 - update_icon() \ No newline at end of file + update_icon() diff --git a/code/modules/modular_computers/documentation.md b/code/modules/modular_computers/documentation.md index 246da7c3d9..88d059da7a 100644 --- a/code/modules/modular_computers/documentation.md +++ b/code/modules/modular_computers/documentation.md @@ -1,5 +1,7 @@ # Modular computer programs +How module computer programs work + Ok. so a quick rundown on how to make a program. This is kind of a shitty documentation, but oh well I was asked to. ## Base setup diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm index 7776fc04d0..4e862c4ae3 100644 --- a/code/modules/modular_computers/file_system/computer_file.dm +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -3,8 +3,8 @@ var/filetype = "XXX" // File full names are [filename].[filetype] so like NewFile.XXX in this case var/size = 1 // File size in GQ. Integers only! var/obj/item/computer_hardware/hard_drive/holder // Holder that contains this file. - var/unsendable = 0 // Whether the file may be sent to someone via NTNet transfer or other means. - var/undeletable = 0 // Whether the file may be deleted. Setting to 1 prevents deletion/renaming/etc. + var/unsendable = FALSE // Whether the file may be sent to someone via NTNet transfer or other means. + var/undeletable = FALSE // Whether the file may be deleted. Setting to TRUE prevents deletion/renaming/etc. var/uid // UID of this file var/static/file_uid = 0 @@ -24,7 +24,7 @@ return ..() // Returns independent copy of this file. -/datum/computer_file/proc/clone(rename = 0) +/datum/computer_file/proc/clone(rename = FALSE) var/datum/computer_file/temp = new type temp.unsendable = unsendable temp.undeletable = undeletable @@ -34,4 +34,4 @@ else temp.filename = filename temp.filetype = filetype - return temp \ No newline at end of file + return temp diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 6b5390fc60..12e5ef6e95 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -31,15 +31,10 @@ var/available_on_ntnet = 1 /// Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable. var/available_on_syndinet = 0 - /// ID of TGUI interface + /// Name of the tgui interface var/tgui_id - /// Default size of TGUI window, in pixels - var/ui_x = 575 - var/ui_y = 700 /// Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /icons/program_icons. Be careful not to use too large images! var/ui_header = null - ///Assets specific to programs - var/list/special_assets = list() /datum/computer_file/program/New(obj/item/modular_computer/comp = null) ..() @@ -69,23 +64,23 @@ /datum/computer_file/program/proc/generate_network_log(text) if(computer) return computer.add_log(text) - return FALSE + return 0 /datum/computer_file/program/proc/is_supported_by_hardware(hardware_flag = 0, loud = 0, mob/user = null) if(!(hardware_flag & usage_flags)) if(loud && computer && user) - to_chat(user, "\The [computer] flashes an \"Hardware Error - Incompatible software\" warning.") - return FALSE - return TRUE + to_chat(user, "\The [computer] flashes a \"Hardware Error - Incompatible software\" warning.") + return 0 + return 1 /datum/computer_file/program/proc/get_signal(specific_action = 0) if(computer) return computer.get_ntnet_status(specific_action) - return FALSE + return 0 // Called by Process() on device that runs us, once every tick. /datum/computer_file/program/proc/process_tick() - return TRUE + return 1 // Check if the user can run program. Only humans can operate computer. Automatically called in run_program() // User has to wear their ID for ID Scan to work. @@ -131,7 +126,7 @@ return TRUE if(loud) to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.") - return FALSE + return 0 // This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones // always include the device here in this proc. This proc basically relays the request to whatever is running the program. @@ -143,11 +138,25 @@ // This is performed on program startup. May be overridden to add extra logic. Remember to include ..() call. Return 1 on success, 0 on failure. // When implementing new program based device, use this to run the program. /datum/computer_file/program/proc/run_program(mob/living/user) - if(can_run(user, TRUE)) + if(can_run(user, 1)) if(requires_ntnet && network_destination) generate_network_log("Connection opened to [network_destination].") program_state = PROGRAM_STATE_ACTIVE - return TRUE + return 1 + return 0 + +/** + * + *Called by the device when it is emagged. + * + *Emagging the device allows certain programs to unlock new functions. However, the program will + *need to be downloaded first, and then handle the unlock on their own in their run_emag() proc. + *The device will allow an emag to be run multiple times, so the user can re-emag to run the + *override again, should they download something new. The run_emag() proc should return TRUE if + *the emagging affected anything, and FALSE if no change was made (already emagged, or has no + *emag functions). +**/ +/datum/computer_file/program/proc/run_emag() return FALSE // Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client. @@ -155,20 +164,14 @@ program_state = PROGRAM_STATE_KILLED if(network_destination) generate_network_log("Connection to [network_destination] closed.") - return TRUE + return 1 - -/datum/computer_file/program/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/computer_file/program/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui && tgui_id) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - for(var/i in special_assets) - assets = get_asset_datum(i) - assets.send(user) - - ui = new(user, src, ui_key, tgui_id, filedesc, ui_x, ui_y, state = state) + ui = new(user, src, tgui_id, filedesc) ui.open() + ui.send_asset(get_asset_datum(/datum/asset/simple/headers)) // CONVENTIONS, READ THIS WHEN CREATING NEW PROGRAM AND OVERRIDING THIS PROC: // Topic calls are automagically forwarded from NanoModule this program contains. @@ -177,17 +180,17 @@ // ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE. /datum/computer_file/program/ui_act(action,list/params,datum/tgui/ui) if(..()) - return TRUE + return 1 if(computer) switch(action) if("PC_exit") computer.kill_program() ui.close() - return TRUE + return 1 if("PC_shutdown") computer.shutdown_computer() ui.close() - return TRUE + return 1 if("PC_minimize") var/mob/user = usr if(!computer.active_program || !computer.all_components[MC_CPU]) diff --git a/code/modules/modular_computers/file_system/program_events.dm b/code/modules/modular_computers/file_system/program_events.dm index 279d646cfd..3c1daa5af3 100644 --- a/code/modules/modular_computers/file_system/program_events.dm +++ b/code/modules/modular_computers/file_system/program_events.dm @@ -13,6 +13,6 @@ /datum/computer_file/program/proc/event_networkfailure(background) kill_program(forced = TRUE) if(background) - computer.visible_message("\The [computer]'s screen displays an \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error") + computer.visible_message("\The [computer]'s screen displays a \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error") else computer.visible_message("\The [computer]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error.") diff --git a/code/modules/modular_computers/file_system/programs/airestorer.dm b/code/modules/modular_computers/file_system/programs/airestorer.dm index 4e9cd85577..364ad79737 100644 --- a/code/modules/modular_computers/file_system/programs/airestorer.dm +++ b/code/modules/modular_computers/file_system/programs/airestorer.dm @@ -9,8 +9,6 @@ transfer_access = ACCESS_HEADS available_on_ntnet = TRUE tgui_id = "NtosAiRestorer" - ui_x = 370 - ui_y = 400 /// Variable dictating if we are in the process of restoring the AI in the inserted intellicard var/restoring = FALSE @@ -74,8 +72,8 @@ restoring = FALSE return ai_slot.locked =TRUE - A.adjustOxyLoss(-5, 0) - A.adjustFireLoss(-5, 0) + A.adjustOxyLoss(-5, 0)//, FALSE) + A.adjustFireLoss(-5, 0)//, FALSE) A.adjustToxLoss(-5, 0) A.adjustBruteLoss(-5, 0) A.updatehealth() @@ -113,7 +111,7 @@ data["restoring"] = restoring data["health"] = (AI.health + 100) / 2 data["isDead"] = AI.stat == DEAD - data["laws"] = AI.laws.get_law_list(include_zeroth = 1) + data["laws"] = AI.laws.get_law_list(include_zeroth = TRUE, render_html = FALSE) return data diff --git a/code/modules/modular_computers/file_system/programs/alarm.dm b/code/modules/modular_computers/file_system/programs/alarm.dm index 34daeff6ca..577fad83d0 100644 --- a/code/modules/modular_computers/file_system/programs/alarm.dm +++ b/code/modules/modular_computers/file_system/programs/alarm.dm @@ -8,9 +8,6 @@ network_destination = "alarm monitoring network" size = 5 tgui_id = "NtosStationAlertConsole" - ui_x = 315 - ui_y = 500 - var/has_alert = 0 var/alarms = list("Fire" = list(), "Atmosphere" = list(), "Power" = list()) diff --git a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm index cf842f086f..3accb8e02d 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/contract_uplink.dm @@ -1,6 +1,6 @@ /datum/computer_file/program/contract_uplink filename = "contractor uplink" - filedesc = "Syndicate Contract Uplink" + filedesc = "Syndicate Contractor Uplink" program_icon_state = "assign" extended_desc = "A standard, Syndicate issued system for handling important contracts while on the field." size = 10 @@ -9,8 +9,6 @@ unsendable = 1 undeletable = 1 tgui_id = "SyndContractor" - ui_x = 500 - ui_y = 600 var/error = "" var/info_screen = TRUE var/assigned = FALSE @@ -22,14 +20,18 @@ /datum/computer_file/program/contract_uplink/ui_act(action, params) if(..()) return TRUE + var/mob/living/user = usr var/obj/item/computer_hardware/hard_drive/small/syndicate/hard_drive = computer.all_components[MC_HDD] + switch(action) if("PRG_contract-accept") var/contract_id = text2num(params["contract_id"]) + // Set as the active contract hard_drive.traitor_data.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ACTIVE hard_drive.traitor_data.contractor_hub.current_contract = hard_drive.traitor_data.contractor_hub.assigned_contracts[contract_id] + program_icon_state = "single_contract" return TRUE if("PRG_login") @@ -38,26 +40,30 @@ // Bake their data right into the hard drive, or we don't allow non-antags gaining access to an unused // contract system. // We also create their contracts at this point. - if(traitor_data) + if (traitor_data) // Only play greet sound, and handle contractor hub when assigning for the first time. - if(!traitor_data.contractor_hub) + if (!traitor_data.contractor_hub) user.playsound_local(user, 'sound/effects/contractstartup.ogg', 100, FALSE) traitor_data.contractor_hub = new traitor_data.contractor_hub.create_hub_items() + // Stops any topic exploits such as logging in multiple times on a single system. - if(!assigned) + if (!assigned) traitor_data.contractor_hub.create_contracts(traitor_data.owner) + hard_drive.traitor_data = traitor_data + program_icon_state = "contracts" assigned = TRUE else error = "UNAUTHORIZED USER" return TRUE if("PRG_call_extraction") - if(hard_drive.traitor_data.contractor_hub.current_contract.status != CONTRACT_STATUS_EXTRACTING) - if(hard_drive.traitor_data.contractor_hub.current_contract.handle_extraction(user)) + if (hard_drive.traitor_data.contractor_hub.current_contract.status != CONTRACT_STATUS_EXTRACTING) + if (hard_drive.traitor_data.contractor_hub.current_contract.handle_extraction(user)) user.playsound_local(user, 'sound/effects/confirmdropoff.ogg', 100, TRUE) hard_drive.traitor_data.contractor_hub.current_contract.status = CONTRACT_STATUS_EXTRACTING + program_icon_state = "extracted" else user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50) @@ -65,15 +71,19 @@ else user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50) error = "Already extracting... Place the target into the pod. If the pod was destroyed, this contract is no longer possible." + return TRUE if("PRG_contract_abort") var/contract_id = hard_drive.traitor_data.contractor_hub.current_contract.id + hard_drive.traitor_data.contractor_hub.current_contract = null hard_drive.traitor_data.contractor_hub.assigned_contracts[contract_id].status = CONTRACT_STATUS_ABORTED + program_icon_state = "contracts" + return TRUE if("PRG_redeem_TC") - if(hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem) + if (hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem) var/obj/item/stack/telecrystal/crystals = new /obj/item/stack/telecrystal(get_turf(user), hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem) if(ishuman(user)) @@ -82,13 +92,14 @@ to_chat(H, "Your payment materializes into your hands!") else to_chat(user, "Your payment materializes onto the floor.") + hard_drive.traitor_data.contractor_hub.contract_TC_payed_out += hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem = 0 return TRUE else user.playsound_local(user, 'sound/machines/uplinkerror.ogg', 50) return TRUE - if("PRG_clear_error") + if ("PRG_clear_error") error = "" return TRUE if("PRG_set_first_load_finished") @@ -97,10 +108,11 @@ if("PRG_toggle_info") info_screen = !info_screen return TRUE - if("buy_hub") - if(hard_drive.traitor_data.owner.current == user) + if ("buy_hub") + if (hard_drive.traitor_data.owner.current == user) var/item = params["item"] - for(var/datum/contractor_item/hub_item in hard_drive.traitor_data.contractor_hub.hub_items) + + for (var/datum/contractor_item/hub_item in hard_drive.traitor_data.contractor_hub.hub_items) if (hub_item.name == item) hub_item.handle_purchase(hard_drive.traitor_data.contractor_hub, user) else @@ -112,13 +124,15 @@ var/screen_to_be = null data["first_load"] = first_load - if(hard_drive && hard_drive.traitor_data != null) + + if (hard_drive && hard_drive.traitor_data != null) var/datum/antagonist/traitor/traitor_data = hard_drive.traitor_data data += get_header_data() - if(traitor_data.contractor_hub.current_contract) + + if (traitor_data.contractor_hub.current_contract) data["ongoing_contract"] = TRUE screen_to_be = "single_contract" - if(traitor_data.contractor_hub.current_contract.status == CONTRACT_STATUS_EXTRACTING) + if (traitor_data.contractor_hub.current_contract.status == CONTRACT_STATUS_EXTRACTING) data["extraction_enroute"] = TRUE screen_to_be = "extracted" else @@ -126,15 +140,19 @@ else data["ongoing_contract"] = FALSE data["extraction_enroute"] = FALSE + data["logged_in"] = TRUE data["station_name"] = GLOB.station_name data["redeemable_tc"] = traitor_data.contractor_hub.contract_TC_to_redeem data["earned_tc"] = traitor_data.contractor_hub.contract_TC_payed_out data["contracts_completed"] = traitor_data.contractor_hub.contracts_completed data["contract_rep"] = traitor_data.contractor_hub.contract_rep + data["info_screen"] = info_screen + data["error"] = error - for(var/datum/contractor_item/hub_item in traitor_data.contractor_hub.hub_items) + + for (var/datum/contractor_item/hub_item in traitor_data.contractor_hub.hub_items) data["contractor_hub_items"] += list(list( "name" = hub_item.name, "desc" = hub_item.desc, @@ -142,7 +160,8 @@ "limited" = hub_item.limited, "item_icon" = hub_item.item_icon )) - for(var/datum/syndicate_contract/contract in traitor_data.contractor_hub.assigned_contracts) + + for (var/datum/syndicate_contract/contract in traitor_data.contractor_hub.assigned_contracts) data["contracts"] += list(list( "target" = contract.contract.target, "target_rank" = contract.target_rank, @@ -155,23 +174,28 @@ )) var/direction - if(traitor_data.contractor_hub.current_contract) + if (traitor_data.contractor_hub.current_contract) var/turf/curr = get_turf(user) var/turf/dropoff_turf data["current_location"] = "[get_area_name(curr, TRUE)]" - for(var/turf/content in traitor_data.contractor_hub.current_contract.contract.dropoff.contents) - if(isturf(content)) + + for (var/turf/content in traitor_data.contractor_hub.current_contract.contract.dropoff.contents) + if (isturf(content)) dropoff_turf = content break + if(curr.z == dropoff_turf.z) //Direction calculations for same z-level only direction = uppertext(dir2text(get_dir(curr, dropoff_turf))) //Direction text (East, etc). Not as precise, but still helpful. if(get_area(user) == traitor_data.contractor_hub.current_contract.contract.dropoff) direction = "LOCATION CONFIRMED" else direction = "???" + data["dropoff_direction"] = direction + else data["logged_in"] = FALSE + program_icon_state = screen_to_be update_computer_icon() return data diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm index 9dedc3810f..803dadc0a0 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -8,8 +8,6 @@ available_on_ntnet = FALSE available_on_syndinet = TRUE tgui_id = "NtosNetDos" - ui_x = 400 - ui_y = 250 var/obj/machinery/ntnet_relay/target = null var/dos_speed = 0 diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm index a312815008..2ba3d69fe6 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -8,9 +8,6 @@ available_on_ntnet = FALSE available_on_syndinet = TRUE tgui_id = "NtosRevelation" - ui_x = 400 - ui_y = 250 - var/armed = 0 /datum/computer_file/program/revelation/run_program(var/mob/living/user) diff --git a/code/modules/modular_computers/file_system/programs/arcade.dm b/code/modules/modular_computers/file_system/programs/arcade.dm index 87debafd6b..2503073f9a 100644 --- a/code/modules/modular_computers/file_system/programs/arcade.dm +++ b/code/modules/modular_computers/file_system/programs/arcade.dm @@ -7,8 +7,6 @@ network_destination = "arcade network" size = 6 tgui_id = "NtosArcade" - ui_x = 450 - ui_y = 350 ///Returns TRUE if the game is being played. var/game_active = TRUE @@ -27,6 +25,7 @@ /datum/computer_file/program/arcade/proc/game_check(mob/user) sleep(5) + //user?.mind?.adjust_experience(/datum/skill/gaming, 1) No gaming(TM) Yet if(boss_hp <= 0) heads_up = "You have crushed [boss_name]! Rejoice!" playsound(computer.loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3, falloff = 10) @@ -35,6 +34,7 @@ if(istype(computer)) computer.update_icon() ticket_count += 1 + //user?.mind?.adjust_experience(/datum/skill/gaming, 50) sleep(10) else if(player_hp <= 0 || player_mp <= 0) heads_up = "You have been defeated... how will the station survive?" @@ -43,6 +43,7 @@ program_icon_state = "arcade_off" if(istype(computer)) computer.update_icon() + //user?.mind?.adjust_experience(/datum/skill/gaming, 10) sleep(10) /datum/computer_file/program/arcade/proc/enemy_check(mob/user) @@ -73,14 +74,13 @@ pause_state = FALSE game_check() -/datum/computer_file/program/arcade/ui_interact(mob/user, ui_key, datum/tgui/ui, force_open, datum/tgui/master_ui, datum/ui_state/state) - . = ..() - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/arcade) - assets.send(user) +/datum/computer_file/program/arcade/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/simple/arcade), + ) /datum/computer_file/program/arcade/ui_data(mob/user) var/list/data = get_header_data() - data["Hitpoints"] = boss_hp data["PlayerHitpoints"] = player_hp data["PlayerMP"] = player_mp @@ -98,11 +98,13 @@ if(computer) printer = computer.all_components[MC_PRINT] + //var/gamerSkillLevel = usr.mind?.get_skill_level(/datum/skill/gaming) + //var/gamerSkill = usr.mind?.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER) switch(action) if("Attack") var/attackamt = 0 //Spam prevention. if(pause_state == FALSE) - attackamt = rand(2,6) + attackamt = rand(2,6)// + rand(0, gamerSkill) pause_state = TRUE heads_up = "You attack for [attackamt] damage." playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3, falloff = 10) @@ -115,8 +117,10 @@ var/healamt = 0 //More Spam Prevention. var/healcost = 0 if(pause_state == FALSE) - healamt = rand(6,8) + healamt = rand(6,8)// + rand(0, gamerSkill) var/maxPointCost = 3 + //if(gamerSkillLevel >= SKILL_LEVEL_JOURNEYMAN) + // maxPointCost = 2 healcost = rand(1, maxPointCost) pause_state = TRUE heads_up = "You heal for [healamt] damage." @@ -130,7 +134,7 @@ if("Recharge_Power") var/rechargeamt = 0 //As above. if(pause_state == FALSE) - rechargeamt = rand(4, 7) + rechargeamt = rand(4,7)// + rand(0, gamerSkill) pause_state = TRUE heads_up = "You regain [rechargeamt] magic power." playsound(computer.loc, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3, falloff = 10) diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm index fe3833facd..2df751bebd 100644 --- a/code/modules/modular_computers/file_system/programs/atmosscan.dm +++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm @@ -6,8 +6,6 @@ network_destination = "atmos scan" size = 4 tgui_id = "NtosAtmos" - ui_x = 300 - ui_y = 350 /datum/computer_file/program/atmosscan/ui_data(mob/user) var/list/data = get_header_data() @@ -15,16 +13,16 @@ var/turf/T = get_turf(ui_host()) if(T) var/datum/gas_mixture/environment = T.return_air() - var/list/env_gases = environment.gases + var/list/env_gases = environment.get_gases() var/pressure = environment.return_pressure() var/total_moles = environment.total_moles() data["AirPressure"] = round(pressure,0.1) - data["AirTemp"] = round(environment.temperature-T0C) + data["AirTemp"] = round(environment.return_temperature()-T0C) if (total_moles) for(var/id in env_gases) - var/gas_level = env_gases[id][MOLES]/total_moles + var/gas_level = environment.get_moles(id)/total_moles if(gas_level > 0) - airlist += list(list("name" = "[env_gases[id][GAS_META][META_GAS_NAME]]", "percentage" = round(gas_level*100, 0.01))) + airlist += list(list("name" = "[GLOB.meta_gas_names[id]]", "percentage" = round(gas_level*100, 0.01))) data["AirData"] = airlist return data diff --git a/code/modules/modular_computers/file_system/programs/borg_monitor.dm b/code/modules/modular_computers/file_system/programs/borg_monitor.dm index c493926c65..c2160a0e92 100644 --- a/code/modules/modular_computers/file_system/programs/borg_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/borg_monitor.dm @@ -9,21 +9,17 @@ network_destination = "cyborg remote monitoring" size = 5 tgui_id = "NtosCyborgRemoteMonitor" - ui_x = 600 - ui_y = 800 /datum/computer_file/program/borg_monitor/ui_data(mob/user) var/list/data = get_header_data() data["card"] = FALSE - if(computer.GetID()) + if(checkID()) data["card"] = TRUE data["cyborgs"] = list() for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs) - if((get_turf(computer)).z != (get_turf(R)).z) - continue - if(R.scrambledcodes) + if(!evaluate_borg(R)) continue var/list/upgrade @@ -36,7 +32,7 @@ var/list/cyborg_data = list( name = R.name, - locked_down = R.locked_down, + locked_down = R.lockcharge, status = R.stat, shell_discon = shell, charge = R.cell ? round(R.cell.percent()) : null, @@ -56,14 +52,53 @@ var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs if(!istype(R)) return - var/obj/item/card/id/ID = computer.GetID() + var/ID = checkID() if(!ID) return var/message = stripped_input(usr, message = "Enter message to be sent to remote cyborg.", title = "Send Message") if(!message) return - to_chat(R, "

Message from [ID.registered_name] -- \"[message]\"
") + to_chat(R, "

Message from [ID] -- \"[message]\"
") SEND_SOUND(R, 'sound/machines/twobeep_high.ogg') if(R.connected_ai) - to_chat(R.connected_ai, "

Message from [ID.registered_name] to [R] -- \"[message]\"
") + to_chat(R.connected_ai, "

Message from [ID] to [R] -- \"[message]\"
") SEND_SOUND(R.connected_ai, 'sound/machines/twobeep_high.ogg') + usr.log_talk(message, LOG_PDA, tag="Cyborg Monitor Program: ID name \"[ID]\" to [R]") + +///This proc is used to determin if a borg should be shown in the list (based on the borg's scrambledcodes var). Syndicate version overrides this to show only syndicate borgs. +/datum/computer_file/program/borg_monitor/proc/evaluate_borg(mob/living/silicon/robot/R) + if((get_turf(computer)).z != (get_turf(R)).z) + return FALSE + if(R.scrambledcodes) + return FALSE + return TRUE + +///Gets the ID's name, if one is inserted into the device. This is a seperate proc solely to be overridden by the syndicate version of the app. +/datum/computer_file/program/borg_monitor/proc/checkID() + var/obj/item/card/id/ID = computer.GetID() + if(!ID) + return FALSE + return ID.registered_name + +/datum/computer_file/program/borg_monitor/syndicate + filename = "scyborgmonitor" + filedesc = "Mission-Specific Cyborg Remote Monitoring" + ui_header = "borg_mon.gif" + program_icon_state = "generic" + extended_desc = "This program allows for remote monitoring of mission-assigned cyborgs." + requires_ntnet = FALSE + available_on_ntnet = FALSE + available_on_syndinet = TRUE + transfer_access = null + network_destination = "cyborg remote monitoring" + tgui_id = "NtosCyborgRemoteMonitorSyndicate" + +/datum/computer_file/program/borg_monitor/syndicate/evaluate_borg(mob/living/silicon/robot/R) + if((get_turf(computer)).z != (get_turf(R)).z) + return FALSE + if(!R.scrambledcodes) + return FALSE + return TRUE + +/datum/computer_file/program/borg_monitor/syndicate/checkID() + return "\[CLASSIFIED\]" //no ID is needed for the syndicate version's message function, and the borg will see "[CLASSIFIED]" as the message sender. diff --git a/code/modules/modular_computers/file_system/programs/bounty_board.dm b/code/modules/modular_computers/file_system/programs/bounty_board.dm new file mode 100644 index 0000000000..46fde84f65 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/bounty_board.dm @@ -0,0 +1,120 @@ +/datum/computer_file/program/bounty_board + filename = "bountyboard" + filedesc = "Bounty Board Request Network" + program_icon_state = "bountyboard" + extended_desc = "A multi-platform network for placing requests across the station, with payment across the network being possible.." + requires_ntnet = TRUE + network_destination = "bounty board interface" + size = 10 + tgui_id = "NtosRequestKiosk" + ///Reference to the currently logged in user. + var/datum/bank_account/current_user + ///The station request datum being affected by UI actions. + var/datum/station_request/active_request + ///Value of the currently bounty input + var/bounty_value = 1 + ///Text of the currently written bounty + var/bounty_text = "" + ///Has the app been added to the network yet? + var/networked = FALSE + +/datum/computer_file/program/bounty_board/ui_data(mob/user) + var/list/data = get_header_data() + var/list/formatted_requests = list() + var/list/formatted_applicants = list() + var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] + if(!networked) + GLOB.allbountyboards += computer + networked = TRUE + if(card_slot && card_slot.stored_card && card_slot.stored_card.registered_account) + current_user = card_slot.stored_card.registered_account + for(var/i in GLOB.request_list) + if(!i) + continue + var/datum/station_request/request = i + formatted_requests += list(list("owner" = request.owner, "value" = request.value, "description" = request.description, "acc_number" = request.req_number)) + if(request.applicants) + for(var/datum/bank_account/j in request.applicants) + formatted_applicants += list(list("name" = j.account_holder, "request_id" = request.owner_account.account_id, "requestee_id" = j.account_id)) + if(current_user) + data["accountName"] = current_user.account_holder + data["requests"] = formatted_requests + data["applicants"] = formatted_applicants + data["bountyValue"] = bounty_value + data["bountyText"] = bounty_text + return data + +/datum/computer_file/program/bounty_board/ui_act(action, list/params) + if(..()) + return + var/current_ref_num = params["request"] + var/current_app_num = params["applicant"] + var/datum/bank_account/request_target + if(current_ref_num) + for(var/datum/station_request/i in GLOB.request_list) + if("[i.req_number]" == "[current_ref_num]") + active_request = i + break + if(active_request) + for(var/datum/bank_account/j in active_request.applicants) + if("[j.account_id]" == "[current_app_num]") + request_target = j + break + switch(action) + if("createBounty") + if(!current_user || !bounty_text) + playsound(src, 'sound/machines/buzz-sigh.ogg', 20, TRUE) + return TRUE + for(var/datum/station_request/i in GLOB.request_list) + if("[i.req_number]" == "[current_user.account_id]") + computer.say("Account already has active bounty.") + return + var/datum/station_request/curr_request = new /datum/station_request(current_user.account_holder, bounty_value,bounty_text,current_user.account_id, current_user) + GLOB.request_list += list(curr_request) + for(var/obj/i in GLOB.allbountyboards) + i.say("New bounty has been added!") + playsound(i.loc, 'sound/effects/cashregister.ogg', 30, TRUE) + return TRUE + if("apply") + if(!current_user) + computer.say("Please swipe a valid ID first.") + return TRUE + if(current_user.account_holder == active_request.owner) + playsound(computer, 'sound/machines/buzz-sigh.ogg', 20, TRUE) + return TRUE + active_request.applicants += list(current_user) + if("payApplicant") + if(!current_user) + return + if(!current_user.has_money(active_request.value) || (current_user.account_holder != active_request.owner)) + playsound(computer, 'sound/machines/buzz-sigh.ogg', 30, TRUE) + return + request_target.transfer_money(current_user, active_request.value) + computer.say("Paid out [active_request.value] credits.") + return TRUE + if("clear") + if(current_user) + current_user = null + computer.say("Account Reset.") + return TRUE + if("deleteRequest") + if(!current_user) + playsound(computer, 'sound/machines/buzz-sigh.ogg', 20, TRUE) + return TRUE + if(active_request.owner != current_user.account_holder) + playsound(computer, 'sound/machines/buzz-sigh.ogg', 20, TRUE) + return TRUE + computer.say("Deleted current request.") + GLOB.request_list.Remove(active_request) + return TRUE + if("bountyVal") + bounty_value = text2num(params["bountyval"]) + if(!bounty_value) + bounty_value = 1 + if("bountyText") + bounty_text = (params["bountytext"]) + . = TRUE + +/datum/computer_file/program/bounty_board/Destroy() + GLOB.allbountyboards -= computer + . = ..() diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm index 07d39438d8..842d6e2588 100644 --- a/code/modules/modular_computers/file_system/programs/card.dm +++ b/code/modules/modular_computers/file_system/programs/card.dm @@ -15,8 +15,6 @@ requires_ntnet = 0 size = 8 tgui_id = "NtosCard" - ui_x = 450 - ui_y = 520 var/is_centcom = FALSE var/minor = FALSE @@ -275,7 +273,7 @@ departments = list("CentCom" = get_all_centcom_jobs()) else if(isnull(departments)) departments = list( - CARDCON_DEPARTMENT_COMMAND = list("Captain"), + CARDCON_DEPARTMENT_COMMAND = list("Captain"),//lol CARDCON_DEPARTMENT_ENGINEERING = GLOB.engineering_positions, CARDCON_DEPARTMENT_MEDICAL = GLOB.medical_positions, CARDCON_DEPARTMENT_SCIENCE = GLOB.science_positions, diff --git a/code/modules/modular_computers/file_system/programs/cargobounty.dm b/code/modules/modular_computers/file_system/programs/cargobounty.dm new file mode 100644 index 0000000000..d9bc65c98d --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/cargobounty.dm @@ -0,0 +1,48 @@ +/datum/computer_file/program/bounty + filename = "bounty" + filedesc = "Nanotrasen Bounty Hunter" + program_icon_state = "bounty" + extended_desc = "A basic interface for supply personnel to check and claim bounties." + requires_ntnet = TRUE + transfer_access = ACCESS_CARGO + network_destination = "cargo claims interface" + size = 10 + tgui_id = "NtosBountyConsole" + ///cooldown var for printing paper sheets. + var/printer_ready = 0 + ///The cargo account for grabbing the cargo account's credits. + var/static/datum/bank_account/cargocash + +/datum/computer_file/program/bounty/proc/print_paper() + new /obj/item/paper/bounty_printout(get_turf(computer)) + +/datum/computer_file/program/bounty/ui_interact(mob/user, datum/tgui/ui) + if(!GLOB.bounties_list.len) + setup_bounties() + printer_ready = world.time + PRINTER_TIMEOUT + cargocash = SSeconomy.get_dep_account(ACCOUNT_CAR) + . = ..() + +/datum/computer_file/program/bounty/ui_data(mob/user) + var/list/data = get_header_data() + var/list/bountyinfo = list() + for(var/datum/bounty/B in GLOB.bounties_list) + bountyinfo += list(list("name" = B.name, "description" = B.description, "reward_string" = B.reward_string(), "completion_string" = B.completion_string() , "claimed" = B.claimed, "can_claim" = B.can_claim(), "priority" = B.high_priority, "bounty_ref" = REF(B))) + data["stored_cash"] = cargocash.account_balance + data["bountydata"] = bountyinfo + return data + +/datum/computer_file/program/bounty/ui_act(action,params) + if(..()) + return + switch(action) + if("ClaimBounty") + var/datum/bounty/cashmoney = locate(params["bounty"]) in GLOB.bounties_list + if(cashmoney) + cashmoney.claim() + return TRUE + if("Print") + if(printer_ready < world.time) + printer_ready = world.time + PRINTER_TIMEOUT + print_paper() + return diff --git a/code/modules/modular_computers/file_system/programs/cargoship.dm b/code/modules/modular_computers/file_system/programs/cargoship.dm index 39543adfa5..3ba08a3719 100644 --- a/code/modules/modular_computers/file_system/programs/cargoship.dm +++ b/code/modules/modular_computers/file_system/programs/cargoship.dm @@ -6,11 +6,9 @@ network_destination = "ship scanner" size = 6 tgui_id = "NtosShipping" - ui_x = 450 - ui_y = 350 ///Account used for creating barcodes. var/datum/bank_account/payments_acc - ///The amount which the tagger will recieve for the sale. + ///The amount which the tagger will receive for the sale. var/percent_cut = 20 /datum/computer_file/program/shipping/ui_data(mob/user) diff --git a/code/modules/modular_computers/file_system/programs/configurator.dm b/code/modules/modular_computers/file_system/programs/configurator.dm index 76da58ea11..fae06544d5 100644 --- a/code/modules/modular_computers/file_system/programs/configurator.dm +++ b/code/modules/modular_computers/file_system/programs/configurator.dm @@ -10,8 +10,6 @@ unsendable = 1 undeletable = 1 size = 4 - ui_x = 420 - ui_y = 630 available_on_ntnet = 0 requires_ntnet = 0 tgui_id = "NtosConfiguration" diff --git a/code/modules/modular_computers/file_system/programs/crewmanifest.dm b/code/modules/modular_computers/file_system/programs/crewmanifest.dm index 662c867a39..a1503ce3a8 100644 --- a/code/modules/modular_computers/file_system/programs/crewmanifest.dm +++ b/code/modules/modular_computers/file_system/programs/crewmanifest.dm @@ -7,12 +7,10 @@ requires_ntnet = FALSE size = 4 tgui_id = "NtosCrewManifest" - ui_x = 400 - ui_y = 480 /datum/computer_file/program/crew_manifest/ui_static_data(mob/user) var/list/data = list() - data["manifest"] = GLOB.data_core.get_manifest() + data["manifest"] = GLOB.data_core.get_manifest_tg() return data /datum/computer_file/program/crew_manifest/ui_data(mob/user) @@ -41,9 +39,9 @@ if(computer && printer) //This option should never be called if there is no printer var/contents = {"

Crew Manifest


- [GLOB.data_core ? GLOB.data_core.get_manifest_html(0) : ""] + [GLOB.data_core ? GLOB.data_core.get_manifest() : ""] "} - if(!printer.print_text(contents,text("crew manifest ([])", station_time_timestamp()))) + if(!printer.print_text(contents,text("crew manifest ([])", STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)))) to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") return else diff --git a/code/modules/modular_computers/file_system/programs/jobmanagement.dm b/code/modules/modular_computers/file_system/programs/jobmanagement.dm index 7b847c123e..bccc6e4dbe 100644 --- a/code/modules/modular_computers/file_system/programs/jobmanagement.dm +++ b/code/modules/modular_computers/file_system/programs/jobmanagement.dm @@ -7,8 +7,6 @@ requires_ntnet = 0 size = 4 tgui_id = "NtosJobManager" - ui_x = 400 - ui_y = 620 var/change_position_cooldown = 30 //Jobs you cannot open new positions for diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm index 352f13f305..6401d6207f 100644 --- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm @@ -11,8 +11,6 @@ available_on_ntnet = 0 ui_header = "downloader_finished.gif" tgui_id = "NtosNetDownloader" - ui_x = 480 - ui_y = 735 var/datum/computer_file/program/downloaded_file = null var/hacked_download = 0 @@ -20,6 +18,21 @@ var/download_netspeed = 0 var/downloaderror = "" var/obj/item/modular_computer/my_computer = null + var/emagged = FALSE + var/list/main_repo + var/list/antag_repo + +/datum/computer_file/program/ntnetdownload/run_program() + . = ..() + main_repo = SSnetworks.station_network.available_station_software + antag_repo = SSnetworks.station_network.available_antag_software + +/datum/computer_file/program/ntnetdownload/run_emag() + if(emagged) + return FALSE + emagged = TRUE + return TRUE + /datum/computer_file/program/ntnetdownload/proc/begin_file_download(filename) if(downloaded_file) @@ -30,8 +43,8 @@ if(!PRG || !istype(PRG)) return 0 - // Attempting to download antag only program, but without having emagged computer. No. - if(PRG.available_on_syndinet && !(computer.obj_flags & EMAGGED)) + // Attempting to download antag only program, but without having emagged/syndicate computer. No. + if(PRG.available_on_syndinet && !emagged) return 0 var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] @@ -41,10 +54,10 @@ ui_header = "downloader_running.gif" - if(PRG in SSnetworks.station_network.available_station_software) + if(PRG in main_repo) generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from NTNet Software Repository.") hacked_download = 0 - else if(PRG in SSnetworks.station_network.available_antag_software) + else if(PRG in antag_repo) generate_network_log("Began downloading file **ENCRYPTED**.[PRG.filetype] from unspecified server.") hacked_download = 1 else @@ -130,7 +143,7 @@ data["disk_size"] = hard_drive.max_capacity data["disk_used"] = hard_drive.used_capacity var/list/all_entries[0] - for(var/A in SSnetworks.station_network.available_station_software) + for(var/A in main_repo) var/datum/computer_file/program/P = A // Only those programs our user can run will show in the list if(!P.can_run(user,transfer = 1) || hard_drive.find_file_by_name(P.filename)) @@ -143,9 +156,9 @@ "size" = P.size, ))) data["hackedavailable"] = FALSE - if(computer.obj_flags & EMAGGED) // If we are running on emagged computer we have access to some "bonus" software + if(emagged) // If we are running on emagged computer we have access to some "bonus" software var/list/hacked_programs[0] - for(var/S in SSnetworks.station_network.available_antag_software) + for(var/S in antag_repo) var/datum/computer_file/program/P = S if(hard_drive.find_file_by_name(P.filename)) continue @@ -172,3 +185,24 @@ /datum/computer_file/program/ntnetdownload/kill_program(forced) abort_file_download() return ..(forced) + +//////////////////////// +//Syndicate Downloader// +//////////////////////// + +/// This app only lists programs normally found in the emagged section of the normal downloader app + +/datum/computer_file/program/ntnetdownload/syndicate + filename = "syndownloader" + filedesc = "Software Download Tool" + program_icon_state = "generic" + extended_desc = "This program allows downloads of software from shared Syndicate repositories" + requires_ntnet = 0 + ui_header = "downloader_finished.gif" + tgui_id = "NtosNetDownloader" + emagged = TRUE + +/datum/computer_file/program/ntnetdownload/syndicate/run_program() + . = ..() + main_repo = SSnetworks.station_network.available_antag_software + antag_repo = null diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm index 4ae5bd326b..df9b02d8ec 100644 --- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm @@ -10,9 +10,6 @@ ui_header = "ntnrc_idle.gif" available_on_ntnet = 1 tgui_id = "NtosNetChat" - ui_x = 900 - ui_y = 675 - var/last_message // Used to generate the toolbar icon var/username var/active_channel diff --git a/code/modules/modular_computers/file_system/programs/nttransfer.dm b/code/modules/modular_computers/file_system/programs/nttransfer.dm deleted file mode 100644 index 698e557941..0000000000 --- a/code/modules/modular_computers/file_system/programs/nttransfer.dm +++ /dev/null @@ -1,183 +0,0 @@ -/datum/computer_file/program/nttransfer - filename = "nttransfer" - filedesc = "P2P Transfer Client" - extended_desc = "This program allows for simple file transfer via direct peer to peer connection." - program_icon_state = "comm_logs" - size = 7 - requires_ntnet = 1 - requires_ntnet_feature = NTNET_PEERTOPEER - network_destination = "other device via P2P tunnel" - available_on_ntnet = 1 - tgui_id = "ntos_net_transfer" - - var/error = "" // Error screen - var/server_password = "" // Optional password to download the file. - var/datum/computer_file/provided_file = null // File which is provided to clients. - var/datum/computer_file/downloaded_file = null // File which is being downloaded - var/list/connected_clients = list() // List of connected clients. - var/datum/computer_file/program/nttransfer/remote // Client var, specifies who are we downloading from. - var/download_completion = 0 // Download progress in GQ - var/download_netspeed = 0 // Our connectivity speed in GQ/s - var/actual_netspeed = 0 // Displayed in the UI, this is the actual transfer speed. - var/unique_token // UID of this program - var/upload_menu = 0 // Whether we show the program list and upload menu - var/static/nttransfer_uid = 0 - -/datum/computer_file/program/nttransfer/New() - unique_token = nttransfer_uid++ - ..() - -/datum/computer_file/program/nttransfer/process_tick() - // Server mode - update_netspeed() - if(provided_file) - for(var/datum/computer_file/program/nttransfer/C in connected_clients) - // Transfer speed is limited by device which uses slower connectivity. - // We can have multiple clients downloading at same time, but let's assume we use some sort of multicast transfer - // so they can all run on same speed. - C.actual_netspeed = min(C.download_netspeed, download_netspeed) - C.download_completion += C.actual_netspeed - if(C.download_completion >= provided_file.size) - C.finish_download() - else if(downloaded_file) // Client mode - if(!remote) - crash_download("Connection to remote server lost") - -/datum/computer_file/program/nttransfer/kill_program(forced = FALSE) - if(downloaded_file) // Client mode, clean up variables for next use - finalize_download() - - if(provided_file) // Server mode, disconnect all clients - for(var/datum/computer_file/program/nttransfer/P in connected_clients) - P.crash_download("Connection terminated by remote server") - downloaded_file = null - ..(forced) - -/datum/computer_file/program/nttransfer/proc/update_netspeed() - download_netspeed = 0 - switch(ntnet_status) - if(1) - download_netspeed = NTNETSPEED_LOWSIGNAL - if(2) - download_netspeed = NTNETSPEED_HIGHSIGNAL - if(3) - download_netspeed = NTNETSPEED_ETHERNET - -// Finishes download and attempts to store the file on HDD -/datum/computer_file/program/nttransfer/proc/finish_download() - var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - if(!computer || !hard_drive || !hard_drive.store_file(downloaded_file)) - error = "I/O Error: Unable to save file. Check your hard drive and try again." - finalize_download() - -// Crashes the download and displays specific error message -/datum/computer_file/program/nttransfer/proc/crash_download(var/message) - error = message ? message : "An unknown error has occurred during download" - finalize_download() - -// Cleans up variables for next use -/datum/computer_file/program/nttransfer/proc/finalize_download() - if(remote) - remote.connected_clients.Remove(src) - downloaded_file = null - remote = null - download_completion = 0 - -/datum/computer_file/program/nttransfer/ui_act(action, params) - if(..()) - return 1 - switch(action) - if("PRG_downloadfile") - for(var/datum/computer_file/program/nttransfer/P in SSnetworks.station_network.fileservers) - if("[P.unique_token]" == params["id"]) - remote = P - break - if(!remote || !remote.provided_file) - return - if(remote.server_password) - var/pass = reject_bad_text(input(usr, "Code 401 Unauthorized. Please enter password:", "Password required")) - if(pass != remote.server_password) - error = "Incorrect Password" - return - downloaded_file = remote.provided_file.clone() - remote.connected_clients.Add(src) - return 1 - if("PRG_reset") - error = "" - upload_menu = 0 - finalize_download() - if(src in SSnetworks.station_network.fileservers) - SSnetworks.station_network.fileservers.Remove(src) - for(var/datum/computer_file/program/nttransfer/T in connected_clients) - T.crash_download("Remote server has forcibly closed the connection") - provided_file = null - return 1 - if("PRG_setpassword") - var/pass = reject_bad_text(input(usr, "Enter new server password. Leave blank to cancel, input 'none' to disable password.", "Server security", "none")) - if(!pass) - return - if(pass == "none") - server_password = "" - return - server_password = pass - return 1 - if("PRG_uploadfile") - var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - for(var/datum/computer_file/F in hard_drive.stored_files) - if("[F.uid]" == params["id"]) - if(F.unsendable) - error = "I/O Error: File locked." - return - if(istype(F, /datum/computer_file/program)) - var/datum/computer_file/program/P = F - if(!P.can_run(usr,transfer = 1)) - error = "Access Error: Insufficient rights to upload file." - provided_file = F - SSnetworks.station_network.fileservers.Add(src) - return - error = "I/O Error: Unable to locate file on hard drive." - return 1 - if("PRG_uploadmenu") - upload_menu = 1 - - -/datum/computer_file/program/nttransfer/ui_data(mob/user) - - var/list/data = get_header_data() - - if(error) - data["error"] = error - else if(downloaded_file) - data["downloading"] = 1 - data["download_size"] = downloaded_file.size - data["download_progress"] = download_completion - data["download_netspeed"] = actual_netspeed - data["download_name"] = "[downloaded_file.filename].[downloaded_file.filetype]" - else if (provided_file) - data["uploading"] = 1 - data["upload_uid"] = unique_token - data["upload_clients"] = connected_clients.len - data["upload_haspassword"] = server_password ? 1 : 0 - data["upload_filename"] = "[provided_file.filename].[provided_file.filetype]" - else if (upload_menu) - var/list/all_files[0] - var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - for(var/datum/computer_file/F in hard_drive.stored_files) - all_files.Add(list(list( - "uid" = F.uid, - "filename" = "[F.filename].[F.filetype]", - "size" = F.size - ))) - data["upload_filelist"] = all_files - else - var/list/all_servers[0] - for(var/datum/computer_file/program/nttransfer/P in SSnetworks.station_network.fileservers) - all_servers.Add(list(list( - "uid" = P.unique_token, - "filename" = "[P.provided_file.filename].[P.provided_file.filetype]", - "size" = P.provided_file.size, - "haspassword" = P.server_password ? 1 : 0 - ))) - data["servers"] = all_servers - - return data \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/powermonitor.dm b/code/modules/modular_computers/file_system/programs/powermonitor.dm index d2d57b1447..bd11474858 100644 --- a/code/modules/modular_computers/file_system/programs/powermonitor.dm +++ b/code/modules/modular_computers/file_system/programs/powermonitor.dm @@ -12,8 +12,6 @@ network_destination = "power monitoring system" size = 9 tgui_id = "NtosPowerMonitor" - ui_x = 550 - ui_y = 700 var/has_alert = 0 var/obj/structure/cable/attached_wire diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm index 7a6e80267c..9b0e09ef99 100644 --- a/code/modules/modular_computers/file_system/programs/radar.dm +++ b/code/modules/modular_computers/file_system/programs/radar.dm @@ -2,31 +2,49 @@ filename = "genericfinder" filedesc = "debug_finder" ui_header = "borg_mon.gif" //DEBUG -- new icon before PR - program_icon_state = "generic" - extended_desc = "generic" + program_icon_state = "radarntos" requires_ntnet = TRUE transfer_access = null available_on_ntnet = FALSE + usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET network_destination = "tracking program" size = 5 tgui_id = "NtosRadar" - ui_x = 800 - ui_y = 600 - special_assets = list( - /datum/asset/simple/radar_assets, - ) ///List of trackable entities. Updated by the scan() proc. var/list/objects ///Ref of the last trackable object selected by the user in the tgui window. Updated in the ui_act() proc. var/atom/selected ///Used to store when the next scan is available. Updated by the scan() proc. var/next_scan = 0 + ///Used to keep track of the last value program_icon_state was set to, to prevent constant unnecessary update_icon() calls + var/last_icon_state = "" + ///Used by the tgui interface, themed NT or Syndicate. + var/arrowstyle = "ntosradarpointer.png" + ///Used by the tgui interface, themed for NT or Syndicate colors. + var/pointercolor = "green" + +/datum/computer_file/program/radar/run_program(mob/living/user) + . = ..() + if(.) + START_PROCESSING(SSfastprocess, src) + return + return FALSE /datum/computer_file/program/radar/kill_program(forced = FALSE) objects = list() selected = null + STOP_PROCESSING(SSfastprocess, src) return ..() +/datum/computer_file/program/radar/Destroy() + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/datum/computer_file/program/radar/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/simple/radar_assets), + ) + /datum/computer_file/program/radar/ui_data(mob/user) var/list/data = get_header_data() data["selected"] = selected @@ -65,7 +83,37 @@ * */ /datum/computer_file/program/radar/proc/track() - return + var/atom/movable/signal = find_atom() + if(!trackable(signal)) + return + + var/turf/here_turf = (get_turf(computer)) + var/turf/target_turf = (get_turf(signal)) + var/userot = FALSE + var/rot = 0 + var/pointer="crosshairs" + var/locx = (target_turf.x - here_turf.x) + 24 + var/locy = (here_turf.y - target_turf.y) + 24 + + if(get_dist_euclidian(here_turf, target_turf) > 24) + userot = TRUE + rot = round(Get_Angle(here_turf, target_turf)) + else + if(target_turf.z > here_turf.z) + pointer="caret-up" + else if(target_turf.z < here_turf.z) + pointer="caret-down" + + var/list/trackinfo = list( + "locx" = locx, + "locy" = locy, + "userot" = userot, + "rot" = rot, + "arrowstyle" = arrowstyle, + "color" = pointercolor, + "pointer" = pointer, + ) + return trackinfo /** * @@ -77,10 +125,12 @@ **arg1 is the atom being evaluated. */ /datum/computer_file/program/radar/proc/trackable(atom/movable/signal) - if(!signal) + if(!signal || !computer) return FALSE var/turf/here = get_turf(computer) var/turf/there = get_turf(signal) + if(!here || !there) + return FALSE //I was still getting a runtime even after the above check while scanning, so fuck it return (there.z == here.z) || (is_station_level(here.z) && is_station_level(there.z)) /** @@ -98,6 +148,59 @@ /datum/computer_file/program/radar/proc/scan() return +/** + * + *Finds the atom in the appropriate list that the `selected` var indicates + * + *The `selected` var holds a REF, which is a string. A mob REF may be + *something like "mob_209". In order to find the actual atom, we need + *to search the appropriate list for the REF string. This is dependant + *on the program (Lifeline uses GLOB.human_list, while Fission360 uses + *GLOB.poi_list), but the result will be the same; evaluate the string and + *return an atom reference. +*/ +/datum/computer_file/program/radar/proc/find_atom() + return + +//We use SSfastprocess for the program icon state because it runs faster than process_tick() does. +/datum/computer_file/program/radar/process() + if(computer.active_program != src) + STOP_PROCESSING(SSfastprocess, src) //We're not the active program, it's time to stop. + return + if(!selected) + return + + var/atom/movable/signal = find_atom() + if(!trackable(signal)) + program_icon_state = "[initial(program_icon_state)]lost" + if(last_icon_state != program_icon_state) + computer.update_icon() + last_icon_state = program_icon_state + return + + var/here_turf = get_turf(computer) + var/target_turf = get_turf(signal) + var/trackdistance = get_dist_euclidian(here_turf, target_turf) + switch(trackdistance) + if(0) + program_icon_state = "[initial(program_icon_state)]direct" + if(1 to 12) + program_icon_state = "[initial(program_icon_state)]close" + if(13 to 24) + program_icon_state = "[initial(program_icon_state)]medium" + if(25 to INFINITY) + program_icon_state = "[initial(program_icon_state)]far" + + if(last_icon_state != program_icon_state) + computer.update_icon() + last_icon_state = program_icon_state + computer.setDir(get_dir(here_turf, target_turf)) + +//We can use process_tick to restart fast processing, since the computer will be running this constantly either way. +/datum/computer_file/program/radar/process_tick() + if(computer.active_program == src) + START_PROCESSING(SSfastprocess, src) + /////////////////// //Suit Sensor App// /////////////////// @@ -106,44 +209,13 @@ /datum/computer_file/program/radar/lifeline filename = "Lifeline" filedesc = "Lifeline" - program_icon_state = "generic" extended_desc = "This program allows for tracking of crew members via their suit sensors." requires_ntnet = TRUE transfer_access = ACCESS_MEDICAL available_on_ntnet = TRUE -/datum/computer_file/program/radar/lifeline/track() - var/mob/living/carbon/human/humanoid = locate(selected) in GLOB.human_list - if(!istype(humanoid) || !trackable(humanoid)) - return - - var/turf/here_turf = (get_turf(computer)) - var/turf/target_turf = (get_turf(humanoid)) - var/userot = FALSE - var/rot = 0 - var/pointer="crosshairs" - var/locx = (target_turf.x - here_turf.x) - var/locy = (here_turf.y - target_turf.y) - if(get_dist_euclidian(here_turf, target_turf) > 24) //If they're too far away, we need the angle for the arrow along the edge of the radar display - userot = TRUE - rot = round(Get_Angle(here_turf, target_turf)) - else - locx = locx + 24 - locy = locy + 24 - if(target_turf.z > here_turf.z) - pointer="caret-up" - else if(target_turf.z < here_turf.z) - pointer="caret-down" - var/list/trackinfo = list( - locx = locx, - locy = locy, - userot = userot, - rot = rot, - arrowstyle = "ntosradarpointer.png", //For the rotation arrow, it's stupid I know - color = "green", - pointer = pointer, - ) - return trackinfo +/datum/computer_file/program/radar/lifeline/find_atom() + return locate(selected) in GLOB.human_list /datum/computer_file/program/radar/lifeline/scan() if(world.time < next_scan) @@ -184,46 +256,18 @@ /datum/computer_file/program/radar/fission360 filename = "Fission360" filedesc = "Fission360" - program_icon_state = "generic" + program_icon_state = "radarsyndicate" extended_desc = "This program allows for tracking of nuclear authorization disks and warheads." requires_ntnet = FALSE transfer_access = null available_on_ntnet = FALSE available_on_syndinet = TRUE tgui_id = "NtosRadarSyndicate" + arrowstyle = "ntosradarpointerS.png" + pointercolor = "red" -/datum/computer_file/program/radar/fission360/track() - var/obj/nuke = locate(selected) in GLOB.poi_list - if(!trackable(nuke)) - return - - var/turf/here_turf = (get_turf(computer)) - var/turf/target_turf = (get_turf(nuke)) - var/userot = FALSE - var/rot = 0 - var/pointer="crosshairs" - var/locx = (target_turf.x - here_turf.x) - var/locy = (here_turf.y - target_turf.y) - if(get_dist_euclidian(here_turf, target_turf) > 24) //If they're too far away, we need the angle for the arrow along the edge of the radar display - userot = TRUE - rot = round(Get_Angle(here_turf, target_turf)) - else - locx = locx + 24 - locy = locy + 24 - if(target_turf.z > here_turf.z) - pointer="caret-up" - else if(target_turf.z < here_turf.z) - pointer="caret-down" - var/list/trackinfo = list( - locx = locx, - locy = locy, - userot = userot, - rot = rot, - arrowstyle = "ntosradarpointerS.png", - color = "red", - pointer = pointer, - ) - return trackinfo +/datum/computer_file/program/radar/fission360/find_atom() + return locate(selected) in GLOB.poi_list /datum/computer_file/program/radar/fission360/scan() if(world.time < next_scan) diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm index 910f923327..8644ce09b4 100644 --- a/code/modules/modular_computers/file_system/programs/robocontrol.dm +++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm @@ -9,8 +9,6 @@ network_destination = "robotics control network" size = 12 tgui_id = "NtosRoboControl" - ui_x = 550 - ui_y = 550 ///Number of simple robots on-station. var/botcount = 0 ///Used to find the location of the user for the purposes of summoning robots. diff --git a/code/modules/modular_computers/file_system/programs/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/sm_monitor.dm index 0a675a7abc..32ad102871 100644 --- a/code/modules/modular_computers/file_system/programs/sm_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/sm_monitor.dm @@ -9,8 +9,6 @@ network_destination = "supermatter monitoring system" size = 5 tgui_id = "NtosSupermatterMonitor" - ui_x = 600 - ui_y = 350 var/last_status = SUPERMATTER_INACTIVE var/list/supermatters var/obj/machinery/power/supermatter_crystal/active // Currently selected supermatter crystal. diff --git a/code/modules/modular_computers/hardware/CPU.dm b/code/modules/modular_computers/hardware/CPU.dm index d08d65ff8b..f13081e1f3 100644 --- a/code/modules/modular_computers/hardware/CPU.dm +++ b/code/modules/modular_computers/hardware/CPU.dm @@ -37,4 +37,4 @@ icon_state = "cpu_super" w_class = WEIGHT_CLASS_TINY power_usage = 75 - max_idle_programs = 2 \ No newline at end of file + max_idle_programs = 2 diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm index 37f3fc434e..b33442f99b 100644 --- a/code/modules/modular_computers/hardware/_hardware.dm +++ b/code/modules/modular_computers/hardware/_hardware.dm @@ -32,28 +32,29 @@ /obj/item/computer_hardware/attackby(obj/item/I, mob/living/user) - // Multitool. Runs diagnostics - if(istype(I, /obj/item/multitool)) - to_chat(user, "***** DIAGNOSTICS REPORT *****") - diagnostics(user) - to_chat(user, "******************************") - return 1 - // Cable coil. Works as repair method, but will probably require multiple applications and more cable. if(istype(I, /obj/item/stack/cable_coil)) + var/obj/item/stack/S = I if(obj_integrity == max_integrity) to_chat(user, "\The [src] doesn't seem to require repairs.") return 1 - if(I.use_tool(src, user, 0, 1)) + if(S.use(1)) to_chat(user, "You patch up \the [src] with a bit of \the [I].") obj_integrity = min(obj_integrity + 10, max_integrity) return 1 if(try_insert(I, user)) - return 1 + return TRUE return ..() +/obj/item/computer_hardware/multitool_act(mob/living/user, obj/item/I) + ..() + to_chat(user, "***** DIAGNOSTICS REPORT *****") + diagnostics(user) + to_chat(user, "******************************") + return TRUE + // Called on multitool click, prints diagnostic information to the user. /obj/item/computer_hardware/proc/diagnostics(var/mob/user) to_chat(user, "Hardware Integrity Test... (Corruption: [damage]/[max_damage]) [damage > damage_failure ? "FAIL" : damage > damage_malfunction ? "WARN" : "PASS"]") diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm index 8428467a87..0ad157afcb 100644 --- a/code/modules/modular_computers/hardware/ai_slot.dm +++ b/code/modules/modular_computers/hardware/ai_slot.dm @@ -9,6 +9,10 @@ var/obj/item/aicard/stored_card = null var/locked = FALSE +/obj/item/computer_hardware/ai_slot/handle_atom_del(atom/A) + if(A == stored_card) + try_eject(0, null, TRUE) + . = ..() /obj/item/computer_hardware/ai_slot/examine(mob/user) . = ..() @@ -41,13 +45,6 @@ /obj/item/computer_hardware/ai_slot/try_eject(slot=0,mob/living/user = null,forced = 0) - if (get_dist(src,user) > 1) - if (iscarbon(user)) - var/mob/living/carbon/H = user - if (!(H.dna && H.dna.check_mutation(TK) && tkMaxRangeCheck(src,H))) - return FALSE - else - return FALSE if(!stored_card) to_chat(user, "There is no card in \the [src].") return FALSE @@ -57,19 +54,21 @@ return FALSE if(stored_card) - stored_card.forceMove(get_turf(src)) + to_chat(user, "You remove [stored_card] from [src].") locked = FALSE - stored_card.verb_pickup() + if(user) + user.put_in_hands(stored_card) + else + stored_card.forceMove(drop_location()) stored_card = null - to_chat(user, "You remove the card from \the [src].") return TRUE return FALSE /obj/item/computer_hardware/ai_slot/attackby(obj/item/I, mob/living/user) if(..()) return - if(istype(I, /obj/item/screwdriver)) + if(I.tool_behaviour == TOOL_SCREWDRIVER) to_chat(user, "You press down on the manual eject button with \the [I].") try_eject(,user,1) - return \ No newline at end of file + return diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm index e03427cc9c..6e3193abfd 100644 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -7,6 +7,9 @@ var/obj/item/stock_parts/cell/battery = null device_type = MC_CELL +/obj/item/computer_hardware/battery/get_cell() + return battery + /obj/item/computer_hardware/battery/New(loc, battery_type = null) if(battery_type) battery = new battery_type(src) @@ -16,6 +19,11 @@ . = ..() QDEL_NULL(battery) +/obj/item/computer_hardware/battery/handle_atom_del(atom/A) + if(A == battery) + try_eject(0, null, TRUE) + . = ..() + /obj/item/computer_hardware/battery/try_insert(obj/item/I, mob/living/user = null) if(!holder) return FALSE @@ -45,7 +53,10 @@ to_chat(user, "There is no power cell connected to \the [src].") return FALSE else - battery.forceMove(get_turf(src)) + if(user) + user.put_in_hands(battery) + else + battery.forceMove(drop_location()) to_chat(user, "You detach \the [battery] from \the [src].") battery = null diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm index e4bc45dbc5..18b423a42e 100644 --- a/code/modules/modular_computers/hardware/card_slot.dm +++ b/code/modules/modular_computers/hardware/card_slot.dm @@ -9,6 +9,13 @@ var/obj/item/card/id/stored_card = null var/obj/item/card/id/stored_card2 = null +/obj/item/computer_hardware/card_slot/handle_atom_del(atom/A) + if(A == stored_card) + try_eject(1, null, TRUE) + if(A == stored_card2) + try_eject(2, null, TRUE) + . = ..() + /obj/item/computer_hardware/card_slot/Destroy() try_eject() return ..() @@ -67,19 +74,15 @@ else stored_card2 = I to_chat(user, "You insert \the [I] into \the [src].") - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.sec_hud_set_ID() return TRUE /obj/item/computer_hardware/card_slot/try_eject(slot=0, mob/living/user = null, forced = 0) - if (get_dist(src,user) > 1) - if (iscarbon(user)) - var/mob/living/carbon/H = user - if (!(H.dna && H.dna.check_mutation(TK) && tkMaxRangeCheck(src,H))) - return FALSE - else - return FALSE if(!stored_card && !stored_card2) to_chat(user, "There are no cards in \the [src].") return FALSE @@ -89,7 +92,7 @@ if(user) user.put_in_hands(stored_card) else - stored_card.forceMove(get_turf(src)) + stored_card.forceMove(drop_location()) stored_card = null ejected++ @@ -97,7 +100,7 @@ if(user) user.put_in_hands(stored_card2) else - stored_card2.forceMove(get_turf(src)) + stored_card2.forceMove(drop_location()) stored_card2 = null ejected++ @@ -109,16 +112,18 @@ for(var/I in holder.idle_threads) var/datum/computer_file/program/P = I P.event_idremoved(1, slot) - + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.sec_hud_set_ID() to_chat(user, "You remove the card[ejected>1 ? "s" : ""] from \the [src].") - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) return TRUE return FALSE /obj/item/computer_hardware/card_slot/attackby(obj/item/I, mob/living/user) if(..()) return - if(istype(I, /obj/item/screwdriver)) + if(I.tool_behaviour == TOOL_SCREWDRIVER) to_chat(user, "You press down on the manual eject button with \the [I].") try_eject(0,user) return diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index e27eaa53ae..b8b9624388 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -157,18 +157,30 @@ max_capacity = 64 icon_state = "ssd_mini" w_class = WEIGHT_CLASS_TINY - custom_price = PRICE_ABOVE_NORMAL + custom_price = 150 -/obj/item/computer_hardware/hard_drive/small/syndicate // Syndicate variant - very slight better +// Syndicate variant - very slight better +/obj/item/computer_hardware/hard_drive/small/syndicate desc = "An efficient SSD for portable devices developed by a rival organisation." power_usage = 8 max_capacity = 70 var/datum/antagonist/traitor/traitor_data // Syndicate hard drive has the user's data baked directly into it on creation +/// For tablets given to nuke ops +/obj/item/computer_hardware/hard_drive/small/nukeops + power_usage = 8 + max_capacity = 70 + +/obj/item/computer_hardware/hard_drive/small/nukeops/install_default_programs() + store_file(new/datum/computer_file/program/computerconfig(src)) + store_file(new/datum/computer_file/program/ntnetdownload/syndicate(src)) // Syndicate version; automatic access to syndicate apps and no NT apps + store_file(new/datum/computer_file/program/filemanager(src)) + store_file(new/datum/computer_file/program/radar/fission360(src)) //I am legitimately afraid if I don't do this, Ops players will think they just don't get a pinpointer anymore. + /obj/item/computer_hardware/hard_drive/micro name = "micro solid state drive" desc = "A highly efficient SSD chip for portable devices." power_usage = 2 max_capacity = 32 icon_state = "ssd_micro" - w_class = WEIGHT_CLASS_TINY \ No newline at end of file + w_class = WEIGHT_CLASS_TINY diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm index 36c666e5d6..ebe40c1922 100644 --- a/code/modules/modular_computers/hardware/printer.dm +++ b/code/modules/modular_computers/hardware/printer.dm @@ -10,14 +10,14 @@ /obj/item/computer_hardware/printer/diagnostics(mob/living/user) ..() - to_chat(user, "Paper level: [stored_paper]/[max_paper].") + to_chat(user, "Paper level: [stored_paper]/[max_paper].") /obj/item/computer_hardware/printer/examine(mob/user) . = ..() . += "Paper level: [stored_paper]/[max_paper]." -/obj/item/computer_hardware/printer/proc/print_text(var/text_to_print, var/paper_title = "") +/obj/item/computer_hardware/printer/proc/print_text(text_to_print, paper_title = "") if(!stored_paper) return FALSE if(!check_functionality()) @@ -27,11 +27,12 @@ // Damaged printer causes the resulting paper to be somewhat harder to read. if(damage > damage_malfunction) - P.setText(stars(text_to_print, 100-malfunction_probability)) + P.info = stars(text_to_print, 100-malfunction_probability) else - P.setText(text_to_print) + P.info = text_to_print if(paper_title) P.name = paper_title + P.update_icon() stored_paper-- P = null return TRUE diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm index ee9def4191..a8d30bad21 100644 --- a/code/modules/modular_computers/laptop_vendor.dm +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -27,9 +27,6 @@ var/dev_printer = 0 // 0: None, 1: Standard var/dev_card = 0 // 0: None, 1: Standard - ui_x = 500 - ui_y = 400 - // Removes all traces of old order and allows you to begin configuration from scratch. /obj/machinery/lapvend/proc/reset_order() state = 0 @@ -224,15 +221,15 @@ return TRUE return FALSE -/obj/machinery/lapvend/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) +/obj/machinery/lapvend/ui_interact(mob/user, datum/tgui/ui) if(stat & (BROKEN | NOPOWER | MAINT)) if(ui) ui.close() return FALSE - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui) if (!ui) - ui = new(user, src, ui_key, "ComputerFabricator", "Personal Computer Vendor", ui_x, ui_y, state = state) + ui = new(user, src, "ComputerFabricator") ui.open() /obj/machinery/lapvend/attackby(obj/item/I, mob/user) @@ -241,7 +238,7 @@ if(!user.temporarilyRemoveItemFromInventory(c)) return credits += c.value - visible_message("[user] inserts [c.value] credits into [src].") + visible_message("[user] inserts [c.value] cr into [src].") qdel(c) return else if(istype(I, /obj/item/holochip)) @@ -257,10 +254,10 @@ var/datum/bank_account/account = ID.registered_account var/target_credits = total_price - credits if(!account.adjust_money(-target_credits)) - say("Insufficient money on card to purchase!") + say("Insufficient credits on card to purchase!") return credits += target_credits - say("[target_credits] cr has been desposited from your account.") + say("[target_credits] cr has been deposited from your account.") return return ..() @@ -308,4 +305,4 @@ state = 3 addtimer(CALLBACK(src, .proc/reset_order), 100) return TRUE - return FALSE \ No newline at end of file + return FALSE diff --git a/code/modules/newscaster/ghostread.dm b/code/modules/newscaster/ghostread.dm index 77cb1a03c8..ff51f5268c 100644 --- a/code/modules/newscaster/ghostread.dm +++ b/code/modules/newscaster/ghostread.dm @@ -3,7 +3,7 @@ set desc = "Open a list of available news channels" set category = "Ghost" - var/datum/browser/B = new(src, "ghost_news_list", "Chanenl List", 450, 600) + var/datum/browser/B = new(src, "ghost_news_list", "Channel List", 450, 600) B.set_content(render_news_channel_list()) B.open() diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 01c25b57f1..5d842ef11a 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -1,4 +1,4 @@ -/* +/** * Paper * also scraps of paper * @@ -11,12 +11,11 @@ #define MODE_WRITING 1 #define MODE_STAMPING 2 - /** - ** This is a custom ui state. All it really does is keep track of pen - ** being used and if they are editing it or not. This way we can keep - ** the data with the ui rather than on the paper - **/ + * This is a custom ui state. All it really does is keep track of pen + * being used and if they are editing it or not. This way we can keep + * the data with the ui rather than on the paper + */ /datum/ui_state/default/paper_state /// What edit mode we are in and who is /// writing on it right now @@ -33,7 +32,6 @@ var/stamp_name = "" var/stamp_class = "" - /datum/ui_state/default/paper_state/proc/copy_from(datum/ui_state/default/paper_state/from) switch(from.edit_mode) if(MODE_READING) @@ -49,12 +47,11 @@ stamp_class = from.stamp_class stamp_name = from.stamp_name - /** - ** Paper is now using markdown (like in github pull notes) for ALL rendering - ** so we do loose a bit of functionality but we gain in easy of use of - ** paper and getting rid of that crashing bug - **/ + * Paper is now using markdown (like in github pull notes) for ALL rendering + * so we do loose a bit of functionality but we gain in easy of use of + * paper and getting rid of that crashing bug + */ /obj/item/paper name = "paper" gender = NEUTER @@ -71,6 +68,8 @@ resistance_flags = FLAMMABLE max_integrity = 50 dog_fashion = /datum/dog_fashion/head + // drop_sound = 'sound/items/handling/paper_drop.ogg' + // pickup_sound = 'sound/items/handling/paper_pickup.ogg' grind_results = list(/datum/reagent/cellulose = 3) color = "white" /// What's actually written on the paper. @@ -89,9 +88,6 @@ var/contact_poison // Reagent ID to transfer on contact var/contact_poison_volume = 0 - // ui stuff - var/ui_x = 600 - var/ui_y = 800 // Ok, so WHY are we caching the ui's? // Since we are not using autoupdate we // need some way to update the ui's of @@ -103,7 +99,6 @@ // people look at it var/list/viewing_ui = list() - /// When the sheet can be "filled out" /// This is an associated list var/list/form_fields = list() @@ -116,10 +111,10 @@ . = ..() /** - ** This proc copies this sheet of paper to a new - ** sheet, Makes it nice and easy for carbon and - ** the copyer machine - **/ + * This proc copies this sheet of paper to a new + * sheet, Makes it nice and easy for carbon and + * the copyer machine + */ /obj/item/paper/proc/copy() var/obj/item/paper/N = new(arglist(args)) N.info = info @@ -133,10 +128,10 @@ return N /** - ** This proc sets the text of the paper and updates the - ** icons. You can modify the pen_color after if need - ** be. - **/ + * This proc sets the text of the paper and updates the + * icons. You can modify the pen_color after if need + * be. + */ /obj/item/paper/proc/setText(text) info = text form_fields = null @@ -152,30 +147,16 @@ contact_poison = null . = ..() - /obj/item/paper/Initialize() . = ..() pixel_y = rand(-8, 8) pixel_x = rand(-9, 9) update_icon() - /obj/item/paper/update_icon_state() if(info && show_written_words) icon_state = "[initial(icon_state)]_words" -/obj/item/paper/ui_base_html(html) - /// This might change in a future PR - var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/paper) - . = replacetext(html, "", assets.css_tag()) - - -/obj/item/paper/examine_more(mob/user) - ui_interact(user) - -/obj/item/paper/proc/show_content(mob/user) - user.examinate(src) - /obj/item/paper/verb/rename() set name = "Rename paper" set category = "Object" @@ -195,17 +176,14 @@ name = "paper[(n_name ? text("- '[n_name]'") : null)]" add_fingerprint(usr) - /obj/item/paper/suicide_act(mob/user) user.visible_message("[user] scratches a grid on [user.p_their()] wrist with the paper! It looks like [user.p_theyre()] trying to commit sudoku...") return (BRUTELOSS) - /// ONLY USED FOR APRIL FOOLS /obj/item/paper/proc/reset_spamflag() spam_flag = FALSE - /obj/item/paper/attack_self(mob/user) if(rigged && (SSevents.holidays && SSevents.holidays[APRIL_FOOLS])) if(!spam_flag) @@ -214,7 +192,6 @@ addtimer(CALLBACK(src, .proc/reset_spamflag), 20) . = ..() - /obj/item/paper/proc/clearpaper() info = "" stamps = null @@ -222,29 +199,28 @@ cut_overlays() update_icon_state() - -/obj/item/paper/examine(mob/user) +/obj/item/paper/examine_more(mob/user) ui_interact(user) - + return list("You try to read [src]...") /obj/item/paper/can_interact(mob/user) if(!..()) return FALSE - if(resistance_flags & ON_FIRE) // Are we on fire? Hard ot read if so + // Are we on fire? Hard ot read if so + if(resistance_flags & ON_FIRE) return FALSE - if(user.is_blind()) // Even harder to read if your blind...braile? humm + // Even harder to read if your blind...braile? humm + if(user.is_blind()) return FALSE - return user.can_read(src) // checks if the user can read. - + // checks if the user can read. + return user.can_read(src) /** - ** This creates the ui, since we are using a custom state but not much else - ** just makes it easyer to make it. Also we make a custom ui_key as I am - ** not sure how tgui handles many producers? -**/ + * This creates the ui, since we are using a custom state but not much else + * just makes it easyer to make it. + */ /obj/item/paper/proc/create_ui(mob/user, datum/ui_state/default/paper_state/state) - ui_interact(user, "main", null, FALSE, null, state) - + ui_interact(user, state = state) /obj/item/proc/burn_paper_product_attackby_check(obj/item/I, mob/living/user, bypass_clumsy) var/ignition_message = I.ignition_effect(src, user) @@ -320,24 +296,27 @@ if(.) info = "[stars(info)]" -/obj/item/paper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/default/paper_state/state = new) - ui_key = "main-[REF(user)]" - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/paper/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/simple/paper), + ) + +/obj/item/paper/ui_interact(mob/user, datum/tgui/ui, + datum/ui_state/default/paper_state/state) + // Update the state + ui = ui || SStgui.get_open_ui(user, src) + if(ui && state) + var/datum/ui_state/default/paper_state/current_state = ui.state + current_state.copy_from(state) + // Update the UI + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/simple/paper) - assets.send(user) - // The x size is because we double the width for the editor - ui = new(user, src, ui_key, "PaperSheet", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "PaperSheet", name) + state = new + ui.set_state(state) ui.set_autoupdate(FALSE) viewing_ui[user] = ui ui.open() - else - var/datum/ui_state/default/paper_state/last_state = ui.state - if(last_state) - last_state.copy_from(state) - else - ui.state = state - /obj/item/paper/ui_close(mob/user) /// close the editing window and change the mode @@ -347,7 +326,7 @@ // Again, we have to do this as autoupdate is off /obj/item/paper/proc/update_all_ui() for(var/datum/tgui/ui in viewing_ui) - ui.update() + ui.process(force = TRUE) // Again, we have to do this as autoupdate is off /obj/item/paper/proc/close_all_ui() @@ -383,7 +362,6 @@ return data - /obj/item/paper/ui_act(action, params, datum/tgui/ui, datum/ui_state/default/paper_state/state) if(..()) return @@ -445,21 +423,18 @@ . = TRUE - -/* +/** * Construction paper */ - /obj/item/paper/construction /obj/item/paper/construction/Initialize() . = ..() color = pick("FF0000", "#33cc33", "#ffb366", "#551A8B", "#ff80d5", "#4d94ff") -/* +/** * Natural paper */ - /obj/item/paper/natural/Initialize() . = ..() color = "#FFF5ED" diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 997ada6b21..91b8a6719b 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -147,7 +147,7 @@ log_game("[user] [key_name(user)] has renamed [O] to [input]") if(penchoice == "Change description") - var/input = stripped_input(user,"Describe \the [O.name] here", ,"", 100) + var/input = stripped_input(user,"Describe \the [O.name] here", ,"", 2048) if(QDELETED(O) || !user.canUseTopic(O, BE_CLOSE)) return O.desc = input diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index ba51eb3bef..a70e508476 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -838,6 +838,46 @@ // attack with hand - remove cell (if cover open) or interact with the APC /obj/machinery/power/apc/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) + if(isethereal(user)) + var/mob/living/carbon/human/H = user + if(H.a_intent == INTENT_HARM) + if(cell.charge <= (cell.maxcharge / 2)) // if charge is under 50% you shouldnt drain it + to_chat(H, "The APC doesn't have much power, you probably shouldn't drain any.") + return + var/obj/item/organ/stomach/ethereal/stomach = H.getorganslot(ORGAN_SLOT_STOMACH) + if(stomach.crystal_charge > 145) + to_chat(H, "Your charge is full!") + return + to_chat(H, "You start channeling some power through the APC into your body.") + if(do_after(user, 75, target = src)) + if(cell.charge <= (cell.maxcharge / 2) || (stomach.crystal_charge > 145)) + return + if(istype(stomach)) + to_chat(H, "You receive some charge from the APC.") + stomach.adjust_charge(10) + cell.charge -= 10 + else + to_chat(H, "You can't receive charge from the APC!") + return + if(H.a_intent == INTENT_GRAB) + if(cell.charge == cell.maxcharge) + to_chat(H, "The APC is full!") + return + var/obj/item/organ/stomach/ethereal/stomach = H.getorganslot(ORGAN_SLOT_STOMACH) + if(stomach.crystal_charge < 10) + to_chat(H, "Your charge is too low!") + return + to_chat(H, "You start channeling power through your body into the APC.") + if(do_after(user, 75, target = src)) + if(cell.charge == cell.maxcharge || (stomach.crystal_charge < 10)) + return + if(istype(stomach)) + to_chat(H, "You transfer some power to the APC.") + stomach.adjust_charge(-10) + cell.charge += 10 + else + to_chat(H, "You can't transfer power to the APC!") + return if(opened && (!issilicon(user))) if(cell) user.visible_message("[user] removes \the [cell] from [src]!","You remove \the [cell].") @@ -850,26 +890,19 @@ if((stat & MAINT) && !opened) //no board; no interface return -/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - +/obj/machinery/power/apc/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Apc", name, 480, 460, master_ui, state) + ui = new(user, src, "Apc", name) ui.open() /obj/machinery/power/apc/ui_data(mob/user) - var/obj/item/implant/hijack/H = user.getImplant(/obj/item/implant/hijack) - var/abilitiesavail = FALSE - if (H && !H.stealthmode && H.toggled) - abilitiesavail = TRUE var/list/data = list( "locked" = locked, - "lock_nightshift" = nightshift_requires_auth, "failTime" = failure_timer, "isOperating" = operating, "externalPower" = main_status, - "powerCellStatus" = (cell?.percent() || null), + "powerCellStatus" = cell ? cell.percent() : null, "chargeMode" = chargemode, "chargingStatus" = charging, "totalLoad" = DisplayPower(lastused_total), @@ -878,10 +911,7 @@ "malfStatus" = get_malf_status(user), "emergencyLights" = !emergency_lights, "nightshiftLights" = nightshift_lights, - "hijackable" = HAS_TRAIT(user,TRAIT_HIJACKER), - "hijacker" = hijacker == user ? TRUE : FALSE, - "drainavail" = cell && cell.percent() >= 85 && abilitiesavail, - "lockdownavail" = cell && cell.percent() >= 35 && abilitiesavail, + "powerChannels" = list( list( "title" = "Equipment", @@ -979,43 +1009,32 @@ . = UI_INTERACTIVE /obj/machinery/power/apc/ui_act(action, params) - if(..() || !can_use(usr, 1)) - return - if(failure_timer) - if(action == "reboot") - failure_timer = 0 - update_icon() - update() - if(action == "hijack" && can_use(usr, 1)) //don't need auth for hijack button - hijack(usr) - return - var/authorized = (!locked || area.hasSiliconAccessInArea(usr, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE) || (integration_cog && (is_servant_of_ratvar(usr)))) - if((action == "toggle_nightshift") && (!nightshift_requires_auth || authorized)) - toggle_nightshift_lights() - return TRUE - if(!authorized) + if(..() || !can_use(usr, 1) || (locked && area.hasSiliconAccessInArea(usr, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE) && !failure_timer && action != "toggle_nightshift") || (integration_cog && (is_servant_of_ratvar(usr)))) return switch(action) if("lock") if(area.hasSiliconAccessInArea(usr)) if((obj_flags & EMAGGED) || (stat & (BROKEN|MAINT))) - to_chat(usr, "The APC does not respond to the command.") + to_chat(usr, "The APC does not respond to the command!") else locked = !locked update_icon() - return TRUE + . = TRUE if("cover") coverlocked = !coverlocked - return TRUE + . = TRUE if("breaker") - toggle_breaker() - return TRUE + toggle_breaker(usr) + . = TRUE + if("toggle_nightshift") + toggle_nightshift_lights() + . = TRUE if("charge") chargemode = !chargemode if(!chargemode) charging = APC_NOT_CHARGING update_icon() - return TRUE + . = TRUE if("channel") if(params["eqp"]) equipment = setsubsystem(text2num(params["eqp"])) @@ -1029,23 +1048,24 @@ environ = setsubsystem(text2num(params["env"])) update_icon() update() - return TRUE + . = TRUE if("overload") - if(area.hasSiliconAccessInArea(usr)) + if(area.hasSiliconAccessInArea(usr, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE)) //usr.has_unlimited_silicon_privilege) overload_lighting() - return TRUE + . = TRUE if("hack") if(get_malf_status(usr)) malfhack(usr) - return TRUE if("occupy") if(get_malf_status(usr)) malfoccupy(usr) - return TRUE if("deoccupy") if(get_malf_status(usr)) malfvacate() - return TRUE + if("reboot") + failure_timer = 0 + update_icon() + update() if("emergency_lighting") emergency_lights = !emergency_lights for(var/obj/machinery/light/L in area) @@ -1053,31 +1073,14 @@ L.no_emergency = emergency_lights INVOKE_ASYNC(L, /obj/machinery/light/.proc/update, FALSE) CHECK_TICK - if("drain") - cell.use(cell.charge) - hijacker.toggleSiliconAccessArea(area) - hijacker = null - set_hijacked_lighting() - update_icon() - var/obj/item/implant/hijack/H = usr.getImplant(/obj/item/implant/hijack) - H.stealthcooldown = world.time + 2 MINUTES - energy_fail(30 SECONDS * (cell.charge / cell.maxcharge)) - if("lockdown") - var/celluse = rand(20,35) - celluse = celluse /100 - for (var/obj/machinery/door/D in GLOB.airlocks) - if (get_area(D) == area) - INVOKE_ASYNC(D,/obj/machinery/door.proc/hostile_lockdown,usr, FALSE) - addtimer(CALLBACK(D,/obj/machinery/door.proc/disable_lockdown, FALSE), 30 SECONDS) - cell.charge -= cell.maxcharge*celluse - var/obj/item/implant/hijack/H = usr.getImplant(/obj/item/implant/hijack) - H.stealthcooldown = world.time + 3 MINUTES return TRUE -/obj/machinery/power/apc/proc/toggle_breaker() +/obj/machinery/power/apc/proc/toggle_breaker(mob/user) if(!is_operational() || failure_timer) return operating = !operating + add_hiddenprint(user) //delete when runtime + log_game("[key_name(user)] turned [operating ? "on" : "off"] the [src] in [AREACOORD(src)]") update() update_icon() @@ -1122,6 +1125,10 @@ if(malf.malfhacking) to_chat(malf, "You are already hacking an APC.") return + var/area/ourarea = get_area(src) + if(!ourarea.valid_malf_hack) + to_chat(malf, "This APC is not well connected enough to the Exonet to provide any useful processing capabilities.") + return to_chat(malf, "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process.") malf.malfhack = src malf.malfhacking = addtimer(CALLBACK(malf, /mob/living/silicon/ai/.proc/malfhacked, src), 600, TIMER_STOPPABLE) diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index ba6311a94d..6425feac31 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -151,6 +151,27 @@ if(prob(25)) corrupt() +/obj/item/stock_parts/cell/attack_self(mob/user) + if(isethereal(user)) + var/mob/living/carbon/human/H = user + if(charge < 100) + to_chat(H, "The [src] doesn't have enough power!") + return + var/obj/item/organ/stomach/ethereal/stomach = H.getorganslot(ORGAN_SLOT_STOMACH) + if(stomach.crystal_charge > 146) + to_chat(H, "Your charge is full!") + return + to_chat(H, "You clumsily channel power through the [src] and into your body, wasting some in the process.") + if(do_after(user, 5, target = src)) + if((charge < 100) || (stomach.crystal_charge > 146)) + return + if(istype(stomach)) + to_chat(H, "You receive some charge from the [src].") + stomach.adjust_charge(3) + charge -= 100 //you waste way more than you receive, so that ethereals cant just steal one cell and forget about hunger + else + to_chat(H, "You can't receive charge from the [src]!") + return /obj/item/stock_parts/cell/blob_act(obj/structure/blob/B) ex_act(EXPLODE_DEVASTATE) diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index 30a7aa152b..e37ae56e71 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -116,8 +116,6 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne sprite_number = 8 use_power = IDLE_POWER_USE interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OFFLINE - ui_x = 400 - ui_y = 165 var/on = TRUE var/breaker = TRUE var/list/parts = list() @@ -222,11 +220,10 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne return return ..() -/obj/machinery/gravity_generator/main/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/gravity_generator/main/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "GravityGenerator", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "GravityGenerator", name) ui.open() /obj/machinery/gravity_generator/main/ui_data(mob/user) diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 20f7ce099a..4c76c4b5b1 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -610,7 +610,18 @@ var/mob/living/carbon/human/H = user if(istype(H)) - + var/datum/species/ethereal/eth_species = H.dna?.species + if(istype(eth_species)) + to_chat(H, "You start channeling some power through the [fitting] into your body.") + if(do_after(user, 50, target = src)) + var/obj/item/organ/stomach/ethereal/stomach = H.getorganslot(ORGAN_SLOT_STOMACH) + if(istype(stomach)) + to_chat(H, "You receive some charge from the [fitting].") + stomach.adjust_charge(2) + else + to_chat(H, "You can't receive charge from the [fitting]!") + return + if(H.gloves) var/obj/item/clothing/gloves/G = H.gloves if(G.max_heat_protection_temperature) diff --git a/code/modules/power/monitor.dm b/code/modules/power/monitor.dm index 974fb1b9e2..393d403c4d 100644 --- a/code/modules/power/monitor.dm +++ b/code/modules/power/monitor.dm @@ -11,8 +11,6 @@ active_power_usage = 100 circuit = /obj/item/circuitboard/computer/powermonitor tgui_id = "PowerMonitor" - ui_x = 550 - ui_y = 700 var/obj/structure/cable/attached_wire var/obj/machinery/power/apc/local_apc @@ -84,11 +82,10 @@ if(demand.len > record_size) demand.Cut(1, 2) -/obj/machinery/computer/monitor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/monitor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "PowerMonitor", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "PowerMonitor", name) ui.open() /obj/machinery/computer/monitor/ui_data() diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index d4e0df8359..e2f8c4e58a 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -7,8 +7,6 @@ density = TRUE anchored = FALSE use_power = NO_POWER_USE - ui_x = 450 - ui_y = 340 var/active = FALSE var/power_gen = 5000 @@ -219,11 +217,10 @@ /obj/machinery/power/port_gen/pacman/attack_paw(mob/user) interact(user) -/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "PortableGenerator", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "PortableGenerator", name) ui.open() /obj/machinery/power/port_gen/pacman/ui_data() diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index 912fc0a72b..96c8d9a263 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -10,8 +10,6 @@ active_power_usage = 10000 dir = NORTH mouse_opacity = MOUSE_OPACITY_OPAQUE - ui_x = 350 - ui_y = 185 var/strength_upper_limit = 2 var/interface_control = TRUE var/list/obj/structure/particle_accelerator/connected_parts @@ -275,11 +273,10 @@ return ..() return UI_CLOSE -/obj/machinery/particle_accelerator/control_box/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/particle_accelerator/control_box/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "ParticleAccelerator", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "ParticleAccelerator", name) ui.open() /obj/machinery/particle_accelerator/control_box/ui_data(mob/user) diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index fcc7faa29e..a4fc7d0641 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -21,8 +21,6 @@ density = TRUE use_power = NO_POWER_USE circuit = /obj/item/circuitboard/machine/smes - ui_x = 340 - ui_y = 350 var/capacity = 5e6 // maximum charge var/charge = 0 // actual charge @@ -202,7 +200,7 @@ /obj/machinery/power/smes/update_overlays() . = ..() - if((stat & BROKEN) || panel_open) + if(stat & BROKEN) return if(panel_open) @@ -318,11 +316,10 @@ return -/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/power/smes/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Smes", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "Smes", name) ui.open() /obj/machinery/power/smes/ui_data() diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index 2c258c1f49..cf526f083d 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -115,9 +115,6 @@ panel.icon_state = "solar_panel-b" else panel.icon_state = "solar_panel" -#if DM_VERSION <= 512 - . += new /mutable_appearance(panel) -#endif /obj/machinery/power/solar/proc/queue_turn(azimuth) needs_to_turn = TRUE @@ -346,11 +343,10 @@ else . += mutable_appearance(icon, icon_screen) -/obj/machinery/power/solar_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/power/solar_control/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "SolarControl", name, 380, 230, master_ui, state) + ui = new(user, src, "SolarControl", name) ui.open() /obj/machinery/power/solar_control/ui_data() diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index e7a97c554f..a9acea719c 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -56,8 +56,6 @@ resistance_flags = FIRE_PROOF CanAtmosPass = ATMOS_PASS_DENSITY circuit = /obj/item/circuitboard/machine/power_turbine - ui_x = 310 - ui_y = 150 var/opened = 0 var/obj/machinery/power/compressor/compressor var/turf/outturf @@ -249,11 +247,10 @@ default_deconstruction_crowbar(I) -/obj/machinery/power/turbine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/power/turbine/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "TurbineComputer", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "TurbineComputer", name) ui.open() /obj/machinery/power/turbine/ui_data(mob/user) @@ -292,8 +289,6 @@ icon_screen = "turbinecomp" icon_keyboard = "tech_key" circuit = /obj/item/circuitboard/computer/turbine_computer - ui_x = 310 - ui_y = 150 var/obj/machinery/power/compressor/compressor var/id = 0 @@ -313,11 +308,10 @@ else compressor = locate(/obj/machinery/power/compressor) in range(7, src) -/obj/machinery/computer/turbine_computer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/turbine_computer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "TurbineComputer", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "TurbineComputer", name) ui.open() /obj/machinery/computer/turbine_computer/ui_data(mob/user) diff --git a/code/modules/procedural_mapping/mapGenerator.dm b/code/modules/procedural_mapping/mapGenerator.dm index f509c409ce..323f74d0ef 100644 --- a/code/modules/procedural_mapping/mapGenerator.dm +++ b/code/modules/procedural_mapping/mapGenerator.dm @@ -147,8 +147,16 @@ set category = "Debug" var/datum/mapGenerator/nature/N = new() - var/startInput = input(usr,"Start turf of Map, (X;Y;Z)", "Map Gen Settings", "1;1;1") as text - var/endInput = input(usr,"End turf of Map (X;Y;Z)", "Map Gen Settings", "[world.maxx];[world.maxy];[mob ? mob.z : 1]") as text + var/startInput = input(usr,"Start turf of Map, (X;Y;Z)", "Map Gen Settings", "1;1;1") as text|null + + if (isnull(startInput)) + return + + var/endInput = input(usr,"End turf of Map (X;Y;Z)", "Map Gen Settings", "[world.maxx];[world.maxy];[mob ? mob.z : 1]") as text|null + + if (isnull(endInput)) + return + //maxx maxy and current z so that if you fuck up, you only fuck up one entire z level instead of the entire universe if(!startInput || !endInput) to_chat(src, "Missing Input") diff --git a/code/modules/projectiles/ammunition/_firing.dm b/code/modules/projectiles/ammunition/_firing.dm index 0ef4c680aa..437b7dcc5a 100644 --- a/code/modules/projectiles/ammunition/_firing.dm +++ b/code/modules/projectiles/ammunition/_firing.dm @@ -36,6 +36,14 @@ if(isgun(fired_from)) var/obj/item/gun/G = fired_from BB.damage *= G.projectile_damage_multiplier + if(HAS_TRAIT(user, TRAIT_INSANE_AIM)) + BB.ricochets_max = max(BB.ricochets_max, 10) //bouncy! + BB.ricochet_chance = max(BB.ricochet_chance, 100) //it wont decay so we can leave it at 100 for always bouncing + BB.ricochet_auto_aim_range = max(BB.ricochet_auto_aim_range, 3) + BB.ricochet_auto_aim_angle = max(BB.ricochet_auto_aim_angle, 360) //it can turn full circle and shoot you in the face because our aim? is insane. + BB.ricochet_decay_chance = 0 + BB.ricochet_decay_damage = max(BB.ricochet_decay_damage, 0.1) + BB.ricochet_incidence_leeway = 0 if(reagents && BB.reagents) reagents.trans_to(BB, reagents.total_volume) //For chemical darts/bullets diff --git a/code/modules/projectiles/boxes_magazines/external/shotgun.dm b/code/modules/projectiles/boxes_magazines/external/shotgun.dm index 1001937678..ed41375aee 100644 --- a/code/modules/projectiles/boxes_magazines/external/shotgun.dm +++ b/code/modules/projectiles/boxes_magazines/external/shotgun.dm @@ -1,5 +1,5 @@ /obj/item/ammo_box/magazine/m12g - name = "shotgun magazine (12g buckshot slugs)" + name = "shotgun magazine (12g buckshot)" desc = "A drum magazine." icon_state = "m12gb" ammo_type = /obj/item/ammo_casing/shotgun/buckshot @@ -17,7 +17,7 @@ /obj/item/ammo_box/magazine/m12g/slug name = "shotgun magazine (12g slugs)" - icon_state = "m12gb" //this may need an unique sprite + icon_state = "m12gsl" ammo_type = /obj/item/ammo_casing/shotgun /obj/item/ammo_box/magazine/m12g/dragon diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index c3248bab2f..ee073dbfcd 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -29,7 +29,7 @@ trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers var/sawn_desc = null //description change if weapon is sawn-off var/sawn_off = FALSE - + /// can we be put into a turret var/can_turret = TRUE /// can we be put in a circuit @@ -310,8 +310,6 @@ randomized_gun_spread = rand(0, spread) else if(burst_size > 1 && burst_spread) randomized_gun_spread = rand(0, burst_spread) - if(HAS_TRAIT(user, TRAIT_POOR_AIM)) //nice shootin' tex - bonus_spread += 25 var/randomized_bonus_spread = rand(0, bonus_spread) if(burst_size > 1) @@ -603,10 +601,16 @@ var/penalty = (last_fire + GUN_AIMING_TIME + fire_delay) - world.time if(penalty > 0) //Yet we only penalize users firing it multiple times in a haste. fire_delay isn't necessarily cumbersomeness. aiming_delay = penalty - if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) //To be removed in favor of something less tactless later. + if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE) || HAS_TRAIT(user, TRAIT_INSANE_AIM)) //To be removed in favor of something less tactless later. base_inaccuracy /= 1.5 if(stamloss > STAMINA_NEAR_SOFTCRIT) //This can null out the above bonus. base_inaccuracy *= 1 + (stamloss - STAMINA_NEAR_SOFTCRIT)/(STAMINA_NEAR_CRIT - STAMINA_NEAR_SOFTCRIT)*0.5 + if(HAS_TRAIT(user, TRAIT_POOR_AIM)) //nice shootin' tex + if(!HAS_TRAIT(user, TRAIT_INSANE_AIM)) + bonus_spread += 25 + else + //you have both poor aim and insane aim, why? + bonus_spread += rand(0,50) var/mult = max((GUN_AIMING_TIME + aiming_delay + user.last_click_move - world.time)/GUN_AIMING_TIME, -0.5) //Yes, there is a bonus for taking time aiming. if(mult < 0) //accurate weapons should provide a proper bonus with negative inaccuracy. the opposite is true too. mult *= 1/inaccuracy_modifier diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index 289b43a669..a5ed45dd48 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -84,6 +84,11 @@ . = ..() . += "[get_ammo(0,0)] of those are live rounds." +/obj/item/gun/ballistic/revolver/syndicate + unique_reskin = list("Default" = "revolver", + "Silver" = "russianrevolver", + "Robust" = "revolvercit") + /obj/item/gun/ballistic/revolver/detective name = "\improper .38 Mars Special" desc = "A cheap Martian knock-off of a classic law enforcement firearm. Uses .38-special rounds." @@ -486,4 +491,4 @@ for(var/i = 0, i < ratio, i++) var/mutable_appearance/charge_bar = mutable_appearance(icon, "[initial(icon_state)]_charge", color = batt_color) charge_bar.pixel_x = i - . += charge_bar \ No newline at end of file + . += charge_bar diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 43384280d4..99a0bedc4d 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -327,16 +327,18 @@ if(!trajectory) return var/turf/T = get_turf(A) - if(check_ricochet(A) && A.handle_ricochet(src)) //if you can ricochet, attempt to ricochet off the object - on_ricochet(A) //if allowed, use autoaim to ricochet into someone, otherwise default to ricocheting off the object from above - var/datum/point/pcache = trajectory.copy_to() - if(hitscan) - store_hitscan_collision(pcache) - decayedRange = max(0, decayedRange - reflect_range_decrease) - ricochet_chance *= ricochet_decay_chance - damage *= ricochet_decay_damage - range = decayedRange - return TRUE + if(check_ricochet_flag(A) && check_ricochet(A)) //if you can ricochet, attempt to ricochet off the object + ricochets++ + if(A.handle_ricochet(src)) + on_ricochet(A) //if allowed, use autoaim to ricochet into someone, otherwise default to ricocheting off the object from above + var/datum/point/pcache = trajectory.copy_to() + if(hitscan) + store_hitscan_collision(pcache) + decayedRange = max(0, decayedRange - reflect_range_decrease) + ricochet_chance *= ricochet_decay_chance + damage *= ricochet_decay_damage + range = decayedRange + return TRUE var/distance = get_dist(T, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. if(def_zone && check_zone(def_zone) != BODY_ZONE_CHEST) @@ -680,7 +682,8 @@ if(!ignore_source_check && firer) var/mob/M = firer if((target == firer) || ((target == firer.loc) && ismecha(firer.loc)) || (target in firer.buckled_mobs) || (istype(M) && (M.buckled == target))) - return FALSE + if(!ricochets) //if it has ricocheted, it can hit the firer. + return FALSE if(!ignore_loc && (loc != target.loc)) return FALSE if(target in passthrough) diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index da33f935da..db16a10d1d 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -177,11 +177,10 @@ beaker = null update_icon() -/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/chem_dispenser/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "ChemDispenser", name, 565, 550, master_ui, state) + ui = new(user, src, "ChemDispenser", name) if(user.hallucinating()) ui.set_autoupdate(FALSE) //to not ruin the immersion by constantly changing the fake chemicals ui.open() @@ -216,7 +215,7 @@ data["beakerTransferAmounts"] = null data["beakerCurrentpH"] = null - var/list/chemicals = list() + var/chemicals[0] var/is_hallucinating = FALSE if(user.hallucinating()) is_hallucinating = TRUE @@ -275,7 +274,7 @@ . = TRUE if("eject") replace_beaker(usr) - . = TRUE //no afterattack + . = TRUE if("dispense_recipe") if(!is_operational() || QDELETED(cell)) return @@ -326,9 +325,9 @@ for(var/reagent in recording_recipe) var/reagent_id = GLOB.name2reagent[translate_legacy_chem_id(reagent)] if(!dispensable_reagents.Find(reagent_id)) - visible_message("[src] buzzes.", "You hear a faint buzz.") + visible_message("[src] buzzes.", "You hear a faint buzz.") to_chat(usr, "[src] cannot find [reagent]!") - playsound(src, 'sound/machines/buzz-two.ogg', 50, 1) + playsound(src, 'sound/machines/buzz-two.ogg', 50, TRUE) return saved_recipes[name] = recording_recipe recording_recipe = null diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index 5697a2385c..28f0b2366a 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -7,6 +7,7 @@ idle_power_usage = 40 resistance_flags = FIRE_PROOF | ACID_PROOF circuit = /obj/item/circuitboard/machine/chem_heater + var/obj/item/reagent_containers/beaker = null var/target_temperature = 300 var/heater_coefficient = 0.1 @@ -30,22 +31,20 @@ /obj/machinery/chem_heater/AltClick(mob/living/user) . = ..() - if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) + if(!can_interact(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) return replace_beaker(user) - return TRUE /obj/machinery/chem_heater/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker) + if(!user) + return FALSE if(beaker) - beaker.forceMove(drop_location()) - if(user && Adjacent(user) && user.can_hold_items()) - user.put_in_hands(beaker) + user.put_in_hands(beaker) + beaker = null if(new_beaker) beaker = new_beaker - else - beaker = null - update_icon() - return TRUE + update_icon() + return TRUE /obj/machinery/chem_heater/RefreshParts() heater_coefficient = 0.1 @@ -63,6 +62,7 @@ return if(on) if(beaker && beaker.reagents.total_volume) + //keep constant with the chemical acclimator please beaker.reagents.adjust_thermal_energy((target_temperature - beaker.reagents.chem_temp) * heater_coefficient * SPECIFIC_HEAT_DEFAULT * beaker.reagents.total_volume) beaker.reagents.handle_reactions() @@ -83,27 +83,16 @@ updateUsrDialog() update_icon() return - - if(beaker) - if(istype(I, /obj/item/reagent_containers/dropper)) - var/obj/item/reagent_containers/dropper/D = I - D.afterattack(beaker, user, 1) - - if(istype(I, /obj/item/reagent_containers/syringe)) - var/obj/item/reagent_containers/syringe/S = I - S.afterattack(beaker, user, 1) - return ..() /obj/machinery/chem_heater/on_deconstruction() replace_beaker() return ..() -/obj/machinery/chem_heater/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/chem_heater/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "ChemHeater", name, 300, 400, master_ui, state) + ui = new(user, src, "ChemHeater", name) ui.open() /obj/machinery/chem_heater/ui_data() @@ -140,14 +129,7 @@ . = TRUE if("temperature") var/target = params["target"] - var/adjust = text2num(params["adjust"]) - if(target == "input") - target = input("New target temperature:", name, target_temperature) as num|null - if(!isnull(target) && !..()) - . = TRUE - else if(adjust) - target = target_temperature + adjust - else if(text2num(target) != null) + if(text2num(target) != null) target = text2num(target) . = TRUE if(.) diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index dd9dd7c0bf..207325e1b3 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -12,6 +12,7 @@ idle_power_usage = 20 resistance_flags = FIRE_PROOF | ACID_PROOF circuit = /obj/item/circuitboard/machine/chem_master + var/obj/item/reagent_containers/beaker = null var/obj/item/storage/pill_bottle/bottle = null var/mode = 1 @@ -154,19 +155,15 @@ bottle?.forceMove(A) return ..() -//Insert our custom spritesheet css link into the html -/obj/machinery/chem_master/ui_base_html(html) - var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/pills) - . = replacetext(html, "", assets.css_tag()) +/obj/machinery/chem_master/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/simple/pills), + ) -/obj/machinery/chem_master/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/chem_master/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/simple/pills) - assets.send(user) - - ui = new(user, src, ui_key, "ChemMaster", name, 520, 550, master_ui, state) + ui = new(user, src, "ChemMaster", name) ui.open() /obj/machinery/chem_master/ui_data(mob/user) @@ -183,8 +180,8 @@ data["isPillBottleLoaded"] = bottle ? 1 : 0 if(bottle) var/datum/component/storage/STRB = bottle.GetComponent(/datum/component/storage) - data["pillBotContent"] = bottle.contents.len - data["pillBotMaxContent"] = STRB.max_items + data["pillBottleCurrentAmount"] = bottle.contents.len + data["pillBottleMaxAmount"] = STRB.max_items var/beakerContents[0] if(beaker) @@ -206,213 +203,219 @@ if(..()) return - switch(action) - if("eject") - replace_beaker(usr) - . = TRUE + if(action == "eject") + replace_beaker(usr) + return TRUE - if("ejectPillBottle") - replace_pillbottle(usr) - . = TRUE - - if("transfer") - if(!beaker) - return FALSE - var/reagent = GLOB.name2reagent[params["id"]] - var/amount = text2num(params["amount"]) - var/to_container = params["to"] - // Custom amount - if (amount == -1) - amount = text2num(input( - "Enter the amount you want to transfer:", - name, "")) - if (amount == null || amount <= 0) - return FALSE - if (to_container == "buffer") - end_fermi_reaction() - beaker.reagents.trans_id_to(src, reagent, amount) - return TRUE - if (to_container == "beaker" && mode) - end_fermi_reaction() - reagents.trans_id_to(beaker, reagent, amount) - return TRUE - if (to_container == "beaker" && !mode) - end_fermi_reaction() - reagents.remove_reagent(reagent, amount) - return TRUE + if(action == "ejectPillBottle") + if(!bottle) return FALSE + bottle.forceMove(drop_location()) + adjust_item_drop_location(bottle) + bottle = null + return TRUE - if("toggleMode") - mode = !mode - . = TRUE + if(action == "transfer") + if(!beaker) + return FALSE + var/reagent = GLOB.name2reagent[params["id"]] + var/amount = text2num(params["amount"]) + var/to_container = params["to"] + // Custom amount + if (amount == -1) + amount = text2num(input( + "Enter the amount you want to transfer:", + name, "")) + if (amount == null || amount <= 0) + return FALSE + if (to_container == "buffer") + end_fermi_reaction() + beaker.reagents.trans_id_to(src, reagent, amount) + return TRUE + if (to_container == "beaker" && mode) + end_fermi_reaction() + reagents.trans_id_to(beaker, reagent, amount) + return TRUE + if (to_container == "beaker" && !mode) + end_fermi_reaction() + reagents.remove_reagent(reagent, amount) + return TRUE + return FALSE - if("pillStyle") - var/id = text2num(params["id"]) - chosenPillStyle = id + if(action == "toggleMode") + mode = !mode + return TRUE + + if(action == "pillStyle") + var/id = text2num(params["id"]) + chosenPillStyle = id + return TRUE + + if(action == "create") + if(reagents.total_volume == 0) + return FALSE + var/item_type = params["type"] + // Get amount of items + var/amount = text2num(params["amount"]) + if(amount == null) + amount = text2num(input(usr, + "Max 10. Buffer content will be split evenly.", + "How many to make?", 1)) + amount = clamp(round(amount), 0, 10) + if (amount <= 0) + return FALSE + // Get units per item + var/vol_each = text2num(params["volume"]) + var/vol_each_text = params["volume"] + var/vol_each_max = reagents.total_volume / amount + if (item_type == "pill") + vol_each_max = min(50, vol_each_max) + else if (item_type == "patch") + vol_each_max = min(40, vol_each_max) + else if (item_type == "bottle") + vol_each_max = min(30, vol_each_max) + else if (item_type == "condimentPack") + vol_each_max = min(10, vol_each_max) + else if (item_type == "condimentBottle") + vol_each_max = min(50, vol_each_max) + else if (item_type == "hypoVial") + vol_each_max = min(60, vol_each_max) + else if (item_type == "smartDart") + vol_each_max = min(20, vol_each_max) + else + return FALSE + if(vol_each_text == "auto") + vol_each = vol_each_max + if(vol_each == null) + vol_each = text2num(input(usr, + "Maximum [vol_each_max] units per item.", + "How many units to fill?", + vol_each_max)) + vol_each = clamp(vol_each, 0, vol_each_max) + if(vol_each <= 0) + return FALSE + // Get item name + var/name = params["name"] + var/name_has_units = item_type == "pill" || item_type == "patch" + if(!name) + var/name_default = reagents.get_master_reagent_name() + if (name_has_units) + name_default += " ([vol_each]u)" + name = stripped_input(usr, + "Name:", + "Give it a name!", + name_default, + MAX_NAME_LEN) + if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr))) + return FALSE + // Start filling + if(item_type == "pill") + var/obj/item/reagent_containers/pill/P + var/target_loc = drop_location() + var/drop_threshold = INFINITY + if(bottle) + var/datum/component/storage/STRB = bottle.GetComponent( + /datum/component/storage) + if(STRB) + drop_threshold = STRB.max_items - bottle.contents.len + for(var/i = 0; i < amount; i++) + if(i < drop_threshold) + P = new/obj/item/reagent_containers/pill(target_loc) + else + P = new/obj/item/reagent_containers/pill(drop_location()) + P.name = trim("[name] pill") + if(chosenPillStyle == RANDOM_PILL_STYLE) + P.icon_state ="pill[rand(1,21)]" + else + P.icon_state = "pill[chosenPillStyle]" + if(P.icon_state == "pill4") + P.desc = "A tablet or capsule, but not just any, a red one, one taken by the ones not scared of knowledge, freedom, uncertainty and the brutal truths of reality." + adjust_item_drop_location(P) + reagents.trans_to(P, vol_each)//, transfered_by = usr) + return TRUE + if(item_type == "patch") + var/obj/item/reagent_containers/pill/patch/P + for(var/i = 0; i < amount; i++) + P = new/obj/item/reagent_containers/pill/patch(drop_location()) + P.name = trim("[name] patch") + adjust_item_drop_location(P) + reagents.trans_to(P, vol_each)//, transfered_by = usr) + return TRUE + if(item_type == "bottle") + var/obj/item/reagent_containers/glass/bottle/P + for(var/i = 0; i < amount; i++) + P = new/obj/item/reagent_containers/glass/bottle(drop_location()) + P.name = trim("[name] bottle") + adjust_item_drop_location(P) + reagents.trans_to(P, vol_each)//, transfered_by = usr) + return TRUE + if(item_type == "condimentPack") + var/obj/item/reagent_containers/food/condiment/pack/P + for(var/i = 0; i < amount; i++) + P = new/obj/item/reagent_containers/food/condiment/pack(drop_location()) + P.originalname = name + P.name = trim("[name] pack") + P.desc = "A small condiment pack. The label says it contains [name]." + reagents.trans_to(P, vol_each)//, transfered_by = usr) + return TRUE + if(item_type == "condimentBottle") + var/obj/item/reagent_containers/food/condiment/P + for(var/i = 0; i < amount; i++) + P = new/obj/item/reagent_containers/food/condiment(drop_location()) + P.originalname = name + P.name = trim("[name] bottle") + reagents.trans_to(P, vol_each)//, transfered_by = usr) + return TRUE + if(item_type == "hypoVial") + var/obj/item/reagent_containers/glass/bottle/vial/small/P + for(var/i = 0; i < amount; i++) + P = new/obj/item/reagent_containers/glass/bottle/vial/small(drop_location()) + P.name = trim("[name] hypovial") + adjust_item_drop_location(P) + reagents.trans_to(P, vol_each)//, transfered_by = usr) + return TRUE + if(item_type == "smartDart") + var/obj/item/reagent_containers/syringe/dart/P + for(var/i = 0; i < amount; i++) + P = new /obj/item/reagent_containers/syringe/dart(drop_location()) + P.name = trim("[name] SmartDart") + adjust_item_drop_location(P) + reagents.trans_to(P, vol_each)//, transfered_by = usr) + P.mode=!mode + P.update_icon() + return TRUE + return FALSE + + if(action == "analyze") + // var/datum/reagent/R = GLOB.name2reagent[params["id"]] + var/reagent = GLOB.name2reagent[params["id"]] + var/datum/reagent/R = GLOB.chemical_reagents_list[reagent] + if(R) + var/state = "Unknown" + if(initial(R.reagent_state) == 1) + state = "Solid" + else if(initial(R.reagent_state) == 2) + state = "Liquid" + else if(initial(R.reagent_state) == 3) + state = "Gas" + var/const/P = 3 //The number of seconds between life ticks + var/T = initial(R.metabolization_rate) * (60 / P) + if(istype(R, /datum/reagent/fermi)) + fermianalyze = TRUE + var/datum/chemical_reaction/Rcr = get_chemical_reaction(reagent) + var/pHpeakCache = (Rcr.OptimalpHMin + Rcr.OptimalpHMax)/2 + analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold), "purityF" = R.purity, "inverseRatioF" = initial(R.inverse_chem_val), "purityE" = initial(Rcr.PurityMin), "minTemp" = initial(Rcr.OptimalTempMin), "maxTemp" = initial(Rcr.OptimalTempMax), "eTemp" = initial(Rcr.ExplodeTemp), "pHpeak" = pHpeakCache) + else + fermianalyze = FALSE + analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold)) + screen = "analyze" return TRUE - if("create") - if(reagents.total_volume == 0) - return FALSE - var/item_type = params["type"] - // Get amount of items - var/amount = text2num(params["amount"]) - if(amount == null) - amount = text2num(input(usr, - "Max 20. Buffer content will be split evenly.", - "How many to make?", 1)) - amount = clamp(round(amount), 0, 20) - if (amount <= 0) - return FALSE - // Get units per item - var/vol_each = text2num(params["volume"]) - var/vol_each_text = params["volume"] - var/vol_each_max = reagents.total_volume / amount - if (item_type == "pill") - vol_each_max = min(50, vol_each_max) - else if (item_type == "patch") - vol_each_max = min(40, vol_each_max) - else if (item_type == "bottle") - vol_each_max = min(30, vol_each_max) - else if (item_type == "condimentPack") - vol_each_max = min(10, vol_each_max) - else if (item_type == "condimentBottle") - vol_each_max = min(50, vol_each_max) - else if (item_type == "hypoVial") - vol_each_max = min(60, vol_each_max) - else if (item_type == "smartDart") - vol_each_max = min(20, vol_each_max) - else - return FALSE - if(vol_each_text == "auto") - vol_each = vol_each_max - if(vol_each == null) - vol_each = text2num(input(usr, - "Maximum [vol_each_max] units per item.", - "How many units to fill?", - vol_each_max)) - vol_each = clamp(vol_each, 0, vol_each_max) - if(vol_each <= 0) - return FALSE - // Get item name - var/name = params["name"] - var/name_has_units = item_type == "pill" || item_type == "patch" - if(!name) - var/name_default = reagents.get_master_reagent_name() - if (name_has_units) - name_default += " ([vol_each]u)" - name = stripped_input(usr, - "Name:", - "Give it a name!", - name_default, - MAX_NAME_LEN) - if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr))) - return FALSE - // Start filling - if(item_type == "pill") - var/obj/item/reagent_containers/pill/P - var/target_loc = drop_location() - var/drop_threshold = INFINITY - if(bottle) - var/datum/component/storage/STRB = bottle.GetComponent( - /datum/component/storage) - if(STRB) - drop_threshold = STRB.max_items - bottle.contents.len - for(var/i = 0; i < amount; i++) - if(i < drop_threshold) - P = new/obj/item/reagent_containers/pill(target_loc) - else - P = new/obj/item/reagent_containers/pill(drop_location()) - P.name = trim("[name] pill") - if(chosenPillStyle == RANDOM_PILL_STYLE) - P.icon_state ="pill[rand(1,21)]" - else - P.icon_state = "pill[chosenPillStyle]" - if(P.icon_state == "pill4") - P.desc = "A tablet or capsule, but not just any, a red one, one taken by the ones not scared of knowledge, freedom, uncertainty and the brutal truths of reality." - adjust_item_drop_location(P) - reagents.trans_to(P, vol_each) - return TRUE - if(item_type == "patch") - var/obj/item/reagent_containers/pill/patch/P - for(var/i = 0; i < amount; i++) - P = new/obj/item/reagent_containers/pill/patch(drop_location()) - P.name = trim("[name] patch") - adjust_item_drop_location(P) - reagents.trans_to(P, vol_each) - return TRUE - if(item_type == "bottle") - var/obj/item/reagent_containers/glass/bottle/P - for(var/i = 0; i < amount; i++) - P = new/obj/item/reagent_containers/glass/bottle(drop_location()) - P.name = trim("[name] bottle") - adjust_item_drop_location(P) - reagents.trans_to(P, vol_each) - return TRUE - if(item_type == "condimentPack") - var/obj/item/reagent_containers/food/condiment/pack/P - for(var/i = 0; i < amount; i++) - P = new/obj/item/reagent_containers/food/condiment/pack(drop_location()) - P.originalname = name - P.name = trim("[name] pack") - P.desc = "A small condiment pack. The label says it contains [name]." - reagents.trans_to(P, vol_each) - return TRUE - if(item_type == "condimentBottle") - var/obj/item/reagent_containers/food/condiment/P - for(var/i = 0; i < amount; i++) - P = new/obj/item/reagent_containers/food/condiment(drop_location()) - P.originalname = name - P.name = trim("[name] bottle") - reagents.trans_to(P, vol_each) - return TRUE - if(item_type == "hypoVial") - var/obj/item/reagent_containers/glass/bottle/vial/small/P - for(var/i = 0; i < amount; i++) - P = new/obj/item/reagent_containers/glass/bottle/vial/small(drop_location()) - P.name = trim("[name] hypovial") - adjust_item_drop_location(P) - reagents.trans_to(P, vol_each) - return TRUE - if(item_type == "smartDart") - var/obj/item/reagent_containers/syringe/dart/P - for(var/i = 0; i < amount; i++) - P = new /obj/item/reagent_containers/syringe/dart(drop_location()) - P.name = trim("[name] SmartDart") - adjust_item_drop_location(P) - reagents.trans_to(P, vol_each) - P.mode=!mode - P.update_icon() - return TRUE - return FALSE + if(action == "goScreen") + screen = params["screen"] + return TRUE - if("analyze") - var/reagent = GLOB.name2reagent[params["id"]] - var/datum/reagent/R = GLOB.chemical_reagents_list[reagent] - if(R) - var/state = "Unknown" - if(initial(R.reagent_state) == 1) - state = "Solid" - else if(initial(R.reagent_state) == 2) - state = "Liquid" - else if(initial(R.reagent_state) == 3) - state = "Gas" - var/const/P = 3 //The number of seconds between life ticks - var/T = initial(R.metabolization_rate) * (60 / P) - if(istype(R, /datum/reagent/fermi)) - fermianalyze = TRUE - var/datum/chemical_reaction/Rcr = get_chemical_reaction(reagent) - var/pHpeakCache = (Rcr.OptimalpHMin + Rcr.OptimalpHMax)/2 - analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold), "purityF" = R.purity, "inverseRatioF" = initial(R.inverse_chem_val), "purityE" = initial(Rcr.PurityMin), "minTemp" = initial(Rcr.OptimalTempMin), "maxTemp" = initial(Rcr.OptimalTempMax), "eTemp" = initial(Rcr.ExplodeTemp), "pHpeak" = pHpeakCache) - else - fermianalyze = FALSE - analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold)) - screen = "analyze" - return TRUE - - if("goScreen") - screen = params["screen"] - . = TRUE + return FALSE diff --git a/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm b/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm index 66c2616972..489f9dd179 100644 --- a/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm +++ b/code/modules/reagents/chemistry/machinery/chem_synthesizer.dm @@ -12,11 +12,10 @@ "tricord" = /datum/reagent/medicine/tricordrazine ) -/obj/machinery/chem_dispenser/chem_synthesizer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/chem_dispenser/chem_synthesizer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "ChemDebugSynthesizer", name, 390, 330, master_ui, state) + ui = new(user, src, "ChemDebugSynthesizer", name) ui.open() /obj/machinery/chem_dispenser/chem_synthesizer/ui_act(action, params) @@ -31,7 +30,11 @@ beaker = null . = TRUE if("input") - var/input_reagent = replacetext(lowertext(input("Enter the name of any reagent", "Input") as text), " ", "") //95% of the time, the reagent types is a lowercase, no spaces / underscored version of the name + var/input_reagent = replacetext(lowertext(input("Enter the name of any reagent", "Input") as text|null), " ", "") //95% of the time, the reagent id is a lowercase/no spaces version of the name + + if (isnull(input_reagent)) + return + if(shortcuts[input_reagent]) input_reagent = shortcuts[input_reagent] else @@ -51,7 +54,7 @@ beaker = new /obj/item/reagent_containers/glass/beaker/bluespace(src) visible_message("[src] dispenses a bluespace beaker.") if("amount") - var/input = input("Units to dispense", "Units") as num|null + var/input = text2num(params["amount"]) if(input) amount = input update_icon() diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm index 4a4bbdb546..36e102be72 100644 --- a/code/modules/reagents/chemistry/machinery/pandemic.dm +++ b/code/modules/reagents/chemistry/machinery/pandemic.dm @@ -7,10 +7,11 @@ density = TRUE icon = 'icons/obj/chemical.dmi' icon_state = "mixer0" - circuit = /obj/item/circuitboard/computer/pandemic use_power = TRUE idle_power_usage = 20 resistance_flags = ACID_PROOF + circuit = /obj/item/circuitboard/computer/pandemic + var/wait var/datum/symptom/selected_symptom var/obj/item/reagent_containers/beaker @@ -23,11 +24,27 @@ QDEL_NULL(beaker) return ..() -/obj/machinery/computer/pandemic/handle_atom_del(atom/A) +/obj/machinery/computer/pandemic/examine(mob/user) . = ..() + if(beaker) + var/is_close + if(Adjacent(user)) //don't reveal exactly what's inside unless they're close enough to see the UI anyway. + . += "It contains \a [beaker]." + is_close = TRUE + else + . += "It has a beaker inside it." + . += "Alt-click to eject [is_close ? beaker : "the beaker"]." + +/obj/machinery/computer/pandemic/AltClick(mob/user) + . = ..() + if(user.canUseTopic(src, BE_CLOSE)) + eject_beaker() + +/obj/machinery/computer/pandemic/handle_atom_del(atom/A) if(A == beaker) beaker = null update_icon() + return ..() /obj/machinery/computer/pandemic/proc/get_by_index(thing, index) if(!beaker || !beaker.reagents) @@ -107,7 +124,7 @@ /obj/machinery/computer/pandemic/proc/reset_replicator_cooldown() wait = FALSE update_icon() - playsound(loc, 'sound/machines/ping.ogg', 30, 1) + playsound(src, 'sound/machines/ping.ogg', 30, TRUE) /obj/machinery/computer/pandemic/update_icon_state() if(stat & BROKEN) @@ -117,13 +134,19 @@ /obj/machinery/computer/pandemic/update_overlays() . = ..() - if(!(stat & BROKEN) && wait) + if(wait) . += "waitlight" -/obj/machinery/computer/pandemic/ui_interact(mob/user, ui_key = "main", datum/tgui/ui, force_open = FALSE, datum/tgui/master_ui, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/pandemic/proc/eject_beaker() + if(beaker) + beaker.forceMove(drop_location()) + beaker = null + update_icon() + +/obj/machinery/computer/pandemic/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Pandemic", name, 520, 550, master_ui, state) + ui = new(user, src, "Pandemic", name) ui.open() /obj/machinery/computer/pandemic/ui_data(mob/user) @@ -135,9 +158,9 @@ var/datum/reagent/blood/B = locate() in beaker.reagents.reagent_list if(B) data["has_blood"] = TRUE - data[/datum/reagent/blood] = list() - data[/datum/reagent/blood]["dna"] = B.data["blood_DNA"] || "none" - data[/datum/reagent/blood]["type"] = B.data["blood_type"] || "none" + data["blood"] = list() //wha why the fuck are we sending pathtypes to tgui frontend? + data["blood"]["dna"] = B.data["blood_DNA"] || "none" + data["blood"]["type"] = B.data["blood_type"] || "none" data["viruses"] = get_viruses_data(B) data["resistances"] = get_resistance_data(B) else @@ -153,7 +176,7 @@ return switch(action) if("eject_beaker") - replace_beaker(usr) + eject_beaker() . = TRUE if("empty_beaker") if(beaker) @@ -162,7 +185,7 @@ if("empty_eject_beaker") if(beaker) beaker.reagents.clear_reagents() - replace_beaker(usr) + eject_beaker() . = TRUE if("rename_disease") var/id = get_virus_id_by_index(text2num(params["index"])) @@ -170,75 +193,62 @@ if(!A.mutable) return if(A) - var/new_name = sanitize_name(html_encode(trim(params["name"], 50))) + var/new_name = sanitize_name(html_encode(trim(params["name"], 50)))//, allow_numbers = TRUE) if(!new_name || ..()) return A.AssignName(new_name) . = TRUE if("create_culture_bottle") + if (wait) + return var/id = get_virus_id_by_index(text2num(params["index"])) var/datum/disease/advance/A = SSdisease.archive_diseases[id] if(!istype(A) || !A.mutable) to_chat(usr, "ERROR: Cannot replicate virus strain.") return - wait = TRUE - addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 50) A = A.Copy() - var/list/data = list("blood_DNA" = "UNKNOWN DNA", "blood_type" = "SY", "viruses" = list(A)) + var/list/data = list("viruses" = list(A)) var/obj/item/reagent_containers/glass/bottle/B = new(drop_location()) B.name = "[A.name] culture bottle" B.desc = "A small bottle. Contains [A.agent] culture in synthblood medium." B.reagents.add_reagent(/datum/reagent/blood/synthetics, 10, data) + wait = TRUE update_icon() var/turf/source_turf = get_turf(src) log_virus("A culture bottle was printed for the virus [A.admin_details()] at [loc_name(source_turf)] by [key_name(usr)]") - + addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 50) . = TRUE if("create_vaccine_bottle") - wait = TRUE - addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 400) + if (wait) + return var/id = params["index"] var/datum/disease/D = SSdisease.archive_diseases[id] var/obj/item/reagent_containers/glass/bottle/B = new(drop_location()) B.name = "[D.name] vaccine bottle" B.reagents.add_reagent(/datum/reagent/vaccine, 15, list(id)) - + wait = TRUE update_icon() - + addtimer(CALLBACK(src, .proc/reset_replicator_cooldown), 200) . = TRUE + /obj/machinery/computer/pandemic/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/reagent_containers) && !(I.item_flags & ABSTRACT) && I.is_open_container()) . = TRUE //no afterattack if(stat & (NOPOWER|BROKEN)) return - var/obj/item/reagent_containers/B = I - if(!user.transferItemToLoc(B, src)) + if(beaker) + to_chat(user, "A container is already loaded into [src]!") return - replace_beaker(user, B) + if(!user.transferItemToLoc(I, src)) + return + + beaker = I to_chat(user, "You insert [I] into [src].") + update_icon() else return ..() -/obj/machinery/computer/pandemic/AltClick(mob/living/user) - . = ..() - if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) - return - replace_beaker(user) - return TRUE - -/obj/machinery/computer/pandemic/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker) - if(beaker) - if(user && Adjacent(user) && user.can_hold_items()) - if(!user.put_in_hands(beaker)) - beaker.forceMove(drop_location()) - if(new_beaker) - beaker = new_beaker - else - beaker = null - update_icon() - return TRUE - /obj/machinery/computer/pandemic/on_deconstruction() - replace_beaker(usr) + eject_beaker() . = ..() diff --git a/code/modules/reagents/chemistry/machinery/smoke_machine.dm b/code/modules/reagents/chemistry/machinery/smoke_machine.dm index cac90d1c14..a539897c9d 100644 --- a/code/modules/reagents/chemistry/machinery/smoke_machine.dm +++ b/code/modules/reagents/chemistry/machinery/smoke_machine.dm @@ -7,6 +7,7 @@ icon_state = "smoke0" density = TRUE circuit = /obj/item/circuitboard/machine/smoke_machine + var/efficiency = 10 var/on = FALSE var/cooldown = 0 @@ -31,6 +32,7 @@ /obj/machinery/smoke_machine/Initialize() . = ..() create_reagents(REAGENTS_BASE_VOLUME) + // AddComponent(/datum/component/plumbing/simple_demand) for(var/obj/item/stock_parts/matter_bin/B in component_parts) reagents.maximum_volume += REAGENTS_BASE_VOLUME * B.rating @@ -81,10 +83,9 @@ add_fingerprint(user) if(istype(I, /obj/item/reagent_containers) && I.is_open_container()) var/obj/item/reagent_containers/RC = I - var/units = RC.reagents.trans_to(src, RC.amount_per_transfer_from_this) + var/units = RC.reagents.trans_to(src, RC.amount_per_transfer_from_this) //, transfered_by = user) if(units) to_chat(user, "You transfer [units] units of the solution to [src].") - log_combat(usr, src, "has added [english_list(RC.reagents.reagent_list)] to [src]") return if(default_unfasten_wrench(user, I, 40)) on = FALSE @@ -100,11 +101,10 @@ reagents.clear_reagents() return ..() -/obj/machinery/smoke_machine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/smoke_machine/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "SmokeMachine", name, 350, 350, master_ui, state) + ui = new(user, src, "SmokeMachine", name) ui.open() /obj/machinery/smoke_machine/ui_data(mob/user) diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm index 103061088e..e1433eb64e 100644 --- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -1044,12 +1044,6 @@ M.emote("nya") if(prob(20)) to_chat(M, "[pick("Headpats feel nice.", "Backrubs would be nice.", "Mew")]") - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/list/adjusted = H.adjust_arousal(5,aphro = TRUE) - for(var/g in adjusted) - var/obj/item/organ/genital/G = g - to_chat(M, "You feel like playing with your [G.name]!") ..() /datum/reagent/consumable/monkey_energy diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index 96f2c04598..44b6e85f47 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -559,128 +559,3 @@ var/mob/living/carbon/C = M if(!C.undergoing_cardiac_arrest()) C.set_heartattack(TRUE) - -//aphrodisiac & anaphrodisiac - -/datum/reagent/drug/aphrodisiac - name = "Crocin" - description = "Naturally found in the crocus and gardenia flowers, this drug acts as a natural and safe aphrodisiac." - taste_description = "strawberries" - color = "#FFADFF"//PINK, rgb(255, 173, 255) - can_synth = FALSE - -/datum/reagent/drug/aphrodisiac/on_mob_life(mob/living/M) - if(M && M.client?.prefs.arousable && !(M.client?.prefs.cit_toggles & NO_APHRO)) - if((prob(min(current_cycle/2,5)))) - M.emote(pick("moan","blush")) - if(prob(min(current_cycle/4,10))) - var/aroused_message = pick("You feel frisky.", "You're having trouble suppressing your urges.", "You feel in the mood.") - to_chat(M, "[aroused_message]") - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/list/genits = H.adjust_arousal(current_cycle, aphro = TRUE) // redundant but should still be here - for(var/g in genits) - var/obj/item/organ/genital/G = g - to_chat(M, "[G.arousal_verb]!") - ..() - -/datum/reagent/drug/aphrodisiacplus - name = "Hexacrocin" - description = "Chemically condensed form of basic crocin. This aphrodisiac is extremely powerful and addictive in most animals.\ - Addiction withdrawals can cause brain damage and shortness of breath. Overdosage can lead to brain damage and a \ - permanent increase in libido (commonly referred to as 'bimbofication')." - taste_description = "liquid desire" - color = "#FF2BFF"//dark pink - addiction_threshold = 20 - overdose_threshold = 20 - can_synth = FALSE - -/datum/reagent/drug/aphrodisiacplus/on_mob_life(mob/living/M) - if(M && M.client?.prefs.arousable && !(M.client?.prefs.cit_toggles & NO_APHRO)) - if(prob(5)) - if(prob(current_cycle)) - M.say(pick("Hnnnnngghh...", "Ohh...", "Mmnnn...")) - else - M.emote(pick("moan","blush")) - if(prob(5)) - var/aroused_message - if(current_cycle>25) - aroused_message = pick("You need to fuck someone!", "You're bursting with sexual tension!", "You can't get sex off your mind!") - else - aroused_message = pick("You feel a bit hot.", "You feel strong sexual urges.", "You feel in the mood.", "You're ready to go down on someone.") - to_chat(M, "[aroused_message]") - REMOVE_TRAIT(M,TRAIT_NEVERBONER,APHRO_TRAIT) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/list/genits = H.adjust_arousal(100, aphro = TRUE) // redundant but should still be here - for(var/g in genits) - var/obj/item/organ/genital/G = g - to_chat(M, "[G.arousal_verb]!") - ..() - -/datum/reagent/drug/aphrodisiacplus/addiction_act_stage2(mob/living/M) - if(prob(30)) - M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2) - ..() -/datum/reagent/drug/aphrodisiacplus/addiction_act_stage3(mob/living/M) - if(prob(30)) - M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3) - - ..() -/datum/reagent/drug/aphrodisiacplus/addiction_act_stage4(mob/living/M) - if(prob(30)) - M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 4) - ..() - -/datum/reagent/drug/aphrodisiacplus/overdose_process(mob/living/M) - if(M && M.client?.prefs.arousable && !(M.client?.prefs.cit_toggles & NO_APHRO) && prob(33)) - if(prob(5) && ishuman(M) && M.has_dna() && (M.client?.prefs.cit_toggles & BIMBOFICATION)) - if(!HAS_TRAIT(M,TRAIT_PERMABONER)) - to_chat(M, "Your libido is going haywire!") - ADD_TRAIT(M,TRAIT_PERMABONER,APHRO_TRAIT) - ..() - -/datum/reagent/drug/anaphrodisiac - name = "Camphor" - description = "Naturally found in some species of evergreen trees, camphor is a waxy substance. When injested by most animals, it acts as an anaphrodisiac\ - , reducing libido and calming them. Non-habit forming and not addictive." - taste_description = "dull bitterness" - taste_mult = 2 - color = "#D9D9D9"//rgb(217, 217, 217) - reagent_state = SOLID - can_synth = FALSE - -/datum/reagent/drug/anaphrodisiac/on_mob_life(mob/living/M) - if(M && M.client?.prefs.arousable && prob(16)) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/list/genits = H.adjust_arousal(-100, aphro = TRUE) - if(genits.len) - to_chat(M, "You no longer feel aroused.") - ..() - -/datum/reagent/drug/anaphrodisiacplus - name = "Hexacamphor" - description = "Chemically condensed camphor. Causes an extreme reduction in libido and a permanent one if overdosed. Non-addictive." - taste_description = "tranquil celibacy" - color = "#D9D9D9"//rgb(217, 217, 217) - reagent_state = SOLID - overdose_threshold = 20 - can_synth = FALSE - -/datum/reagent/drug/anaphrodisiacplus/on_mob_life(mob/living/M) - if(M && M.client?.prefs.arousable) - REMOVE_TRAIT(M,TRAIT_PERMABONER,APHRO_TRAIT) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/list/genits = H.adjust_arousal(-100, aphro = TRUE) - if(genits.len) - to_chat(M, "You no longer feel aroused.") - - ..() - -/datum/reagent/drug/anaphrodisiacplus/overdose_process(mob/living/M) - if(M && M.client?.prefs.arousable && prob(5)) - to_chat(M, "You feel like you'll never feel aroused again...") - ADD_TRAIT(M,TRAIT_NEVERBONER,APHRO_TRAIT) - ..() diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index d449fa310c..a6e78ae98c 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -768,7 +768,6 @@ color = "#97ee63" taste_description = "pure electricity" -/* //We don't have ethereals here, so I'll just comment it out. /datum/reagent/consumable/liquidelectricity/reaction_mob(mob/living/M, method=TOUCH, reac_volume) //can't be on life because of the way blood works. if((method == INGEST || method == INJECT || method == PATCH) && iscarbon(M)) @@ -776,10 +775,9 @@ var/obj/item/organ/stomach/ethereal/stomach = C.getorganslot(ORGAN_SLOT_STOMACH) if(istype(stomach)) stomach.adjust_charge(reac_volume * REM) -*/ /datum/reagent/consumable/liquidelectricity/on_mob_life(mob/living/carbon/M) - if(prob(25)) // && !isethereal(M)) + if(prob(25) && !isethereal(M)) M.electrocute_act(rand(10,15), "Liquid Electricity in their body", 1) //lmao at the newbs who eat energy bars playsound(M, "sparks", 50, TRUE) return ..() diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 4ade7607c6..c88a440260 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -2197,13 +2197,6 @@ M.emote("nya") if(prob(20)) to_chat(M, "[pick("Headpats feel nice.", "The feeling of a hairball...", "Backrubs would be nice.", "Whats behind those doors?")]") - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/list/adjusted = H.adjust_arousal(2,aphro = TRUE) - for(var/g in adjusted) - var/obj/item/organ/genital/G = g - to_chat(M, "You feel like playing with your [G.name]!") - ..() /datum/reagent/preservahyde diff --git a/code/modules/reagents/chemistry/recipes/drugs.dm b/code/modules/reagents/chemistry/recipes/drugs.dm index a2b8c27552..468d29c052 100644 --- a/code/modules/reagents/chemistry/recipes/drugs.dm +++ b/code/modules/reagents/chemistry/recipes/drugs.dm @@ -62,34 +62,3 @@ results = list(/datum/reagent/moonsugar = 1, /datum/reagent/medicine/morphine = 2.5) required_temp = 315 //a little above normal body temperature required_reagents = list(/datum/reagent/drug/skooma = 1) -/datum/chemical_reaction/aphro - name = "crocin" - id = /datum/reagent/drug/aphrodisiac - results = list(/datum/reagent/drug/aphrodisiac = 6) - required_reagents = list(/datum/reagent/carbon = 2, /datum/reagent/hydrogen = 2, /datum/reagent/oxygen = 2, /datum/reagent/water = 1) - required_temp = 400 - mix_message = "The mixture boils off a pink vapor..."//The water boils off, leaving the crocin - -/datum/chemical_reaction/aphroplus - name = "hexacrocin" - id = /datum/reagent/drug/aphrodisiacplus - results = list(/datum/reagent/drug/aphrodisiacplus = 1) - required_reagents = list(/datum/reagent/drug/aphrodisiac = 6, /datum/reagent/phenol = 1) - required_temp = 400 - mix_message = "The mixture rapidly condenses and darkens in color..." - -/datum/chemical_reaction/anaphro - name = "camphor" - id = /datum/reagent/drug/anaphrodisiac - results = list(/datum/reagent/drug/anaphrodisiac = 6) - required_reagents = list(/datum/reagent/carbon = 2, /datum/reagent/hydrogen = 2, /datum/reagent/oxygen = 2, /datum/reagent/sulfur = 1) - required_temp = 400 - mix_message = "The mixture boils off a yellow, smelly vapor..."//Sulfur burns off, leaving the camphor - -/datum/chemical_reaction/anaphroplus - name = "pentacamphor" - id = /datum/reagent/drug/anaphrodisiacplus - results = list(/datum/reagent/drug/anaphrodisiacplus = 1) - required_reagents = list(/datum/reagent/drug/aphrodisiac = 5, /datum/reagent/acetone = 1) - required_temp = 300 - mix_message = "The mixture thickens and heats up slighty..." diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index f32bc708d0..09b7eabbc1 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -684,10 +684,10 @@ required_reagents = list(/datum/reagent/toxin/mindbreaker = 1, /datum/reagent/medicine/synaptizine = 1, /datum/reagent/water = 1) /datum/chemical_reaction/cat - name = "felined mutation toxic" + name = "felinid mutation toxic" id = /datum/reagent/mutationtoxin/felinid results = list(/datum/reagent/mutationtoxin/felinid = 1) - required_reagents = list(/datum/reagent/toxin/mindbreaker = 1, /datum/reagent/ammonia = 1, /datum/reagent/water = 1, /datum/reagent/drug/aphrodisiac = 10, /datum/reagent/mutationtoxin = 1) // Maybe aphro+ if it becomes a shitty meme + required_reagents = list(/datum/reagent/toxin/mindbreaker = 1, /datum/reagent/ammonia = 1, /datum/reagent/water = 1, /datum/reagent/pax/catnip = 1, /datum/reagent/mutationtoxin = 1) required_temp = 450 /datum/chemical_reaction/moff @@ -837,4 +837,4 @@ /datum/chemical_reaction/cellulose_carbonization results = list(/datum/reagent/carbon = 1) required_reagents = list(/datum/reagent/cellulose = 1) - required_temp = 512 \ No newline at end of file + required_temp = 512 diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm index eb4554b7e5..2cd0a76a4b 100644 --- a/code/modules/reagents/reagent_containers/bottle.dm +++ b/code/modules/reagents/reagent_containers/bottle.dm @@ -432,26 +432,4 @@ /obj/item/reagent_containers/glass/bottle/ichor/green name = "green potion" - list_reagents = list(/datum/reagent/green_ichor = 1) - -//Lewd Stuff - -/obj/item/reagent_containers/glass/bottle/crocin - name = "Crocin bottle" - desc = "A bottle of mild aphrodisiac. Increases libido." - list_reagents = list(/datum/reagent/drug/aphrodisiac = 30) - -/obj/item/reagent_containers/glass/bottle/hexacrocin - name = "Hexacrocin bottle" - desc = "A bottle of strong aphrodisiac. Increases libido." - list_reagents = list(/datum/reagent/drug/aphrodisiacplus = 30) - -/obj/item/reagent_containers/glass/bottle/camphor - name = "Camphor bottle" - desc = "A bottle of mild anaphrodisiac. Reduces libido." - list_reagents = list(/datum/reagent/drug/anaphrodisiac = 30) - -/obj/item/reagent_containers/glass/bottle/hexacamphor - name = "Hexacamphor bottle" - desc = "A bottle of strong anaphrodisiac. Reduces libido." - list_reagents = list(/datum/reagent/drug/anaphrodisiacplus = 30) + list_reagents = list(/datum/reagent/green_ichor = 1) \ No newline at end of file diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 819012a61a..79edb37913 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -222,19 +222,6 @@ icon_state = "orangekeg" reagent_id = /datum/reagent/consumable/ethanol/mead -/obj/structure/reagent_dispensers/keg/aphro - name = "keg of aphrodisiac" - desc = "A keg of aphrodisiac." - icon_state = "pinkkeg" - reagent_id = /datum/reagent/drug/aphrodisiac - tank_volume = 150 - -/obj/structure/reagent_dispensers/keg/aphro/strong - name = "keg of strong aphrodisiac" - desc = "A keg of strong and addictive aphrodisiac." - reagent_id = /datum/reagent/drug/aphrodisiacplus - tank_volume = 120 - /obj/structure/reagent_dispensers/keg/milk name = "keg of milk" desc = "A keg of pasteurised, homogenised, filtered and semi-skimmed space milk." diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm index ba4870903e..c8da9ab5e3 100644 --- a/code/modules/recycling/disposal/bin.dm +++ b/code/modules/recycling/disposal/bin.dm @@ -299,13 +299,15 @@ // handle machine interaction -/obj/machinery/disposal/bin/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state) +/obj/machinery/disposal/bin/ui_state(mob/user) + return GLOB.notcontained_state + +/obj/machinery/disposal/bin/ui_interact(mob/user, datum/tgui/ui) if(stat & BROKEN) return - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "DisposalUnit", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "DisposalUnit", name) ui.open() /obj/machinery/disposal/bin/ui_data(mob/user) diff --git a/code/modules/research/anomaly/anomaly_core.dm b/code/modules/research/anomaly/anomaly_core.dm new file mode 100644 index 0000000000..7aeb7b3a9b --- /dev/null +++ b/code/modules/research/anomaly/anomaly_core.dm @@ -0,0 +1,63 @@ +// Embedded signaller used in anomalies. +/obj/item/assembly/signaler/anomaly + name = "anomaly core" + desc = "The neutralized core of an anomaly. It'd probably be valuable for research." + icon_state = "anomaly_core" + //inhand_icon_state = "electronic" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + resistance_flags = FIRE_PROOF + var/anomaly_type = /obj/effect/anomaly + +/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal) + if(!signal) + return FALSE + if(signal.data["code"] != code) + return FALSE + if(suicider) + manual_suicide(suicider) + for(var/obj/effect/anomaly/A in get_turf(src)) + A.anomalyNeutralize() + return TRUE + +/obj/item/assembly/signaler/anomaly/manual_suicide(mob/living/carbon/user) + user.visible_message("[user]'s [src] is reacting to the radio signal, warping [user.p_their()] body!") + //user.set_suicide(TRUE) + user.suicide_log() + user.gib() + +/obj/item/assembly/signaler/anomaly/attackby(obj/item/I, mob/user, params) + if(I.tool_behaviour == TOOL_ANALYZER) + to_chat(user, "Analyzing... [src]'s stabilized field is fluctuating along frequency [format_frequency(frequency)], code [code].") + return ..() + +//Anomaly cores +/obj/item/assembly/signaler/anomaly/pyro + name = "\improper pyroclastic anomaly core" + desc = "The neutralized core of a pyroclastic anomaly. It feels warm to the touch. It'd probably be valuable for research." + icon_state = "pyro_core" + anomaly_type = /obj/effect/anomaly/pyro + +/obj/item/assembly/signaler/anomaly/grav + name = "\improper gravitational anomaly core" + desc = "The neutralized core of a gravitational anomaly. It feels much heavier than it looks. It'd probably be valuable for research." + icon_state = "grav_core" + anomaly_type = /obj/effect/anomaly/grav + +/obj/item/assembly/signaler/anomaly/flux + name = "\improper flux anomaly core" + desc = "The neutralized core of a flux anomaly. Touching it makes your skin tingle. It'd probably be valuable for research." + icon_state = "flux_core" + anomaly_type = /obj/effect/anomaly/flux + +/obj/item/assembly/signaler/anomaly/bluespace + name = "\improper bluespace anomaly core" + desc = "The neutralized core of a bluespace anomaly. It keeps phasing in and out of view. It'd probably be valuable for research." + icon_state = "anomaly_core" + anomaly_type = /obj/effect/anomaly/bluespace + +/obj/item/assembly/signaler/anomaly/vortex + name = "\improper vortex anomaly core" + desc = "The neutralized core of a vortex anomaly. It won't sit still, as if some invisible force is acting on it. It'd probably be valuable for research." + icon_state = "vortex_core" + anomaly_type = /obj/effect/anomaly/bhole diff --git a/code/modules/research/bepis.dm b/code/modules/research/bepis.dm index 87aec72646..7b36a614a7 100644 --- a/code/modules/research/bepis.dm +++ b/code/modules/research/bepis.dm @@ -33,11 +33,13 @@ var/inaccuracy_percentage = 1.5 var/positive_cash_offset = 0 var/negative_cash_offset = 0 - var/minor_rewards = list(/obj/item/stack/circuit_stack/full, //To add a new minor reward, add it here. - /obj/item/flashlight/flashdark, - /obj/item/pen/survival, - /obj/item/circuitboard/machine/sleeper/party, - /obj/item/toy/sprayoncan) + var/list/minor_rewards = list( + //To add a new minor reward, add it here. + /obj/item/stack/circuit_stack/full, + /obj/item/pen/survival, + /obj/item/circuitboard/machine/sleeper/party, + /obj/item/toy/sprayoncan, + ) var/static/list/item_list = list() /obj/machinery/rnd/bepis/attackby(obj/item/O, mob/user, params) @@ -101,6 +103,7 @@ return account.adjust_money(-deposit_value) //The money vanishes, not paid to any accounts. SSblackbox.record_feedback("amount", "BEPIS_credits_spent", deposit_value) + //log_econ("[deposit_value] credits were inserted into [src] by [account.account_holder]") banked_cash += deposit_value use_power(1000 * power_saver) say("Cash deposit successful. There is [banked_cash] in the chamber.") @@ -179,10 +182,10 @@ icon_state = "chamber" return -/obj/machinery/rnd/bepis/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/rnd/bepis/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "Bepis", name, 500, 480, master_ui, state) + ui = new(user, src, "Bepis", name) ui.open() RefreshParts() diff --git a/code/modules/research/nanites/nanite_chamber_computer.dm b/code/modules/research/nanites/nanite_chamber_computer.dm index b9623b751d..70e4d05590 100644 --- a/code/modules/research/nanites/nanite_chamber_computer.dm +++ b/code/modules/research/nanites/nanite_chamber_computer.dm @@ -5,8 +5,6 @@ var/obj/item/disk/nanite_program/disk icon_screen = "nanite_chamber_control" circuit = /obj/item/circuitboard/computer/nanite_chamber_control - ui_x = 380 - ui_y = 570 /obj/machinery/computer/nanite_chamber_control/Initialize() . = ..() @@ -25,10 +23,10 @@ find_chamber() ..() -/obj/machinery/computer/nanite_chamber_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/nanite_chamber_control/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "NaniteChamberControl", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "NaniteChamberControl", name) ui.open() /obj/machinery/computer/nanite_chamber_control/ui_data() diff --git a/code/modules/research/nanites/nanite_cloud_controller.dm b/code/modules/research/nanites/nanite_cloud_controller.dm index ef0fc33bd7..1a0a5dbd4d 100644 --- a/code/modules/research/nanites/nanite_cloud_controller.dm +++ b/code/modules/research/nanites/nanite_cloud_controller.dm @@ -1,11 +1,9 @@ /obj/machinery/computer/nanite_cloud_controller name = "nanite cloud controller" desc = "Stores and controls nanite cloud backups." - circuit = /obj/item/circuitboard/computer/nanite_cloud_controller icon = 'icons/obj/machines/research.dmi' icon_state = "nanite_cloud_controller" - ui_x = 375 - ui_y = 700 + circuit = /obj/item/circuitboard/computer/nanite_cloud_controller var/obj/item/disk/nanite_program/disk var/list/datum/nanite_cloud_backup/cloud_backups = list() @@ -20,19 +18,25 @@ /obj/machinery/computer/nanite_cloud_controller/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/disk/nanite_program)) var/obj/item/disk/nanite_program/N = I - if(disk) - eject(user) - if(user.transferItemToLoc(N, src)) + if (user.transferItemToLoc(N, src)) to_chat(user, "You insert [N] into [src].") playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + if(disk) + eject(user) disk = N else ..() +/obj/machinery/computer/nanite_cloud_controller/AltClick(mob/user) + if(disk && user.canUseTopic(src, !issilicon(user))) + to_chat(user, "You take out [disk] from [src].") + eject(user) + return + /obj/machinery/computer/nanite_cloud_controller/proc/eject(mob/living/user) if(!disk) return - if(!istype(user) || !Adjacent(user) ||!user.put_in_active_hand(disk)) + if(!istype(user) || !Adjacent(user))// ||!user.put_in_active_hand(disk)) disk.forceMove(drop_location()) disk = null @@ -53,10 +57,10 @@ backup.nanites = cloud_copy investigate_log("[key_name(user)] created a new nanite cloud backup with id #[cloud_id]", INVESTIGATE_NANITES) -/obj/machinery/computer/nanite_cloud_controller/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/nanite_cloud_controller/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "NaniteCloudControl", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "NaniteCloudControl", name) ui.open() /obj/machinery/computer/nanite_cloud_controller/ui_data() diff --git a/code/modules/research/nanites/nanite_program_hub.dm b/code/modules/research/nanites/nanite_program_hub.dm index 3f62a11f89..5a330f7a4f 100644 --- a/code/modules/research/nanites/nanite_program_hub.dm +++ b/code/modules/research/nanites/nanite_program_hub.dm @@ -3,26 +3,24 @@ desc = "Compiles nanite programs from the techweb servers and downloads them into disks." icon = 'icons/obj/machines/research.dmi' icon_state = "nanite_program_hub" - circuit = /obj/item/circuitboard/machine/nanite_program_hub use_power = IDLE_POWER_USE anchored = TRUE density = TRUE - ui_x = 500 - ui_y = 700 + circuit = /obj/item/circuitboard/machine/nanite_program_hub var/obj/item/disk/nanite_program/disk var/datum/techweb/linked_techweb var/current_category = "Main" var/detail_view = TRUE var/categories = list( - list(name = "Utility Nanites"), - list(name = "Medical Nanites"), - list(name = "Sensor Nanites"), - list(name = "Augmentation Nanites"), - list(name = "Suppression Nanites"), - list(name = "Weaponized Nanites"), - list(name = "Protocols") //Moved to default techweb from B.E.P.I.S. research, for now - ) + list(name = "Utility Nanites"), + list(name = "Medical Nanites"), + list(name = "Sensor Nanites"), + list(name = "Augmentation Nanites"), + list(name = "Suppression Nanites"), + list(name = "Weaponized Nanites"), + list(name = "Protocols"), + ) /obj/machinery/nanite_program_hub/Initialize() . = ..() @@ -31,26 +29,44 @@ /obj/machinery/nanite_program_hub/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/disk/nanite_program)) var/obj/item/disk/nanite_program/N = I - if(disk) - eject(user) if(user.transferItemToLoc(N, src)) to_chat(user, "You insert [N] into [src].") playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + if(disk) + eject(user) disk = N else ..() +/obj/machinery/nanite_program_hub/screwdriver_act(mob/living/user, obj/item/I) //remove when runtimed + if(..()) + return TRUE + + return default_deconstruction_screwdriver(user, "nanite_program_hub_t", "nanite_program_hub", I) + +/obj/machinery/nanite_program_hub/crowbar_act(mob/living/user, obj/item/I) + if(..()) + return TRUE + + return default_deconstruction_crowbar(I) + /obj/machinery/nanite_program_hub/proc/eject(mob/living/user) if(!disk) return - if(!istype(user) || !Adjacent(user) || !user.put_in_active_hand(disk)) + if(!istype(user) || !Adjacent(user))// || !user.put_in_active_hand(disk)) disk.forceMove(drop_location()) disk = null -/obj/machinery/nanite_program_hub/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/nanite_program_hub/AltClick(mob/user) + if(disk && user.canUseTopic(src, !issilicon(user))) + to_chat(user, "You take out [disk] from [src].") + eject(user) + return + +/obj/machinery/nanite_program_hub/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "NaniteProgramHub", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "NaniteProgramHub", name) ui.open() /obj/machinery/nanite_program_hub/ui_data() @@ -123,9 +139,3 @@ disk.program = null disk.name = initial(disk.name) . = TRUE - - -/obj/machinery/nanite_program_hub/admin/Initialize() - . = ..() - linked_techweb = SSresearch.admin_tech - diff --git a/code/modules/research/nanites/nanite_programmer.dm b/code/modules/research/nanites/nanite_programmer.dm index 3de2a974e2..804f256cf6 100644 --- a/code/modules/research/nanites/nanite_programmer.dm +++ b/code/modules/research/nanites/nanite_programmer.dm @@ -3,41 +3,57 @@ desc = "A device that can edit nanite program disks to adjust their functionality." var/obj/item/disk/nanite_program/disk var/datum/nanite_program/program - circuit = /obj/item/circuitboard/machine/nanite_programmer icon = 'icons/obj/machines/research.dmi' icon_state = "nanite_programmer" use_power = IDLE_POWER_USE anchored = TRUE density = TRUE flags_1 = HEAR_1 - ui_x = 420 - ui_y = 550 + circuit = /obj/item/circuitboard/machine/nanite_programmer /obj/machinery/nanite_programmer/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/disk/nanite_program)) var/obj/item/disk/nanite_program/N = I - if(disk) - eject(user) if(user.transferItemToLoc(N, src)) to_chat(user, "You insert [N] into [src]") playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) + if(disk) + eject(user) disk = N program = N.program else ..() +/obj/machinery/nanite_programmer/screwdriver_act(mob/living/user, obj/item/I) + if(..()) + return TRUE + + return default_deconstruction_screwdriver(user, "nanite_programmer_t", "nanite_programmer", I) + +/obj/machinery/nanite_programmer/crowbar_act(mob/living/user, obj/item/I) + if(..()) + return TRUE + + return default_deconstruction_crowbar(I) + /obj/machinery/nanite_programmer/proc/eject(mob/living/user) if(!disk) return - if(!istype(user) || !Adjacent(user) || !user.put_in_active_hand(disk)) + if(!istype(user) || !Adjacent(user))// || !user.put_in_active_hand(disk)) disk.forceMove(drop_location()) disk = null program = null -/obj/machinery/nanite_programmer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/nanite_programmer/AltClick(mob/user) + if(disk && user.canUseTopic(src, !issilicon(user))) + to_chat(user, "You take out [disk] from [src].") + eject(user) + return + +/obj/machinery/nanite_programmer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "NaniteProgrammer", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "NaniteProgrammer", name) ui.open() /obj/machinery/nanite_programmer/ui_data() @@ -131,7 +147,7 @@ program.timer_trigger_delay = timer . = TRUE -/obj/machinery/nanite_programmer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source) +/obj/machinery/nanite_programmer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list()) . = ..() var/static/regex/when = regex("(?:^\\W*when|when\\W*$)", "i") //starts or ends with when if(findtext(raw_message, when) && !istype(speaker, /obj/machinery/nanite_programmer)) diff --git a/code/modules/research/nanites/nanite_remote.dm b/code/modules/research/nanites/nanite_remote.dm index b222b2ad35..e3f5a0f286 100644 --- a/code/modules/research/nanites/nanite_remote.dm +++ b/code/modules/research/nanites/nanite_remote.dm @@ -80,10 +80,13 @@ var/datum/nanite_program/relay/N = X N.relay_signal(code, relay_code, source) -/obj/item/nanite_remote/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/nanite_remote/ui_state(mob/user) + return GLOB.hands_state + +/obj/item/nanite_remote/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "NaniteRemote", name, 420, 500, master_ui, state) + ui = new(user, src, "NaniteRemote", name) ui.open() /obj/item/nanite_remote/ui_data() @@ -94,7 +97,6 @@ data["locked"] = locked data["saved_settings"] = saved_settings data["program_name"] = current_program_name - return data /obj/item/nanite_remote/ui_act(action, params) diff --git a/code/modules/ruins/spaceruin_code/TheDerelict.dm b/code/modules/ruins/spaceruin_code/TheDerelict.dm index 513d16a0cb..d26e023df0 100644 --- a/code/modules/ruins/spaceruin_code/TheDerelict.dm +++ b/code/modules/ruins/spaceruin_code/TheDerelict.dm @@ -17,7 +17,6 @@ desc = "Looks like someone started shakily writing a will in space common, but were interrupted by something bloody..." info = "__Objectives #1__: Find out what is hidden in Kosmicheskaya Stantsiya 13s Vault" - /// Vault controller for use on the derelict/KS13. /obj/machinery/computer/vaultcontroller name = "vault controller" @@ -35,15 +34,10 @@ var/siphoned_power = 0 var/siphon_max = 1e7 - ui_x = 300 - ui_y = 120 - - /obj/machinery/computer/monitor/examine(mob/user) . = ..() . += "It appears to be powered via a cable connector." - //Checks for cable connection, charges if possible. /obj/machinery/computer/vaultcontroller/process() if(siphoned_power >= siphon_max) @@ -52,13 +46,11 @@ if(attached_cable) attempt_siphon() - ///Looks for a cable connection beneath the machine. /obj/machinery/computer/vaultcontroller/proc/update_cable() var/turf/T = get_turf(src) attached_cable = locate(/obj/structure/cable) in T - ///Initializes airlock links. /obj/machinery/computer/vaultcontroller/proc/find_airlocks() for(var/obj/machinery/door/airlock/A in GLOB.airlocks) @@ -70,7 +62,6 @@ door2 = A break - ///Tries to charge from powernet excess, no upper limit except max charge. /obj/machinery/computer/vaultcontroller/proc/attempt_siphon() var/surpluspower = clamp(attached_cable.surplus(), 0, (siphon_max - siphoned_power)) @@ -78,7 +69,6 @@ attached_cable.add_load(surpluspower) siphoned_power += surpluspower - ///Handles the doors closing /obj/machinery/computer/vaultcontroller/proc/cycle_close(obj/machinery/door/airlock/A) A.safe = FALSE //Make sure its forced closed, always @@ -86,14 +76,12 @@ A.close() A.bolt() - ///Handles the doors opening /obj/machinery/computer/vaultcontroller/proc/cycle_open(obj/machinery/door/airlock/A) A.unbolt() A.open() A.bolt() - ///Attempts to lock the vault doors /obj/machinery/computer/vaultcontroller/proc/lock_vault() if(door1 && !door1.density) @@ -103,7 +91,6 @@ if(door1.density && door1.locked && door2.density && door2.locked) locked = TRUE - ///Attempts to unlock the vault doors /obj/machinery/computer/vaultcontroller/proc/unlock_vault() if(door1 && door1.density) @@ -113,7 +100,6 @@ if(!door1.density && door1.locked && !door2.density && door2.locked) locked = FALSE - ///Attempts to lock/unlock vault doors, if machine is charged. /obj/machinery/computer/vaultcontroller/proc/activate_lock() if(siphoned_power < siphon_max) @@ -125,15 +111,12 @@ else lock_vault() - -/obj/machinery/computer/vaultcontroller/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/vaultcontroller/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "VaultController", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "VaultController", name) ui.open() - /obj/machinery/computer/vaultcontroller/ui_act(action, params) if(..()) return @@ -141,7 +124,6 @@ if("togglelock") activate_lock() - /obj/machinery/computer/vaultcontroller/ui_data() var/list/data = list() data["stored"] = siphoned_power @@ -149,7 +131,6 @@ data["doorstatus"] = locked return data - ///Airlock that can't be deconstructed, broken or hacked. /obj/machinery/door/airlock/vault/derelict locked = TRUE @@ -158,16 +139,13 @@ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF id_tag = "derelictvault" - ///Overrides screwdriver attack to prevent all deconstruction and hacking. /obj/machinery/door/airlock/vault/derelict/attackby(obj/item/C, mob/user, params) if(C.tool_behaviour == TOOL_SCREWDRIVER) return ..() - // So drones can teach borgs and AI dronespeak. For best effect, combine with mother drone lawset. - /obj/item/dronespeak_manual name = "dronespeak manual" desc = "The book's cover reads: \"Understanding Dronespeak - An exercise in futility.\"" @@ -181,7 +159,7 @@ to_chat(user, "You start skimming through [src], but you already know dronespeak.") else to_chat(user, "You start skimming through [src], and suddenly the drone chittering makes sense.") - user.grant_language(/datum/language/drone, TRUE, TRUE) + user.grant_language(/datum/language/drone, TRUE, TRUE)//, LANGUAGE_MIND) return if(user.has_language(/datum/language/drone)) @@ -202,7 +180,7 @@ M.visible_message("[user] beats [M] over the head with [src]!", "[user] beats you over the head with [src]!", "You hear smacking.") else M.visible_message("[user] teaches [M] by beating [M.p_them()] over the head with [src]!", "As [user] hits you with [src], chitters resonate in your mind.", "You hear smacking.") - M.grant_language(/datum/language/drone, TRUE, TRUE) + M.grant_language(/datum/language/drone, TRUE, TRUE) //, LANGUAGE_MIND) return /obj/structure/fluff/oldturret diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm index f6418b9236..7326cad816 100644 --- a/code/modules/security_levels/keycard_authentication.dm +++ b/code/modules/security_levels/keycard_authentication.dm @@ -35,11 +35,13 @@ GLOBAL_DATUM_INIT(keycard_events, /datum/events, new) QDEL_NULL(ev) return ..() -/obj/machinery/keycard_auth/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/keycard_auth/ui_state(mob/user) + return GLOB.physical_state + +/obj/machinery/keycard_auth/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "KeycardAuth", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "KeycardAuth", name) ui.open() /obj/machinery/keycard_auth/ui_data() diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index 637d9ae334..810cadcd2c 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -45,11 +45,14 @@ say("Please equip your ID card into your ID slot to authenticate.") . = ..() -/obj/machinery/computer/emergency_shuttle/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.human_adjacent_state) +/obj/machinery/computer/emergency_shuttle/ui_state(mob/user) + return GLOB.human_adjacent_state - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/emergency_shuttle/ui_interact(mob/user, datum/tgui/ui) + + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "EmergencyShuttleConsole", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "EmergencyShuttleConsole", name) ui.open() /obj/machinery/computer/emergency_shuttle/ui_data() @@ -65,8 +68,8 @@ var/job = ID.assignment if(obj_flags & EMAGGED) - name = Gibberish(name, 0) - job = Gibberish(job, 0) + name = Gibberish(name) + job = Gibberish(job) A += list(list("name" = name, "job" = job)) data["authorizations"] = A diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm index 2ca0e65477..4c3f30a786 100644 --- a/code/modules/station_goals/bsa.dm +++ b/code/modules/station_goals/bsa.dm @@ -210,20 +210,23 @@ /obj/machinery/computer/bsa_control name = "bluespace artillery control" - var/obj/machinery/bsa/full/cannon - var/notice - var/target use_power = NO_POWER_USE circuit = /obj/item/circuitboard/computer/bsa_control icon = 'icons/obj/machines/particle_accelerator.dmi' icon_state = "control_boxp" + + var/obj/machinery/bsa/full/cannon + var/notice + var/target var/area_aim = FALSE //should also show areas for targeting -/obj/machinery/computer/bsa_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/bsa_control/ui_state(mob/user) + return GLOB.physical_state + +/obj/machinery/computer/bsa_control/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "BluespaceArtillery", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "BluespaceArtillery", name) ui.open() /obj/machinery/computer/bsa_control/ui_data() @@ -255,7 +258,7 @@ if(!GLOB.bsa_unlock) return var/list/gps_locators = list() - for(var/obj/item/gps/G in GLOB.GPS_list) //nulls on the list somehow + for(var/datum/component/gps/G in GLOB.GPS_list) //nulls on the list somehow if(G.tracking) gps_locators[G.gpstag] = G diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm index 6d8ab9cc7f..4ac3777a41 100644 --- a/code/modules/station_goals/dna_vault.dm +++ b/code/modules/station_goals/dna_vault.dm @@ -174,14 +174,13 @@ . = ..() -/obj/machinery/dna_vault/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/dna_vault/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) roll_powers(user) - ui = new(user, src, ui_key, "DnaVault", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "DnaVault", name) ui.open() - /obj/machinery/dna_vault/proc/roll_powers(mob/user) if(user in power_lottery) return diff --git a/code/modules/station_goals/shield.dm b/code/modules/station_goals/shield.dm index 299fda4a26..c8fbda8988 100644 --- a/code/modules/station_goals/shield.dm +++ b/code/modules/station_goals/shield.dm @@ -42,10 +42,10 @@ circuit = /obj/item/circuitboard/computer/sat_control var/notice -/obj/machinery/computer/sat_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/sat_control/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "SatelliteControl", name, ui_x, ui_y, master_ui, state) + ui = new(user, src, "SatelliteControl", name) ui.open() /obj/machinery/computer/sat_control/ui_act(action, params) diff --git a/code/modules/surgery/organs/stomach.dm b/code/modules/surgery/organs/stomach.dm index d9cbf9be03..44b4f6362a 100755 --- a/code/modules/surgery/organs/stomach.dm +++ b/code/modules/surgery/organs/stomach.dm @@ -91,3 +91,36 @@ /obj/item/organ/stomach/ipc name = "ipc stomach" icon_state = "stomach-ipc" + + +/obj/item/organ/stomach/ethereal + name = "biological battery" + icon_state = "stomach-p" //Welp. At least it's more unique in functionaliy. + desc = "A crystal-like organ that stores the electric charge of ethereals." + var/crystal_charge = ETHEREAL_CHARGE_FULL + +/obj/item/organ/stomach/ethereal/on_life() + ..() + adjust_charge(-ETHEREAL_CHARGE_FACTOR) + +/obj/item/organ/stomach/ethereal/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE) + ..() + RegisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, .proc/charge) + RegisterSignal(owner, COMSIG_LIVING_ELECTROCUTE_ACT, .proc/on_electrocute) + +/obj/item/organ/stomach/ethereal/Remove(mob/living/carbon/M, special = 0) + UnregisterSignal(owner, COMSIG_PROCESS_BORGCHARGER_OCCUPANT) + UnregisterSignal(owner, COMSIG_LIVING_ELECTROCUTE_ACT) + ..() + +/obj/item/organ/stomach/ethereal/proc/charge(datum/source, amount, repairs) + adjust_charge(amount / 70) + +/obj/item/organ/stomach/ethereal/proc/on_electrocute(datum/source, shock_damage, siemens_coeff = 1, flags = NONE) + if(flags & SHOCK_ILLUSION) + return + adjust_charge(shock_damage * siemens_coeff * 2) + to_chat(owner, "You absorb some of the shock into your body!") + +/obj/item/organ/stomach/ethereal/proc/adjust_charge(amount) + crystal_charge = clamp(crystal_charge + amount, ETHEREAL_CHARGE_NONE, ETHEREAL_CHARGE_DANGEROUS) diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index 1c4a2d3043..7090ab62e2 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -312,3 +312,26 @@ desc = "A voice synthesizer used by IPCs to smoothly interface with organic lifeforms." electronics_magic = FALSE organ_flags = ORGAN_SYNTHETIC + +/obj/item/organ/tongue/ethereal + name = "electric discharger" + desc = "A sophisticated ethereal organ, capable of synthesising speech via electrical discharge." + icon_state = "electrotongue" + say_mod = "crackles" + attack_verb = list("shocked", "jolted", "zapped") + taste_sensitivity = 101 // Not a tongue, they can't taste shit + var/static/list/languages_possible_ethereal = typecacheof(list( + /datum/language/common, + /datum/language/draconic, + /datum/language/codespeak, + /datum/language/monkey, + /datum/language/narsie, + /datum/language/beachbum, + /datum/language/aphasia, + /datum/language/sylvan, + /datum/language/voltaic + )) + +/obj/item/organ/tongue/ethereal/Initialize(mapload) + . = ..() + languages_possible = languages_possible_ethereal diff --git a/code/modules/tgui/external.dm b/code/modules/tgui/external.dm index 5de54c439c..46b324e151 100644 --- a/code/modules/tgui/external.dm +++ b/code/modules/tgui/external.dm @@ -1,7 +1,8 @@ /** - * tgui external + * External tgui definitions, such as src_object APIs. * - * Contains all external tgui declarations. + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ /** @@ -11,13 +12,9 @@ * If this proc is not implemented properly, the UI will not update correctly. * * required user mob The mob who opened/is using the UI. - * optional ui_key string The ui_key of the UI. * optional ui datum/tgui The UI to be updated, if it exists. - * optional force_open bool If the UI should be re-opened instead of updated. - * optional master_ui datum/tgui The parent UI. - * optional state datum/ui_state The state used to determine status. */ -/datum/proc/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) +/datum/proc/ui_interact(mob/user, datum/tgui/ui) return FALSE // Not implemented. /** @@ -37,10 +34,11 @@ * public * * Static Data to be sent to the UI. - * Static data differs from normal data in that it's large data that should be sent infrequently - * This is implemented optionally for heavy uis that would be sending a lot of redundant data - * frequently. - * Gets squished into one object on the frontend side, but the static part is cached. + * + * Static data differs from normal data in that it's large data that should be + * sent infrequently. This is implemented optionally for heavy uis that would + * be sending a lot of redundant data frequently. Gets squished into one + * object on the frontend side, but the static part is cached. * * required user mob The mob interacting with the UI. * @@ -52,18 +50,17 @@ /** * public * - * Forces an update on static data. Should be done manually whenever something happens to change static data. + * Forces an update on static data. Should be done manually whenever something + * happens to change static data. * * required user the mob currently interacting with the ui * optional ui ui to be updated - * optional ui_key ui key of ui to be updated */ -/datum/proc/update_static_data(mob/user, datum/tgui/ui, ui_key = "main") - ui = SStgui.try_update_ui(user, src, ui_key, ui) - // If there was no ui to update, there's no static data to update either. +/datum/proc/update_static_data(mob/user, datum/tgui/ui) if(!ui) - return - ui.push_data(null, ui_static_data(), TRUE) + ui = SStgui.get_open_ui(user, src) + if(ui) + ui.send_full_update() /** * public @@ -85,17 +82,12 @@ * public * * Called on an object when a tgui object is being created, allowing you to - * customise the html - * For example: inserting a custom stylesheet that you need in the head + * push various assets to tgui, for examples spritesheets. * - * For this purpose, some tags are available in the html, to be parsed out - ^ with replacetext - * (customheadhtml) - Additions to the head tag - * - * required html the html base text + * return list List of asset datums or file paths. */ -/datum/proc/ui_base_html(html) - return html +/datum/proc/ui_assets(mob/user) + return list() /** * private @@ -107,6 +99,15 @@ /datum/proc/ui_host(mob/user) return src // Default src. +/** + * private + * + * The UI's state controller to be used for created uis + * This is a proc over a var for memory reasons + */ +/datum/proc/ui_state(mob/user) + return GLOB.default_state + /** * global * @@ -118,9 +119,17 @@ /** * global * - * Used to track UIs for a mob. + * Tracks open UIs for a user. */ -/mob/var/list/open_uis = list() +/mob/var/list/tgui_open_uis = list() + +/** + * global + * + * Tracks open windows for a user. + */ +/client/var/list/tgui_windows = list() + /** * public * @@ -137,17 +146,43 @@ * * required uiref ref The UI that was closed. */ -/client/verb/uiclose(ref as text) +/client/verb/uiclose(window_id as text) // Name the verb, and hide it from the user panel. set name = "uiclose" - set hidden = 1 + set hidden = TRUE + var/mob/user = src && src.mob + if(!user) + return + // Close all tgui datums based on window_id. + SStgui.force_close_window(user, window_id) - // Get the UI based on the ref. - var/datum/tgui/ui = locate(ref) - - // If we found the UI, close it. - if(istype(ui)) - ui.close() - // Unset machine just to be sure. - if(src && src.mob) - src.mob.unset_machine() +/** + * Middleware for /client/Topic. + * + * return bool Whether the topic is passed (TRUE), or cancelled (FALSE). + */ +/proc/tgui_Topic(href_list) + // Skip non-tgui topics + if(!href_list["tgui"]) + return TRUE + var/type = href_list["type"] + // Unconditionally collect tgui logs + if(type == "log") + log_tgui(usr, href_list["message"]) + // Locate window + var/window_id = href_list["window_id"] + var/datum/tgui_window/window + if(window_id) + window = usr.client.tgui_windows[window_id] + if(!window) + log_tgui(usr, "Error: Couldn't find the window datum, force closing.") + SStgui.force_close_window(usr, window_id) + return FALSE + // Decode payload + var/payload + if(href_list["payload"]) + payload = json_decode(href_list["payload"]) + // Pass message to window + if(window) + window.on_message(type, payload, href_list) + return FALSE diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm index e626b98815..fa88cc1338 100644 --- a/code/modules/tgui/states.dm +++ b/code/modules/tgui/states.dm @@ -1,7 +1,9 @@ /** - * tgui states + * Base state and helpers for states. Just does some sanity checks, + * implement a proper state for in-depth checks. * - * Base state and helpers for states. Just does some sanity checks, implement a state for in-depth checks. + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ /** @@ -26,9 +28,10 @@ . = max(., UI_INTERACTIVE) // Regular ghosts can always at least view if in range. - var/clientviewlist = getviewsize(user.client.view) - if(get_dist(src_object, user) < max(clientviewlist[1],clientviewlist[2])) - . = max(., UI_UPDATE) + if(user.client) + var/clientviewlist = getviewsize(user.client.view) + if(get_dist(src_object, user) < max(clientviewlist[1], clientviewlist[2])) + . = max(., UI_UPDATE) // Check if the state allows interaction var/result = state.can_use_topic(src_object, user) @@ -46,7 +49,8 @@ * return UI_state The state of the UI. */ /datum/ui_state/proc/can_use_topic(src_object, mob/user) - return UI_CLOSE // Don't allow interaction by default. + // Don't allow interaction by default. + return UI_CLOSE /** * public @@ -56,21 +60,31 @@ * return UI_state The state of the UI. */ /mob/proc/shared_ui_interaction(src_object) - if(!client) // Close UIs if mindless. + // Close UIs if mindless. + if(!client) return UI_CLOSE - else if(stat) // Disable UIs if unconcious. + // Disable UIs if unconcious. + else if(stat) return UI_DISABLED - else if(incapacitated() || lying) // Update UIs if incapicitated but concious. + // Update UIs if incapicitated but concious. + else if(incapacitated()) return UI_UPDATE return UI_INTERACTIVE +/mob/living/shared_ui_interaction(src_object) + . = ..() + if(!(mobility_flags & MOBILITY_UI) && . == UI_INTERACTIVE) + return UI_UPDATE + /mob/living/silicon/ai/shared_ui_interaction(src_object) - if(lacks_power()) // Disable UIs if the AI is unpowered. + // Disable UIs if the AI is unpowered. + if(lacks_power()) return UI_DISABLED return ..() /mob/living/silicon/robot/shared_ui_interaction(src_object) - if(!cell || cell.charge <= 0 || locked_down) // Disable UIs if the Borg is unpowered or locked. + // Disable UIs if the Borg is unpowered or locked. + if(!cell || cell.charge <= 0 || locked_down) return UI_DISABLED return ..() @@ -87,7 +101,8 @@ * return UI_state The state of the UI. */ /atom/proc/contents_ui_distance(src_object, mob/living/user) - return user.shared_living_ui_distance(src_object) // Just call this mob's check. + // Just call this mob's check. + return user.shared_living_ui_distance(src_object) /** * public @@ -99,17 +114,21 @@ * return UI_state The state of the UI. */ /mob/living/proc/shared_living_ui_distance(atom/movable/src_object, viewcheck = TRUE) - if(viewcheck && !(src_object in view(src))) // If the object is obscured, close it. + // If the object is obscured, close it. + if(viewcheck && !(src_object in view(src))) return UI_CLOSE - var/dist = get_dist(src_object, src) - if(dist <= 1 || src_object.hasSiliconAccessInArea(src)) // Open and interact if 1-0 tiles away. + // Open and interact if 1-0 tiles away. + if(dist <= 1) return UI_INTERACTIVE - else if(dist <= 2) // View only if 2-3 tiles away. + // View only if 2-3 tiles away. + else if(dist <= 2) return UI_UPDATE - else if(dist <= 5) // Disable if 5 tiles away. + // Disable if 5 tiles away. + else if(dist <= 5) return UI_DISABLED - return UI_CLOSE // Otherwise, we got nothing. + // Otherwise, we got nothing. + return UI_CLOSE /mob/living/carbon/human/shared_living_ui_distance(atom/movable/src_object, viewcheck = TRUE) if(dna.check_mutation(TK) && tkMaxRangeCheck(src, src_object)) diff --git a/code/modules/tgui/states/admin.dm b/code/modules/tgui/states/admin.dm index 61fc373118..227a294078 100644 --- a/code/modules/tgui/states/admin.dm +++ b/code/modules/tgui/states/admin.dm @@ -2,6 +2,9 @@ * tgui state: admin_state * * Checks that the user is an admin, end-of-story. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(admin_state, /datum/ui_state/admin_state, new) diff --git a/code/modules/tgui/states/always.dm b/code/modules/tgui/states/always.dm index a741e2e3d4..210f0896a2 100644 --- a/code/modules/tgui/states/always.dm +++ b/code/modules/tgui/states/always.dm @@ -2,6 +2,9 @@ * tgui state: always_state * * Always grants the user UI_INTERACTIVE. Period. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(always_state, /datum/ui_state/always_state, new) diff --git a/code/modules/tgui/states/conscious.dm b/code/modules/tgui/states/conscious.dm index 4e2793d130..670ca7c07e 100644 --- a/code/modules/tgui/states/conscious.dm +++ b/code/modules/tgui/states/conscious.dm @@ -2,6 +2,9 @@ * tgui state: conscious_state * * Only checks if the user is conscious. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(conscious_state, /datum/ui_state/conscious_state, new) diff --git a/code/modules/tgui/states/contained.dm b/code/modules/tgui/states/contained.dm index f02424d01e..1eb8edba25 100644 --- a/code/modules/tgui/states/contained.dm +++ b/code/modules/tgui/states/contained.dm @@ -2,6 +2,9 @@ * tgui state: contained_state * * Checks that the user is inside the src_object. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(contained_state, /datum/ui_state/contained_state, new) diff --git a/code/modules/tgui/states/deep_inventory.dm b/code/modules/tgui/states/deep_inventory.dm index 43758cbab1..a2b9276a59 100644 --- a/code/modules/tgui/states/deep_inventory.dm +++ b/code/modules/tgui/states/deep_inventory.dm @@ -1,7 +1,11 @@ /** * tgui state: deep_inventory_state * - * Checks that the src_object is in the user's deep (backpack, box, toolbox, etc) inventory. + * Checks that the src_object is in the user's deep + * (backpack, box, toolbox, etc) inventory. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(deep_inventory_state, /datum/ui_state/deep_inventory_state, new) diff --git a/code/modules/tgui/states/default.dm b/code/modules/tgui/states/default.dm index 6bb159640e..367e57beff 100644 --- a/code/modules/tgui/states/default.dm +++ b/code/modules/tgui/states/default.dm @@ -1,7 +1,11 @@ /** * tgui state: default_state * - * Checks a number of things -- mostly physical distance for humans and view for robots. + * Checks a number of things -- mostly physical distance for humans + * and view for robots. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(default_state, /datum/ui_state/default, new) diff --git a/code/modules/tgui/states/default_contained.dm b/code/modules/tgui/states/default_contained.dm deleted file mode 100644 index c532e9f5d1..0000000000 --- a/code/modules/tgui/states/default_contained.dm +++ /dev/null @@ -1,13 +0,0 @@ -/** - * tgui state: default_contained - * - * Basically default and contained combined, allowing for both - */ - -GLOBAL_DATUM_INIT(default_contained_state, /datum/ui_state/default/contained, new) - -/datum/ui_state/default/contained/can_use_topic(atom/src_object, mob/user) - if(src_object.contains(user)) - return UI_INTERACTIVE - return ..() - \ No newline at end of file diff --git a/code/modules/tgui/states/hands.dm b/code/modules/tgui/states/hands.dm index d73d1058ea..1c885ed414 100644 --- a/code/modules/tgui/states/hands.dm +++ b/code/modules/tgui/states/hands.dm @@ -2,6 +2,9 @@ * tgui state: hands_state * * Checks that the src_object is in the user's hands. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(hands_state, /datum/ui_state/hands_state, new) @@ -19,7 +22,7 @@ GLOBAL_DATUM_INIT(hands_state, /datum/ui_state/hands_state, new) return UI_INTERACTIVE return UI_CLOSE -/mob/living/silicon/robot/hands_can_use_topic(obj/src_object) - if(activated(src_object) || istype(src_object.loc, /obj/item/weapon/gripper)) +/mob/living/silicon/robot/hands_can_use_topic(src_object) + if(activated(src_object)) return UI_INTERACTIVE return UI_CLOSE diff --git a/code/modules/tgui/states/human_adjacent.dm b/code/modules/tgui/states/human_adjacent.dm index 7aefe43e44..2ac7c8637b 100644 --- a/code/modules/tgui/states/human_adjacent.dm +++ b/code/modules/tgui/states/human_adjacent.dm @@ -3,6 +3,9 @@ * * In addition to default checks, only allows interaction for a * human adjacent user. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(human_adjacent_state, /datum/ui_state/human_adjacent_state, new) diff --git a/code/modules/tgui/states/inventory.dm b/code/modules/tgui/states/inventory.dm index 43fe2cb451..dc5dd0d57e 100644 --- a/code/modules/tgui/states/inventory.dm +++ b/code/modules/tgui/states/inventory.dm @@ -1,7 +1,11 @@ /** * tgui state: inventory_state * - * Checks that the src_object is in the user's top-level (hand, ear, pocket, belt, etc) inventory. + * Checks that the src_object is in the user's top-level + * (hand, ear, pocket, belt, etc) inventory. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(inventory_state, /datum/ui_state/inventory_state, new) diff --git a/code/modules/tgui/states/language_menu.dm b/code/modules/tgui/states/language_menu.dm index 5c816c8922..6389b05cd5 100644 --- a/code/modules/tgui/states/language_menu.dm +++ b/code/modules/tgui/states/language_menu.dm @@ -1,5 +1,8 @@ /** * tgui state: language_menu_state + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(language_menu_state, /datum/ui_state/language_menu, new) diff --git a/code/modules/tgui/states/not_incapacitated.dm b/code/modules/tgui/states/not_incapacitated.dm index 364b59424d..16dcb7881e 100644 --- a/code/modules/tgui/states/not_incapacitated.dm +++ b/code/modules/tgui/states/not_incapacitated.dm @@ -2,6 +2,9 @@ * tgui state: not_incapacitated_state * * Checks that the user isn't incapacitated + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(not_incapacitated_state, /datum/ui_state/not_incapacitated_state, new) @@ -24,6 +27,10 @@ GLOBAL_DATUM_INIT(not_incapacitated_turf_state, /datum/ui_state/not_incapacitate /datum/ui_state/not_incapacitated_state/can_use_topic(src_object, mob/user) if(user.stat) return UI_CLOSE - if(user.incapacitated() || user.lying || (turf_check && !isturf(user.loc))) + if(user.incapacitated() || (turf_check && !isturf(user.loc))) return UI_DISABLED - return UI_INTERACTIVE \ No newline at end of file + if(isliving(user)) + var/mob/living/L = user + if(!(L.mobility_flags & MOBILITY_STAND)) + return UI_DISABLED + return UI_INTERACTIVE diff --git a/code/modules/tgui/states/notcontained.dm b/code/modules/tgui/states/notcontained.dm index 642c6ce95f..1d4e6aec19 100644 --- a/code/modules/tgui/states/notcontained.dm +++ b/code/modules/tgui/states/notcontained.dm @@ -1,7 +1,11 @@ /** * tgui state: notcontained_state * - * Checks that the user is not inside src_object, and then makes the default checks. + * Checks that the user is not inside src_object, and then makes the + * default checks. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(notcontained_state, /datum/ui_state/notcontained_state, new) diff --git a/code/modules/tgui/states/observer.dm b/code/modules/tgui/states/observer.dm index 86ad776b13..d105de1c0c 100644 --- a/code/modules/tgui/states/observer.dm +++ b/code/modules/tgui/states/observer.dm @@ -2,6 +2,9 @@ * tgui state: observer_state * * Checks that the user is an observer/ghost. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(observer_state, /datum/ui_state/observer_state, new) diff --git a/code/modules/tgui/states/physical.dm b/code/modules/tgui/states/physical.dm index 88c8a291aa..3073039d14 100644 --- a/code/modules/tgui/states/physical.dm +++ b/code/modules/tgui/states/physical.dm @@ -2,6 +2,9 @@ * tgui state: physical_state * * Short-circuits the default state to only check physical distance. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(physical_state, /datum/ui_state/physical, new) diff --git a/code/modules/tgui/states/self.dm b/code/modules/tgui/states/self.dm index b0c9500fbc..4b6e3b9fd9 100644 --- a/code/modules/tgui/states/self.dm +++ b/code/modules/tgui/states/self.dm @@ -2,6 +2,9 @@ * tgui state: self_state * * Only checks that the user and src_object are the same. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(self_state, /datum/ui_state/self_state, new) diff --git a/code/modules/tgui/states/zlevel.dm b/code/modules/tgui/states/zlevel.dm index 5e3ccfb7de..64ea2fa1c0 100644 --- a/code/modules/tgui/states/zlevel.dm +++ b/code/modules/tgui/states/zlevel.dm @@ -2,6 +2,9 @@ * tgui state: z_state * * Only checks that the Z-level of the user and src_object are the same. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ GLOBAL_DATUM_INIT(z_state, /datum/ui_state/z_state, new) diff --git a/code/modules/tgui/subsystem.dm b/code/modules/tgui/subsystem.dm deleted file mode 100644 index cbe94e2d7f..0000000000 --- a/code/modules/tgui/subsystem.dm +++ /dev/null @@ -1,247 +0,0 @@ -/** - * tgui subsystem - * - * Contains all tgui state and subsystem code. - */ - -/** - * public - * - * Get a open UI given a user, src_object, and ui_key and try to update it with data. - * - * required user mob The mob who opened/is using the UI. - * required src_object datum The object/datum which owns the UI. - * required ui_key string The ui_key of the UI. - * optional ui datum/tgui The UI to be updated, if it exists. - * optional force_open bool If the UI should be re-opened instead of updated. - * - * return datum/tgui The found UI. - */ -/datum/controller/subsystem/tgui/proc/try_update_ui(mob/user, datum/src_object, ui_key, datum/tgui/ui, force_open = FALSE) - if(isnull(ui)) // No UI was passed, so look for one. - ui = get_open_ui(user, src_object, ui_key) - - if(!isnull(ui)) - var/data = src_object.ui_data(user) // Get data from the src_object. - if(!force_open) // UI is already open; update it. - ui.push_data(data) - else // Re-open it anyways. - ui.reinitialize(null, data) - return ui // We found the UI, return it. - else - return null // We couldn't find a UI. - -/** - * private - * - * Get a open UI given a user, src_object, and ui_key. - * - * required user mob The mob who opened/is using the UI. - * required src_object datum The object/datum which owns the UI. - * required ui_key string The ui_key of the UI. - * - * return datum/tgui The found UI. - */ -/datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object, ui_key) - var/src_object_key = "[REF(src_object)]" - if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - return null // No UIs open. - else if(isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list)) - return null // No UIs open for this object. - - for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) // Find UIs for this object. - if(ui.user == user) // Make sure we have the right user - return ui - - return null // Couldn't find a UI! - -/** - * private - * - * Update all UIs attached to src_object. - * - * required src_object datum The object/datum which owns the UIs. - * - * return int The number of UIs updated. - */ -/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object) - var/src_object_key = "[REF(src_object)]" - if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - return 0 // Couldn't find any UIs for this object. - - var/update_count = 0 - for(var/ui_key in open_uis[src_object_key]) - for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) - if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user)) // Check the UI is valid. - ui.process(force = 1) // Update the UI. - update_count++ // Count each UI we update. - return update_count - -/** - * private - * - * Close all UIs attached to src_object. - * - * required src_object datum The object/datum which owns the UIs. - * - * return int The number of UIs closed. - */ -/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object) - var/src_object_key = "[REF(src_object)]" - if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - return 0 // Couldn't find any UIs for this object. - - var/close_count = 0 - for(var/ui_key in open_uis[src_object_key]) - for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) - if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user)) // Check the UI is valid. - ui.close() // Close the UI. - close_count++ // Count each UI we close. - return close_count - -/** - * private - * - * Close *ALL* UIs - * - * return int The number of UIs closed. - */ -/datum/controller/subsystem/tgui/proc/close_all_uis() - var/close_count = 0 - for(var/src_object_key in open_uis) - for(var/ui_key in open_uis[src_object_key]) - for(var/datum/tgui/ui in open_uis[src_object_key][ui_key]) - if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user)) // Check the UI is valid. - ui.close() // Close the UI. - close_count++ // Count each UI we close. - return close_count - -/** - * private - * - * Update all UIs belonging to a user. - * - * required user mob The mob who opened/is using the UI. - * optional src_object datum If provided, only update UIs belonging this src_object. - * optional ui_key string If provided, only update UIs with this UI key. - * - * return int The number of UIs updated. - */ -/datum/controller/subsystem/tgui/proc/update_user_uis(mob/user, datum/src_object = null, ui_key = null) - if(isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0) - return 0 // Couldn't find any UIs for this user. - - var/update_count = 0 - for(var/datum/tgui/ui in user.open_uis) - if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) - ui.process(force = 1) // Update the UI. - update_count++ // Count each UI we upadte. - return update_count - -/** - * private - * - * Close all UIs belonging to a user. - * - * required user mob The mob who opened/is using the UI. - * optional src_object datum If provided, only close UIs belonging this src_object. - * optional ui_key string If provided, only close UIs with this UI key. - * - * return int The number of UIs closed. - */ -/datum/controller/subsystem/tgui/proc/close_user_uis(mob/user, datum/src_object = null, ui_key = null) - if(isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0) - return 0 // Couldn't find any UIs for this user. - - var/close_count = 0 - for(var/datum/tgui/ui in user.open_uis) - if((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) - ui.close() // Close the UI. - close_count++ // Count each UI we close. - return close_count - -/** - * private - * - * Add a UI to the list of open UIs. - * - * required ui datum/tgui The UI to be added. - */ -/datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui) - var/src_object_key = "[REF(ui.src_object)]" - if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - open_uis[src_object_key] = list(ui.ui_key = list()) // Make a list for the ui_key and src_object. - else if(isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list)) - open_uis[src_object_key][ui.ui_key] = list() // Make a list for the ui_key. - - // Append the UI to all the lists. - ui.user.open_uis |= ui - var/list/uis = open_uis[src_object_key][ui.ui_key] - uis |= ui - processing_uis |= ui - -/** - * private - * - * Remove a UI from the list of open UIs. - * - * required ui datum/tgui The UI to be removed. - * - * return bool If the UI was removed or not. - */ -/datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui) - var/src_object_key = "[REF(ui.src_object)]" - if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - return 0 // It wasn't open. - else if(isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list)) - return 0 // It wasn't open. - - processing_uis.Remove(ui) // Remove it from the list of processing UIs. - if(ui.user) // If the user exists, remove it from them too. - ui.user.open_uis.Remove(ui) - var/Ukey = ui.ui_key - var/list/uis = open_uis[src_object_key][Ukey] // Remove it from the list of open UIs. - uis.Remove(ui) - if(!uis.len) - var/list/uiobj = open_uis[src_object_key] - uiobj.Remove(Ukey) - if(!uiobj.len) - open_uis.Remove(src_object_key) - - return 1 // Let the caller know we did it. - -/** - * private - * - * Handle client logout, by closing all their UIs. - * - * required user mob The mob which logged out. - * - * return int The number of UIs closed. - */ -/datum/controller/subsystem/tgui/proc/on_logout(mob/user) - return close_user_uis(user) - -/** - * private - * - * Handle clients switching mobs, by transferring their UIs. - * - * required user source The client's original mob. - * required user target The client's new mob. - * - * return bool If the UIs were transferred. - */ -/datum/controller/subsystem/tgui/proc/on_transfer(mob/source, mob/target) - if(!source || isnull(source.open_uis) || !istype(source.open_uis, /list) || open_uis.len == 0) - return 0 // The old mob had no open UIs. - - if(isnull(target.open_uis) || !istype(target.open_uis, /list)) - target.open_uis = list() // Create a list for the new mob if needed. - - for(var/datum/tgui/ui in source.open_uis) - ui.user = target // Inform the UIs of their new owner. - target.open_uis.Add(ui) // Transfer all the UIs. - - source.open_uis.Cut() // Clear the old list. - return 1 // Let the caller know we did it. diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index 7971a940d4..d0d5ff8ebb 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -1,7 +1,6 @@ /** - * tgui - * - * /tg/station user interface library + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT */ /** @@ -14,34 +13,26 @@ var/datum/src_object /// The title of te UI. var/title - /// The ui_key of the UI. This allows multiple UIs for one src_object. - var/ui_key /// The window_id for browse() and onclose(). - var/window_id - /// The window width. - var/width = 0 - /// The window height - var/height = 0 + var/datum/tgui_window/window + /// Key that is used for remembering the window geometry. + var/window_key + /// Deprecated: Window size. + var/window_size /// The interface (template) to be used for this UI. var/interface /// Update the UI every MC tick. var/autoupdate = TRUE /// If the UI has been initialized yet. var/initialized = FALSE - /// The data (and datastructure) used to initialize the UI. - var/list/initial_data - /// The static data used to initialize the UI. - var/list/initial_static_data - /// Holder for the json string, that is sent during the initial update - var/_initial_update + /// Time of opening the window. + var/opened_at + /// Stops further updates when close() was called. + var/closing = FALSE /// The status/visibility of the UI. var/status = UI_INTERACTIVE /// Topic state used to determine status/interactability. var/datum/ui_state/state = null - /// The parent UI. - var/datum/tgui/master_ui - /// Children of this UI. - var/list/datum/tgui/children = list() /** * public @@ -50,38 +41,25 @@ * * required user mob The mob who opened/is using the UI. * required src_object datum The object or datum which owns the UI. - * required ui_key string The ui_key of the UI. * required interface string The interface used to render the UI. * optional title string The title of the UI. - * optional width int The window width. - * optional height int The window height. - * optional master_ui datum/tgui The parent UI. - * optional state datum/ui_state The state used to determine status. + * optional ui_x int Deprecated: Window width. + * optional ui_y int Deprecated: Window height. * * return datum/tgui The requested UI. */ -/datum/tgui/New(mob/user, datum/src_object, ui_key, interface, title, width = 0, height = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) +/datum/tgui/New(mob/user, datum/src_object, interface, title, ui_x, ui_y) + log_tgui(user, "new [interface] fancy [user.client.prefs.tgui_fancy]") src.user = user src.src_object = src_object - src.ui_key = ui_key - // DO NOT replace with \ref here. src_object could potentially be tagged - src.window_id = "[REF(src_object)]-[ui_key]" + src.window_key = "[REF(src_object)]-main" src.interface = interface - if(title) - src.title = sanitize(title) - if(width) - src.width = width - if(height) - src.height = height - - src.master_ui = master_ui - if(master_ui) - master_ui.children += src - src.state = state - - var/datum/asset/assets = get_asset_datum(/datum/asset/group/tgui) - assets.send(user) + src.title = title + src.state = src_object.ui_state() + // Deprecated + if(ui_x && ui_y) + src.window_size = list(ui_x, ui_y) /** * public @@ -90,85 +68,53 @@ */ /datum/tgui/proc/open() if(!user.client) - return // Bail if there is no client. - - update_status(push = FALSE) // Update the window status. + return null + if(window) + return null + process_status() if(status < UI_UPDATE) - return // Bail if we're not supposed to open. - - // Build window options - var/window_options = "can_minimize=0;auto_format=0;" - // If we have a width and height, use them. - if(width && height) - window_options += "size=[width]x[height];" - // Remove titlebar and resize handles for a fancy window - if(user.client.prefs.tgui_fancy) - window_options += "titlebar=0;can_resize=0;" + return null + window = SStgui.request_pooled_window(user) + if(!window) + return null + opened_at = world.time + window.acquire_lock(src) + if(!window.is_ready()) + window.initialize(inline_assets = list( + get_asset_datum(/datum/asset/simple/tgui), + )) else - window_options += "titlebar=1;can_resize=1;" - - // Generate page html - var/html - html = SStgui.basehtml - // Allow the src object to override the html if needed - html = src_object.ui_base_html(html) - // Replace template tokens with important UI data - // NOTE: Intentional \ref usage; tgui datums can't/shouldn't - // be tagged, so this is an effective unwrap - html = replacetextEx(html, "\[tgui:ref]", "\ref[src]") - - // Open the window. - user << browse(html, "window=[window_id];[window_options]") - - // Instruct the client to signal UI when the window is closed. - // NOTE: Intentional \ref usage; tgui datums can't/shouldn't - // be tagged, so this is an effective unwrap - winset(user, window_id, "on-close=\"uiclose \ref[src]\"") - - // Pre-fetch initial state while browser is still loading in - // another thread - if(!initial_data) { - initial_data = src_object.ui_data(user) - } - if(!initial_static_data) { - initial_static_data = src_object.ui_static_data(user) - } - _initial_update = url_encode(get_json(initial_data, initial_static_data)) - + window.send_message("ping") + window.send_asset(get_asset_datum(/datum/asset/simple/fontawesome)) + for(var/datum/asset/asset in src_object.ui_assets(user)) + window.send_asset(asset) + window.send_message("update", get_payload( + with_data = TRUE, + with_static_data = TRUE)) SStgui.on_open(src) /** * public * - * Reinitialize the UI. - * (Possibly with a new interface and/or data). + * Close the UI. * - * optional template string The name of the new interface. - * optional data list The new initial data. + * optional can_be_suspended bool */ -/datum/tgui/proc/reinitialize(interface, list/data, list/static_data) - if(interface) - src.interface = interface - if(data) - initial_data = data - if(static_data) - initial_static_data = static_data - open() - -/** - * public - * - * Close the UI, and all its children. - */ -/datum/tgui/proc/close() - user << browse(null, "window=[window_id]") // Close the window. - src_object.ui_close(user) - SStgui.on_close(src) - for(var/datum/tgui/child in children) // Loop through and close all children. - child.close() - children.Cut() +/datum/tgui/proc/close(can_be_suspended = TRUE) + if(closing) + return + closing = TRUE + // If we don't have window_id, open proc did not have the opportunity + // to finish, therefore it's safe to skip this whole block. + if(window) + // Windows you want to keep are usually blue screens of death + // and we want to keep them around, to allow user to read + // the error message properly. + window.release_lock() + window.close(can_be_suspended) + src_object.ui_close(user) + SStgui.on_close(src) state = null - master_ui = null qdel(src) /** @@ -176,187 +122,173 @@ * * Enable/disable auto-updating of the UI. * - * required state bool Enable/disable auto-updating. + * required value bool Enable/disable auto-updating. */ -/datum/tgui/proc/set_autoupdate(state = TRUE) - autoupdate = state +/datum/tgui/proc/set_autoupdate(autoupdate) + src.autoupdate = autoupdate + +/** + * public + * + * Replace current ui.state with a new one. + * + * required state datum/ui_state/state Next state + */ +/datum/tgui/proc/set_state(datum/ui_state/state) + src.state = state + +/** + * public + * + * Makes an asset available to use in tgui. + * + * required asset datum/asset + */ +/datum/tgui/proc/send_asset(datum/asset/asset) + if(!window) + CRASH("send_asset() can only be called after open().") + window.send_asset(asset) + +/** + * public + * + * Send a full update to the client (includes static data). + * + * optional custom_data list Custom data to send instead of ui_data. + * optional force bool Send an update even if UI is not interactive. + */ +/datum/tgui/proc/send_full_update(custom_data, force) + if(!user.client || !initialized || closing) + return + var/should_update_data = force || status >= UI_UPDATE + window.send_message("update", get_payload( + custom_data, + with_data = should_update_data, + with_static_data = TRUE)) + +/** + * public + * + * Send a partial update to the client (excludes static data). + * + * optional custom_data list Custom data to send instead of ui_data. + * optional force bool Send an update even if UI is not interactive. + */ +/datum/tgui/proc/send_update(custom_data, force) + if(!user.client || !initialized || closing) + return + var/should_update_data = force || status >= UI_UPDATE + window.send_message("update", get_payload( + custom_data, + with_data = should_update_data)) /** * private * * Package the data to send to the UI, as JSON. - * This includes the UI data and config_data. * - * return string The packaged JSON. + * return list */ -/datum/tgui/proc/get_json(list/data, list/static_data) +/datum/tgui/proc/get_payload(custom_data, with_data, with_static_data) var/list/json_data = list() - json_data["config"] = list( "title" = title, "status" = status, "interface" = interface, - "fancy" = user.client.prefs.tgui_fancy, - "locked" = user.client.prefs.tgui_lock, - "observer" = isobserver(user), - "window" = window_id, - // NOTE: Intentional \ref usage; tgui datums can't/shouldn't - // be tagged, so this is an effective unwrap - "ref" = "\ref[src]" + "window" = list( + "key" = window_key, + "size" = window_size, + "fancy" = user.client.prefs.tgui_fancy, + "locked" = user.client.prefs.tgui_lock + ), + "user" = list( + "name" = "[user]", + "ckey" = "[user.ckey]", + "observer" = isobserver(user) + ) ) - - if(!isnull(data)) + var/data = custom_data || with_data && src_object.ui_data(user) + if(data) json_data["data"] = data - if(!isnull(static_data)) + var/static_data = with_static_data && src_object.ui_static_data(user) + if(static_data) json_data["static_data"] = static_data - - // Send shared states if(src_object.tgui_shared_states) json_data["shared"] = src_object.tgui_shared_states - - // Generate the JSON. - var/json = json_encode(json_data) - // Strip #255/improper. - json = replacetext(json, "\proper", "") - json = replacetext(json, "\improper", "") - return json + return json_data /** * private * - * Handle clicks from the UI. - * Call the src_object's ui_act() if status is UI_INTERACTIVE. - * If the src_object's ui_act() returns 1, update all UIs attacked to it. - */ -/datum/tgui/Topic(href, href_list) - if(user != usr) - return // Something is not right here. - - var/action = href_list["action"] - var/params = href_list; params -= "action" - - switch(action) - if("tgui:initialize") - user << output(_initial_update, "[window_id].browser:update") - initialized = TRUE - if("tgui:setSharedState") - // Update the window state. - update_status(push = FALSE) - // Bail if UI is not interactive or usr calling Topic - // is not the UI user. - if(status != UI_INTERACTIVE) - return - var/key = params["key"] - var/value = params["value"] - if(!src_object.tgui_shared_states) - src_object.tgui_shared_states = list() - src_object.tgui_shared_states[key] = value - SStgui.update_uis(src_object) - if("tgui:setFancy") - var/value = text2num(params["value"]) - user.client.prefs.tgui_fancy = value - if("tgui:log") - // Force window to show frills on fatal errors - if(params["fatal"]) - winset(user, window_id, "titlebar=1;can-resize=1;size=600x600") - log_message(params["log"]) - if("tgui:link") - user << link(params["url"]) - else - // Update the window state. - update_status(push = FALSE) - // Call ui_act() on the src_object. - if(src_object.ui_act(action, params, src, state)) - // Update if the object requested it. - SStgui.update_uis(src_object) - -/** - * private - * - * Update the UI. - * Only updates the data if update is true, otherwise only updates the status. - * - * optional force bool If the UI should be forced to update. + * Run an update cycle for this UI. Called internally by SStgui + * every second or so. */ /datum/tgui/process(force = FALSE) + if(closing) + return var/datum/host = src_object.ui_host(user) - if(!src_object || !host || !user) // If the object or user died (or something else), abort. + // If the object or user died (or something else), abort. + if(!src_object || !host || !user || !window) + close(can_be_suspended = FALSE) + return + // Validate ping + if(!initialized && world.time - opened_at > TGUI_PING_TIMEOUT) + log_tgui(user, \ + "Error: Zombie window detected, killing it with fire.\n" \ + + "window_id: [window.id]\n" \ + + "opened_at: [opened_at]\n" \ + + "world.time: [world.time]") + close(can_be_suspended = FALSE) + return + // Update through a normal call to ui_interact + if(status != UI_DISABLED && (autoupdate || force)) + src_object.ui_interact(user, src) + return + // Update status only + var/needs_update = process_status() + if(status <= UI_CLOSE) close() return - - if(status && (force || autoupdate)) - update() // Update the UI if the status and update settings allow it. - else - update_status(push = TRUE) // Otherwise only update status. + if(needs_update) + window.send_message("update", get_payload()) /** * private * - * Push data to an already open UI. - * - * required data list The data to send. - * optional force bool If the update should be sent regardless of state. + * Updates the status, and returns TRUE if status has changed. */ -/datum/tgui/proc/push_data(data, static_data, force = FALSE) - // Update the window state. - update_status(push = FALSE) - // Cannot update UI if it is not set up yet. - if(!initialized) - return - // Cannot update UI, we have no visibility. - if(status <= UI_DISABLED && !force) - return - // Send the new JSON to the update() Javascript function. - user << output( - url_encode(get_json(data, static_data)), - "[window_id].browser:update") +/datum/tgui/proc/process_status() + var/prev_status = status + status = src_object.ui_status(user, state) + return prev_status != status /** * private * - * Updates the UI by interacting with the src_object again, which will hopefully - * call try_ui_update on it. - * - * optional force_open bool If force_open should be passed to ui_interact. + * Callback for handling incoming tgui messages. */ -/datum/tgui/proc/update(force_open = FALSE) - src_object.ui_interact(user, ui_key, src, force_open, master_ui, state) - -/** - * private - * - * Update the status/visibility of the UI for its user. - * - * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED). - */ -/datum/tgui/proc/update_status(push = FALSE) - var/status = src_object.ui_status(user, state) - if(master_ui) - status = min(status, master_ui.status) - set_status(status, push) - if(status == UI_CLOSE) - close() - -/** - * private - * - * Set the status/visibility of the UI. - * - * required status int The status to set (UI_CLOSE/UI_DISABLED/UI_UPDATE/UI_INTERACTIVE). - * optional push bool Push an update to the UI (an update is always sent for UI_DISABLED). - */ -/datum/tgui/proc/set_status(status, push = FALSE) - // Only update if status has changed. - if(src.status != status) - if(src.status == UI_DISABLED) - src.status = status - if(push) - update() - else - src.status = status - // Update if the UI just because disabled, or a push is requested. - if(status == UI_DISABLED || push) - push_data(null, force = TRUE) - -/datum/tgui/proc/log_message(message) - log_tgui("[user] ([user.ckey]) using \"[title]\":\n[message]") +/datum/tgui/proc/on_message(type, list/payload, list/href_list) + // Pass act type messages to ui_act + if(type && copytext(type, 1, 5) == "act/") + process_status() + if(src_object.ui_act(copytext(type, 5), payload, src, state)) + SStgui.update_uis(src_object) + return FALSE + switch(type) + if("ready") + initialized = TRUE + if("pingReply") + initialized = TRUE + if("suspend") + close(can_be_suspended = TRUE) + if("close") + close(can_be_suspended = FALSE) + if("log") + if(href_list["fatal"]) + close(can_be_suspended = FALSE) + if("setSharedState") + if(status != UI_INTERACTIVE) + return + LAZYINITLIST(src_object.tgui_shared_states) + src_object.tgui_shared_states[href_list["key"]] = href_list["value"] + SStgui.update_uis(src_object) diff --git a/code/modules/tgui/tgui_window.dm b/code/modules/tgui/tgui_window.dm new file mode 100644 index 0000000000..3f271163c9 --- /dev/null +++ b/code/modules/tgui/tgui_window.dm @@ -0,0 +1,238 @@ +/** + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + +/datum/tgui_window + var/id + var/client/client + var/pooled + var/pool_index + var/status = TGUI_WINDOW_CLOSED + var/locked = FALSE + var/datum/tgui/locked_by + var/fatally_errored = FALSE + var/message_queue + var/sent_assets = list() + +/** + * public + * + * Create a new tgui window. + * + * required client /client + * required id string A unique window identifier. + */ +/datum/tgui_window/New(client/client, id, pooled = FALSE) + src.id = id + src.client = client + src.pooled = pooled + if(pooled) + client.tgui_windows[id] = src + src.pool_index = TGUI_WINDOW_INDEX(id) + +/** + * public + * + * Initializes the window with a fresh page. Puts window into the "loading" + * state. You can begin sending messages right after initializing. Messages + * will be put into the queue until the window finishes loading. + * + * optional inline_assets list List of assets to inline into the html. + */ +/datum/tgui_window/proc/initialize(inline_assets = list()) + log_tgui(client, "[id]/initialize") + if(!client) + return + status = TGUI_WINDOW_LOADING + fatally_errored = FALSE + message_queue = null + // Build window options + var/options = "file=[id].html;can_minimize=0;auto_format=0;" + // Remove titlebar and resize handles for a fancy window + if(client.prefs.tgui_fancy) + options += "titlebar=0;can_resize=0;" + else + options += "titlebar=1;can_resize=1;" + // Generate page html + var/html = SStgui.basehtml + html = replacetextEx(html, "\[tgui:windowId]", id) + // Process inline assets + var/inline_styles = "" + var/inline_scripts = "" + for(var/datum/asset/asset in inline_assets) + var/mappings = asset.get_url_mappings() + for(var/name in mappings) + var/url = mappings[name] + // Not urlencoding since asset strings are considered safe + if(copytext(name, -4) == ".css") + inline_styles += "\n" + else if(copytext(name, -3) == ".js") + inline_scripts += "\n" + asset.send() + html = replacetextEx(html, "\n", inline_styles) + html = replacetextEx(html, "\n", inline_scripts) + // Open the window + client << browse(html, "window=[id];[options]") + // Instruct the client to signal UI when the window is closed. + winset(client, id, "on-close=\"uiclose [id]\"") + +/** + * public + * + * Checks if the window is ready to receive data. + * + * return bool + */ +/datum/tgui_window/proc/is_ready() + return status == TGUI_WINDOW_READY + +/** + * public + * + * Checks if the window can be sanely suspended. + * + * return bool + */ +/datum/tgui_window/proc/can_be_suspended() + return !fatally_errored \ + && pooled \ + && pool_index > 0 \ + && pool_index <= TGUI_WINDOW_SOFT_LIMIT \ + && status == TGUI_WINDOW_READY + +/** + * public + * + * Acquire the window lock. Pool will not be able to provide this window + * to other UIs for the duration of the lock. + * + * Can be given an optional tgui datum, which will hook its on_message + * callback into the message stream. + * + * optional ui /datum/tgui + */ +/datum/tgui_window/proc/acquire_lock(datum/tgui/ui) + locked = TRUE + locked_by = ui + +/** + * Release the window lock. + */ +/datum/tgui_window/proc/release_lock() + // Clean up assets sent by tgui datum which requested the lock + if(locked) + sent_assets = list() + locked = FALSE + locked_by = null + +/** + * public + * + * Close the UI. + * + * optional can_be_suspended bool + */ +/datum/tgui_window/proc/close(can_be_suspended = TRUE) + if(!client) + return + if(can_be_suspended && can_be_suspended()) + log_tgui(client, "[id]/close: suspending") + status = TGUI_WINDOW_READY + send_message("suspend") + return + log_tgui(client, "[id]/close") + release_lock() + status = TGUI_WINDOW_CLOSED + message_queue = null + // Do not close the window to give user some time + // to read the error message. + if(!fatally_errored) + client << browse(null, "window=[id]") + +/** + * public + * + * Sends a message to tgui window. + * + * required type string Message type + * required payload list Message payload + * optional force bool Send regardless of the ready status. + */ +/datum/tgui_window/proc/send_message(type, list/payload, force) + if(!client) + return + var/message = json_encode(list( + "type" = type, + "payload" = payload, + )) + // Strip #255/improper. + message = replacetext(message, "\proper", "") + message = replacetext(message, "\improper", "") + // Pack for sending via output() + message = url_encode(message) + // Place into queue if window is still loading + if(!force && status != TGUI_WINDOW_READY) + if(!message_queue) + message_queue = list() + message_queue += list(message) + return + client << output(message, "[id].browser:update") + +/** + * public + * + * Makes an asset available to use in tgui. + * + * required asset datum/asset + */ +/datum/tgui_window/proc/send_asset(datum/asset/asset) + if(!client || !asset) + return + if(istype(asset, /datum/asset/spritesheet)) + var/datum/asset/spritesheet/spritesheet = asset + send_message("asset/stylesheet", spritesheet.css_filename()) + send_message("asset/mappings", asset.get_url_mappings()) + sent_assets += list(asset) + asset.send(client) + +/** + * private + * + * Sends queued messages if the queue wasn't empty. + */ +/datum/tgui_window/proc/flush_message_queue() + if(!client || !message_queue) + return + for(var/message in message_queue) + client << output(message, "[id].browser:update") + message_queue = null + +/** + * private + * + * Callback for handling incoming tgui messages. + */ +/datum/tgui_window/proc/on_message(type, list/payload, list/href_list) + switch(type) + if("ready") + // Status can be READY if user has refreshed the window. + if(status == TGUI_WINDOW_READY) + // Resend the assets + for(var/asset in sent_assets) + send_asset(asset) + status = TGUI_WINDOW_READY + if("log") + if(href_list["fatal"]) + fatally_errored = TRUE + // Pass message to UI that requested the lock + if(locked && locked_by) + locked_by.on_message(type, payload, href_list) + flush_message_queue() + return + // If not locked, handle these message types + switch(type) + if("suspend") + close(can_be_suspended = TRUE) + if("close") + close(can_be_suspended = FALSE) diff --git a/code/modules/uplink/uplink_items/uplink_ammo.dm b/code/modules/uplink/uplink_items/uplink_ammo.dm index ce74773f8d..853f6111b2 100644 --- a/code/modules/uplink/uplink_items/uplink_ammo.dm +++ b/code/modules/uplink/uplink_items/uplink_ammo.dm @@ -291,10 +291,17 @@ /datum/uplink_item/ammo/bolt_action name = "Surplus Rifle Clip" desc = "A stripper clip used to quickly load bolt action rifles. Contains 5 rounds." - item = /obj/item/ammo_box/a762 + item = /obj/item/ammo_box/a762 cost = 1 include_modes = list(/datum/game_mode/nuclear) +/datum/uplink_item/ammo/bolt_action_bulk + name = "Surplus Rifle Clip Box" + desc = "An ammo box we found in a warehouse, holding 7 clips of 5 rounds for bolt-action rifles. Yes, the cheap ones." + item = /obj/item/storage/toolbox/ammo + cost = 4 + include_modes = list(/datum/game_mode/nuclear) + /datum/uplink_item/ammo/dark_gygax/bag name = "Dark Gygax Ammo Bag" desc = "A duffel bag containing ammo for three full reloads of the incendiary carbine and flash bang launcher that are equipped on a standard Dark Gygax exosuit." diff --git a/code/modules/uplink/uplink_items/uplink_bundles.dm b/code/modules/uplink/uplink_items/uplink_bundles.dm index fbeaee8939..1b7909a50d 100644 --- a/code/modules/uplink/uplink_items/uplink_bundles.dm +++ b/code/modules/uplink/uplink_items/uplink_bundles.dm @@ -173,8 +173,7 @@ /datum/uplink_item/bundles_TC/reroll/purchase(mob/user, datum/component/uplink/U) var/datum/antagonist/traitor/T = user?.mind?.has_antag_datum(/datum/antagonist/traitor) if(istype(T)) - var/new_traitor_kind = get_random_traitor_kind(list(T.traitor_kind.type)) - T.set_traitor_kind(new_traitor_kind) + T.set_traitor_kind(/datum/traitor_class/human/subterfuge) else to_chat(user,"Invalid user for contract renegotiation.") diff --git a/code/modules/uplink/uplink_items/uplink_clothing.dm b/code/modules/uplink/uplink_items/uplink_clothing.dm index 014e0452b5..c26a9ae1f0 100644 --- a/code/modules/uplink/uplink_items/uplink_clothing.dm +++ b/code/modules/uplink/uplink_items/uplink_clothing.dm @@ -97,3 +97,9 @@ item = /obj/item/clothing/gloves/tackler/combat/insulated include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) cost = 2 + +/datum/uplink_item/device_tools/syndicate_eyepatch + name = "Mechanical Eyepatch" + desc = "An eyepatch that connects itself to your eye socket, enhancing your shooting to an impossible degree, allowing your bullets to ricochet far more often than usual." + item = /obj/item/clothing/glasses/eyepatch/syndicate + cost = 8 diff --git a/code/modules/uplink/uplink_items/uplink_devices.dm b/code/modules/uplink/uplink_items/uplink_devices.dm index eddf1760ed..5f5eb91a04 100644 --- a/code/modules/uplink/uplink_items/uplink_devices.dm +++ b/code/modules/uplink/uplink_items/uplink_devices.dm @@ -206,9 +206,7 @@ this primer of questionable worth and value is rumored to increase your rifle-bolt-working and/or shotgun \ racking fivefold. Then again, the techniques here only work on bolt-actions and pump-actions..." item = /obj/item/book/granter/trait/rifleman - cost = 3 - restricted_roles = list("Operative") // i want it to be surplusable but i also want it to be mostly nukie only, please advise - surplus = 90 + cost = 3 // fuck it available for everyone /datum/uplink_item/device_tools/stimpack name = "Stimpack" diff --git a/code/modules/uplink/uplink_items/uplink_stealthdevices.dm b/code/modules/uplink/uplink_items/uplink_stealthdevices.dm index f1c27c640b..28d02cf79b 100644 --- a/code/modules/uplink/uplink_items/uplink_stealthdevices.dm +++ b/code/modules/uplink/uplink_items/uplink_stealthdevices.dm @@ -112,13 +112,13 @@ name = "Radio Jammer" desc = "This device will disrupt any nearby outgoing radio communication when activated. Does not affect binary chat." item = /obj/item/jammer - cost = 5 + cost = 2 /datum/uplink_item/stealthy_tools/smugglersatchel name = "Smuggler's Satchel" desc = "This satchel is thin enough to be hidden in the gap between plating and tiling; great for stashing \ your stolen goods. Comes with a crowbar and a floor tile inside. Properly hidden satchels have been \ - known to survive intact even beyond the current shift. " + known to survive intact even beyond the current shift, but this is just a myth. " item = /obj/item/storage/backpack/satchel/flat - cost = 2 + cost = 1 surplus = 30 diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index a42fe97cb4..a115300085 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -501,10 +501,10 @@ GLOBAL_LIST_EMPTY(vending_products) C.bleed(150) var/obj/item/bodypart/l_leg/l = C.get_bodypart(BODY_ZONE_L_LEG) if(l) - l.receive_damage(brute=200) + l.receive_damage(brute=200, updating_health=TRUE) var/obj/item/bodypart/r_leg/r = C.get_bodypart(BODY_ZONE_R_LEG) if(r) - r.receive_damage(brute=200) + r.receive_damage(brute=200, updating_health=TRUE) if(l || r) C.visible_message("[C]'s legs shatter with a sickening crunch!", \ "Your legs shatter with a sickening crunch!") @@ -672,21 +672,21 @@ GLOBAL_LIST_EMPTY(vending_products) return return ..() -/obj/machinery/vending/ui_base_html(html) - var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/vending) - . = replacetext(html, "", assets.css_tag()) +/obj/machinery/vending/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/vending), + ) -/obj/machinery/vending/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/vending/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/vending) - assets.send(user) - ui = new(user, src, ui_key, "Vending", ui_key, 450, 600, master_ui, state) + ui = new(user, src, "Vending") ui.open() /obj/machinery/vending/ui_static_data(mob/user) . = list() .["onstation"] = onstation + .["department"] = payment_department .["product_records"] = list() for (var/datum/data/vending_product/R in product_records) var/list/data = list( @@ -713,7 +713,7 @@ GLOBAL_LIST_EMPTY(vending_products) var/list/data = list( path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), name = R.name, - price = R.custom_price || default_price, + price = R.custom_premium_price || extra_price, //may cause breakage. please note max_amount = R.max_amount, ref = REF(R), premium = TRUE @@ -722,28 +722,24 @@ GLOBAL_LIST_EMPTY(vending_products) /obj/machinery/vending/ui_data(mob/user) . = list() - var/obj/item/card/id/C = user.get_idcard(TRUE) - .["cost_mult"] = 1 - .["cost_text"] = "" - if(C && C.registered_account) - .["user"] = list() - .["user"]["name"] = C.registered_account.account_holder - .["user"]["cash"] = C.registered_account.account_balance - if(C.registered_account.account_job) - .["user"]["job"] = C.registered_account.account_job.title - else - .["user"]["job"] = "No Job" - var/cost_mult = get_best_discount(C) - if(cost_mult != 1) - .["cost_mult"] = cost_mult - if(cost_mult < 1) - .["cost_text"] = " ([(1 - cost_mult) * 100]% OFF)" + var/mob/living/carbon/human/H + var/obj/item/card/id/C + if(ishuman(user)) + H = user + C = H.get_idcard(TRUE) + if(C?.registered_account) + .["user"] = list() + .["user"]["name"] = C.registered_account.account_holder + .["user"]["cash"] = C.registered_account.account_balance + if(C.registered_account.account_job) + .["user"]["job"] = C.registered_account.account_job.title + .["user"]["department"] = C.registered_account.account_job.paycheck_department else - .["cost_text"] = " ([(cost_mult - 1) * 100]% EXTRA)" + .["user"]["job"] = "No Job" + .["user"]["department"] = "No Department" .["stock"] = list() for (var/datum/data/vending_product/R in product_records + coin_records + hidden_records) .["stock"][R.name] = R.amount - . .["extended_inventory"] = extended_inventory /obj/machinery/vending/ui_act(action, params) @@ -766,7 +762,9 @@ GLOBAL_LIST_EMPTY(vending_products) if(!R || !istype(R) || !R.product_path) vend_ready = TRUE return - var/price_to_use = R.custom_price || default_price + var/price_to_use = default_price + if(R.custom_price) + price_to_use = R.custom_price if(R in hidden_records) if(!extended_inventory) vend_ready = TRUE @@ -780,8 +778,10 @@ GLOBAL_LIST_EMPTY(vending_products) flick(icon_deny,src) vend_ready = TRUE return - if(onstation && price_to_use >= 0) - var/obj/item/card/id/C = usr.get_idcard(TRUE) + if(onstation && ishuman(usr)) + var/mob/living/carbon/human/H = usr + var/obj/item/card/id/C = H.get_idcard(TRUE) + if(!C) say("No card found.") flick(icon_deny,src) @@ -792,11 +792,20 @@ GLOBAL_LIST_EMPTY(vending_products) flick(icon_deny,src) vend_ready = TRUE return + // else if(age_restrictions && R.age_restricted && (!C.registered_age || C.registered_age < AGE_MINOR)) + // say("You are not of legal age to purchase [R.name].") + // if(!(usr in GLOB.narcd_underages)) + // Radio.set_frequency(FREQ_SECURITY) + // Radio.talk_into(src, "SECURITY ALERT: Underaged crewmember [H] recorded attempting to purchase [R.name] in [get_area(src)]. Please watch for substance abuse.", FREQ_SECURITY) + // GLOB.narcd_underages += H + // flick(icon_deny,src) + // vend_ready = TRUE + // return var/datum/bank_account/account = C.registered_account - if(coin_records.Find(R)) - price_to_use = R.custom_premium_price || extra_price - else if(!hidden_records.Find(R)) - price_to_use = round(price_to_use * get_best_discount(C)) + if(account.account_job && account.account_job.paycheck_department == payment_department) + price_to_use = 0 + if(coin_records.Find(R) || hidden_records.Find(R)) + price_to_use = R.custom_premium_price ? R.custom_premium_price : extra_price if(price_to_use && !account.adjust_money(-price_to_use)) say("You do not possess the funds to purchase [R.name].") flick(icon_deny,src) @@ -805,6 +814,8 @@ GLOBAL_LIST_EMPTY(vending_products) var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department) if(D) D.adjust_money(price_to_use) + SSblackbox.record_feedback("amount", "vending_spent", price_to_use) + //log_econ("[price_to_use] credits were inserted into [src] by [D.account_holder] to buy [R].") if(last_shopper != usr || purchase_message_cooldown < world.time) say("Thank you for shopping with [src]!") purchase_message_cooldown = world.time + 5 SECONDS diff --git a/code/modules/vending/kinkmate.dm b/code/modules/vending/kinkmate.dm index df8a4e8a96..dc4f4e9273 100644 --- a/code/modules/vending/kinkmate.dm +++ b/code/modules/vending/kinkmate.dm @@ -36,13 +36,10 @@ /obj/item/clothing/under/misc/keyholesweater = 2, /obj/item/clothing/under/misc/stripper/mankini = 2, /obj/item/clothing/under/costume/jabroni = 2, - /obj/item/dildo/flared/huge = 3, - /obj/item/reagent_containers/glass/bottle/crocin = 5, - /obj/item/reagent_containers/glass/bottle/camphor = 5 + /obj/item/dildo/flared/huge = 3 ) premium = list( /obj/item/clothing/accessory/skullcodpiece/fake = 3, - /obj/item/reagent_containers/glass/bottle/hexacrocin = 10, /obj/item/clothing/under/pants/chaps = 5 ) refill_canister = /obj/item/vending_refill/kink diff --git a/code/modules/vending/snack.dm b/code/modules/vending/snack.dm index 7aef2b627c..ff8fd46676 100644 --- a/code/modules/vending/snack.dm +++ b/code/modules/vending/snack.dm @@ -12,7 +12,8 @@ /obj/item/reagent_containers/food/snacks/no_raisin = 5, /obj/item/reagent_containers/food/snacks/spacetwinkie = 5, /obj/item/reagent_containers/food/snacks/cheesiehonkers = 5, - /obj/item/reagent_containers/food/snacks/cornchips = 5) + /obj/item/reagent_containers/food/snacks/cornchips = 5, + /obj/item/reagent_containers/food/snacks/energybar = 6) contraband = list( /obj/item/reagent_containers/food/snacks/cracker = 10, /obj/item/reagent_containers/food/snacks/honeybar = 5, diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm index 3a7cd4b03f..2cb3a83257 100644 --- a/code/modules/zombie/items.dm +++ b/code/modules/zombie/items.dm @@ -81,3 +81,14 @@ user.updatehealth() user.adjustOrganLoss(ORGAN_SLOT_BRAIN, -hp_gained) // Zom Bee gibbers "BRAAAAISNSs!1!" user.adjust_nutrition(hp_gained, NUTRITION_LEVEL_FULL) + +/obj/item/paper/guides/antag/romerol_instructions + info = "How to do necromancy with chemicals:
\ +
    \ +
  • Use a dropper or syringe (provided) to inject the Romerol (provided) into a target (not provided)
  • \ +
  • Wait for said target to die, or speed the process up by doing it yourself
  • \ +
  • Run away from the target, as they will be hostile when rising back up
  • \ +
  • Optionally: Inject chemical into foods and drinks to further spread possible infection
  • \ +
  • ???
  • \ +
  • Complete assigned objectives amidst the chaos
  • \ +
" \ No newline at end of file diff --git a/config/game_options.txt b/config/game_options.txt index 405ec0405a..1585d45e6d 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -445,6 +445,7 @@ ROUNDSTART_RACES plasmaman #ROUNDSTART_RACES shadow ROUNDSTART_RACES felinid ROUNDSTART_RACES dwarf +ROUNDSTART_RACES ethereal ## Races that are better than humans in some ways, but worse in others #ROUNDSTART_RACES jelly diff --git a/html/changelog.html b/html/changelog.html index 65a9d69167..b8560b032e 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -50,6 +50,211 @@ -->
+

12 August 2020

+

DeltaFire15 updated:

+
    +
  • hellgun single-pack classification: goodies -> armory
  • +
+

Detective-Google updated:

+
    +
  • hallway table hallway table
  • +
+

Hatterhat updated:

+
    +
  • The temporal katana is now slightly more worthy of the 2 spell point cost, with a smaller, antimagic respecting timestop, less force, and no random blockchance. Society has progressed past the need for blockchance.
  • +
+

LetterN updated:

+
    +
  • Mafia Component
  • +
  • Fixed missing icons and handtele
  • +
+

Putnam3145 updated:

+
    +
  • a whole lot of jank regarding funny part sprite display.
  • +
+

Toriate updated:

+
    +
  • Opossums have migrated into the maintenance tunnels! Seek them out at your own peril!
  • +
+

ancientpower updated:

+
    +
  • Doors added to the west side of box medbay to make things a bit more manageable.
  • +
+

kappa-sama updated:

+
    +
  • smuggler satchel cost 2->1
  • +
  • radio jammer cost 5->2
  • +
  • smuggler satchel uplink description now implies that persistence is disabled
  • +
+

lolman360 updated:

+
    +
  • renameable necklace (accessory, attaches to suit) and ring (glove slot.)
  • +
  • custom rename is now 2048 characters? i think it's characters.
  • +
+

silicons updated:

+
    +
  • You can now use anything as an emoji by doing :/obj/item/path/to/item:. This works for any /atom or subtype.
  • +
+

timothyteakettle updated:

+
    +
  • syndicate agents now have access to mechanical aim enhancers which allow them to aim bullets to bounce off walls
  • +
  • ricochets work properly now for the bullets that support them
  • +
+

zeroisthebiggay updated:

+
    +
  • hair and some sechuds
  • +
  • ce hardsuit radproofing
  • +
+ +

11 August 2020

+

Hatterhat updated:

+
    +
  • PDA uplinks can now steal from pens. Properly. Just make sure to have a pen in your PDA, first.
  • +
+

kappa-sama updated:

+
    +
  • tracer no longer gives you full stamheals per use
  • +
+

zeroisthebiggay updated:

+
    +
  • volaju two
  • +
+ +

10 August 2020

+

Hatterhat updated:

+
    +
  • Parry counterattack text now shows up.
  • +
  • Sterilized gauze is now better at stopping bleeding, and applies slightly faster. Very slightly faster.
  • +
  • Ointment and sutures now hold more in a stack (12 and 15, respectively).
  • +
  • Sterilized gauze can now be made by just pouring 10u sterilizine onto standard medical gauze, instead of having to craft it. Why you had to craft it, I will honestly never know.
  • +
  • Proto-kinetic glaives are more expensive, stagger/cooldown on failed parries increased slightly, perfect parries required for counterattack.
  • +
  • New item: Temporal Katana. 2 points for wizards, timestops upon successful parry, bokken quickparry stats (100 force on melee counter!).
  • +
  • Also you can *smirk. This has no mechanical effect, other than being smug.
  • +
+

KeRSedChaplain updated:

+
    +
  • Added a guide for romerol usage
  • +
  • made infectious zombies not enter softcrit and take no stamina damage
  • +
+

LetterN updated:

+
    +
  • clocktheme color
  • +
  • Ports TGUI-4
  • +
+

Lynxless updated:

+
    +
  • Ports TG #51879
  • +
+

Owai-Seek updated:

+
    +
  • Meatballs now spawn raw from food processors.
  • +
+

Putnam3145 updated:

+
    +
  • Ethereals
  • +
  • (Hexa)crocin
  • +
  • (Hexa)camphor
  • +
  • Tweaked wording for marking tickets IC issue.
  • +
  • Rerolling your traitor goals will ONLY give you "proper" objectives.
  • +
+

Seris02 updated:

+
    +
  • borgs being able to select and use a module when it's too damaged
  • +
+

Sishen1542 updated:

+
    +
  • gave chairs active block/parry in exchange for removal of block_chance
  • +
  • replaces box whiteship tbaton with truncheon
  • +
+

kappa-sama updated:

+
    +
  • made the Dirty Magazines crate cost 4000 instead of 12000 credits
  • +
  • MODS I SPILLED MU JUICE HEJPPHRLP HELPJ JLEP HELP
  • +
+

silicons updated:

+
    +
  • player made areas are no longer valid for malf hacking
  • +
  • default space levels is 4 again.
  • +
  • rats now swarm instead of stacking on one spot.
  • +
  • getting hit by an explosion will now barely hard knockdown, but will leave you somewhat winded.
  • +
+

timothyteakettle updated:

+
    +
  • speech verbs copy through dna copying now
  • +
+ +

09 August 2020

+

Hatterhat updated:

+
    +
  • Proto-kinetic glaives (not crushers) can parry now.
  • +
+

MrJWhit updated:

+
    +
  • Adds a second shutter on the top of the hop line
  • +
+

silicons updated:

+
    +
  • immovable rods no longer drop down chasms
  • +
  • fun removal: squeaking objects now have an 1 second cooldown between squeaks, and will have a 33% chance of interrupting any other squeaking object when Cross()ing, meaning no more ear-fuck conveyor belts.
  • +
+ +

08 August 2020

+

DeltaFire15 updated:

+
    +
  • Roundstart cultists now start with a replica fabricator - no brass though, make your own.
  • +
  • Kindle cast time: 10 > 15, mute after stun end: 2 > 5, slur after mute end: 3 > 5
  • +
  • The ratvarian spear no longer adds negative vitality under very specific circumstances.
  • +
  • The Ratvarian Spear can parry now! Short parries with low leeway, but low cooldown.
  • +
  • The brass claw, a implant-based weapon which gains combo on consecutive hits against the same target.
  • +
  • The sigil of rites, a sigil used to perform various rites with a cost of power and materials
  • +
  • The Rite of Advancement: Used to add a organ or cyberimplant to a clockie without need for surgery.
  • +
  • The Rite of Woundmending: Used to heal all wounds on another cultist, causing toxins damage in return.
  • +
  • The Rite of the Claw: Used to summon a brass claw implant. Maximum of 4 uses per round.
  • +
+

Hatterhat updated:

+
    +
  • You can now buy a toolbox's worth of Mosin-Nagant ammo for a fairly discounted price.
  • +
  • Revolvers from the dedicated kit now have reskinning capabilities.
  • +
  • You can now actually buy the riflery primer, which lets you pump shotguns and work the Mosin's bolt faster.
  • +
  • Bulldog slug magazines now have a unique sprite.
  • +
+

Ludox235 updated:

+
    +
  • Removed an abductee objective that told you to remove all oxygen.
  • +
  • Added a new abductee objective to replace the removed one.
  • +
+

Sishen1542 updated:

+
    +
  • 🅱️oneless
  • +
  • squishy slime emotes
  • +
+

timothyteakettle updated:

+
    +
  • heparin makes you bleed half as much now
  • +
  • cuts make you bleed 25% less now
  • +
  • more items in the loadout and loadout has subcategories now for easier searching
  • +
+ +

07 August 2020

+

dapnee updated:

+
    +
  • fixed active tufs on some space ruins, murderdome VR, and a few on pubby, changed cargo autolathe to techfab, messed with pipe room leading to monastery.
  • +
+

lolman360 updated:

+
    +
  • vendors are now unanchored when tipped. it just fell over it's not bolted to the ground anymore.
  • +
  • podpeople no fat when sunbathing.
  • +
+

silicons updated:

+
    +
  • explosions only recurse one level into storage before dropping 1 level per storage layer.
  • +
  • volumetric storage is now minimum 16 pixels per item because 8 was ridiculous
  • +
  • shieldbash balanace --> balance
  • +
  • attempting to send too long of an emote will now reflect it back to you instead of cutting it off and discarding the overflow.
  • +
  • holoparasites can now play music
  • +
  • lethal blood now causes damaging bleeding instead of outright gibbing
  • +
+

06 August 2020

Auris456852 updated:

    @@ -1231,87 +1436,6 @@
    • Balanced vending machine prices to be generally more affordable, a minority has been priced up though.
    - -

    10 June 2020

    -

    DeltaFire15 updated:

    -
      -
    • Golems / simillar now inherit the neutered antag datum if the creator is neutered.
    • -
    -

    Ghommie updated:

    -
      -
    • Fixing missing pill type buttons from the chem master UI.
    • -
    -

    Naksu updated:

    -
      -
    • Lighting corner updates are ever so slightly faster.
    • -
    -

    Putnam for helping me code the contamination clearing on people updated:

    -
      -
    • Lab made Zeolites have been remade anew and more affective now that they refined the best possable way to mix and make a supper Zeolite capable of clearing contamination form not only people but items!
    • -
    -

    Trilbyspaceclone updated:

    -
      -
    • Tank Dispender has been moved into toxin storage from toxins
    • -
    -

    kevinz000 updated:

    -
      -
    • traitor classes can now be poplocked. hijack/glorious death are now locked to 25/20 respectively.
    • -
    -

    timothyteakettle updated:

    -
      -
    • adds a new fermichem, used for creating sentient plushies!
    • -
    • Mice can now breed using cheese wedges
    • -
    • Royal cheese can be crafted to convert a mouse into king rat
    • -
    - -

    09 June 2020

    -

    Anonymous updated:

    -
      -
    • Added Orville-inspired clothing as a worthy alternative to Trek stuff.
    • -
    • Adds chaplain role allowance to the TMP Service Uniform loadout.
    • -
    • Adds paramedic in every medsci mentioned uniform loadout.
    • -
    -

    DeltaFire15 updated:

    -
      -
    • Offstation AIs can once again only interact with their z-level
    • -
    -

    Ghommie updated:

    -
      -
    • Fixing IC material containers interaction with stacks, for real.
    • -
    -

    Trilbyspaceclone updated:

    -
      -
    • Gasses like BZ and Masiam seem to just sell for less in cargo, markets seem to change it seems
    • -
    -

    kevinz000 updated:

    -
      -
    • plantpeople should stop dying in the halls now
    • -
    • Modifier-independent hotkey bindings have been added.
    • -
    - -

    08 June 2020

    -

    DeltaFire15 updated:

    -
      -
    • Delinging now properly removes their special role
    • -
    • Keycard auth devices now require two seperate IDs with sufficient access to auth.
    • -
    -

    Linzolle updated:

    -
      -
    • shotguns no longer delete chambered shells while firing
    • -
    -

    kevinz000 updated:

    -
      -
    • test
    • -
    -

    shellspeed1 updated:

    -
      -
    • Internal tanks are now printable at the engineering lathe.
    • -
    -

    timothyteakettle updated:

    -
      -
    • newly created areas using blueprints now maintain the previous areas noteleport value
    • -
    • kudzu seeds now actually spawn vines
    • -
GoonStation 13 Development Team diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index c82d7d4058..64b9e13271 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -26753,3 +26753,158 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. less often zeroisthebiggay: - rscadd: nukies can buy holoparasites +2020-08-07: + dapnee: + - bugfix: fixed active tufs on some space ruins, murderdome VR, and a few on pubby, + changed cargo autolathe to techfab, messed with pipe room leading to monastery. + lolman360: + - bugfix: vendors are now unanchored when tipped. it just fell over it's not bolted + to the ground anymore. + - bugfix: podpeople no fat when sunbathing. + silicons: + - balance: explosions only recurse one level into storage before dropping 1 level + per storage layer. + - tweak: volumetric storage is now minimum 16 pixels per item because 8 was ridiculous + - spellcheck: shieldbash balanace --> balance + - rscadd: attempting to send too long of an emote will now reflect it back to you + instead of cutting it off and discarding the overflow. + - rscadd: holoparasites can now play music + - balance: lethal blood now causes damaging bleeding instead of outright gibbing +2020-08-08: + DeltaFire15: + - balance: Roundstart cultists now start with a replica fabricator - no brass though, + make your own. + - balance: 'Kindle cast time: 10 > 15, mute after stun end: 2 > 5, slur after mute + end: 3 > 5' + - bugfix: The ratvarian spear no longer adds negative vitality under very specific + circumstances. + - balance: The Ratvarian Spear can parry now! Short parries with low leeway, but + low cooldown. + - rscadd: The brass claw, a implant-based weapon which gains combo on consecutive + hits against the same target. + - rscadd: The sigil of rites, a sigil used to perform various rites with a cost + of power and materials + - rscadd: 'The Rite of Advancement: Used to add a organ or cyberimplant to a clockie + without need for surgery.' + - rscadd: 'The Rite of Woundmending: Used to heal all wounds on another cultist, + causing toxins damage in return.' + - rscadd: 'The Rite of the Claw: Used to summon a brass claw implant. Maximum of + 4 uses per round.' + Hatterhat: + - rscadd: You can now buy a toolbox's worth of Mosin-Nagant ammo for a fairly discounted + price. + - rscadd: Revolvers from the dedicated kit now have reskinning capabilities. + - bugfix: You can now actually buy the riflery primer, which lets you pump shotguns + and work the Mosin's bolt faster. + - imageadd: Bulldog slug magazines now have a unique sprite. + Ludox235: + - rscdel: Removed an abductee objective that told you to remove all oxygen. + - rscadd: Added a new abductee objective to replace the removed one. + Sishen1542: + - tweak: "\U0001F171\uFE0Foneless" + - rscadd: squishy slime emotes + timothyteakettle: + - tweak: heparin makes you bleed half as much now + - tweak: cuts make you bleed 25% less now + - rscadd: more items in the loadout and loadout has subcategories now for easier + searching +2020-08-09: + Hatterhat: + - rscadd: Proto-kinetic glaives (not crushers) can parry now. + MrJWhit: + - rscadd: Adds a second shutter on the top of the hop line + silicons: + - bugfix: immovable rods no longer drop down chasms + - rscdel: 'fun removal: squeaking objects now have an 1 second cooldown between + squeaks, and will have a 33% chance of interrupting any other squeaking object + when Cross()ing, meaning no more ear-fuck conveyor belts.' +2020-08-10: + Hatterhat: + - bugfix: Parry counterattack text now shows up. + - balance: Sterilized gauze is now better at stopping bleeding, and applies slightly + faster. Very slightly faster. + - balance: Ointment and sutures now hold more in a stack (12 and 15, respectively). + - tweak: Sterilized gauze can now be made by just pouring 10u sterilizine onto standard + medical gauze, instead of having to craft it. Why you had to craft it, I will + honestly never know. + - balance: Proto-kinetic glaives are more expensive, stagger/cooldown on failed + parries increased slightly, perfect parries required for counterattack. + - rscadd: 'New item: Temporal Katana. 2 points for wizards, timestops upon successful + parry, bokken quickparry stats (100 force on melee counter!).' + - rscadd: Also you can *smirk. This has no mechanical effect, other than being smug. + KeRSedChaplain: + - rscadd: Added a guide for romerol usage + - balance: made infectious zombies not enter softcrit and take no stamina damage + LetterN: + - tweak: clocktheme color + - code_imp: Ports TGUI-4 + Lynxless: + - rscadd: 'Ports TG #51879' + Owai-Seek: + - tweak: Meatballs now spawn raw from food processors. + Putnam3145: + - rscadd: Ethereals + - rscdel: (Hexa)crocin + - rscdel: (Hexa)camphor + - tweak: Tweaked wording for marking tickets IC issue. + - tweak: Rerolling your traitor goals will ONLY give you "proper" objectives. + Seris02: + - bugfix: borgs being able to select and use a module when it's too damaged + Sishen1542: + - balance: gave chairs active block/parry in exchange for removal of block_chance + - tweak: replaces box whiteship tbaton with truncheon + kappa-sama: + - balance: made the Dirty Magazines crate cost 4000 instead of 12000 credits + - rscadd: MODS I SPILLED MU JUICE HEJPPHRLP HELPJ JLEP HELP + silicons: + - balance: player made areas are no longer valid for malf hacking + - tweak: default space levels is 4 again. + - tweak: rats now swarm instead of stacking on one spot. + - balance: getting hit by an explosion will now barely hard knockdown, but will + leave you somewhat winded. + timothyteakettle: + - tweak: speech verbs copy through dna copying now +2020-08-11: + Hatterhat: + - bugfix: PDA uplinks can now steal from pens. Properly. Just make sure to have + a pen in your PDA, first. + kappa-sama: + - tweak: tracer no longer gives you full stamheals per use + zeroisthebiggay: + - rscadd: volaju two +2020-08-12: + DeltaFire15: + - balance: 'hellgun single-pack classification: goodies -> armory' + Detective-Google: + - bugfix: hallway table hallway table + Hatterhat: + - balance: The temporal katana is now slightly more worthy of the 2 spell point + cost, with a smaller, antimagic respecting timestop, less force, and no random + blockchance. Society has progressed past the need for blockchance. + LetterN: + - rscadd: Mafia Component + - bugfix: Fixed missing icons and handtele + Putnam3145: + - bugfix: a whole lot of jank regarding funny part sprite display. + Toriate: + - rscadd: Opossums have migrated into the maintenance tunnels! Seek them out at + your own peril! + ancientpower: + - tweak: Doors added to the west side of box medbay to make things a bit more manageable. + kappa-sama: + - tweak: smuggler satchel cost 2->1 + - tweak: radio jammer cost 5->2 + - bugfix: smuggler satchel uplink description now implies that persistence is disabled + lolman360: + - rscadd: renameable necklace (accessory, attaches to suit) and ring (glove slot.) + - tweak: custom rename is now 2048 characters? i think it's characters. + silicons: + - rscadd: You can now use anything as an emoji by doing :/obj/item/path/to/item:. + This works for any /atom or subtype. + timothyteakettle: + - rscadd: syndicate agents now have access to mechanical aim enhancers which allow + them to aim bullets to bounce off walls + - bugfix: ricochets work properly now for the bullets that support them + zeroisthebiggay: + - imageadd: hair and some sechuds + - balance: ce hardsuit radproofing diff --git a/html/changelogs/AutoChangeLog-pr-12969.yml b/html/changelogs/AutoChangeLog-pr-12969.yml deleted file mode 100644 index 8042556cde..0000000000 --- a/html/changelogs/AutoChangeLog-pr-12969.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "silicons" -delete-after: True -changes: - - rscadd: "holoparasites can now play music" diff --git a/html/changelogs/AutoChangeLog-pr-13001.yml b/html/changelogs/AutoChangeLog-pr-13001.yml deleted file mode 100644 index bc4e5b5311..0000000000 --- a/html/changelogs/AutoChangeLog-pr-13001.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "silicons" -delete-after: True -changes: - - balance: "explosions only recurse one level into storage before dropping 1 level per storage layer." diff --git a/html/changelogs/AutoChangeLog-pr-13006.yml b/html/changelogs/AutoChangeLog-pr-13006.yml deleted file mode 100644 index e13902099a..0000000000 --- a/html/changelogs/AutoChangeLog-pr-13006.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "silicons" -delete-after: True -changes: - - tweak: "volumetric storage is now minimum 16 pixels per item because 8 was ridiculous" diff --git a/html/changelogs/AutoChangeLog-pr-13032.yml b/html/changelogs/AutoChangeLog-pr-13032.yml deleted file mode 100644 index 06a8e274b5..0000000000 --- a/html/changelogs/AutoChangeLog-pr-13032.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "lolman360" -delete-after: True -changes: - - bugfix: "podpeople no fat when sunbathing." diff --git a/html/changelogs/AutoChangeLog-pr-13035.yml b/html/changelogs/AutoChangeLog-pr-13035.yml deleted file mode 100644 index 1c2bf4dc8d..0000000000 --- a/html/changelogs/AutoChangeLog-pr-13035.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "lolman360" -delete-after: True -changes: - - bugfix: "vendors are now unanchored when tipped. it just fell over it's not bolted to the ground anymore." diff --git a/icons/effects/supplypod_down_target.dmi b/icons/effects/mouse_pointers/supplypod_down_target.dmi similarity index 100% rename from icons/effects/supplypod_down_target.dmi rename to icons/effects/mouse_pointers/supplypod_down_target.dmi diff --git a/icons/effects/mouse_pointers/supplypod_pickturf.dmi b/icons/effects/mouse_pointers/supplypod_pickturf.dmi new file mode 100644 index 0000000000..3ca1131e1a Binary files /dev/null and b/icons/effects/mouse_pointers/supplypod_pickturf.dmi differ diff --git a/icons/effects/mouse_pointers/supplypod_pickturf_down.dmi b/icons/effects/mouse_pointers/supplypod_pickturf_down.dmi new file mode 100644 index 0000000000..113fe47540 Binary files /dev/null and b/icons/effects/mouse_pointers/supplypod_pickturf_down.dmi differ diff --git a/icons/effects/supplypod_target.dmi b/icons/effects/mouse_pointers/supplypod_target.dmi similarity index 100% rename from icons/effects/supplypod_target.dmi rename to icons/effects/mouse_pointers/supplypod_target.dmi diff --git a/icons/misc/language.dmi b/icons/misc/language.dmi index 155dbab98d..9501dc9216 100644 Binary files a/icons/misc/language.dmi and b/icons/misc/language.dmi differ diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi index 13f97d3761..50bf65b27f 100644 Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ diff --git a/icons/mob/clothing/eyes.dmi b/icons/mob/clothing/eyes.dmi index cd2b84a143..876159c258 100644 Binary files a/icons/mob/clothing/eyes.dmi and b/icons/mob/clothing/eyes.dmi differ diff --git a/icons/mob/clothing/head.dmi b/icons/mob/clothing/head.dmi index a6e75123bb..0ad9452994 100644 Binary files a/icons/mob/clothing/head.dmi and b/icons/mob/clothing/head.dmi differ diff --git a/icons/mob/clothing/suit.dmi b/icons/mob/clothing/suit.dmi index 7a690bb29f..c94e8a46d3 100644 Binary files a/icons/mob/clothing/suit.dmi and b/icons/mob/clothing/suit.dmi differ diff --git a/icons/mob/clothing/suit_digi.dmi b/icons/mob/clothing/suit_digi.dmi index 2616c0b893..dd00713770 100644 Binary files a/icons/mob/clothing/suit_digi.dmi and b/icons/mob/clothing/suit_digi.dmi differ diff --git a/icons/mob/clothing/uniform.dmi b/icons/mob/clothing/uniform.dmi index fa376635f9..3d24a9addd 100644 Binary files a/icons/mob/clothing/uniform.dmi and b/icons/mob/clothing/uniform.dmi differ diff --git a/icons/mob/clothing/uniform_digi.dmi b/icons/mob/clothing/uniform_digi.dmi index af72ac0e7b..bcb894033e 100644 Binary files a/icons/mob/clothing/uniform_digi.dmi and b/icons/mob/clothing/uniform_digi.dmi differ diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi index c21fa47b9c..c6dc7130c7 100644 Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi index a4e2a9d5b2..8055233ea7 100644 Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ diff --git a/icons/mob/inhands/antag/clockwork_lefthand.dmi b/icons/mob/inhands/antag/clockwork_lefthand.dmi index 88bd8ab710..080d7fdc83 100644 Binary files a/icons/mob/inhands/antag/clockwork_lefthand.dmi and b/icons/mob/inhands/antag/clockwork_lefthand.dmi differ diff --git a/icons/mob/inhands/antag/clockwork_righthand.dmi b/icons/mob/inhands/antag/clockwork_righthand.dmi index 20190e4add..42715d6e92 100644 Binary files a/icons/mob/inhands/antag/clockwork_righthand.dmi and b/icons/mob/inhands/antag/clockwork_righthand.dmi differ diff --git a/icons/obj/ammo.dmi b/icons/obj/ammo.dmi index bebb625440..e1caad8279 100644 Binary files a/icons/obj/ammo.dmi and b/icons/obj/ammo.dmi differ diff --git a/icons/obj/clockwork_objects.dmi b/icons/obj/clockwork_objects.dmi index ae919d85a4..156d4fa0c6 100644 Binary files a/icons/obj/clockwork_objects.dmi and b/icons/obj/clockwork_objects.dmi differ diff --git a/icons/obj/clothing/accessories.dmi b/icons/obj/clothing/accessories.dmi index c62a88c829..7d13e3f802 100644 Binary files a/icons/obj/clothing/accessories.dmi and b/icons/obj/clothing/accessories.dmi differ diff --git a/icons/obj/clothing/glasses.dmi b/icons/obj/clothing/glasses.dmi index e8ba88a12f..4fce479a9d 100644 Binary files a/icons/obj/clothing/glasses.dmi and b/icons/obj/clothing/glasses.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index 90f1fba315..60304999fa 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index 258eb0dc35..e8a360bc87 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/clothing/uniforms.dmi b/icons/obj/clothing/uniforms.dmi index 57424c7b99..76f5722c8b 100644 Binary files a/icons/obj/clothing/uniforms.dmi and b/icons/obj/clothing/uniforms.dmi differ diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi index 28cdefd331..078cadfd60 100644 Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ diff --git a/icons/obj/food/piecake.dmi b/icons/obj/food/piecake.dmi index 5638235217..935f7e8ad5 100644 Binary files a/icons/obj/food/piecake.dmi and b/icons/obj/food/piecake.dmi differ diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi index 8c50e7da27..9c6df36f49 100644 Binary files a/icons/obj/guns/projectile.dmi and b/icons/obj/guns/projectile.dmi differ diff --git a/icons/obj/janitor.dmi b/icons/obj/janitor.dmi index 1f520a42bd..b240391328 100644 Binary files a/icons/obj/janitor.dmi and b/icons/obj/janitor.dmi differ diff --git a/icons/obj/machines/research.dmi b/icons/obj/machines/research.dmi index 7d64c494fd..7dcd4e6bcb 100644 Binary files a/icons/obj/machines/research.dmi and b/icons/obj/machines/research.dmi differ diff --git a/icons/obj/mafia.dmi b/icons/obj/mafia.dmi new file mode 100644 index 0000000000..c44b80aba1 Binary files /dev/null and b/icons/obj/mafia.dmi differ diff --git a/icons/obj/modular_console.dmi b/icons/obj/modular_console.dmi index cdcf6f1bd5..5d3cd0312c 100644 Binary files a/icons/obj/modular_console.dmi and b/icons/obj/modular_console.dmi differ diff --git a/icons/obj/modular_laptop.dmi b/icons/obj/modular_laptop.dmi index 7cf5a77621..fd24d27a97 100644 Binary files a/icons/obj/modular_laptop.dmi and b/icons/obj/modular_laptop.dmi differ diff --git a/icons/obj/modular_tablet.dmi b/icons/obj/modular_tablet.dmi index 2f9a0559ae..32edb57475 100644 Binary files a/icons/obj/modular_tablet.dmi and b/icons/obj/modular_tablet.dmi differ diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index 3996f0ead2..8db1156dea 100755 Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ diff --git a/modular_citadel/code/datums/status_effects/chems.dm b/modular_citadel/code/datums/status_effects/chems.dm index 4fa2a51fac..0e971d4ced 100644 --- a/modular_citadel/code/datums/status_effects/chems.dm +++ b/modular_citadel/code/datums/status_effects/chems.dm @@ -583,16 +583,6 @@ C.Stun(60) to_chat(owner, "Your muscles seize up, then start spasming wildy!") - //wah intensifies wah-rks - else if (lowertext(customTriggers[trigger]) == "cum")//aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - if (lewd) - if(ishuman(C)) - var/mob/living/carbon/human/H = C - H.mob_climax(forced_climax=TRUE) - C.SetStun(10)//We got your stun effects in somewhere, Kev. - else - C.throw_at(get_step_towards(hearing_args[HEARING_SPEAKER],C), 3, 1) //cut this if it's too hard to get working - //kneel (knockdown) else if (lowertext(customTriggers[trigger]) == "kneel")//as close to kneeling as you can get, I suppose. to_chat(owner, "You drop to the ground unsurreptitiously.") @@ -674,15 +664,6 @@ deltaResist *= 1.25 if (owner.reagents.has_reagent(/datum/reagent/medicine/neurine)) deltaResist *= 1.5 - if (!(owner.client?.prefs.cit_toggles & NO_APHRO) && lewd) - if (owner.reagents.has_reagent(/datum/reagent/drug/anaphrodisiac)) - deltaResist *= 1.5 - if (owner.reagents.has_reagent(/datum/reagent/drug/anaphrodisiacplus)) - deltaResist *= 2 - if (owner.reagents.has_reagent(/datum/reagent/drug/aphrodisiac)) - deltaResist *= 0.75 - if (owner.reagents.has_reagent(/datum/reagent/drug/aphrodisiacplus)) - deltaResist *= 0.5 //Antag resistance //cultists are already brainwashed by their god if(iscultist(owner)) diff --git a/modular_citadel/code/modules/client/loadout/__donator.dm b/modular_citadel/code/modules/client/loadout/__donator.dm index d428fc290a..378c70d187 100644 --- a/modular_citadel/code/modules/client/loadout/__donator.dm +++ b/modular_citadel/code/modules/client/loadout/__donator.dm @@ -1,496 +1,497 @@ //This is the file that handles donator loadout items. -/datum/gear/pingcoderfailsafe +/datum/gear/donator name = "IF YOU SEE THIS, PING A CODER RIGHT NOW!" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/bikehorn/golden + category = LOADOUT_CATEGORY_DONATOR ckeywhitelist = list("This entry should never appear with this variable set.") //If it does, then that means somebody fucked up the whitelist system pretty hard -/datum/gear/donortestingbikehorn +/datum/gear/donator/donortestingbikehorn name = "Donor item testing bikehorn" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/bikehorn geargroupID = list("DONORTEST") //This is a list mainly for the sake of testing, but geargroupID works just fine with ordinary strings -/datum/gear/kevhorn +/datum/gear/donator/kevhorn name = "Airhorn" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/bikehorn/airhorn ckeywhitelist = list("kevinz000") -/datum/gear/cebusoap +/datum/gear/donator/cebusoap name = "Cebutris' soap" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/custom/ceb_soap ckeywhitelist = list("cebutris") -/datum/gear/kiaracloak +/datum/gear/donator/kiaracloak name = "Kiara's cloak" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/cloak/inferno ckeywhitelist = list("inferno707") -/datum/gear/kiaracollar +/datum/gear/donator/kiaracollar name = "Kiara's collar" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/petcollar/inferno ckeywhitelist = list("inferno707") -/datum/gear/kiaramedal +/datum/gear/donator/kiaramedal name = "Insignia of Steele" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/clothing/accessory/medal/steele ckeywhitelist = list("inferno707") -/datum/gear/hheart +/datum/gear/donator/hheart name = "The Hollow Heart" - category = SLOT_WEAR_MASK + slot = SLOT_WEAR_MASK path = /obj/item/clothing/mask/hheart ckeywhitelist = list("inferno707") -/datum/gear/engravedzippo +/datum/gear/donator/engravedzippo name = "Engraved zippo" - category = SLOT_HANDS + slot = SLOT_HANDS path = /obj/item/lighter/gold ckeywhitelist = list("dirtyoldharry") -/datum/gear/geisha +/datum/gear/donator/geisha name = "Geisha suit" - category = SLOT_W_UNIFORM + slot = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/geisha ckeywhitelist = list("atiefling") -/datum/gear/specialscarf +/datum/gear/donator/specialscarf name = "Special scarf" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/scarf/zomb ckeywhitelist = list("zombierobin") -/datum/gear/redmadcoat +/datum/gear/donator/redmadcoat name = "The Mad's labcoat" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/toggle/labcoat/mad/red ckeywhitelist = list("zombierobin") -/datum/gear/santahat +/datum/gear/donator/santahat name = "Santa hat" - category = SLOT_HEAD + slot = SLOT_HEAD path = /obj/item/clothing/head/santa/fluff ckeywhitelist = list("illotafv") -/datum/gear/reindeerhat +/datum/gear/donator/reindeerhat name = "Reindeer hat" - category = SLOT_HEAD + slot = SLOT_HEAD path = /obj/item/clothing/head/hardhat/reindeer/fluff ckeywhitelist = list("illotafv") -/datum/gear/treeplushie +/datum/gear/donator/treeplushie name = "Christmas tree plushie" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/toy/plush/tree ckeywhitelist = list("illotafv") -/datum/gear/santaoutfit +/datum/gear/donator/santaoutfit name = "Santa costume" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/space/santa/fluff ckeywhitelist = list("illotafv") -/datum/gear/treecloak +/datum/gear/donator/treecloak name = "Christmas tree cloak" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/cloak/festive ckeywhitelist = list("illotafv") -/datum/gear/carrotplush +/datum/gear/donator/carrotplush name = "Carrot plushie" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/toy/plush/carrot ckeywhitelist = list("improvedname") -/datum/gear/carrotcloak +/datum/gear/donator/carrotcloak name = "Carrot cloak" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/cloak/carrot ckeywhitelist = list("improvedname") -/datum/gear/albortorosamask +/datum/gear/donator/albortorosamask name = "Alborto Rosa mask" - category = SLOT_WEAR_MASK + slot = SLOT_WEAR_MASK path = /obj/item/clothing/mask/luchador/zigfie ckeywhitelist = list("zigfie") -/datum/gear/mankini +/datum/gear/donator/mankini name = "Mankini" - category = SLOT_W_UNIFORM + slot = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/stripper/mankini ckeywhitelist = list("zigfie") -/datum/gear/pinkshoes +/datum/gear/donator/pinkshoes name = "Pink shoes" - category = SLOT_SHOES + slot = SLOT_SHOES path = /obj/item/clothing/shoes/sneakers/pink ckeywhitelist = list("zigfie") -/datum/gear/reecesgreatcoat +/datum/gear/donator/reecesgreatcoat name = "Reece's Great Coat" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/trenchcoat/green ckeywhitelist = list("geemiesif") -/datum/gear/russianflask +/datum/gear/donator/russianflask name = "Russian flask" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/reagent_containers/food/drinks/flask/russian cost = 2 ckeywhitelist = list("slomka") -/datum/gear/stalkermask +/datum/gear/donator/stalkermask name = "S.T.A.L.K.E.R. mask" - category = SLOT_WEAR_MASK + slot = SLOT_WEAR_MASK path = /obj/item/clothing/mask/gas/stalker ckeywhitelist = list("slomka") -/datum/gear/stripedcollar +/datum/gear/donator/stripedcollar name = "Striped collar" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/petcollar/stripe ckeywhitelist = list("jademanique") -/datum/gear/performersoutfit +/datum/gear/donator/performersoutfit name = "Bluish performer's outfit" - category = SLOT_W_UNIFORM + slot = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/singer/yellow/custom ckeywhitelist = list("killer402402") -/datum/gear/vermillion +/datum/gear/donator/vermillion name = "Vermillion clothing" - category = SLOT_W_UNIFORM + slot = SLOT_W_UNIFORM path = /obj/item/clothing/suit/vermillion ckeywhitelist = list("fractious") -/datum/gear/AM4B +/datum/gear/donator/AM4B name = "Foam Force AM4-B" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/gun/ballistic/automatic/AM4B ckeywhitelist = list("zeronetalpha") -/datum/gear/carrotsatchel +/datum/gear/donator/carrotsatchel name = "Carrot Satchel" - category = SLOT_HANDS + slot = SLOT_HANDS path = /obj/item/storage/backpack/satchel/carrot ckeywhitelist = list("improvedname") -/datum/gear/naomisweater +/datum/gear/donator/naomisweater name = "worn black sweater" - category = SLOT_W_UNIFORM + slot = SLOT_W_UNIFORM path = /obj/item/clothing/under/sweater/black/naomi ckeywhitelist = list("technicalmagi") -/datum/gear/naomicollar +/datum/gear/donator/naomicollar name = "worn pet collar" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/petcollar/naomi ckeywhitelist = list("technicalmagi") -/datum/gear/gladiator +/datum/gear/donator/gladiator name = "Gladiator Armor" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/under/costume/gladiator ckeywhitelist = list("aroche") -/datum/gear/bloodredtie +/datum/gear/donator/bloodredtie name = "Blood Red Tie" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/tie/bloodred ckeywhitelist = list("kyutness") -/datum/gear/puffydress +/datum/gear/donator/puffydress name = "Puffy Dress" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/puffydress ckeywhitelist = list("stallingratt") -/datum/gear/labredblack +/datum/gear/donator/labredblack name = "Black and Red Coat" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/toggle/labcoat/labredblack ckeywhitelist = list("blakeryan", "durandalphor") -/datum/gear/torisword +/datum/gear/donator/torisword name = "Rainbow Zweihander" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/dualsaber/hypereutactic/toy/rainbow ckeywhitelist = list("annoymous35") -/datum/gear/darksabre +/datum/gear/donator/darksabre name = "Dark Sabre" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/toy/darksabre ckeywhitelist = list("inferno707") -datum/gear/darksabresheath +/datum/gear/donator/darksabresheath name = "Dark Sabre Sheath" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/storage/belt/sabre/darksabre ckeywhitelist = list("inferno707") -/datum/gear/toriball +/datum/gear/donator/toriball name = "Rainbow Tennis Ball" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/toy/tennis/rainbow ckeywhitelist = list("annoymous35") -/datum/gear/izzyball +/datum/gear/donator/izzyball name = "Katlin's Ball" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/toy/tennis/rainbow/izzy ckeywhitelist = list("izzyinbox") -/datum/gear/cloak +/datum/gear/donator/cloak name = "Green Cloak" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/cloak/green ckeywhitelist = list("killer402402") -/datum/gear/steelflask +/datum/gear/donator/steelflask name = "Steel Flask" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/reagent_containers/food/drinks/flask/steel cost = 2 ckeywhitelist = list("nik707") -/datum/gear/paperhat +/datum/gear/donator/paperhat name = "Paper Hat" - category = SLOT_HEAD + slot = SLOT_HEAD path = /obj/item/clothing/head/paperhat ckeywhitelist = list("kered2") -/datum/gear/cloakce +/datum/gear/donator/cloakce name = "Polychromic CE Cloak" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/clothing/neck/cloak/polychromic/polyce ckeywhitelist = list("worksbythesea", "blakeryan") -/datum/gear/ssk +/datum/gear/donator/ssk name = "Stun Sword Kit" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/ssword_kit ckeywhitelist = list("phillip458") -/datum/gear/techcoat +/datum/gear/donator/techcoat name = "Techomancers Labcoat" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/clothing/suit/toggle/labcoat/mad/techcoat ckeywhitelist = list("wilchen") -/datum/gear/leechjar +/datum/gear/donator/leechjar name = "Jar of Leeches" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/custom/leechjar ckeywhitelist = list("sgtryder") -/datum/gear/darkarmor +/datum/gear/donator/darkarmor name = "Dark Armor" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/clothing/suit/armor/vest/darkcarapace ckeywhitelist = list("inferno707") -/datum/gear/devilwings +/datum/gear/donator/devilwings name = "Strange Wings" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/devilwings ckeywhitelist = list("kitsun") -/datum/gear/flagcape +/datum/gear/donator/flagcape name = "US Flag Cape" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/clothing/neck/flagcape ckeywhitelist = list("darnchacha") -/datum/gear/luckyjack +/datum/gear/donator/luckyjack name = "Lucky Jackboots" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/clothing/shoes/lucky ckeywhitelist = list("donaldtrumpthecommunist") -/datum/gear/raiqbawks +/datum/gear/donator/raiqbawks name = "Miami Boombox" - category = SLOT_HANDS + slot = SLOT_HANDS cost = 2 path = /obj/item/boombox/raiq ckeywhitelist = list("chefferz") -/datum/gear/m41 +/datum/gear/donator/m41 name = "Toy M41" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/toy/gun/m41 ckeywhitelist = list("thalverscholen") -/datum/gear/Divine_robes +/datum/gear/donator/Divine_robes name = "Divine robes" - category = SLOT_W_UNIFORM + slot = SLOT_W_UNIFORM path = /obj/item/clothing/under/custom/lunasune ckeywhitelist = list("invader4352") -/datum/gear/gothcoat +/datum/gear/donator/gothcoat name = "Goth Coat" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/gothcoat ckeywhitelist = list("norko") -/datum/gear/corgisuit +/datum/gear/donator/corgisuit name = "Corgi Suit" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/ian_costume ckeywhitelist = list("cathodetherobot") -/datum/gear/sharkcloth +/datum/gear/donator/sharkcloth name = "Leon's Skimpy Outfit" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/under/custom/leoskimpy ckeywhitelist = list("spectrosis") -/datum/gear/mimemask +/datum/gear/donator/mimemask name = "Mime Mask" - category = SLOT_WEAR_MASK + slot = SLOT_WEAR_MASK path = /obj/item/clothing/mask/gas/mime ckeywhitelist = list("pireamaineach") -/datum/gear/mimeoveralls +/datum/gear/donator/mimeoveralls name = "Mime's Overalls" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/under/custom/mimeoveralls ckeywhitelist = list("pireamaineach") -/datum/gear/soulneck +/datum/gear/donator/soulneck name = "Soul Necklace" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/undertale ckeywhitelist = list("twilightic") -/datum/gear/frenchberet +/datum/gear/donator/frenchberet name = "French Beret" - category = SLOT_HEAD + slot = SLOT_HEAD path = /obj/item/clothing/head/frenchberet ckeywhitelist = list("notazoltan") -/datum/gear/zuliecloak +/datum/gear/donator/zuliecloak name = "Project: Zul-E" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/cloak/zuliecloak ckeywhitelist = list("asky") -/datum/gear/blackredgold +/datum/gear/donator/blackredgold name = "Black, Red, and Gold Coat" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/blackredgold ckeywhitelist = list("ttbnc") -/datum/gear/fritzplush +/datum/gear/donator/fritzplush name = "Fritz Plushie" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/toy/plush/mammal/dog/fritz ckeywhitelist = list("analwerewolf") -/datum/gear/kimono +/datum/gear/donator/kimono name = "Kimono" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/kimono ckeywhitelist = list("sfox63") -/datum/gear/commjacket +/datum/gear/donator/commjacket name = "Dusty Commisar's Cloak" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/commjacket ckeywhitelist = list("sadisticbatter") -/datum/gear/mw2_russian_para +/datum/gear/donator/mw2_russian_para name = "Russian Paratrooper Jumper" - category = SLOT_W_UNIFORM + slot = SLOT_W_UNIFORM path = /obj/item/clothing/under/custom/mw2_russian_para ckeywhitelist = list("investigator77") -/datum/gear/longblackgloves +/datum/gear/donator/longblackgloves name = "Luna's Gauntlets" - category = SLOT_GLOVES + slot = SLOT_GLOVES path = /obj/item/clothing/gloves/longblackgloves ckeywhitelist = list("bigmanclancy") -/datum/gear/trendy_fit +/datum/gear/donator/trendy_fit name = "Trendy Fit" - category = SLOT_W_UNIFORM + slot = SLOT_W_UNIFORM path = /obj/item/clothing/under/custom/trendy_fit ckeywhitelist = list("midgetdragon") -/datum/gear/singery +/datum/gear/donator/singery name = "Yellow Performer Outfit" - category = SLOT_W_UNIFORM + slot = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/singer/yellow ckeywhitelist = list("maxlynchy") -/datum/gear/csheet +/datum/gear/donator/csheet name = "NT Bedsheet" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/bedsheet/captain ckeywhitelist = list("tikibomb") -/datum/gear/borgplush +/datum/gear/donator/borgplush name = "Robot Plush" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/toy/plush/borgplushie ckeywhitelist = list("nicholaiavenicci") -/datum/gear/donorberet +/datum/gear/donator/donorberet name = "Atmos Beret" - category = SLOT_HEAD + slot = SLOT_HEAD path = /obj/item/clothing/head/blueberet ckeywhitelist = list("foxystalin") -/datum/gear/donorgoggles +/datum/gear/donator/donorgoggles name = "Flight Goggles" - category = SLOT_HEAD + slot = SLOT_HEAD path = /obj/item/clothing/head/flight ckeywhitelist = list("maxlynchy") -/datum/gear/onionneck +/datum/gear/donator/onionneck name = "Onion Necklace" - category = SLOT_NECK + slot = SLOT_NECK path = /obj/item/clothing/neck/necklace/onion ckeywhitelist = list("cdrcross") -/datum/gear/mikubikini +/datum/gear/donator/mikubikini name = "starlight singer bikini" - category = SLOT_W_UNIFORM + slot = SLOT_W_UNIFORM path = /obj/item/clothing/under/custom/mikubikini ckeywhitelist = list("grandvegeta") -/datum/gear/mikujacket +/datum/gear/donator/mikujacket name = "starlight singer jacket" - category = SLOT_WEAR_SUIT + slot = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/mikujacket ckeywhitelist = list("grandvegeta") -/datum/gear/mikuhair +/datum/gear/donator/mikuhair name = "starlight singer hair" - category = SLOT_W_UNIFORM + slot = SLOT_W_UNIFORM path = /obj/item/clothing/head/mikuhair ckeywhitelist = list("grandvegeta") -/datum/gear/mikugloves +/datum/gear/donator/mikugloves name = "starlight singer gloves" - category = SLOT_GLOVES + slot = SLOT_GLOVES path = /obj/item/clothing/gloves/mikugloves ckeywhitelist = list("grandvegeta") -/datum/gear/mikuleggings +/datum/gear/donator/mikuleggings name = "starlight singer leggings" - category = SLOT_SHOES + slot = SLOT_SHOES path = /obj/item/clothing/shoes/sneakers/mikuleggings ckeywhitelist = list("grandvegeta") -/datum/gear/cosmos +/datum/gear/donator/cosmos name = "cosmic space bedsheet" - category = SLOT_IN_BACKPACK + slot = SLOT_IN_BACKPACK path = /obj/item/bedsheet/cosmos ckeywhitelist = list("grunnyyy") diff --git a/modular_citadel/code/modules/client/loadout/_loadout.dm b/modular_citadel/code/modules/client/loadout/_loadout.dm index 51256f8cde..0ebfa060f2 100644 --- a/modular_citadel/code/modules/client/loadout/_loadout.dm +++ b/modular_citadel/code/modules/client/loadout/_loadout.dm @@ -27,8 +27,13 @@ GLOBAL_LIST_EMPTY(loadout_whitelist_ids) /proc/initialize_global_loadout_items() load_loadout_config() for(var/item in subtypesof(/datum/gear)) - var/datum/gear/I = new item - LAZYSET(GLOB.loadout_items[slot_to_string(I.category)], I.name, I) + var/datum/gear/I = item + if(!initial(I.name)) + continue + I = new item + LAZYINITLIST(GLOB.loadout_items[I.category]) + LAZYINITLIST(GLOB.loadout_items[I.category][I.subcategory]) + GLOB.loadout_items[I.category][I.subcategory][I.name] = I if(islist(I.geargroupID)) var/list/ggidlist = I.geargroupID I.ckeywhitelist = list() @@ -41,7 +46,9 @@ GLOBAL_LIST_EMPTY(loadout_whitelist_ids) /datum/gear var/name - var/category + var/category = LOADOUT_CATEGORY_NONE + var/subcategory = LOADOUT_SUBCATEGORY_NONE + var/slot var/description var/path //item-to-spawn path var/cost = 1 //normally, each loadout costs a single point. diff --git a/modular_citadel/code/modules/client/loadout/_medical.dm b/modular_citadel/code/modules/client/loadout/_medical.dm index 604a0f96ae..e371db94fc 100644 --- a/modular_citadel/code/modules/client/loadout/_medical.dm +++ b/modular_citadel/code/modules/client/loadout/_medical.dm @@ -1,47 +1,45 @@ -/datum/gear/medicbriefcase +/datum/gear/hands/medicbriefcase name = "Medical Briefcase" - category = SLOT_HANDS path = /obj/item/storage/briefcase/medical restricted_roles = list("Medical Doctor", "Chief Medical Officer") restricted_desc = "MD, CMO" -/datum/gear/stethoscope +/datum/gear/neck/stethoscope name = "Stethoscope" - category = SLOT_NECK path = /obj/item/clothing/neck/stethoscope restricted_roles = list("Medical Doctor", "Chief Medical Officer") -/datum/gear/bluescrubs +/datum/gear/uniform/bluescrubs name = "Blue Scrubs" - category = SLOT_W_UNIFORM + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS path = /obj/item/clothing/under/rank/medical/doctor/blue restricted_roles = list("Medical Doctor", "Chief Medical Officer", "Geneticist", "Chemist", "Virologist") restricted_desc = "Medical" - -/datum/gear/greenscrubs + +/datum/gear/uniform/greenscrubs name = "Green Scrubs" - category = SLOT_W_UNIFORM + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS path = /obj/item/clothing/under/rank/medical/doctor/green restricted_roles = list("Medical Doctor", "Chief Medical Officer", "Geneticist", "Chemist", "Virologist") restricted_desc = "Medical" -/datum/gear/purplescrubs +/datum/gear/uniform/purplescrubs name = "Purple Scrubs" - category = SLOT_W_UNIFORM + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS path = /obj/item/clothing/under/rank/medical/doctor/purple restricted_roles = list("Medical Doctor", "Chief Medical Officer", "Geneticist", "Chemist", "Virologist") restricted_desc = "Medical" -/datum/gear/nursehat +/datum/gear/head/nursehat name = "Nurse Hat" - category = SLOT_HEAD path = /obj/item/clothing/head/nursehat + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_roles = list("Medical Doctor", "Chief Medical Officer", "Geneticist", "Chemist", "Virologist") restricted_desc = "Medical" -/datum/gear/nursesuit +/datum/gear/uniform/nursesuit name = "Nurse Suit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/rank/medical/doctor/nurse + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_roles = list("Medical Doctor", "Chief Medical Officer", "Geneticist", "Chemist", "Virologist") - restricted_desc = "Medical" \ No newline at end of file + restricted_desc = "Medical" diff --git a/modular_citadel/code/modules/client/loadout/_security.dm b/modular_citadel/code/modules/client/loadout/_security.dm index 72a6aab394..ab316d577b 100644 --- a/modular_citadel/code/modules/client/loadout/_security.dm +++ b/modular_citadel/code/modules/client/loadout/_security.dm @@ -1,71 +1,70 @@ -/datum/gear/navyblueuniformhos +/datum/gear/uniform/navyblueuniformhos name = "Head of Security navyblue uniform" - category = SLOT_W_UNIFORM + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS path = /obj/item/clothing/under/rank/security/head_of_security/formal restricted_roles = list("Head of Security") -/datum/gear/navybluehosberet +/datum/gear/head/navybluehosberet name = "Head of security's navyblue beret" - category = SLOT_HEAD path = /obj/item/clothing/head/beret/sec/navyhos + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_roles = list("Head of Security") -/datum/gear/navybluejackethos +/datum/gear/suit/navybluejackethos name = "head of security's navyblue jacket" - category = SLOT_WEAR_SUIT + subcategory = LOADOUT_SUBCATEGORY_SUIT_JOBS path = /obj/item/clothing/suit/armor/hos/navyblue restricted_roles = list("Head of Security") -/datum/gear/navybluejacketofficer +/datum/gear/suit/navybluejacketofficer name = "security officer's navyblue jacket" - category = SLOT_WEAR_SUIT + subcategory = LOADOUT_SUBCATEGORY_SUIT_JOBS path = /obj/item/clothing/suit/armor/navyblue restricted_roles = list("Security Officer") -/datum/gear/navyblueofficerberet +/datum/gear/head/navyblueofficerberet name = "Security officer's Navyblue beret" - category = SLOT_HEAD path = /obj/item/clothing/head/beret/sec/navyofficer + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_roles = list("Security Officer") -/datum/gear/navyblueuniformofficer +/datum/gear/uniform/navyblueuniformofficer name = "Security officer navyblue uniform" - category = SLOT_W_UNIFORM + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS path = /obj/item/clothing/under/rank/security/officer/formal restricted_roles = list("Security Officer") -/datum/gear/navybluejacketwarden +/datum/gear/suit/navybluejacketwarden name = "warden navyblue jacket" - category = SLOT_WEAR_SUIT + subcategory = LOADOUT_SUBCATEGORY_SUIT_JOBS path = /obj/item/clothing/suit/armor/vest/warden/navyblue restricted_roles = list("Warden") -/datum/gear/navybluewardenberet +/datum/gear/head/navybluewardenberet name = "Warden's navyblue beret" - category = SLOT_HEAD path = /obj/item/clothing/head/beret/sec/navywarden + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_roles = list("Warden") -/datum/gear/navyblueuniformwarden +/datum/gear/uniform/navyblueuniformwarden name = "Warden navyblue uniform" - category = SLOT_W_UNIFORM + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS path = /obj/item/clothing/under/rank/security/warden/formal restricted_roles = list("Warden") -/datum/gear/secskirt +/datum/gear/uniform/secskirt name = "Security skirt" - category = SLOT_W_UNIFORM + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS path = /obj/item/clothing/under/rank/security/officer/skirt restricted_roles = list("Security Officer", "Warden", "Head of Security") -/datum/gear/hosskirt +/datum/gear/uniform/hosskirt name = "Head of security's skirt" - category = SLOT_W_UNIFORM + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS path = /obj/item/clothing/under/rank/security/head_of_security/skirt restricted_roles = list("Head of Security") -/datum/gear/sechud +/datum/gear/glasses/sechud name = "Security Hud" - category = SLOT_GLASSES path = /obj/item/clothing/glasses/hud/security - restricted_roles = list("Security Officer", "Warden", "Head of Security") \ No newline at end of file + restricted_roles = list("Security Officer", "Warden", "Head of Security") diff --git a/modular_citadel/code/modules/client/loadout/_service.dm b/modular_citadel/code/modules/client/loadout/_service.dm index ab3daa5f3c..848ad6233c 100644 --- a/modular_citadel/code/modules/client/loadout/_service.dm +++ b/modular_citadel/code/modules/client/loadout/_service.dm @@ -1,33 +1,33 @@ -/datum/gear/greytidestationwide +/datum/gear/uniform/greytidestationwide name = "Staff Assistant's jumpsuit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/staffassistant + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_roles = list("Assistant") -/datum/gear/neetsuit +/datum/gear/suit/neetsuit name = "D.A.B. suit" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/assu_suit + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_roles = list("Assistant") cost = 2 -/datum/gear/neethelm +/datum/gear/head/neethelm name = "D.A.B. helmet" - category = SLOT_HEAD path = /obj/item/clothing/head/assu_helmet + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_roles = list("Assistant") cost = 2 -/datum/gear/plushvar +/datum/gear/backpack/plushvar name = "Ratvar Plushie" - category = SLOT_IN_BACKPACK path = /obj/item/toy/plush/plushvar + subcategory = LOADOUT_SUBCATEGORY_BACKPACK_TOYS cost = 5 restricted_roles = list("Chaplain") -/datum/gear/narplush +/datum/gear/backpack/narplush name = "Narsie Plushie" - category = SLOT_IN_BACKPACK path = /obj/item/toy/plush/narplush + subcategory = LOADOUT_SUBCATEGORY_BACKPACK_TOYS cost = 5 restricted_roles = list("Chaplain") diff --git a/modular_citadel/code/modules/client/loadout/backpack.dm b/modular_citadel/code/modules/client/loadout/backpack.dm index 690e012840..0b700b11e2 100644 --- a/modular_citadel/code/modules/client/loadout/backpack.dm +++ b/modular_citadel/code/modules/client/loadout/backpack.dm @@ -1,112 +1,104 @@ -/datum/gear/plushbox +/datum/gear/backpack + category = LOADOUT_CATEGORY_BACKPACK + subcategory = LOADOUT_SUBCATEGORY_BACKPACK_GENERAL + slot = SLOT_IN_BACKPACK + +/datum/gear/backpack/plushbox name = "Plushie Choice Box" - category = SLOT_IN_BACKPACK path = /obj/item/choice_beacon/box/plushie + subcategory = LOADOUT_SUBCATEGORY_BACKPACK_TOYS -/datum/gear/tennis +/datum/gear/backpack/tennis name = "Classic Tennis Ball" - category = SLOT_IN_BACKPACK path = /obj/item/toy/tennis + subcategory = LOADOUT_SUBCATEGORY_BACKPACK_TOYS -/datum/gear/tennisred +/datum/gear/backpack/tennis/red name = "Red Tennis Ball" - category = SLOT_IN_BACKPACK path = /obj/item/toy/tennis/red -/datum/gear/tennisyellow +/datum/gear/backpack/tennis/yellow name = "Yellow Tennis Ball" - category = SLOT_IN_BACKPACK path = /obj/item/toy/tennis/yellow -/datum/gear/tennisgreen +/datum/gear/backpack/tennis/green name = "Green Tennis Ball" - category = SLOT_IN_BACKPACK path = /obj/item/toy/tennis/green -/datum/gear/tenniscyan +/datum/gear/backpack/tennis/cyan name = "Cyan Tennis Ball" - category = SLOT_IN_BACKPACK path = /obj/item/toy/tennis/cyan -/datum/gear/tennisblue +/datum/gear/backpack/tennis/blue name = "Blue Tennis Ball" - category = SLOT_IN_BACKPACK path = /obj/item/toy/tennis/blue -/datum/gear/tennispurple +/datum/gear/backpack/tennis/purple name = "Purple Tennis Ball" - category = SLOT_IN_BACKPACK path = /obj/item/toy/tennis/purple -/datum/gear/dildo +/datum/gear/backpack/dildo name = "Customizable dildo" - category = SLOT_IN_BACKPACK path = /obj/item/dildo/custom + subcategory = LOADOUT_SUBCATEGORY_BACKPACK_TOYS -/datum/gear/toykatana +/datum/gear/backpack/toykatana name = "Toy Katana" - category = SLOT_IN_BACKPACK path = /obj/item/toy/katana + subcategory = LOADOUT_SUBCATEGORY_BACKPACK_TOYS cost = 3 -/datum/gear/tapeplayer +/datum/gear/backpack/tapeplayer name = "Taperecorder" - category = SLOT_IN_BACKPACK path = /obj/item/taperecorder -/datum/gear/tape +/datum/gear/backpack/tape name = "Spare cassette tape" - category = SLOT_IN_BACKPACK path = /obj/item/tape/random -/datum/gear/newspaper +/datum/gear/backpack/newspaper name = "Newspaper" - category = SLOT_IN_BACKPACK path = /obj/item/newspaper -/datum/gear/crayons +/datum/gear/backpack/crayons name = "Box of crayons" - category = SLOT_IN_BACKPACK path = /obj/item/storage/crayons + subcategory = LOADOUT_SUBCATEGORY_BACKPACK_TOYS -/datum/gear/multipen +/datum/gear/backpack/multipen name = "A multicolored pen" - category = SLOT_IN_BACKPACK path = /obj/item/pen/fourcolor -/datum/gear/fountainpen +/datum/gear/backpack/fountainpen name = "A fancy pen" - category = SLOT_IN_BACKPACK path = /obj/item/pen/fountain cost = 2 -/datum/gear/modular_tablet +/datum/gear/backpack/modular_tablet name = "A modular tablet" - category = SLOT_IN_BACKPACK path = /obj/item/modular_computer/tablet/preset/cheap/ cost = 4 -/datum/gear/modular_laptop +/datum/gear/backpack/modular_laptop name = "A modular laptop" - category = SLOT_IN_BACKPACK path = /obj/item/modular_computer/laptop/preset/civilian cost = 7 -/datum/gear/ringbox_gold +/datum/gear/backpack/ringbox_gold name = "A gold ring box" - category = SLOT_IN_BACKPACK path = /obj/item/storage/fancy/ringbox cost = 3 -/datum/gear/ringbox_silver +/datum/gear/backpack/ringbox_silver name = "A silver ring box" - category = SLOT_IN_BACKPACK path = /obj/item/storage/fancy/ringbox/silver cost = 3 -/datum/gear/ringbox_diamond +/datum/gear/backpack/ringbox_diamond name = "A diamond ring box" - category = SLOT_IN_BACKPACK path = /obj/item/storage/fancy/ringbox/diamond cost = 5 +/datum/gear/backpack/necklace//this is here because loadout doesn't support proper accessories + name = "A renameable necklace" + path = /obj/item/clothing/accessory/necklace diff --git a/modular_citadel/code/modules/client/loadout/glasses.dm b/modular_citadel/code/modules/client/loadout/glasses.dm index 57270d8e57..b0eecbbf28 100644 --- a/modular_citadel/code/modules/client/loadout/glasses.dm +++ b/modular_citadel/code/modules/client/loadout/glasses.dm @@ -1,49 +1,43 @@ -/datum/gear/blindfold +/datum/gear/glasses + category = LOADOUT_CATEGORY_GLASSES + slot = SLOT_GLASSES + +/datum/gear/glasses/blindfold name = "Blindfold" - category = SLOT_GLASSES path = /obj/item/clothing/glasses/sunglasses/blindfold -/datum/gear/cold +/datum/gear/glasses/cold name = "Cold goggles" - category = SLOT_GLASSES path = /obj/item/clothing/glasses/cold -/datum/gear/eyepatch +/datum/gear/glasses/eyepatch name = "Eyepatch" - category = SLOT_GLASSES path = /obj/item/clothing/glasses/eyepatch -/datum/gear/heat +/datum/gear/glasses/heat name = "Heat goggles" - category = SLOT_GLASSES path = /obj/item/clothing/glasses/heat -/datum/gear/hipster +/datum/gear/glasses/hipster name = "Hipster glasses" - category = SLOT_GLASSES path = /obj/item/clothing/glasses/regular/hipster -/datum/gear/jamjar +/datum/gear/glasses/jamjar name = "Jamjar glasses" - category = SLOT_GLASSES path = /obj/item/clothing/glasses/regular/jamjar -/datum/gear/monocle +/datum/gear/glasses/monocle name = "Monocle" - category = SLOT_GLASSES path = /obj/item/clothing/glasses/monocle -/datum/gear/orange +/datum/gear/glasses/orange name = "Orange glasses" - category = SLOT_GLASSES path = /obj/item/clothing/glasses/orange -/datum/gear/red +/datum/gear/glasses/red name = "Red Glasses" - category = SLOT_GLASSES path = /obj/item/clothing/glasses/red -/datum/gear/prescription +/datum/gear/glasses/prescription name = "Prescription glasses" - category = SLOT_GLASSES path = /obj/item/clothing/glasses/regular diff --git a/modular_citadel/code/modules/client/loadout/gloves.dm b/modular_citadel/code/modules/client/loadout/gloves.dm index e9e8e3939b..ffa4724f63 100644 --- a/modular_citadel/code/modules/client/loadout/gloves.dm +++ b/modular_citadel/code/modules/client/loadout/gloves.dm @@ -1,28 +1,30 @@ -/datum/gear/fingerless +/datum/gear/gloves + category = LOADOUT_CATEGORY_GLOVES + slot = SLOT_GLOVES + +/datum/gear/gloves/fingerless name = "Fingerless Gloves" - category = SLOT_GLOVES path = /obj/item/clothing/gloves/fingerless -/datum/gear/evening +/datum/gear/gloves/evening name = "Evening gloves" - category = SLOT_GLOVES path = /obj/item/clothing/gloves/evening -/datum/gear/goldring +/datum/gear/gloves/goldring name = "A gold ring" - category = SLOT_GLOVES path = /obj/item/clothing/gloves/ring cost = 2 -/datum/gear/silverring +/datum/gear/gloves/silverring name = "A silver ring" - category = SLOT_GLOVES path = /obj/item/clothing/gloves/ring/silver cost = 2 -/datum/gear/diamondring +/datum/gear/gloves/diamondring name = "A diamond ring" - category = SLOT_GLOVES path = /obj/item/clothing/gloves/ring/diamond cost = 4 +/datum/gear/gloves/customring + name = "A ring, renameable" + path = /obj/item/clothing/gloves/ring/custom diff --git a/modular_citadel/code/modules/client/loadout/hands.dm b/modular_citadel/code/modules/client/loadout/hands.dm index 2f03bd3b07..db57fb466b 100644 --- a/modular_citadel/code/modules/client/loadout/hands.dm +++ b/modular_citadel/code/modules/client/loadout/hands.dm @@ -1,67 +1,54 @@ -/datum/gear/cane +/datum/gear/hands + category = LOADOUT_CATEGORY_HANDS + slot = SLOT_HANDS + +/datum/gear/hands/cane name = "Cane" - category = SLOT_HANDS path = /obj/item/cane -/datum/gear/cigarettes +/datum/gear/hands/cigarettes name = "Cigarette pack" - category = SLOT_HANDS path = /obj/item/storage/fancy/cigarettes -/datum/gear/dice +/datum/gear/hands/dice name = "Dice bag" - category = SLOT_HANDS path = /obj/item/storage/box/dice -/datum/gear/eightball +/datum/gear/hands/eightball name = "Magic eightball" - category = SLOT_HANDS path = /obj/item/toy/eightball -/datum/gear/matches +/datum/gear/hands/matches name = "Matchbox" - category = SLOT_HANDS path = /obj/item/storage/box/matches -/datum/gear/cheaplighter +/datum/gear/hands/cheaplighter name = "Cheap lighter" - category = SLOT_HANDS path = /obj/item/lighter/greyscale -/datum/gear/cards +/datum/gear/hands/cards name = "Playing cards" - category = SLOT_HANDS path = /obj/item/toy/cards/deck -/datum/gear/skub +/datum/gear/hands/skub name = "Skub" - category = SLOT_HANDS path = /obj/item/skub -/datum/gear/carpplushie - name = "Space carp plushie" - category = SLOT_HANDS - path = /obj/item/toy/plush/carpplushie - -/datum/gear/wallet +/datum/gear/hands/wallet name = "Wallet" - category = SLOT_HANDS path = /obj/item/storage/wallet -/datum/gear/flask +/datum/gear/hands/flask name = "Flask" - category = SLOT_HANDS path = /obj/item/reagent_containers/food/drinks/flask cost = 2 -/datum/gear/zippolighter +/datum/gear/hands/zippolighter name = "Zippo Lighter" - category = SLOT_HANDS path = /obj/item/lighter cost = 2 -/datum/gear/cigar +/datum/gear/hands/cigar name = "Cigar" - category = SLOT_HANDS path = /obj/item/clothing/mask/cigarette/cigar cost = 4 //smoking is bad mkay diff --git a/modular_citadel/code/modules/client/loadout/head.dm b/modular_citadel/code/modules/client/loadout/head.dm index 2156c9c481..fd03e2279f 100644 --- a/modular_citadel/code/modules/client/loadout/head.dm +++ b/modular_citadel/code/modules/client/loadout/head.dm @@ -1,146 +1,136 @@ -/datum/gear/baseball +/datum/gear/head + category = LOADOUT_CATEGORY_HEAD + subcategory = LOADOUT_SUBCATEGORY_HEAD_GENERAL + slot = SLOT_HEAD + +/datum/gear/head/baseball name = "Ballcap" - category = SLOT_HEAD path = /obj/item/clothing/head/soft/mime -/datum/gear/beanie +/datum/gear/head/beanie name = "Beanie" - category = SLOT_HEAD path = /obj/item/clothing/head/beanie -/datum/gear/beret +/datum/gear/head/beret name = "Black beret" - category = SLOT_HEAD path = /obj/item/clothing/head/beret/black -/datum/gear/flatcap +/datum/gear/head/flatcap name = "Flat cap" - category = SLOT_HEAD path = /obj/item/clothing/head/flatcap -/datum/gear/pirate +/datum/gear/head/pirate name = "Pirate hat" - category = SLOT_HEAD path = /obj/item/clothing/head/pirate -/datum/gear/rice_hat +/datum/gear/head/rice_hat name = "Rice hat" - category = SLOT_HEAD path = /obj/item/clothing/head/rice_hat -/datum/gear/ushanka - name = "Ushanka" - category = SLOT_HEAD +/datum/gear/head/ushanka path = /obj/item/clothing/head/ushanka -/datum/gear/slime +/datum/gear/head/slime name = "Slime hat" - category = SLOT_HEAD path = /obj/item/clothing/head/collectable/slime -/datum/gear/fedora +/datum/gear/head/fedora name = "Fedora" - category = SLOT_HEAD path = /obj/item/clothing/head/fedora -/datum/gear/that +/datum/gear/head/that name = "Top Hat" - category = SLOT_HEAD path = /obj/item/clothing/head/that -/datum/gear/maidband +/datum/gear/head/maidband name = "Maid headband" - category = SLOT_HEAD path= /obj/item/clothing/head/maid -/datum/gear/flakhelm +/datum/gear/head/flakhelm name = "Flak Helmet" - category = SLOT_HEAD path = /obj/item/clothing/head/flakhelm cost = 2 -/datum/gear/bunnyears +/datum/gear/head/bunnyears name = "Bunny Ears" - category = SLOT_HEAD path = /obj/item/clothing/head/rabbitears -/datum/gear/mailmanhat +/datum/gear/head/mailmanhat name = "Mailman's Hat" - category = SLOT_HEAD path = /obj/item/clothing/head/mailman //trek fancy Hats! -/datum/gear/trekcap +/datum/gear/head/trekcap name = "Federation Officer's Cap (White)" - category = SLOT_HEAD path = /obj/item/clothing/head/caphat/formal/fedcover + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_roles = list("Captain","Head of Personnel") -/datum/gear/trekcapcap +/datum/gear/head/trekcapcap name = "Federation Officer's Cap (Black)" - category = SLOT_HEAD path = /obj/item/clothing/head/caphat/formal/fedcover/black + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_roles = list("Captain","Head of Personnel") -/datum/gear/trekcapmedisci +/datum/gear/head/trekcapmedisci name = "Federation Officer's Cap (Blue)" - category = SLOT_HEAD path = /obj/item/clothing/head/caphat/formal/fedcover/medsci + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_desc = "Medical and Science" restricted_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Virologist","Paramedic","Geneticist","Research Director","Scientist", "Roboticist") -/datum/gear/trekcapeng +/datum/gear/head/trekcapeng name = "Federation Officer's Cap (Yellow)" - category = SLOT_HEAD path = /obj/item/clothing/head/caphat/formal/fedcover/eng + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_desc = "Engineering, Security, and Cargo" restricted_roles = list("Chief Engineer","Atmospheric Technician","Station Engineer","Warden","Detective","Security Officer","Head of Security","Cargo Technician", "Shaft Miner", "Quartermaster") -/datum/gear/trekcapsec +/datum/gear/head/trekcapsec name = "Federation Officer's Cap (Red)" - category = SLOT_HEAD path = /obj/item/clothing/head/caphat/formal/fedcover/sec + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_desc = "Engineering, Security, and Cargo" restricted_roles = list("Chief Engineer","Atmospheric Technician","Station Engineer","Warden","Detective","Security Officer","Head of Security","Cargo Technician", "Shaft Miner", "Quartermaster") // orvilike "original" kepi -/datum/gear/orvkepicom +/datum/gear/head/orvkepicom name = "Federation Kepi, command" description = "A visored cap. Intended to be used with ORV uniform." - category = SLOT_HEAD path = /obj/item/clothing/head/kepi/orvi/command + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_desc = "Heads of Staff" restricted_roles = list("Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Research Director", "Chief Medical Officer", "Quartermaster") -/datum/gear/orvkepiops +/datum/gear/head/orvkepiops name = "Federation Kepi, ops/sec" description = "A visored cap. Intended to be used with ORV uniform." - category = SLOT_HEAD path = /obj/item/clothing/head/kepi/orvi/engsec + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_desc = "Engineering, Security and Cargo" restricted_roles = list("Chief Engineer", "Atmospheric Technician", "Station Engineer", "Warden", "Detective", "Security Officer", "Head of Security", "Cargo Technician", "Shaft Miner", "Quartermaster") -/datum/gear/orvkepimedsci +/datum/gear/head/orvkepimedsci name = "Federation Kepi, medsci" description = "A visored cap. Intended to be used with ORV uniform." - category = SLOT_HEAD path = /obj/item/clothing/head/kepi/orvi/medsci + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_desc = "Medical and Science" restricted_roles = list("Chief Medical Officer", "Medical Doctor", "Chemist", "Virologist", "Paramedic", "Geneticist", "Research Director", "Scientist", "Roboticist") -/datum/gear/orvkepisrv +/datum/gear/head/orvkepisrv name = "Federation Kepi, service" description = "A visored cap. Intended to be used with ORV uniform." - category = SLOT_HEAD path = /obj/item/clothing/head/kepi/orvi/service + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_desc = "Service and Civilian, barring Clown, Mime and Lawyer" restricted_roles = list("Assistant", "Bartender", "Botanist", "Cook", "Curator", "Janitor", "Chaplain") -/datum/gear/orvkepiass +/datum/gear/head/orvkepiass name = "Federation Kepi, assistant" description = "A visored cap. Intended to be used with ORV uniform." - category = SLOT_HEAD path = /obj/item/clothing/head/kepi/orvi + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_roles = list("Assistant") /*Commenting out Until next Christmas or made automatic @@ -156,35 +146,37 @@ */ //Cowboy Stuff -/datum/gear/cowboyhat +/datum/gear/head/cowboyhat name = "Cowboy Hat, Brown" - category = SLOT_HEAD path = /obj/item/clothing/head/cowboyhat -/datum/gear/cowboyhat/black +/datum/gear/head/cowboyhat/black name = "Cowboy Hat, Black" - category = SLOT_HEAD path = /obj/item/clothing/head/cowboyhat/black -/datum/gear/cowboyhat/white +/datum/gear/head/cowboyhat/white name = "Cowboy Hat, White" - category = SLOT_HEAD path = /obj/item/clothing/head/cowboyhat/white -/datum/gear/cowboyhat/pink +/datum/gear/head/cowboyhat/pink name = "Cowboy Hat, Pink" - category = SLOT_HEAD path = /obj/item/clothing/head/cowboyhat/pink -/datum/gear/cowboyhat/sec +/datum/gear/head/cowboyhat/sec name = "Cowboy Hat, Security" - category = SLOT_HEAD path = /obj/item/clothing/head/cowboyhat/sec + subcategory = LOADOUT_SUBCATEGORY_HEAD_JOBS restricted_desc = "Security" restricted_roles = list("Warden","Detective","Security Officer","Head of Security") -// Misc -/datum/gear/wkepi +/datum/gear/head/wkepi name = "white kepi" - category = SLOT_HEAD path = /obj/item/clothing/head/kepi + +/datum/gear/head/widered + name = "Wide red hat" + path = /obj/item/clothing/head/widered + +/datum/gear/head/kabuto + name = "Kabuto helmet" + path = /obj/item/clothing/head/kabuto diff --git a/modular_citadel/code/modules/client/loadout/mask.dm b/modular_citadel/code/modules/client/loadout/mask.dm index eeba06cad4..0d7e32552e 100644 --- a/modular_citadel/code/modules/client/loadout/mask.dm +++ b/modular_citadel/code/modules/client/loadout/mask.dm @@ -1,16 +1,16 @@ -/datum/gear/balaclava +/datum/gear/mask + category = LOADOUT_CATEGORY_MASK + slot = SLOT_WEAR_MASK + +/datum/gear/mask/balaclava name = "Balaclava" - category = SLOT_WEAR_MASK path = /obj/item/clothing/mask/balaclava -/datum/gear/moustache +/datum/gear/mask/moustache name = "Fake moustache" - category = SLOT_WEAR_MASK path = /obj/item/clothing/mask/fakemoustache -/datum/gear/joy +/datum/gear/mask/joy name = "Joy mask" - category = SLOT_WEAR_MASK path = /obj/item/clothing/mask/joy cost = 3 - diff --git a/modular_citadel/code/modules/client/loadout/neck.dm b/modular_citadel/code/modules/client/loadout/neck.dm index 0f69296406..19311f703a 100644 --- a/modular_citadel/code/modules/client/loadout/neck.dm +++ b/modular_citadel/code/modules/client/loadout/neck.dm @@ -1,99 +1,88 @@ -/datum/gear/bluetie +/datum/gear/neck + category = LOADOUT_CATEGORY_NECK + subcategory = LOADOUT_SUBCATEGORY_NECK_GENERAL + slot = SLOT_NECK + +/datum/gear/neck/bluetie name = "Blue tie" - category = SLOT_NECK + subcategory = LOADOUT_SUBCATEGORY_NECK_TIE path = /obj/item/clothing/neck/tie/blue -/datum/gear/redtie +/datum/gear/neck/redtie name = "Red tie" - category = SLOT_NECK + subcategory = LOADOUT_SUBCATEGORY_NECK_TIE path = /obj/item/clothing/neck/tie/red -/datum/gear/blacktie +/datum/gear/neck/blacktie name = "Black tie" - category = SLOT_NECK + subcategory = LOADOUT_SUBCATEGORY_NECK_TIE path = /obj/item/clothing/neck/tie/black -/datum/gear/collar +/datum/gear/neck/collar name = "Collar" - category = SLOT_NECK path = /obj/item/clothing/neck/petcollar -/datum/gear/leathercollar +/datum/gear/neck/leathercollar name = "Leather collar" - category = SLOT_NECK path = /obj/item/clothing/neck/petcollar/leather -/datum/gear/choker +/datum/gear/neck/choker name = "Choker" - category = SLOT_NECK path = /obj/item/clothing/neck/petcollar/choker -/datum/gear/scarf +/datum/gear/neck/scarf name = "White scarf" - category = SLOT_NECK + subcategory = LOADOUT_SUBCATEGORY_NECK_SCARVES path = /obj/item/clothing/neck/scarf -/datum/gear/blackscarf +/datum/gear/neck/scarf/black name = "Black scarf" - category = SLOT_NECK path = /obj/item/clothing/neck/scarf/black -/datum/gear/redscarf +/datum/gear/neck/scarf/red name = "Red scarf" - category = SLOT_NECK path = /obj/item/clothing/neck/scarf/red -/datum/gear/greenscarf +/datum/gear/neck/scarf/green name = "Green scarf" - category = SLOT_NECK path = /obj/item/clothing/neck/scarf/green -/datum/gear/darkbluescarf +/datum/gear/neck/scarf/darkblue name = "Dark blue scarf" - category = SLOT_NECK path = /obj/item/clothing/neck/scarf/darkblue -/datum/gear/purplescarf +/datum/gear/neck/scarf/purple name = "Purple scarf" - category = SLOT_NECK path = /obj/item/clothing/neck/scarf/purple -/datum/gear/yellowscarf +/datum/gear/neck/scarf/yellow name = "Yellow scarf" - category = SLOT_NECK path = /obj/item/clothing/neck/scarf/yellow -/datum/gear/orangescarf +/datum/gear/neck/scarf/orange name = "Orange scarf" - category = SLOT_NECK path = /obj/item/clothing/neck/scarf/orange -/datum/gear/cyanscarf +/datum/gear/neck/scarf/cyan name = "Cyan scarf" - category = SLOT_NECK path = /obj/item/clothing/neck/scarf/cyan -/datum/gear/stripedredscarf +/datum/gear/neck/scarf/stripedred name = "Striped red scarf" - category = SLOT_NECK path = /obj/item/clothing/neck/stripedredscarf -/datum/gear/stripedbluescarf +/datum/gear/neck/scarf/stripedblue name = "Striped blue scarf" - category = SLOT_NECK path = /obj/item/clothing/neck/stripedbluescarf -/datum/gear/stripedgreenscarf +/datum/gear/neck/scarf/stripedgreen name = "Striped green scarf" - category = SLOT_NECK path = /obj/item/clothing/neck/stripedgreenscarf -/datum/gear/headphones +/datum/gear/neck/headphones name = "Headphones" - category = SLOT_NECK path = /obj/item/clothing/ears/headphones -/datum/gear/polycloak +/datum/gear/neck/polycloak name = "Polychromatic Cloak" - category = SLOT_NECK path = /obj/item/clothing/neck/cloak/polychromic diff --git a/modular_citadel/code/modules/client/loadout/shoes.dm b/modular_citadel/code/modules/client/loadout/shoes.dm index 3531e69cfd..76d7305971 100644 --- a/modular_citadel/code/modules/client/loadout/shoes.dm +++ b/modular_citadel/code/modules/client/loadout/shoes.dm @@ -1,84 +1,71 @@ -/datum/gear/laceup +/datum/gear/shoes + category = LOADOUT_CATEGORY_SHOES + slot = SLOT_SHOES + +/datum/gear/shoes/laceup name = "Laceup shoes" - category = SLOT_SHOES path = /obj/item/clothing/shoes/laceup -/datum/gear/workboots +/datum/gear/shoes/workboots name = "Work boots" - category = SLOT_SHOES path = /obj/item/clothing/shoes/workboots -/datum/gear/jackboots +/datum/gear/shoes/jackboots name = "Jackboots" - category = SLOT_SHOES path = /obj/item/clothing/shoes/jackboots -/datum/gear/winterboots +/datum/gear/shoes/winterboots name = "Winter boots" - category = SLOT_SHOES path = /obj/item/clothing/shoes/winterboots -/datum/gear/sandals +/datum/gear/shoes/sandals name = "Sandals" - category = SLOT_SHOES path = /obj/item/clothing/shoes/sandal -/datum/gear/blackshoes +/datum/gear/shoes/blackshoes name = "Black shoes" - category = SLOT_SHOES path = /obj/item/clothing/shoes/sneakers/black -/datum/gear/brownshoes +/datum/gear/shoes/brownshoes name = "Brown shoes" - category = SLOT_SHOES path = /obj/item/clothing/shoes/sneakers/brown -/datum/gear/whiteshoes +/datum/gear/shoes/whiteshoes name = "White shoes" - category = SLOT_SHOES path = /obj/item/clothing/shoes/sneakers/white -/datum/gear/gildedcuffs +/datum/gear/shoes/gildedcuffs name = "Gilded leg wraps" - category = SLOT_SHOES path= /obj/item/clothing/shoes/wraps -/datum/gear/silvercuffs +/datum/gear/shoes/silvercuffs name = "Silver leg wraps" - category = SLOT_SHOES path= /obj/item/clothing/shoes/wraps/silver -/datum/gear/redcuffs +/datum/gear/shoes/redcuffs name = "Red leg wraps" - category = SLOT_SHOES path= /obj/item/clothing/shoes/wraps/red -/datum/gear/bluecuffs +/datum/gear/shoes/bluecuffs name = "Blue leg wraps" - category = SLOT_SHOES path= /obj/item/clothing/shoes/wraps/blue -/datum/gear/christmasbootsr +/datum/gear/shoes/christmasbootsr name = "Red Christmas Boots" - category = SLOT_SHOES path= /obj/item/clothing/shoes/winterboots/christmasbootsr -/datum/gear/christmasbootsg +/datum/gear/shoes/christmasbootsg name = "Green Christmas Boots" - category = SLOT_SHOES path= /obj/item/clothing/shoes/winterboots/christmasbootsg -/datum/gear/santaboots +/datum/gear/shoes/santaboots name = "Santa Boots" - category = SLOT_SHOES path= /obj/item/clothing/shoes/winterboots/santaboots -/datum/gear/cowboyboots +/datum/gear/shoes/cowboyboots name = "Cowboy Boots, Brown" - category = SLOT_SHOES path = /obj/item/clothing/shoes/cowboyboots -/datum/gear/cowboyboots/black +/datum/gear/shoes/cowboyboots/black name = "Cowboy Boots, Black" - category = SLOT_SHOES - path = /obj/item/clothing/shoes/cowboyboots/black \ No newline at end of file + path = /obj/item/clothing/shoes/cowboyboots/black diff --git a/modular_citadel/code/modules/client/loadout/suit.dm b/modular_citadel/code/modules/client/loadout/suit.dm index 57c5772f48..d0be26a8a4 100644 --- a/modular_citadel/code/modules/client/loadout/suit.dm +++ b/modular_citadel/code/modules/client/loadout/suit.dm @@ -1,261 +1,250 @@ -/datum/gear/poncho +/datum/gear/suit + category = LOADOUT_CATEGORY_SUIT + subcategory = LOADOUT_SUBCATEGORY_SUIT_GENERAL + slot = SLOT_WEAR_SUIT + +/datum/gear/suit/poncho name = "Poncho" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/poncho -/datum/gear/ponchogreen +/datum/gear/suit/ponchogreen name = "Green poncho" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/poncho/green -/datum/gear/ponchored +/datum/gear/suit/ponchored name = "Red poncho" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/poncho/red -/datum/gear/redhood +/datum/gear/suit/redhood name = "Red cloak" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/cloak/david cost = 3 -/datum/gear/jacketbomber +/datum/gear/suit/jacketbomber name = "Bomber jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/jacketflannelblack // all of these are reskins of bomber jackets but with the vibe to make you look like a true lumberjack +/datum/gear/suit/jacketflannelblack // all of these are reskins of bomber jackets but with the vibe to make you look like a true lumberjack name = "Black flannel jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/flannel + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/jacketflannelred +/datum/gear/suit/jacketflannelred name = "Red flannel jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/flannel/red + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/jacketflannelaqua +/datum/gear/suit/jacketflannelaqua name = "Aqua flannel jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/flannel/aqua + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/jacketflannelbrown +/datum/gear/suit/jacketflannelbrown name = "Brown flannel jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/flannel/brown + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/jacketleather +/datum/gear/suit/jacketleather name = "Leather jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/leather + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/overcoatleather +/datum/gear/suit/overcoatleather name = "Leather overcoat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/leather/overcoat + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/jacketpuffer +/datum/gear/suit/jacketpuffer name = "Puffer jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/puffer + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/vestpuffer +/datum/gear/suit/vestpuffer name = "Puffer vest" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/puffer/vest + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/jacketlettermanbrown +/datum/gear/suit/jacketlettermanbrown name = "Brown letterman jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/letterman + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/jacketlettermanred +/datum/gear/suit/jacketlettermanred name = "Red letterman jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/letterman_red + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/jacketlettermanNT +/datum/gear/suit/jacketlettermanNT name = "Nanotrasen letterman jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/letterman_nanotrasen + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/coat +/datum/gear/suit/coat name = "Winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat + subcategory = LOADOUT_SUBCATEGORY_SUIT_COATS -/datum/gear/coat/aformal +/datum/gear/suit/coat/aformal name = "Assistant's formal winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/aformal -/datum/gear/coat/runed +/datum/gear/suit/coat/runed name = "Runed winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/narsie/fake -/datum/gear/coat/brass +/datum/gear/suit/coat/brass name = "Brass winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/ratvar/fake -/datum/gear/coat/polycoat +/datum/gear/suit/coat/polycoat name = "Polychromic winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/polychromic cost = 4 //too many people with neon green coats is hard on the eyes -/datum/gear/coat/med +/datum/gear/suit/coat/med name = "Medical winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/medical restricted_roles = list("Chief Medical Officer", "Medical Doctor") // Reserve it to Medical Doctors and their boss, the Chief Medical Officer -/datum/gear/coat/paramedic +/datum/gear/suit/coat/paramedic name = "Paramedic winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/paramedic restricted_roles = list("Chief Medical Officer", "Paramedic") // Reserve it to Paramedics and their boss, the Chief Medical Officer -/datum/gear/coat/robotics +/datum/gear/suit/coat/robotics name = "Robotics winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/robotics restricted_roles = list("Research Director", "Roboticist") -/datum/gear/coat/sci +/datum/gear/suit/coat/sci name = "Science winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/science restricted_roles = list("Research Director", "Scientist", "Roboticist") // Reserve it to the Science Departement -/datum/gear/coat/eng +/datum/gear/suit/coat/eng name = "Engineering winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/engineering restricted_roles = list("Chief Engineer", "Station Engineer") // Reserve it to Station Engineers and their boss, the Chief Engineer -/datum/gear/coat/eng/atmos +/datum/gear/suit/coat/eng/atmos name = "Atmospherics winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/engineering/atmos restricted_roles = list("Chief Engineer", "Atmospheric Technician") // Reserve it to Atmos Techs and their boss, the Chief Engineer -/datum/gear/coat/hydro +/datum/gear/suit/coat/hydro name = "Hydroponics winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/hydro restricted_roles = list("Head of Personnel", "Botanist") // Reserve it to Botanists and their boss, the Head of Personnel -/datum/gear/coat/bar +/datum/gear/suit/coat/bar name = "Bar winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/bar restricted_roles = list("Bartender") // Reserve it to Bartenders and not the Head of Personnel because he doesnt deserve to look as fancy as them -/datum/gear/coat/cargo +/datum/gear/suit/coat/cargo name = "Cargo winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/cargo restricted_roles = list("Quartermaster", "Cargo Technician") // Reserve it to Cargo Techs and their boss, the Quartermaster -/datum/gear/coat/miner +/datum/gear/suit/coat/miner name = "Mining winter coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/miner restricted_roles = list("Quartermaster", "Shaft Miner") // Reserve it to Miners and their boss, the Quartermaster -/datum/gear/militaryjacket +/datum/gear/suit/militaryjacket name = "Military Jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/jacket/miljacket + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS -/datum/gear/ianshirt +/datum/gear/suit/ianshirt name = "Ian Shirt" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/ianshirt -/datum/gear/flakjack +/datum/gear/suit/flakjack name = "Flak Jacket" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/flakjack + subcategory = LOADOUT_SUBCATEGORY_SUIT_JACKETS cost = 2 -/datum/gear/trekds9_coat +/datum/gear/suit/trekds9_coat name = "DS9 Overcoat (use uniform)" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/storage/trek/ds9 + subcategory = LOADOUT_SUBCATEGORY_SUIT_JOBS restricted_desc = "All, barring Service and Civilian" restricted_roles = list("Head of Security","Captain","Head of Personnel","Chief Engineer","Research Director","Chief Medical Officer","Quartermaster", "Medical Doctor","Chemist","Virologist","Paramedic","Geneticist","Scientist", "Roboticist", "Atmospheric Technician","Station Engineer","Warden","Detective","Security Officer", "Cargo Technician", "Shaft Miner") //everyone who actually deserves a job. //Federation jackets from movies -/datum/gear/trekcmdcap +/datum/gear/suit/trekcmdcap name = "Fed (movie) uniform, Black" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/storage/fluff/fedcoat/capt + subcategory = LOADOUT_SUBCATEGORY_SUIT_JOBS restricted_roles = list("Captain","Head of Personnel") -/datum/gear/trekcmdmov +/datum/gear/suit/trekcmdmov name = "Fed (movie) uniform, Red" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/storage/fluff/fedcoat + subcategory = LOADOUT_SUBCATEGORY_SUIT_JOBS restricted_desc = "Heads of Staff and Security" restricted_roles = list("Head of Security","Captain","Head of Personnel","Chief Engineer","Research Director","Chief Medical Officer","Quartermaster","Warden","Detective","Security Officer") -/datum/gear/trekmedscimov +/datum/gear/suit/trekmedscimov name = "Fed (movie) uniform, Blue" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/storage/fluff/fedcoat/medsci + subcategory = LOADOUT_SUBCATEGORY_SUIT_JOBS restricted_desc = "Medical and Science" restricted_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Virologist","Paramedic","Geneticist","Research Director","Scientist", "Roboticist") -/datum/gear/trekengmov +/datum/gear/suit/trekengmov name = "Fed (movie) uniform, Yellow" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/storage/fluff/fedcoat/eng + subcategory = LOADOUT_SUBCATEGORY_SUIT_JOBS restricted_desc = "Engineering and Cargo" restricted_roles = list("Chief Engineer","Atmospheric Technician","Station Engineer","Cargo Technician", "Shaft Miner", "Quartermaster") -/datum/gear/trekcmdcapmod +/datum/gear/suit/trekcmdcapmod name = "Fed (Modern) uniform, White" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/storage/fluff/modernfedcoat restricted_roles = list("Captain","Head of Personnel") -/datum/gear/trekcmdmod +/datum/gear/suit/trekcmdmod name = "Fed (Modern) uniform, Red" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/storage/fluff/modernfedcoat/sec + subcategory = LOADOUT_SUBCATEGORY_SUIT_JOBS restricted_desc = "Heads of Staff and Security" restricted_roles = list("Head of Security","Captain","Head of Personnel","Chief Engineer","Research Director","Chief Medical Officer","Quartermaster","Warden","Detective","Security Officer") -/datum/gear/trekmedscimod +/datum/gear/suit/trekmedscimod name = "Fed (Modern) uniform, Blue" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/storage/fluff/modernfedcoat/medsci + subcategory = LOADOUT_SUBCATEGORY_SUIT_JOBS restricted_desc = "Medical and Science" restricted_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Virologist","Paramedic","Geneticist","Research Director","Scientist", "Roboticist") -/datum/gear/trekengmod +/datum/gear/suit/trekengmod name = "Fed (Modern) uniform, Yellow" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/storage/fluff/modernfedcoat/eng + subcategory = LOADOUT_SUBCATEGORY_SUIT_JOBS restricted_desc = "Engineering and Cargo" restricted_roles = list("Chief Engineer","Atmospheric Technician","Station Engineer","Cargo Technician", "Shaft Miner", "Quartermaster") -/datum/gear/christmascoatr +/datum/gear/suit/christmascoatr name = "Red Christmas Coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/christmascoatr + subcategory = LOADOUT_SUBCATEGORY_SUIT_COATS -/datum/gear/christmascoatg +/datum/gear/suit/christmascoatg name = "Green Christmas Coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/christmascoatg + subcategory = LOADOUT_SUBCATEGORY_SUIT_COATS -/datum/gear/christmascoatrg +/datum/gear/suit/christmascoatrg name = "Red and Green Christmas Coat" - category = SLOT_WEAR_SUIT path = /obj/item/clothing/suit/hooded/wintercoat/christmascoatrg + subcategory = LOADOUT_SUBCATEGORY_SUIT_COATS + +/datum/gear/suit/samurai + name = "Samurai outfit" + path = /obj/item/clothing/suit/samurai diff --git a/modular_citadel/code/modules/client/loadout/uniform.dm b/modular_citadel/code/modules/client/loadout/uniform.dm index e667626968..5ce73d1cfd 100644 --- a/modular_citadel/code/modules/client/loadout/uniform.dm +++ b/modular_citadel/code/modules/client/loadout/uniform.dm @@ -1,562 +1,542 @@ -/datum/gear/suitblack - name = "Black suit" - category = SLOT_W_UNIFORM - path = /obj/item/clothing/under/suit/black +/datum/gear/uniform + category = LOADOUT_CATEGORY_UNIFORM + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_GENERAL + slot = SLOT_W_UNIFORM -/datum/gear/suitgreen +/datum/gear/uniform/suit + name = "Black suit" + path = /obj/item/clothing/under/suit/black + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_SUITS + +/datum/gear/uniform/suit/green name = "Green suit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/suit/green -/datum/gear/suitred +/datum/gear/uniform/suit/red name = "Red suit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/suit/red -/datum/gear/suitcharcoal +/datum/gear/uniform/suit/charcoal name = "Charcoal suit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/suit/charcoal -/datum/gear/suitnavy +/datum/gear/uniform/suit/navy name = "Navy suit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/suit/navy -/datum/gear/suitburgundy +/datum/gear/uniform/suit/burgundy name = "Burgundy suit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/suit/burgundy -/datum/gear/suittan +/datum/gear/uniform/suit/tan name = "Tan suit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/suit/tan -/datum/gear/suitwhite +/datum/gear/uniform/suit/white name = "White suit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/suit/white -/datum/gear/assistantformal +/datum/gear/uniform/assistantformal name = "Assistant's formal uniform" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/assistantformal -/datum/gear/maidcostume +/datum/gear/uniform/maidcostume name = "Maid costume" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/maid -/datum/gear/mailmanuniform +/datum/gear/uniform/mailmanuniform name = "Mailman's jumpsuit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/mailman -/datum/gear/skirtblack +/datum/gear/uniform/skirt name = "Black skirt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/skirt + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_SKIRTS -/datum/gear/skirtblue +/datum/gear/uniform/skirt/blue name = "Blue skirt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/skirt/blue -/datum/gear/skirtred +/datum/gear/uniform/skirt/red name = "Red skirt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/skirt/red -/datum/gear/skirtpurple +/datum/gear/uniform/skirt/purple name = "Purple skirt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/skirt/purple -/datum/gear/skirtplaid +/datum/gear/uniform/skirt/plaid name = "Plaid skirt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/skirt/plaid -/datum/gear/schoolgirlblue +/datum/gear/uniform/schoolgirlblue name = "Blue Schoolgirl Uniform" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/schoolgirl -/datum/gear/schoolgirlred +/datum/gear/uniform/schoolgirlred name = "Red Schoolgirl Uniform" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/schoolgirl/red -/datum/gear/schoolgirlgreen +/datum/gear/uniform/schoolgirlgreen name = "Green Schoolgirl Uniform" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/schoolgirl/green -/datum/gear/schoolgirlorange +/datum/gear/uniform/schoolgirlorange name = "Orange Schoolgirl Uniform" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/schoolgirl/orange -/datum/gear/stripeddress +/datum/gear/uniform/dress name = "Striped Dress" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/striped + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_DRESSES -/datum/gear/sundresswhite +/datum/gear/uniform/dress/sun/white name = "White Sundress" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/sundress/white -/datum/gear/sundress +/datum/gear/uniform/dress/sun name = "Sundress" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/sundress -/datum/gear/greendress +/datum/gear/uniform/dress/green name = "Green Dress" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/green -/datum/gear/pinkdress +/datum/gear/uniform/dress/pink name = "Pink Dress" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/pink -/datum/gear/flowerdress + +/datum/gear/uniform/dress/orange name = "Flower Dress" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/flower -/datum/gear/sweptskirt +/datum/gear/uniform/skirt/swept name = "Swept skirt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/skirt/swept -/datum/gear/croptop +/datum/gear/uniform/croptop name = "Croptop" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/croptop -/datum/gear/yoga +/datum/gear/uniform/pants name = "Yoga Pants" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/yoga + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_PANTS -/datum/gear/kilt +/datum/gear/uniform/kilt name = "Kilt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/kilt -/datum/gear/camoshorts +/datum/gear/uniform/pants/camo name = "Camo Pants" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/camo -/datum/gear/athleticshorts +/datum/gear/uniform/shorts name = "Athletic Shorts" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/shorts/red + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_SHORTS -/datum/gear/bjeans +/datum/gear/uniform/pants/bjeans name = "Black Jeans" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/blackjeans -/datum/gear/cjeans +/datum/gear/uniform/pants/cjeans name = "Classic Jeans" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/classicjeans -/datum/gear/khaki +/datum/gear/uniform/pants/khaki name = "Khaki Pants" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/khaki -/datum/gear/wpants +/datum/gear/uniform/pants/white name = "White Pants" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/white -/datum/gear/rpants +/datum/gear/uniform/pants/red name = "Red Pants" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/red -/datum/gear/tpants +/datum/gear/uniform/pants/tan name = "Tan Pants" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/tan -/datum/gear/trpants +/datum/gear/uniform/pants/track name = "Track Pants" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/track -/datum/gear/rippedjeans +/datum/gear/uniform/pants/ripped name = "Ripped Jeans" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/jeanripped -/datum/gear/jeanshort +/datum/gear/uniform/shorts/jean name = "Jean Shorts" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/jeanshort -/datum/gear/denimskirt +/datum/gear/uniform/skirt/denim name = "Denim Skirt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/pants/denimskirt -/datum/gear/yoga - name = "Yoga Pants" - category = SLOT_W_UNIFORM - path = /obj/item/clothing/under/pants/yoga - // Pantsless Sweaters -/datum/gear/turtleneck +/datum/gear/uniform/turtleneck name = "Tactitool Turtleneck" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/syndicate/cosmetic -/datum/gear/creamsweater +/datum/gear/uniform/sweater name = "Cream Commando Sweater" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/sweater + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_SWEATERS -/datum/gear/blacksweater +/datum/gear/uniform/sweater/black name = "Black Commando Sweater" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/sweater/black -/datum/gear/purpsweater +/datum/gear/uniform/sweater/purple name = "Purple Commando Sweater" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/sweater/purple -/datum/gear/greensweater +/datum/gear/uniform/sweater/green name = "Green Commando Sweater" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/sweater/green -/datum/gear/redsweater +/datum/gear/uniform/sweater/red name = "Red Commando Sweater" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/sweater/red -/datum/gear/bluesweater +/datum/gear/uniform/sweater/blue name = "Navy Commando Sweater" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/sweater/blue -/datum/gear/keyholesweater +/datum/gear/uniform/sweater/keyhole name = "Keyhole Sweater" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/keyholesweater -/datum/gear/polyjump +/datum/gear/uniform/polyjump name = "Polychromic Jumpsuit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/polyjumpsuit cost = 2 - -/datum/gear/polyskirt + +/datum/gear/uniform/skirt/poly name = "Polychromic Jumpskirt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/skirt/polychromic cost = 2 -/datum/gear/polysuit +/datum/gear/uniform/suit/poly name = "Polychromic Button-up Shirt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/poly_shirt cost = 3 - -/datum/gear/polypleated + +/datum/gear/uniform/skirt/poly/pleated name = "Polychromic Pleated Sweaterskirt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/dress/skirt/polychromic/pleated cost = 3 -/datum/gear/polykilt +/datum/gear/uniform/polykilt name = "Polychromic Kilt" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/kilt/polychromic cost = 3 -/datum/gear/polyshorts +/datum/gear/uniform/shorts/poly name = "Polychromic Shorts" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/polyshorts cost = 3 -/datum/gear/polyshortpants +/datum/gear/uniform/shorts/poly/athletic name = "Polychromic Athletic Shorts" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/shorts/polychromic cost = 2 // Trekie things //TOS -/datum/gear/trekcmdtos +/datum/gear/uniform/trekcmdtos name = "TOS uniform, cmd" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/command + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Heads of Staff" restricted_roles = list("Head of Security","Captain","Head of Personnel","Chief Engineer","Research Director","Chief Medical Officer","Quartermaster") -/datum/gear/trekmedscitos +/datum/gear/uniform/trekmedscitos name = "TOS uniform, med/sci" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/medsci + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Medical and Science" restricted_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Virologist","Paramedic","Geneticist","Research Director","Scientist", "Roboticist") -/datum/gear/trekengtos +/datum/gear/uniform/trekengtos name = "TOS uniform, ops/sec" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/engsec + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Engineering, Security, and Cargo" restricted_roles = list("Chief Engineer","Atmospheric Technician","Station Engineer","Warden","Detective","Security Officer","Head of Security","Cargo Technician", "Shaft Miner", "Quartermaster") //TNG -/datum/gear/trekcmdtng +/datum/gear/uniform/trekcmdtng name = "TNG uniform, cmd" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/command/next + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Heads of Staff" restricted_roles = list("Head of Security","Captain","Head of Personnel","Chief Engineer","Research Director","Chief Medical Officer","Quartermaster") -/datum/gear/trekmedscitng +/datum/gear/uniform/trekmedscitng name = "TNG uniform, med/sci" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/medsci/next + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Medical and Science" restricted_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Virologist","Paramedic","Geneticist","Research Director","Scientist", "Roboticist") -/datum/gear/trekengtng +/datum/gear/uniform/trekengtng name = "TNG uniform, ops/sec" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/engsec/next + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Engineering, Security, and Cargo" restricted_roles = list("Chief Engineer","Atmospheric Technician","Station Engineer","Warden","Detective","Security Officer","Head of Security","Cargo Technician", "Shaft Miner", "Quartermaster") //VOY -/datum/gear/trekcmdvoy +/datum/gear/uniform/trekcmdvoy name = "VOY uniform, cmd" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/command/voy + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Heads of Staff" restricted_roles = list("Head of Security","Captain","Head of Personnel","Chief Engineer","Research Director","Chief Medical Officer","Quartermaster") -/datum/gear/trekmedscivoy +/datum/gear/uniform/trekmedscivoy name = "VOY uniform, med/sci" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/medsci/voy + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Medical and Science" restricted_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Virologist","Paramedic","Geneticist","Research Director","Scientist", "Roboticist") -/datum/gear/trekengvoy +/datum/gear/uniform/trekengvoy name = "VOY uniform, ops/sec" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/engsec/voy + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Engineering, Security, and Cargo" restricted_roles = list("Chief Engineer","Atmospheric Technician","Station Engineer","Warden","Detective","Security Officer","Head of Security","Cargo Technician", "Shaft Miner", "Quartermaster") //DS9 -/datum/gear/trekcmdds9 +/datum/gear/uniform/trekcmdds9 name = "DS9 uniform, cmd" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/command/ds9 + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Heads of Staff" restricted_roles = list("Head of Security","Captain","Head of Personnel","Chief Engineer","Research Director","Chief Medical Officer","Quartermaster") -/datum/gear/trekmedscids9 +/datum/gear/uniform/trekmedscids9 name = "DS9 uniform, med/sci" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/medsci/ds9 + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Medical and Science" restricted_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Virologist","Paramedic","Geneticist","Research Director","Scientist", "Roboticist") -/datum/gear/trekengds9 +/datum/gear/uniform/trekengds9 name = "DS9 uniform, ops/sec" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/engsec/ds9 + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Engineering, Security, and Cargo" restricted_roles = list("Chief Engineer","Atmospheric Technician","Station Engineer","Warden","Detective","Security Officer","Head of Security","Cargo Technician", "Shaft Miner", "Quartermaster") //ENT -/datum/gear/trekcmdent +/datum/gear/uniform/trekcmdent name = "ENT uniform, cmd" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/command/ent + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Heads of Staff" restricted_roles = list("Head of Security","Captain","Head of Personnel","Chief Engineer","Research Director","Chief Medical Officer","Quartermaster") -/datum/gear/trekmedscient +/datum/gear/uniform/trekmedscient name = "ENT uniform, med/sci" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/medsci/ent + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Medical and Science" restricted_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Virologist","Paramedic","Geneticist","Research Director","Scientist", "Roboticist") -/datum/gear/trekengent +/datum/gear/uniform/trekengent name = "ENT uniform, ops/sec" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/engsec/ent + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Engineering, Security, and Cargo" restricted_roles = list("Chief Engineer","Atmospheric Technician","Station Engineer","Warden","Detective","Security Officer","Head of Security","Cargo Technician", "Shaft Miner", "Quartermaster") //TheMotionPicture -/datum/gear/trekfedutil +/datum/gear/uniform/trekfedutil name = "TMP uniform" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/fedutil + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "All, barring Service and Civilian" restricted_roles = list("Head of Security","Captain","Head of Personnel","Chief Engineer","Research Director","Chief Medical Officer","Quartermaster", "Medical Doctor","Chemist","Virologist","Paramedic","Geneticist","Scientist", "Roboticist", "Atmospheric Technician","Station Engineer","Warden","Detective","Security Officer", "Cargo Technician", "Shaft Miner") -/datum/gear/trekfedtrainee +/datum/gear/uniform/trekfedtrainee name = "TMP uniform, trainee" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/fedutil/trainee + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_roles = list("Assistant", "Janitor", "Cargo Technician") -/datum/gear/trekfedservice +/datum/gear/uniform/trekfedservice name = "TMP uniform, service" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/fedutil/service + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Service and Civilian, barring Clown, Mime and Lawyer" restricted_roles = list("Assistant", "Bartender", "Botanist", "Cook", "Curator", "Janitor", "Chaplain") //Orvilike -/datum/gear/orvcmd +/datum/gear/uniform/orvcmd name = "ORV uniform, cmd" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/command/orv + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Heads of Staff" restricted_roles = list("Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Research Director", "Chief Medical Officer", "Quartermaster") -/datum/gear/orvcmd_capt +/datum/gear/uniform/orvcmd_capt name = "ORV uniform, capt" - category = SLOT_W_UNIFORM + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS path = /obj/item/clothing/under/trek/command/orv/captain restricted_roles = list("Captain") -/datum/gear/orvmedsci +/datum/gear/uniform/orvmedsci name = "ORV uniform, med/sci" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/medsci/orv + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Medical and Science" restricted_roles = list("Chief Medical Officer", "Medical Doctor", "Chemist", "Virologist", "Paramedic", "Geneticist", "Research Director", "Scientist", "Roboticist") -/datum/gear/orvcmd_medsci +/datum/gear/uniform/orvcmd_medsci name = "ORV uniform, med/sci, cmd" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/command/orv/medsci + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_roles = list("Chief Medical Officer", "Research Director") -/datum/gear/orvops +/datum/gear/uniform/orvops name = "ORV uniform, ops/sec" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/engsec/orv + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_desc = "Engineering, Security and Cargo" restricted_roles = list("Chief Engineer", "Atmospheric Technician", "Station Engineer", "Warden", "Detective", "Security Officer", "Head of Security", "Cargo Technician", "Shaft Miner", "Quartermaster") -/datum/gear/orvcmd_ops +/datum/gear/uniform/orvcmd_ops name = "ORV uniform, ops/sec, cmd" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/command/orv/engsec + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_roles = list("Chief Engineer", "Head of Security") -/datum/gear/orvass +/datum/gear/uniform/orvass name = "ORV uniform, assistant" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/orv + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_roles = list("Assistant") -/datum/gear/orvsrv +/datum/gear/uniform/orvsrv name = "ORV uniform, service" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/trek/orv/service + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_roles = list("Assistant", "Bartender", "Botanist", "Cook", "Curator", "Janitor", "Chaplain") restricted_desc = "Service and Civilian, barring Clown, Mime and Lawyer" //Memes -/datum/gear/gear_harnesses +/datum/gear/uniform/gear_harnesses name = "Gear Harness" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/gear_harness //Christmas /*Commenting out Until next Christmas or made automatic -/datum/gear/christmasmaler +/datum/gear/uniform/christmasmaler name = "Red Masculine Christmas Suit" category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/christmas -/datum/gear/christmasmaleg +/datum/gear/uniform/christmasmaleg name = "Green Masculine Christmas Suit" category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/christmas/green -/datum/gear/christmasfemaler +/datum/gear/uniform/christmasfemaler name = "Red Feminine Christmas Suit" category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/christmas/croptop -/datum/gear/christmasfemaleg +/datum/gear/uniform/christmasfemaleg name = "Green Feminine Christmas Suit" category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/christmas/croptop/green -/datum/gear/pinkstripper +/datum/gear/uniform/pinkstripper name = "Pink stripper outfit" category = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/stripper cost = 3 */ -/datum/gear/greenstripper +/datum/gear/uniform/greenstripper name = "Green stripper outfit" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/misc/stripper/green cost = 3 -/datum/gear/qipao +/datum/gear/uniform/qipao name = "Qipao, Black" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/qipao + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_DRESSES cost = 3 -/datum/gear/qipao/white +/datum/gear/uniform/qipao/white name = "Qipao, White" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/qipao/white + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_DRESSES cost = 3 -/datum/gear/qipao/red +/datum/gear/uniform/qipao/red name = "Qipao, Red" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/qipao/red + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_DRESSES cost = 3 -/datum/gear/cheongsam +/datum/gear/uniform/cheongsam name = "Cheongsam, Black" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/cheongsam + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_DRESSES cost = 3 -/datum/gear/cheongsam/white +/datum/gear/uniform/cheongsam/white name = "Cheongsam, White" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/cheongsam/white + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_DRESSES cost = 3 -/datum/gear/cheongsam/red +/datum/gear/uniform/cheongsam/red name = "Cheongsam, Red" - category = SLOT_W_UNIFORM path = /obj/item/clothing/under/costume/cheongsam/red + subcategory = LOADOUT_SUBCATEGORY_UNIFORM_DRESSES cost = 3 + +/datum/gear/uniform/dress/black + name = "Black dress" + path = /obj/item/clothing/under/misc/black_dress + +/datum/gear/uniform/skirt/pinktutu + name = "Pink tutu" + path = /obj/item/clothing/under/misc/pinktutu + +/datum/gear/uniform/bathrobe + name = "Bathrobe" + path = /obj/item/clothing/under/misc/bathrobe + +/datum/gear/uniform/kimono + name = "Kimono" + path = /obj/item/clothing/under/costume/kimono + +/datum/gear/uniform/kimono/black + name = "Black kimono" + path = /obj/item/clothing/under/costume/kimono/black + +/datum/gear/uniform/kimono/kamishimo + name = "Kamishimo" + path = /obj/item/clothing/under/costume/kimono/kamishimo + +/datum/gear/uniform/kimono/fancy + name = "Fancy kimono" + path = /obj/item/clothing/under/costume/kimono/fancy + +/datum/gear/uniform/kimono/sakura + name = "Sakura kimono" + path = /obj/item/clothing/under/costume/kimono/sakura diff --git a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm index a24a5beaad..39ba69bd61 100644 --- a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm +++ b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm @@ -178,7 +178,7 @@ name = "Sucubus milk" id = /datum/reagent/fermi/breast_enlarger results = list(/datum/reagent/fermi/breast_enlarger = 8) - required_reagents = list(/datum/reagent/medicine/salglu_solution = 1, /datum/reagent/consumable/milk = 1, /datum/reagent/medicine/synthflesh = 2, /datum/reagent/silicon = 3, /datum/reagent/drug/aphrodisiac = 3) + required_reagents = list(/datum/reagent/medicine/salglu_solution = 2, /datum/reagent/consumable/milk = 1, /datum/reagent/medicine/synthflesh = 2, /datum/reagent/silicon = 5) mix_message = "the reaction gives off a mist of milk." //FermiChem vars: OptimalTempMin = 200 @@ -218,7 +218,7 @@ name = "Incubus draft" id = /datum/reagent/fermi/penis_enlarger results = list(/datum/reagent/fermi/penis_enlarger = 8) - required_reagents = list(/datum/reagent/blood = 5, /datum/reagent/medicine/synthflesh = 2, /datum/reagent/carbon = 2, /datum/reagent/drug/aphrodisiac = 2, /datum/reagent/medicine/salglu_solution = 1) + required_reagents = list(/datum/reagent/blood = 5, /datum/reagent/medicine/synthflesh = 2, /datum/reagent/carbon = 5, /datum/reagent/medicine/salglu_solution = 2) mix_message = "the reaction gives off a spicy mist." //FermiChem vars: OptimalTempMin = 200 @@ -384,7 +384,7 @@ name = "Furranium" id = /datum/reagent/fermi/furranium results = list(/datum/reagent/fermi/furranium = 5) - required_reagents = list(/datum/reagent/drug/aphrodisiac = 1, /datum/reagent/moonsugar = 1, /datum/reagent/silver = 2, /datum/reagent/medicine/salglu_solution = 1) + required_reagents = list(/datum/reagent/pax/catnip = 1, /datum/reagent/silver = 2, /datum/reagent/medicine/salglu_solution = 2) mix_message = "You think you can hear a howl come from the beaker." //FermiChem vars: OptimalTempMin = 350 @@ -402,10 +402,6 @@ FermiChem = TRUE PurityMin = 0.3 -/datum/chemical_reaction/fermi/furranium/organic - id = "furranium_organic" - required_reagents = list(/datum/reagent/drug/aphrodisiac = 1, /datum/reagent/pax/catnip = 1, /datum/reagent/silver = 2, /datum/reagent/medicine/salglu_solution = 1) - //FOR INSTANT REACTIONS - DO NOT MULTIPLY LIMIT BY 10. //There's a weird rounding error or something ugh. @@ -607,4 +603,4 @@ ThermicConstant = 0 HIonRelease = 0.01 RateUpLim = 15 - FermiChem = TRUE \ No newline at end of file + FermiChem = TRUE diff --git a/modular_citadel/icons/mob/mam_snouts.dmi b/modular_citadel/icons/mob/mam_snouts.dmi index ab1a4654b4..4f6682f789 100644 Binary files a/modular_citadel/icons/mob/mam_snouts.dmi and b/modular_citadel/icons/mob/mam_snouts.dmi differ diff --git a/sound/weapons/etherealhit.ogg b/sound/weapons/etherealhit.ogg new file mode 100644 index 0000000000..19da870961 Binary files /dev/null and b/sound/weapons/etherealhit.ogg differ diff --git a/sound/weapons/etherealmiss.ogg b/sound/weapons/etherealmiss.ogg new file mode 100644 index 0000000000..8feb7cdc91 Binary files /dev/null and b/sound/weapons/etherealmiss.ogg differ diff --git a/strings/abductee_objectives.txt b/strings/abductee_objectives.txt index 93fd946818..135bb7673b 100644 --- a/strings/abductee_objectives.txt +++ b/strings/abductee_objectives.txt @@ -31,7 +31,7 @@ Flood the station's powernet with as much electricity as you can. Replace all the floor tiles with wood, carpeting, grass or bling. You must escape the station! Get the shuttle called! Don't allow anyone to be cloned. -The oxygen is killing them all and they don't even know it. Make sure no oxygen is on the station. +Their cold, mindless gaze pierce your soul. They see your every movements, it's merely a question of time before they try to control us further. Deactivate all cameras! Your body must be improved. Ingest as many drugs as you can. You are hungry. Eat as much food as you can find. You see you see what they cannot you see the open door you seeE you SEeEe you SEe yOU seEee SHOW THEM ALL diff --git a/strings/names/ethereal.txt b/strings/names/ethereal.txt new file mode 100644 index 0000000000..d3e6a26e6e --- /dev/null +++ b/strings/names/ethereal.txt @@ -0,0 +1,38 @@ +Aten +Apollo +Arche +Atlas +Eos +Halo +Kale +Nysa +Orion +Pallas +Rigel +Themis +Aurora +Andromeda +Lyra +Saggitarius +Crux +Canis +Cygnus +Corvus +Cepheus +Auriga +Corona +Aquilla +Serpens +Cetus +Puppis +Ophiuchus +Carina +Cassiopeia +Canes +Fornax +Berenices +Coma +Vela +Triangulum +Tau +Ceti \ No newline at end of file diff --git a/strings/traumas.json b/strings/traumas.json index f461c5f5fd..58170bd55a 100644 --- a/strings/traumas.json +++ b/strings/traumas.json @@ -125,13 +125,13 @@ ";chemist can u @pick(create_verbs) holy @pick(mellens) for @pick(s_roles)???!!", "@pick(semicolon) LIZZARRD SPEAKIGN IN EVIL BULL LANGUAGE SCI!!", "@pick(semicolon)POST REBOOT MESSAGE LOLOL FUCK FUCK FUCK YOU", - "@pick(semicolon)so, i was trying to talk to someone on rp today, and then a mime walks up and pies them in the face along with some other prankster--i thought that mimes and clowns are supposed to be hired to entertain not to be a nuisance, and that if entertainment comes at someone elses expense then it's not supposed to be done. is that enough to like submit a player complaint or some shit or am i just being petty?", "@pick(semicolon)*nya", "@pick(semicolon)*awoo", "@pick(semicolon)*merp", "@pick(semicolon)*weh", "@pick(semicolon)My balls finally feel full, again.", - "@pick(semicolon)Assaltign a sec osficer aren't crime if ur @pick(roles)" + "@pick(semicolon)Assaltign a sec osficer aren't crime if ur @pick(roles)", + ";SEC I SPILED MU JICE HELELPH HELPJ JLEP HELP" ], "mutations": [ diff --git a/tgstation.dme b/tgstation.dme index 203a6ad8e5..bfe0c58b8c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -47,6 +47,7 @@ #include "code\__DEFINES\dynamic.dm" #include "code\__DEFINES\economy.dm" #include "code\__DEFINES\events.dm" +#include "code\__DEFINES\exosuit_fabs.dm" #include "code\__DEFINES\exports.dm" #include "code\__DEFINES\fantasy_affixes.dm" #include "code\__DEFINES\food.dm" @@ -61,6 +62,7 @@ #include "code\__DEFINES\language.dm" #include "code\__DEFINES\layers_planes.dm" #include "code\__DEFINES\lighting.dm" +#include "code\__DEFINES\loadout.dm" #include "code\__DEFINES\logging.dm" #include "code\__DEFINES\machines.dm" #include "code\__DEFINES\maps.dm" @@ -200,6 +202,7 @@ #include "code\_globalvars\lists\client.dm" #include "code\_globalvars\lists\flavor_misc.dm" #include "code\_globalvars\lists\keybindings.dm" +#include "code\_globalvars\lists\loadout_categories.dm" #include "code\_globalvars\lists\maintenance_loot.dm" #include "code\_globalvars\lists\mapping.dm" #include "code\_globalvars\lists\medals.dm" @@ -428,6 +431,7 @@ #include "code\datums\components\field_of_vision.dm" #include "code\datums\components\footstep.dm" #include "code\datums\components\fried.dm" +#include "code\datums\components\gps.dm" #include "code\datums\components\identification.dm" #include "code\datums\components\igniter.dm" #include "code\datums\components\infective.dm" @@ -1405,6 +1409,7 @@ #include "code\modules\antagonists\abductor\equipment\abduction_outfits.dm" #include "code\modules\antagonists\abductor\equipment\abduction_surgery.dm" #include "code\modules\antagonists\abductor\equipment\gland.dm" +#include "code\modules\antagonists\abductor\equipment\orderable_gear.dm" #include "code\modules\antagonists\abductor\equipment\glands\access.dm" #include "code\modules\antagonists\abductor\equipment\glands\blood.dm" #include "code\modules\antagonists\abductor\equipment\glands\chem.dm" @@ -1522,6 +1527,7 @@ #include "code\modules\antagonists\clockcult\clock_effects\servant_blocker.dm" #include "code\modules\antagonists\clockcult\clock_effects\spatial_gateway.dm" #include "code\modules\antagonists\clockcult\clock_helpers\clock_powerdrain.dm" +#include "code\modules\antagonists\clockcult\clock_helpers\clock_rites.dm" #include "code\modules\antagonists\clockcult\clock_helpers\component_helpers.dm" #include "code\modules\antagonists\clockcult\clock_helpers\fabrication_helpers.dm" #include "code\modules\antagonists\clockcult\clock_helpers\hierophant_network.dm" @@ -1529,6 +1535,7 @@ #include "code\modules\antagonists\clockcult\clock_helpers\ratvarian_language.dm" #include "code\modules\antagonists\clockcult\clock_helpers\scripture_checks.dm" #include "code\modules\antagonists\clockcult\clock_helpers\slab_abilities.dm" +#include "code\modules\antagonists\clockcult\clock_items\clock_augments.dm" #include "code\modules\antagonists\clockcult\clock_items\clock_components.dm" #include "code\modules\antagonists\clockcult\clock_items\clockwork_armor.dm" #include "code\modules\antagonists\clockcult\clock_items\clockwork_slab.dm" @@ -1540,6 +1547,7 @@ #include "code\modules\antagonists\clockcult\clock_items\soul_vessel.dm" #include "code\modules\antagonists\clockcult\clock_items\wraith_spectacles.dm" #include "code\modules\antagonists\clockcult\clock_items\clock_weapons\_call_weapon.dm" +#include "code\modules\antagonists\clockcult\clock_items\clock_weapons\brass_claw.dm" #include "code\modules\antagonists\clockcult\clock_items\clock_weapons\ratvarian_shield.dm" #include "code\modules\antagonists\clockcult\clock_items\clock_weapons\ratvarian_spear.dm" #include "code\modules\antagonists\clockcult\clock_mobs\_eminence.dm" @@ -2266,6 +2274,7 @@ #include "code\modules\language\swarmer.dm" #include "code\modules\language\sylvan.dm" #include "code\modules\language\vampiric.dm" +#include "code\modules\language\voltaic.dm" #include "code\modules\language\xenocommon.dm" #include "code\modules\library\lib_codex_gigas.dm" #include "code\modules\library\lib_items.dm" @@ -2280,6 +2289,11 @@ #include "code\modules\lighting\lighting_setup.dm" #include "code\modules\lighting\lighting_source.dm" #include "code\modules\lighting\lighting_turf.dm" +#include "code\modules\mafia\_defines.dm" +#include "code\modules\mafia\controller.dm" +#include "code\modules\mafia\map_pieces.dm" +#include "code\modules\mafia\outfits.dm" +#include "code\modules\mafia\roles.dm" #include "code\modules\mapping\map_config.dm" #include "code\modules\mapping\map_orientation_pattern.dm" #include "code\modules\mapping\map_template.dm" @@ -2494,6 +2508,7 @@ #include "code\modules\mob\living\carbon\human\species_types\corporate.dm" #include "code\modules\mob\living\carbon\human\species_types\dullahan.dm" #include "code\modules\mob\living\carbon\human\species_types\dwarves.dm" +#include "code\modules\mob\living\carbon\human\species_types\ethereal.dm" #include "code\modules\mob\living\carbon\human\species_types\felinid.dm" #include "code\modules\mob\living\carbon\human\species_types\flypeople.dm" #include "code\modules\mob\living\carbon\human\species_types\furrypeople.dm" @@ -2607,6 +2622,7 @@ #include "code\modules\mob\living\simple_animal\friendly\penguin.dm" #include "code\modules\mob\living\simple_animal\friendly\pet.dm" #include "code\modules\mob\living\simple_animal\friendly\plushie.dm" +#include "code\modules\mob\living\simple_animal\friendly\possum.dm" #include "code\modules\mob\living\simple_animal\friendly\sloth.dm" #include "code\modules\mob\living\simple_animal\friendly\snake.dm" #include "code\modules\mob\living\simple_animal\friendly\drone\_drone.dm" @@ -2714,6 +2730,7 @@ #include "code\modules\mob\living\simple_animal\slime\slime_mobility.dm" #include "code\modules\mob\living\simple_animal\slime\subtypes.dm" #include "code\modules\modular_computers\laptop_vendor.dm" +#include "code\modules\modular_computers\computers\_modular_computer_shared.dm" #include "code\modules\modular_computers\computers\item\computer.dm" #include "code\modules\modular_computers\computers\item\computer_components.dm" #include "code\modules\modular_computers\computers\item\computer_damage.dm" @@ -2734,13 +2751,16 @@ #include "code\modules\modular_computers\file_system\programs\airestorer.dm" #include "code\modules\modular_computers\file_system\programs\alarm.dm" #include "code\modules\modular_computers\file_system\programs\arcade.dm" +#include "code\modules\modular_computers\file_system\programs\atmosscan.dm" #include "code\modules\modular_computers\file_system\programs\card.dm" +#include "code\modules\modular_computers\file_system\programs\cargobounty.dm" #include "code\modules\modular_computers\file_system\programs\configurator.dm" +#include "code\modules\modular_computers\file_system\programs\crewmanifest.dm" #include "code\modules\modular_computers\file_system\programs\file_browser.dm" +#include "code\modules\modular_computers\file_system\programs\jobmanagement.dm" #include "code\modules\modular_computers\file_system\programs\ntdownloader.dm" #include "code\modules\modular_computers\file_system\programs\ntmonitor.dm" #include "code\modules\modular_computers\file_system\programs\ntnrc_client.dm" -#include "code\modules\modular_computers\file_system\programs\nttransfer.dm" #include "code\modules\modular_computers\file_system\programs\powermonitor.dm" #include "code\modules\modular_computers\file_system\programs\radar.dm" #include "code\modules\modular_computers\file_system\programs\robocontrol.dm" @@ -3074,6 +3094,7 @@ #include "code\modules\research\research_disk.dm" #include "code\modules\research\server.dm" #include "code\modules\research\stock_parts.dm" +#include "code\modules\research\anomaly\anomaly_core.dm" #include "code\modules\research\designs\AI_module_designs.dm" #include "code\modules\research\designs\autobotter_designs.dm" #include "code\modules\research\designs\autoylathe_designs.dm" @@ -3356,15 +3377,14 @@ #include "code\modules\tgs\includes.dm" #include "code\modules\tgui\external.dm" #include "code\modules\tgui\states.dm" -#include "code\modules\tgui\subsystem.dm" #include "code\modules\tgui\tgui.dm" +#include "code\modules\tgui\tgui_window.dm" #include "code\modules\tgui\states\admin.dm" #include "code\modules\tgui\states\always.dm" #include "code\modules\tgui\states\conscious.dm" #include "code\modules\tgui\states\contained.dm" #include "code\modules\tgui\states\deep_inventory.dm" #include "code\modules\tgui\states\default.dm" -#include "code\modules\tgui\states\default_contained.dm" #include "code\modules\tgui\states\hands.dm" #include "code\modules\tgui\states\human_adjacent.dm" #include "code\modules\tgui\states\inventory.dm" diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml index 67e74085c7..9fd4db9fd2 100644 --- a/tgui/.eslintrc.yml +++ b/tgui/.eslintrc.yml @@ -8,6 +8,8 @@ env: es6: true browser: true node: true +globals: + Byond: readonly plugins: - react settings: @@ -388,7 +390,7 @@ rules: ## Enforce a particular style for multiline comments # multiline-comment-style: error ## Enforce newlines between operands of ternary expressions - multiline-ternary: [error, always-multiline] + # multiline-ternary: [error, always-multiline] ## Require constructor names to begin with a capital letter # new-cap: error ## Enforce or disallow parentheses when invoking a constructor with no diff --git a/tgui/.gitattributes b/tgui/.gitattributes index 0016cc3bf6..9382416e69 100644 --- a/tgui/.gitattributes +++ b/tgui/.gitattributes @@ -2,9 +2,18 @@ ## Enforce text mode and LF line breaks *.js text eol=lf +*.jsx text eol=lf +*.ts text eol=lf +*.tsx text eol=lf *.css text eol=lf +*.scss text eol=lf *.html text eol=lf *.json text eol=lf +*.yml text eol=lf +*.md text eol=lf +*.bat text eol=lf +yarn.lock text eol=lf +bin/tgui text eol=lf ## Treat bundles as binary and ignore them during conflicts *.bundle.* binary merge=tgui-merge-bundle diff --git a/tgui/README.md b/tgui/README.md index 5ddeb18fdd..9eab0196de 100644 --- a/tgui/README.md +++ b/tgui/README.md @@ -67,8 +67,9 @@ Run one of the following: game as you code it. Very useful, highly recommended. - In order to use it, you should start the game server first, connect to it and wait until the world has been properly loaded and you are no longer - in the lobby. Start tgui dev server. You'll know that it's hooked correctly - if data gets dumped to the log when tgui windows are opened. + in the lobby. Start tgui dev server, and once it has finished building, + press F5 on any tgui window. You'll know that it's hooked correctly if + you see a green bug icon in titlebar and data gets dumped to the console. - `bin/tgui --dev --reload` - reload byond cache once. - `bin/tgui --dev --debug` - run server with debug logging enabled. - `bin/tgui --dev --no-hot` - disable hot module replacement (helps when @@ -134,11 +135,11 @@ logs and time spent on rendering. Use this information to optimize your code, and try to keep re-renders below 16ms. **Kitchen Sink.** -Press `Ctrl+Alt+=` to open the KitchenSink interface. This interface is a +Press `F12` to open the KitchenSink interface. This interface is a playground to test various tgui components. **Layout Debugger.** -Press `Ctrl+Alt+-` to toggle the *layout debugger*. It will show outlines of +Press `F11` to toggle the *layout debugger*. It will show outlines of all tgui elements, which makes it easy to understand how everything comes together, and can reveal certain layout bugs which are not normally visible. @@ -180,8 +181,11 @@ See: [Component Reference](docs/component-reference.md). ## License -All code is licensed with the parent license of *tgstation*, **AGPL-3.0**. +Source code is covered by /tg/station's parent license - **AGPL-3.0** +(see the main [README](../README.md)), unless otherwise indicated. -See the main [README](../README.md) for more details. +Some files are annotated with a copyright header, which explicitly states +the copyright holder and license of the file. Most of the core tgui +source code is available under the **MIT** license. The Authors retain all copyright to their respective work here submitted. diff --git a/tgui/bin/tgui b/tgui/bin/tgui index eb1f200b31..97a86159e6 100755 --- a/tgui/bin/tgui +++ b/tgui/bin/tgui @@ -52,6 +52,7 @@ task-dev-server() { task-eslint() { cd "${base_dir}" eslint ./packages "${@}" + echo "tgui: eslint check passed" } ## Mr. Proper @@ -153,6 +154,13 @@ if [[ ${1} == '--lint-harder' ]]; then exit 0 fi +if [[ ${1} == '--fix' ]]; then + shift 1 + task-install + task-eslint --fix "${@}" + exit 0 +fi + ## Analyze the bundle if [[ ${1} == '--analyze' ]]; then task-install diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md index a2a0066a70..ff1b4e7dfd 100644 --- a/tgui/docs/component-reference.md +++ b/tgui/docs/component-reference.md @@ -30,6 +30,8 @@ Make sure to add new items to this list if you document new components. - [`Icon`](#icon) - [`Input`](#input) - [`Knob`](#knob) + - [`LabeledControls`](#labeledcontrols) + - [`LabeledControls.Item`](#labeledcontrolsitem) - [`LabeledList`](#labeledlist) - [`LabeledList.Item`](#labeledlistitem) - [`LabeledList.Divider`](#labeledlistdivider) @@ -239,7 +241,7 @@ A ghetto checkbox, made entirely using existing Button API. ### `Button.Confirm` -A button with a an extra confirmation step, using native button component. +A button with an extra confirmation step, using native button component. **Props:** @@ -273,11 +275,11 @@ interface. Example (button): -``` +```jsx @@ -285,11 +287,10 @@ Example (button): Example (map): -``` +```jsx ``` @@ -584,6 +585,24 @@ the input, or successfully enter a number. - `onDrag: (e, value) => void` - An event, which fires about every 500ms when you drag the input up and down, on release and on manual editing. +### `LabeledControls` + +LabeledControls is a horizontal grid, that is designed to hold various +controls, like [Knobs](#knob) or small [Buttons](#button). Every item in +this grid is labeled at the bottom. + +**Props:** + +- See inherited props: [Box](#box) +- `children: LabeledControls.Item` - Items to render. + +### `LabeledControls.Item` + +**Props:** + +- See inherited props: [Box](#box) +- `label: string` - Item label. + ### `LabeledList` LabeledList is a continuous, vertical list of text and other content, where @@ -962,6 +981,7 @@ Example: - `className: string` - Applies a CSS class to the element. - `theme: string` - A name of the theme. - For a list of themes, see `packages/tgui/styles/themes`. +- `title: string` - Window title. - `resizable: boolean` - Controls resizability of the window. - `children: any` - Child elements, which are rendered directly inside the window. If you use a [Dimmer](#dimmer) or [Modal](#modal) in your UI, diff --git a/tgui/docs/converting-old-tgui-interfaces.md b/tgui/docs/converting-old-tgui-interfaces.md index a42724e05c..fe2feebfee 100644 --- a/tgui/docs/converting-old-tgui-interfaces.md +++ b/tgui/docs/converting-old-tgui-interfaces.md @@ -73,6 +73,7 @@ This might look a bit intimidating compared to the reactive part but it's not as You don't really need to know all this to understand how to use it, but I find it helps with understanding when things go wrong. Ractive conditionals can have an `else` as well + ```ractive {{#if data.condition}} value @@ -116,7 +117,7 @@ and you can mix string literals, values, and tags as well. Ractive has loops for iterating over data and inserting something for each member of an array or object -``` +```ractive {{#each data.list_of_foo}} foo {{number}} is here. {{/each}} @@ -135,6 +136,7 @@ Objects are represented by `{}`, arrays by `[]` `list("bla", "blo")` would become `["bla", "blo"]` and `list("foo" = 1, "bar" = 2)` would become `{"foo": 1, "bar": 2}` First things first, above the `return` of the function you're making the interface in, you're going to want to add something like this + ```jsx const things = data.things || []; ``` @@ -142,6 +144,7 @@ const things = data.things || []; This ensures that you'll never be reading a null entry by mistake. Substitute `{}` for objects as appropriate. If it's an array, you'll want to do this in the template + ```jsx {things.map(thing => ( @@ -187,7 +190,7 @@ const fooArray = toArray(fooObject); Also occasionally you'd see an else: -``` +```ractive {{#each data.potentially_empty_list}} Thing "{{name}}" is in this list! {{else}} @@ -220,7 +223,7 @@ This will be a reference of tgui components and the tgui-next equivalent. Equivalent of `` is `
` -``` +```ractive Contents @@ -236,7 +239,7 @@ becomes A feature sometimes used is if `ui-display` has the `button` property, it will contain a `partial` command. This becomes the `buttons` property on `Section`: -``` +```ractive {{#partial button}} // lots more button bullshit here @@ -263,7 +266,7 @@ Very important to note `ui-section` is NOT the equivalent of `Section` `` does not have a direct equivalent, but the closest equivalent is `` -``` +```ractive No Power @@ -293,7 +296,7 @@ Also good to know that if you need the contents of a `LabeledList.Item` to be co `` has a direct equivalent in `` -``` +```ractive Notice stuff! @@ -311,7 +314,7 @@ becomes The equivalent of `ui-button` is `Button` but it works quite a bit differently. -``` +```ractive {
@@ -294,10 +292,10 @@ here's what you need (note that you'll probably be forced to clean your shit up upon code review): ```dm -/obj/copypasta/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state) // Remember to use the appropriate state. - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/copypasta/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "copypasta", name, 300, 300, master_ui, state) + ui = new(user, src, "copypasta") ui.open() /obj/copypasta/ui_data(mob/user) @@ -345,7 +343,7 @@ export const SampleInterface = (props, context) => { + ))} + + + + + + + + + + ); +}; + +const KitchenSinkButton = props => { + return ( +
+ +
+ ); +}; + +const KitchenSinkBox = props => { + return ( +
+ + bold + + + italic + + + opacity 0.5 + + + opacity 0.25 + + + m: 2 + + + left + + + center + + + right + +
+ ); +}; + +const KitchenSinkFlexAndSections = (props, context) => { + const [grow, setGrow] = useLocalState( + context, 'fs_grow', 1); + const [direction, setDirection] = useLocalState( + context, 'fs_direction', 'column'); + const [fill, setFill] = useLocalState( + context, 'fs_fill', true); + const [hasTitle, setHasTitle] = useLocalState( + context, 'fs_title', true); + return ( + + +
+ + + + +
+
+ + + +
+ Content +
+
+ +
+ Content +
+
+
+
+
+ ); +}; + +const KitchenSinkProgressBar = (props, context) => { + const [ + progress, + setProgress, + ] = useLocalState(context, 'progress', 0.5); + return ( +
+ + Value: {Number(progress).toFixed(1)} + + +
+ ); +}; + +const KitchenSinkTabs = (props, context) => { + const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0); + const [vertical, setVertical] = useLocalState(context, 'tabVert'); + const [altSelection, setAltSelection] = useLocalState(context, 'tabAlt'); + const TAB_RANGE = [1, 2, 3, 4, 5]; + return ( +
+ + setVertical(!vertical)} /> + setAltSelection(!altSelection)} /> + + + {TAB_RANGE.map((number, i) => ( + setTabIndex(i)}> + Tab #{number} + + ))} + +
+ ); +}; + +const KitchenSinkTooltip = props => { + const positions = [ + 'top', + 'left', + 'right', + 'bottom', + 'bottom-left', + 'bottom-right', + ]; + return ( +
+ + + Box (hover me). + + +
+ ); +}; + +const KitchenSinkInput = (props, context) => { + const [ + number, + setNumber, + ] = useLocalState(context, 'number', 0); + const [ + text, + setText, + ] = useLocalState(context, 'text', "Sample text"); + return ( +
+ + + setText(value)} /> + + + setText(value)} /> + + + setNumber(value)} /> + + + setNumber(value)} /> + + + setNumber(value)} /> + + + setNumber(value)} /> + setNumber(value)} /> + + + + setNumber(value)}> + {control => ( + + + {control.inputElement} + + )} + + + + +
+ ); +}; + +const KitchenSinkCollapsible = props => { + return ( +
+ + )}> + + +
+ ); +}; + +const BoxWithSampleText = props => { + return ( + + + Jackdaws love my big sphinx of quartz. + + + The wide electrification of the southern + provinces will give a powerful impetus to the + growth of agriculture. + + + ); +}; + +const KitchenSinkBlockQuote = props => { + return ( +
+
+ +
+
+ ); +}; + +const KitchenSinkByondUi = (props, context) => { + const { config } = useBackend(context); + const [code, setCode] = useLocalState(context, + 'byondUiEvalCode', + `Byond.winset('${window.__windowId__}', {\n 'is-visible': true,\n})`); + return ( + +
+ +
+
setImmediate(() => { + try { + const result = new Function('return (' + code + ')')(); + if (result && result.then) { + logger.log('Promise'); + result.then(logger.log); + } + else { + logger.log(result); + } + } + catch (err) { + logger.log(err); + } + })}> + Evaluate + + )}> + setCode(e.target.value)}> + {code} + +
+
+ ); +}; + +const KitchenSinkThemes = (props, context) => { + const [theme, setTheme] = useLocalState(context, 'kitchenSinkTheme'); + return ( +
+ + + setTheme(value)} /> + + +
+ ); +}; + +const KitchenSinkStorage = (props, context) => { + if (!window.localStorage) { + return ( + + Local storage is not available. + + ); + } + return ( +
{ + localStorage.clear(); + }}> + Clear + + )}> + + + {localStorage.length} + + + {formatSiUnit(localStorage.remainingSpace, 0, 'B')} + + +
+ ); +}; diff --git a/tgui/packages/tgui/debug/index.js b/tgui/packages/tgui/debug/index.js new file mode 100644 index 0000000000..83fc136548 --- /dev/null +++ b/tgui/packages/tgui/debug/index.js @@ -0,0 +1,49 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { subscribeToHotKey } from '../hotkeys'; + +export const toggleKitchenSink = () => ({ + type: 'debug/toggleKitchenSink', +}); + +export const toggleDebugLayout = () => ({ + type: 'debug/toggleDebugLayout', +}); + +subscribeToHotKey('F11', () => toggleDebugLayout()); +subscribeToHotKey('F12', () => toggleKitchenSink()); +subscribeToHotKey('Ctrl+Alt+[8]', () => { + // NOTE: We need to call this in a timeout, because we need a clean + // stack in order for this to be a fatal error. + setTimeout(() => { + throw new Error( + 'OOPSIE WOOPSIE!! UwU We made a fucky wucky!! A wittle' + + ' fucko boingo! The code monkeys at our headquarters are' + + ' working VEWY HAWD to fix this!'); + }); +}); + +export const selectDebug = state => state.debug; + +export const useDebug = context => selectDebug(context.store.getState()); + +export const debugReducer = (state = {}, action) => { + const { type, payload } = action; + if (type === 'debug/toggleKitchenSink') { + return { + ...state, + kitchenSink: !state.kitchenSink, + }; + } + if (type === 'debug/toggleDebugLayout') { + return { + ...state, + debugLayout: !state.debugLayout, + }; + } + return state; +}; diff --git a/tgui/packages/tgui/drag.js b/tgui/packages/tgui/drag.js index a29146b796..99db716410 100644 --- a/tgui/packages/tgui/drag.js +++ b/tgui/packages/tgui/drag.js @@ -1,76 +1,174 @@ -import { vecAdd, vecInverse, vecMultiply } from 'common/vector'; -import { winget, winset } from './byond'; +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { storage } from 'common/storage'; +import { vecAdd, vecInverse, vecMultiply, vecScale } from 'common/vector'; import { createLogger } from './logging'; const logger = createLogger('drag'); -let ref; +let windowKey = window.__windowId__; let dragging = false; let resizing = false; let screenOffset = [0, 0]; +let screenOffsetPromise; let dragPointOffset; let resizeMatrix; let initialSize; let size; -const getWindowPosition = ref => { - return winget(ref, 'pos').then(pos => [pos.x, pos.y]); +export const setWindowKey = key => { + windowKey = key; }; -const setWindowPosition = (ref, vec) => { - return winset(ref, 'pos', vec[0] + ',' + vec[1]); +export const getWindowPosition = () => [ + window.screenLeft, + window.screenTop, +]; + +export const getWindowSize = () => [ + window.innerWidth, + window.innerHeight, +]; + +export const setWindowPosition = vec => { + const byondPos = vecAdd(vec, screenOffset); + return Byond.winset(window.__windowId__, { + pos: byondPos[0] + ',' + byondPos[1], + }); }; -const setWindowSize = (ref, vec) => { - return winset(ref, 'size', vec[0] + ',' + vec[1]); +export const setWindowSize = vec => { + return Byond.winset(window.__windowId__, { + size: vec[0] + 'x' + vec[1], + }); }; -export const setupDrag = async state => { - logger.log('setting up'); - ref = state.config.window; - // Calculate offset caused by windows taskbar - const realPosition = await getWindowPosition(ref); - screenOffset = [ - realPosition[0] - window.screenLeft, - realPosition[1] - window.screenTop, - ]; - // Constraint window position - const [relocated, safePosition] = constraintPosition(realPosition); - if (relocated) { - setWindowPosition(ref, safePosition); +export const getScreenPosition = () => [ + 0 - screenOffset[0], + 0 - screenOffset[1], +]; + +export const getScreenSize = () => [ + window.screen.availWidth, + window.screen.availHeight, +]; + +/** + * Moves an item to the top of the recents array, and keeps its length + * limited to the number in `limit` argument. + * + * Uses a strict equality check for comparisons. + * + * Returns new recents and an item which was trimmed. + */ +const touchRecents = (recents, touchedItem, limit = 50) => { + const nextRecents = [touchedItem]; + let trimmedItem; + for (let i = 0; i < recents.length; i++) { + const item = recents[i]; + if (item === touchedItem) { + continue; + } + if (nextRecents.length < limit) { + nextRecents.push(item); + } + else { + trimmedItem = item; + } } - logger.debug('current state', { ref, screenOffset }); + return [nextRecents, trimmedItem]; +}; + +export const storeWindowGeometry = windowKey => { + logger.log('storing geometry'); + const geometry = { + pos: getWindowPosition(), + size: getWindowSize(), + }; + storage.set(windowKey, geometry); + // Update the list of stored geometries + const [geometries, trimmedKey] = touchRecents( + storage.get('geometries') || [], + windowKey); + if (trimmedKey) { + storage.remove(trimmedKey); + } + storage.set('geometries', geometries); +}; + +export const recallWindowGeometry = async (windowKey, options = {}) => { + // Only recall geometry in fancy mode + const geometry = options.fancy && storage.get(windowKey); + if (geometry) { + logger.log('recalled geometry:', geometry); + } + let pos = geometry?.pos || options.pos; + const size = options.size; + // Set window size + if (size) { + setWindowSize(size); + } + // Set window position + if (pos) { + await screenOffsetPromise; + // Constraint window position if monitor lock was set in preferences. + if (size && options.locked) { + pos = constraintPosition(pos, size)[1]; + } + setWindowPosition(pos); + } + // Set window position at the center of the screen. + else if (size) { + await screenOffsetPromise; + const areaAvailable = [ + window.screen.availWidth - Math.abs(screenOffset[0]), + window.screen.availHeight - Math.abs(screenOffset[1]), + ]; + const pos = vecAdd( + vecScale(areaAvailable, 0.5), + vecScale(size, -0.5), + vecScale(screenOffset, -1.0)); + setWindowPosition(pos); + } +}; + +export const setupDrag = async () => { + // Calculate screen offset caused by the windows taskbar + screenOffsetPromise = Byond.winget(window.__windowId__, 'pos') + .then(pos => [ + pos.x - window.screenLeft, + pos.y - window.screenTop, + ]); + screenOffset = await screenOffsetPromise; + logger.debug('screen offset', screenOffset); }; /** * Constraints window position to safe screen area, accounting for safe * margins which could be a system taskbar. */ -const constraintPosition = position => { - let x = position[0]; - let y = position[1]; +const constraintPosition = (pos, size) => { + const screenPos = getScreenPosition(); + const screenSize = getScreenSize(); + const nextPos = [pos[0], pos[1]]; let relocated = false; - // Left - if (x < 0) { - x = 0; - relocated = true; + for (let i = 0; i < 2; i++) { + const leftBoundary = screenPos[i]; + const rightBoundary = screenPos[i] + screenSize[i]; + if (pos[i] < leftBoundary) { + nextPos[i] = leftBoundary; + relocated = true; + } + else if (pos[i] + size[i] > rightBoundary) { + nextPos[i] = rightBoundary - size[i]; + relocated = true; + } } - // Right - else if (x + window.innerWidth > window.screen.availWidth) { - x = window.screen.availWidth - window.innerWidth; - relocated = true; - } - // Top - if (y < 0) { - y = 0; - relocated = true; - } - // Bottom - else if (y + window.innerHeight > window.screen.availHeight) { - y = window.screen.availHeight - window.innerHeight; - relocated = true; - } - return [relocated, [x, y]]; + return [relocated, nextPos]; }; export const dragStartHandler = event => { @@ -91,6 +189,7 @@ const dragEndHandler = event => { document.removeEventListener('mousemove', dragMoveHandler); document.removeEventListener('mouseup', dragEndHandler); dragging = false; + storeWindowGeometry(windowKey); }; const dragMoveHandler = event => { @@ -98,9 +197,8 @@ const dragMoveHandler = event => { return; } event.preventDefault(); - setWindowPosition(ref, vecAdd( + setWindowPosition(vecAdd( [event.screenX, event.screenY], - screenOffset, dragPointOffset)); }; @@ -127,6 +225,7 @@ const resizeEndHandler = event => { document.removeEventListener('mousemove', resizeMoveHandler); document.removeEventListener('mouseup', resizeEndHandler); resizing = false; + storeWindowGeometry(windowKey); }; const resizeMoveHandler = event => { @@ -140,7 +239,7 @@ const resizeMoveHandler = event => { dragPointOffset, [1, 1]))); // Sane window size values - size[0] = Math.max(size[0], 250); - size[1] = Math.max(size[1], 120); - setWindowSize(ref, size); + size[0] = Math.max(size[0], 150); + size[1] = Math.max(size[1], 50); + setWindowSize(size); }; diff --git a/tgui/packages/tgui/format.js b/tgui/packages/tgui/format.js index 8ce1915f51..1f8420860e 100644 --- a/tgui/packages/tgui/format.js +++ b/tgui/packages/tgui/format.js @@ -1,3 +1,9 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + import { clamp, round, toFixed } from 'common/math'; const SI_SYMBOLS = [ @@ -28,7 +34,14 @@ const SI_BASE_INDEX = SI_SYMBOLS.indexOf(' '); * Formats a number to a human readable form, by reducing it to SI units. * TODO: This is quite a shit code and shit math, needs optimization. */ -const formatSiUnit = (value, minBase1000 = -SI_BASE_INDEX, unit = '') => { +export const formatSiUnit = ( + value, + minBase1000 = -SI_BASE_INDEX, + unit = '' +) => { + if (typeof value !== 'number' || !Number.isFinite(value)) { + return value; + } const realBase10 = Math.floor(Math.log10(value)); const base10 = Math.floor(Math.max(minBase1000 * 3, realBase10)); const realBase1000 = Math.floor(realBase10 / 3); diff --git a/tgui/packages/tgui/global.d.ts b/tgui/packages/tgui/global.d.ts new file mode 100644 index 0000000000..ab8870f15c --- /dev/null +++ b/tgui/packages/tgui/global.d.ts @@ -0,0 +1,95 @@ +interface ByondType { + /** + * True if javascript is running in BYOND. + */ + IS_BYOND: boolean; + + /** + * True if browser is IE8 or lower. + */ + IS_LTE_IE8: boolean; + + /** + * True if browser is IE9 or lower. + */ + IS_LTE_IE9: boolean; + + /** + * True if browser is IE10 or lower. + */ + IS_LTE_IE10: boolean; + + /** + * True if browser is IE11 or lower. + */ + IS_LTE_IE11: boolean; + + /** + * Makes a BYOND call. + * + * If path is empty, this will trigger a Topic call. + * You can reference a specific object by setting the "src" parameter. + * + * See: https://secure.byond.com/docs/ref/skinparams.html + */ + call(path: string, params: object): void; + + /** + * Makes an asynchronous BYOND call. Returns a promise. + */ + callAsync(path: string, params: object): Promise; + + /** + * Makes a Topic call. + * + * You can reference a specific object by setting the "src" parameter. + */ + topic(params: object): void; + + /** + * Runs a command or a verb. + */ + command(command: string): void; + + /** + * Retrieves all properties of the BYOND skin element. + * + * Returns a promise with a key-value object containing all properties. + */ + winget(id: string): Promise; + + /** + * Retrieves all properties of the BYOND skin element. + * + * Returns a promise with a key-value object containing all properties. + */ + winget(id: string, propName: '*'): Promise; + + /** + * Retrieves an exactly one property of the BYOND skin element, + * as defined in `propName`. + * + * Returns a promise with the value of that property. + */ + winget(id: string, propName: string): Promise; + + /** + * Retrieves multiple properties of the BYOND skin element, + * as listen in the `propNames` array. + * + * Returns a promise with a key-value object containing listed properties. + */ + winget(id: string, propNames: string[]): Promise; + + /** + * Assigns properties to the BYOND skin element. + */ + winset(id: string, props: object): void; + + /** + * Sets a property on the BYOND skin element to a certain value. + */ + winset(id: string, propName: string, propValue: any): void; +}; + +declare const Byond: ByondType; diff --git a/tgui/packages/tgui/hotkeys.js b/tgui/packages/tgui/hotkeys.js index 30c1b910bf..0beff6b028 100644 --- a/tgui/packages/tgui/hotkeys.js +++ b/tgui/packages/tgui/hotkeys.js @@ -1,4 +1,9 @@ -import { callByond, IS_IE8 } from './byond'; +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + import { createLogger } from './logging'; const logger = createLogger('hotkeys'); @@ -48,6 +53,18 @@ export const KEY_W = 87; export const KEY_X = 88; export const KEY_Y = 89; export const KEY_Z = 90; +export const KEY_F1 = 112; +export const KEY_F2 = 113; +export const KEY_F3 = 114; +export const KEY_F4 = 115; +export const KEY_F5 = 116; +export const KEY_F6 = 117; +export const KEY_F7 = 118; +export const KEY_F8 = 119; +export const KEY_F9 = 120; +export const KEY_F10 = 121; +export const KEY_F11 = 122; +export const KEY_F12 = 123; export const KEY_EQUAL = 187; export const KEY_MINUS = 189; @@ -64,6 +81,18 @@ const NO_PASSTHROUGH_KEYS = [ KEY_TAB, KEY_CTRL, KEY_SHIFT, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, ]; // Tracks the "pressed" state of keys @@ -83,6 +112,9 @@ const createHotkeyString = (ctrlKey, altKey, shiftKey, keyCode) => { if (keyCode >= 48 && keyCode <= 90) { str += String.fromCharCode(keyCode); } + else if (keyCode >= KEY_F1 && keyCode <= KEY_F12) { + str += 'F' + (keyCode - 111); + } else { str += '[' + keyCode + ']'; } @@ -129,11 +161,11 @@ const handlePassthrough = (e, eventType) => { // Send this keypress to BYOND if (eventType === 'keydown' && !keyState[keyCode]) { logger.debug('passthrough', eventType, keyData); - return callByond('', { __keydown: keyCode }); + return Byond.topic({ __keydown: keyCode }); } if (eventType === 'keyup' && keyState[keyCode]) { logger.debug('passthrough', eventType, keyData); - return callByond('', { __keyup: keyCode }); + return Byond.topic({ __keyup: keyCode }); } }; @@ -146,41 +178,45 @@ export const releaseHeldKeys = () => { if (keyState[keyCode]) { logger.log(`releasing [${keyCode}] key`); keyState[keyCode] = false; - callByond('', { __keyup: keyCode }); + Byond.topic({ __keyup: keyCode }); } } }; -const handleHotKey = (e, eventType, dispatch) => { +const hotKeySubscribers = []; + +/** + * Subscribes to a certain hotkey, and dispatches a redux action returned + * by the callback function. + */ +export const subscribeToHotKey = (keyString, fn) => { + hotKeySubscribers.push((store, keyData) => { + if (keyData.keyString === keyString) { + const action = fn(store); + if (action) { + store.dispatch(action); + } + } + }); +}; + +const handleHotKey = (e, eventType, store) => { if (eventType !== 'keyup') { return; } const keyData = getKeyData(e); const { - ctrlKey, - altKey, keyCode, hasModifierKeys, keyString, } = keyData; // Dispatch a detected hotkey as a store action - if (hasModifierKeys && !MODIFIER_KEYS.includes(keyCode)) { + if (hasModifierKeys && !MODIFIER_KEYS.includes(keyCode) + || keyCode >= KEY_F1 && keyCode <= KEY_F12) { logger.log(keyString); - // Fun stuff - if (ctrlKey && altKey && keyCode === KEY_BACKSPACE) { - // NOTE: We need to call this in a timeout, because we need a clean - // stack in order for this to be a fatal error. - setTimeout(() => { - throw new Error( - 'OOPSIE WOOPSIE!! UwU We made a fucky wucky!! A wittle' - + ' fucko boingo! The code monkeys at our headquarters are' - + ' working VEWY HAWD to fix this!'); - }); + for (let subscriberFn of hotKeySubscribers) { + subscriberFn(store, keyData); } - dispatch({ - type: 'hotKey', - payload: keyData, - }); } }; @@ -218,18 +254,17 @@ const subscribeToKeyPresses = listenerFn => { // Middleware export const hotKeyMiddleware = store => { - const { dispatch } = store; // Subscribe to key events subscribeToKeyPresses((e, eventType) => { // IE8: Can't determine the focused element, so by extension it passes // keypresses when inputs are focused. - if (!IS_IE8) { + if (!Byond.IS_LTE_IE8) { handlePassthrough(e, eventType); } - handleHotKey(e, eventType, dispatch); + handleHotKey(e, eventType, store); }); // IE8: focusin/focusout only available on IE9+ - if (!IS_IE8) { + if (!Byond.IS_LTE_IE8) { // Clean up when browser window completely loses focus subscribeToLossOfFocus(() => { releaseHeldKeys(); @@ -238,27 +273,3 @@ export const hotKeyMiddleware = store => { // Pass through store actions (do nothing) return next => action => next(action); }; - -// Reducer -export const hotKeyReducer = (state, action) => { - const { type, payload } = action; - if (type === 'hotKey') { - const { ctrlKey, altKey, keyCode } = payload; - // Toggle kitchen sink mode - if (ctrlKey && altKey && keyCode === KEY_EQUAL) { - return { - ...state, - showKitchenSink: !state.showKitchenSink, - }; - } - // Toggle layout debugger - if (ctrlKey && altKey && keyCode === KEY_MINUS) { - return { - ...state, - debugLayout: !state.debugLayout, - }; - } - return state; - } - return state; -}; diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js index fb778ac58c..69092ce00f 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.js @@ -1,3 +1,9 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + // Polyfills import 'core-js/es'; import 'core-js/web/immediate'; @@ -12,75 +18,80 @@ import './polyfills/inferno'; // Themes import './styles/main.scss'; -import './styles/themes/malfunction.scss'; -import './styles/themes/ntos.scss'; +import './styles/themes/abductor.scss'; +import './styles/themes/cardtable.scss'; import './styles/themes/hackerman.scss'; +import './styles/themes/malfunction.scss'; +import './styles/themes/neutral.scss'; +import './styles/themes/ntos.scss'; +import './styles/themes/paper.scss'; import './styles/themes/retro.scss'; import './styles/themes/syndicate.scss'; import './styles/themes/clockcult.scss'; -import './styles/themes/paper.scss'; -import { loadCSS } from 'fg-loadcss'; +import { perf } from 'common/perf'; import { render } from 'inferno'; import { setupHotReloading } from 'tgui-dev-server/link/client'; -import { backendUpdate } from './backend'; -import { IS_IE8 } from './byond'; +import { backendUpdate, backendSuspendSuccess, selectBackend, sendMessage } from './backend'; import { setupDrag } from './drag'; import { logger } from './logging'; import { createStore, StoreProvider } from './store'; -const enteredBundleAt = Date.now(); +perf.mark('inception', window.__inception__); +perf.mark('init'); + const store = createStore(); let reactRoot; let initialRender = true; const renderLayout = () => { - // Mark the beginning of the render - let startedAt; - if (process.env.NODE_ENV !== 'production') { - startedAt = Date.now(); - } - try { - const state = store.getState(); - // Initial render setup - if (initialRender) { - logger.log('initial render', state); - // Setup dragging - setupDrag(state); + perf.mark('render/start'); + const state = store.getState(); + const { suspended, assets } = selectBackend(state); + // Initial render setup + if (initialRender) { + logger.log('initial render', state); + // Setup dragging + if (initialRender !== 'recycled') { + setupDrag(); } - // Start rendering - const { getRoutedComponent } = require('./routes'); - const Component = getRoutedComponent(state); - const element = ( - - - - ); - if (!reactRoot) { - reactRoot = document.getElementById('react-root'); - } - render(element, reactRoot); } - catch (err) { - logger.error('rendering error', err); - throw err; + // Start rendering + const { getRoutedComponent } = require('./routes'); + const Component = getRoutedComponent(state); + const element = ( + + + + ); + if (!reactRoot) { + reactRoot = document.getElementById('react-root'); } + render(element, reactRoot); + if (suspended) { + return; + } + perf.mark('render/finish'); // Report rendering time if (process.env.NODE_ENV !== 'production') { - const finishedAt = Date.now(); - if (initialRender) { + if (initialRender === 'recycled') { + logger.log('rendered in', + perf.measure('render/start', 'render/finish')); + } + else if (initialRender) { logger.debug('serving from:', location.href); - logger.debug('bundle entered in', timeDiff( - window.__inception__, enteredBundleAt)); - logger.debug('initialized in', timeDiff( - enteredBundleAt, startedAt)); - logger.log('rendered in', timeDiff( - startedAt, finishedAt)); - logger.log('fully loaded in', timeDiff( - window.__inception__, finishedAt)); + logger.debug('bundle entered in', + perf.measure('inception', 'init')); + logger.debug('initialized in', + perf.measure('init', 'render/start')); + logger.log('rendered in', + perf.measure('render/start', 'render/finish')); + logger.log('fully loaded in', + perf.measure('inception', 'render/finish')); } else { - logger.debug('rendered in', timeDiff(startedAt, finishedAt)); + logger.debug('rendered in', + perf.measure('render/start', 'render/finish')); } } if (initialRender) { @@ -88,12 +99,6 @@ const renderLayout = () => { } }; -const timeDiff = (startedAt, finishedAt) => { - const diff = finishedAt - startedAt; - const diffFrames = (diff / 16.6667).toFixed(2); - return `${diff}ms (${diffFrames} frames)`; -}; - // Parse JSON and report all abnormal JSON strings coming from BYOND const parseStateJson = json => { let reviver = (key, value) => { @@ -106,7 +111,7 @@ const parseStateJson = json => { }; // IE8: No reviver for you! // See: https://stackoverflow.com/questions/1288962 - if (IS_IE8) { + if (Byond.IS_LTE_IE8) { reviver = undefined; } try { @@ -127,14 +132,36 @@ const setupApp = () => { }); // Subscribe for bankend updates - window.update = stateJson => { - // NOTE: stateJson can be an object only if called manually from console. + window.update = messageJson => { + const { suspended } = selectBackend(store.getState()); + // NOTE: messageJson can be an object only if called manually from console. // This is useful for debugging tgui in external browsers, like Chrome. - const state = typeof stateJson === 'string' - ? parseStateJson(stateJson) - : stateJson; - // Backend update dispatches a store action - store.dispatch(backendUpdate(state)); + const message = typeof messageJson === 'string' + ? parseStateJson(messageJson) + : messageJson; + logger.debug(`received message '${message?.type}'`); + const { type, payload } = message; + if (type === 'update') { + if (suspended) { + logger.log('resuming'); + initialRender = 'recycled'; + } + // Backend update dispatches a store action + store.dispatch(backendUpdate(payload)); + return; + } + if (type === 'suspend') { + store.dispatch(backendSuspendSuccess()); + return; + } + if (type === 'ping') { + sendMessage({ + type: 'pingReply', + }); + return; + } + // Pass the message directly to the store + store.dispatch(message); }; // Enable hot module reloading @@ -157,9 +184,26 @@ const setupApp = () => { } window.update(stateJson); } +}; - // Dynamically load font-awesome from browser's cache - loadCSS('font-awesome.css'); +// Setup a fatal error reporter +window.__logger__ = { + fatal: (error, stack) => { + // Get last state for debugging purposes + const backendState = selectBackend(store.getState()); + const reportedState = { + config: backendState.config, + suspended: backendState.suspended, + suspending: backendState.suspending, + }; + // Send to development server + logger.log('FatalError:', error || stack); + logger.log('State:', reportedState); + // Append this data to the stack + stack += '\nState: ' + JSON.stringify(reportedState); + // Return an updated stack + return stack; + }, }; if (document.readyState === 'loading') { diff --git a/tgui/packages/tgui/interfaces/AbductorConsole.js b/tgui/packages/tgui/interfaces/AbductorConsole.js new file mode 100644 index 0000000000..de750bc9ca --- /dev/null +++ b/tgui/packages/tgui/interfaces/AbductorConsole.js @@ -0,0 +1,157 @@ +import { GenericUplink } from './Uplink'; +import { useBackend, useSharedState } from '../backend'; +import { Button, LabeledList, NoticeBox, Section, Tabs } from '../components'; +import { Fragment } from 'inferno'; +import { Window } from '../layouts'; + +export const AbductorConsole = (props, context) => { + const [tab, setTab] = useSharedState(context, 'tab', 1); + return ( + + + + setTab(1)}> + Abductsoft 3000 + + setTab(2)}> + Mission Settings + + + {tab === 1 && ( + + )} + {tab === 2 && ( + + + + + )} + + + ); +}; + +const Abductsoft = (props, context) => { + const { act, data } = useBackend(context); + const { + experiment, + points, + credits, + } = data; + + if (!experiment) { + return ( + + No Experiment Machine Detected + + ); + } + + return ( + +
+ + + {points} + + +
+ +
+ ); +}; + +const EmergencyTeleporter = (props, context) => { + const { act, data } = useBackend(context); + const { + pad, + gizmo, + } = data; + + if (!pad) { + return ( + + No Telepad Detected + + ); + } + + return ( +
act('teleporter_send')} /> + )}> + + +
+ ); +}; + +const VestSettings = (props, context) => { + const { act, data } = useBackend(context); + const { + vest, + vest_mode, + vest_lock, + } = data; + + if (!vest) { + return ( + + No Agent Vest Detected + + ); + } + + return ( +
act('toggle_vest')} /> + )}> + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Achievements.js b/tgui/packages/tgui/interfaces/Achievements.js index 464708d248..512c39fb59 100644 --- a/tgui/packages/tgui/interfaces/Achievements.js +++ b/tgui/packages/tgui/interfaces/Achievements.js @@ -12,7 +12,11 @@ export const Achievements = (props, context) => { const achievements = data.achievements .filter(x => x.category === selectedCategory); return ( - + {categories.map(category => ( diff --git a/tgui/packages/tgui/interfaces/AiAirlock.js b/tgui/packages/tgui/interfaces/AiAirlock.js index 4886e71034..a48ce24402 100644 --- a/tgui/packages/tgui/interfaces/AiAirlock.js +++ b/tgui/packages/tgui/interfaces/AiAirlock.js @@ -24,7 +24,9 @@ export const AiAirlock = (props, context) => { const statusBackup = dangerMap[data.power.backup] || dangerMap[0]; const statusElectrify = dangerMap[data.shock] || dangerMap[0]; return ( - +
diff --git a/tgui/packages/tgui/interfaces/AiRestorer.js b/tgui/packages/tgui/interfaces/AiRestorer.js index e91d392d10..9dfacda0c8 100644 --- a/tgui/packages/tgui/interfaces/AiRestorer.js +++ b/tgui/packages/tgui/interfaces/AiRestorer.js @@ -5,7 +5,10 @@ import { Window } from '../layouts'; export const AiRestorer = () => { return ( - + diff --git a/tgui/packages/tgui/interfaces/AirAlarm.js b/tgui/packages/tgui/interfaces/AirAlarm.js index c6f440985a..e669d8d948 100644 --- a/tgui/packages/tgui/interfaces/AirAlarm.js +++ b/tgui/packages/tgui/interfaces/AirAlarm.js @@ -6,12 +6,16 @@ import { Box, Button, LabeledList, NumberInput, Section } from '../components'; import { getGasLabel } from '../constants'; import { Window } from '../layouts'; import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; +import { Vent, Scrubber } from './common/AtmosControls'; export const AirAlarm = (props, context) => { const { act, data } = useBackend(context); const locked = data.locked && !data.siliconUser; return ( - + @@ -202,108 +206,6 @@ const AirAlarmControlVents = (props, context) => { )); }; -const Vent = (props, context) => { - const { vent } = props; - const { act } = useBackend(context); - const { - id_tag, - long_name, - power, - checks, - excheck, - incheck, - direction, - external, - internal, - extdefault, - intdefault, - } = vent; - return ( -
act('power', { - id_tag, - val: Number(!power), - })} /> - )}> - - - {direction === 'release' ? 'Pressurizing' : 'Releasing'} - - -
- ); -}; - - // Scrubbers // -------------------------------------------------------- @@ -320,71 +222,6 @@ const AirAlarmControlScrubbers = (props, context) => { )); }; -const Scrubber = (props, context) => { - const { scrubber } = props; - const { act } = useBackend(context); - const { - long_name, - power, - scrubbing, - id_tag, - widenet, - filter_types, - } = scrubber; - return ( -
act('power', { - id_tag, - val: Number(!power), - })} /> - )}> - - -
- ); -}; - - // Modes // -------------------------------------------------------- diff --git a/tgui/packages/tgui/interfaces/AirlockElectronics.js b/tgui/packages/tgui/interfaces/AirlockElectronics.js index e291052e25..a448f55e9b 100644 --- a/tgui/packages/tgui/interfaces/AirlockElectronics.js +++ b/tgui/packages/tgui/interfaces/AirlockElectronics.js @@ -12,7 +12,9 @@ export const AirlockElectronics = (props, context) => { const regions = data.regions || []; const accesses = data.accesses || []; return ( - +
diff --git a/tgui/packages/tgui/interfaces/Apc.js b/tgui/packages/tgui/interfaces/Apc.js index 0f44e3509d..60a11789bc 100644 --- a/tgui/packages/tgui/interfaces/Apc.js +++ b/tgui/packages/tgui/interfaces/Apc.js @@ -6,7 +6,10 @@ import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; export const Apc = (props, context) => { return ( - + @@ -82,7 +85,6 @@ const ApcContent = (props, context) => { ); } - return ( @@ -182,57 +184,35 @@ const ApcContent = (props, context) => { onClick={() => act('overload')} /> )}> - act('cover')} /> - )} /> - act('emergency_lighting')} /> - )} /> - act('toggle_nightshift')} /> - )} /> -
- {data.hijackable && ( -
+ + act('hijack')} /> -
); }; diff --git a/tgui/packages/tgui/interfaces/ApcControl.js b/tgui/packages/tgui/interfaces/ApcControl.js new file mode 100644 index 0000000000..449101d529 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ApcControl.js @@ -0,0 +1,306 @@ +import { map, sortBy } from 'common/collections'; +import { flow } from 'common/fp'; +import { pureComponentHooks } from 'common/react'; +import { useBackend, useLocalState } from '../backend'; +import { Box, Button, Dimmer, Flex, Icon, Table, Tabs } from '../components'; +import { Fragment, Window } from '../layouts'; +import { AreaCharge, powerRank } from './PowerMonitor'; + +export const ApcControl = (props, context) => { + const { data } = useBackend(context); + return ( + + {data.authenticated === 1 && ( + + )} + {data.authenticated === 0 && ( + + )} + + ); +}; + +const ApcLoggedOut = (props, context) => { + const { act, data } = useBackend(context); + const { emagged } = data; + const text = emagged === 1 ? 'Open' : 'Log In'; + return ( + +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/AutomatedAnnouncement.js b/tgui/packages/tgui/interfaces/AutomatedAnnouncement.js index d7b517d961..0baea4de97 100644 --- a/tgui/packages/tgui/interfaces/AutomatedAnnouncement.js +++ b/tgui/packages/tgui/interfaces/AutomatedAnnouncement.js @@ -17,7 +17,10 @@ export const AutomatedAnnouncement = (props, context) => { newhead, } = data; return ( - +
{ station_name, } = data; return ( - +
- { amount, } = data; return ( - +
{ + const { data } = useBackend(context); + const { + beaker, + processing, + } = data; + return ( + + {!!processing && ( + + + {' Processing...'} + + )} + + {!beaker && ( + No Container + )} + {!!beaker && ( + + )} + + + ); +}; + +export const BiogeneratorContent = (props, context) => { + const { act, data } = useBackend(context); + const { + biomass, + can_process, + categories = [], + } = data; + const [ + searchText, + setSearchText, + ] = useLocalState(context, 'searchText', ''); + const [ + selectedCategory, + setSelectedCategory, + ] = useLocalState(context, 'category', categories[0]?.name); + const testSearch = createSearch(searchText, item => { + return item.name; + }); + const items = searchText.length > 0 + // Flatten all categories and apply search to it + && categories + .flatMap(category => category.items || []) + .filter(testSearch) + .filter((item, i) => i < MAX_SEARCH_RESULTS) + // Select a category and show all items in it + || categories + .find(category => category.name === selectedCategory) + ?.items + // If none of that results in a list, return an empty list + || []; + return ( +
0 ? 'good' : 'bad'}> + {formatMoney(biomass)} Biomass + + )} + buttons={( + + Search + setSearchText(value)} + mx={1} /> +
+ ); +}; + +const ItemList = (props, context) => { + const { act } = useBackend(context); + const [ + hoveredItem, + setHoveredItem, + ] = useLocalState(context, 'hoveredItem', {}); + const hoveredCost = hoveredItem && hoveredItem.cost || 0; + // Append extra hover data to items + const items = props.items.map(item => { + const [ + amount, + setAmount, + ] = useLocalState(context, "amount" + item.name, 1); + const notSameItem = hoveredItem && hoveredItem.name !== item.name; + const notEnoughHovered = props.biomass - hoveredCost + * hoveredItem.amount < item.cost * amount; + const disabledDueToHovered = notSameItem && notEnoughHovered; + const disabled = props.biomass < item.cost * amount || disabledDueToHovered; + return { + ...item, + disabled, + amount, + setAmount, + }; + }); + return items.map(item => ( + + + + {' '}{item.name} + + + item.setAmount(value)} /> + + +
+ + + ); +}; + +const BountyHeader = (props, context) => { + const { act, data } = useBackend(context); + const { + stored_cash, + } = data; + return ( + + formatMoney(value)} /> + {' credits'} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/CargoExpress.js b/tgui/packages/tgui/interfaces/CargoExpress.js index 4e4ccf6a88..685569f6d5 100644 --- a/tgui/packages/tgui/interfaces/CargoExpress.js +++ b/tgui/packages/tgui/interfaces/CargoExpress.js @@ -8,7 +8,10 @@ import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; export const CargoExpress = (props, context) => { const { act, data } = useBackend(context); return ( - + diff --git a/tgui/packages/tgui/interfaces/CargoHoldTerminal.js b/tgui/packages/tgui/interfaces/CargoHoldTerminal.js index aaf66e701c..b20c0c9f9b 100644 --- a/tgui/packages/tgui/interfaces/CargoHoldTerminal.js +++ b/tgui/packages/tgui/interfaces/CargoHoldTerminal.js @@ -12,7 +12,10 @@ export const CargoHoldTerminal = (props, context) => { status_report, } = data; return ( - +
diff --git a/tgui/packages/tgui/interfaces/CellularEmporium.js b/tgui/packages/tgui/interfaces/CellularEmporium.js index 8e8d58cb94..07989d8bad 100644 --- a/tgui/packages/tgui/interfaces/CellularEmporium.js +++ b/tgui/packages/tgui/interfaces/CellularEmporium.js @@ -7,7 +7,10 @@ export const CellularEmporium = (props, context) => { const { act, data } = useBackend(context); const { abilities } = data; return ( - +
diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher.js b/tgui/packages/tgui/interfaces/CentcomPodLauncher.js index 7b21239144..05982b2fbf 100644 --- a/tgui/packages/tgui/interfaces/CentcomPodLauncher.js +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher.js @@ -6,7 +6,11 @@ import { Window } from '../layouts'; export const CentcomPodLauncher = () => { return ( - + @@ -57,6 +61,42 @@ export const CentcomPodLauncherContent = (props, context) => { `} onClick={() => act('bay5')} /> + {!!data.effectReverse && ( + + - ))} - + {product === "pill" && ( + + {pill_styles.map(pill => ( + + ))} + + )}
diff --git a/tgui/packages/tgui/interfaces/ChemReactionChamber.js b/tgui/packages/tgui/interfaces/ChemReactionChamber.js index 9af7ef7ddb..cbcf5574d8 100644 --- a/tgui/packages/tgui/interfaces/ChemReactionChamber.js +++ b/tgui/packages/tgui/interfaces/ChemReactionChamber.js @@ -17,7 +17,10 @@ export const ChemReactionChamber = (props, context) => { const emptying = data.emptying; const reagents = data.reagents || []; return ( - +
{ max_transfer, } = data; return ( - +
diff --git a/tgui/packages/tgui/interfaces/ChemSynthesizer.js b/tgui/packages/tgui/interfaces/ChemSynthesizer.js index 26db58cb3a..536173f039 100644 --- a/tgui/packages/tgui/interfaces/ChemSynthesizer.js +++ b/tgui/packages/tgui/interfaces/ChemSynthesizer.js @@ -12,7 +12,9 @@ export const ChemSynthesizer = (props, context) => { possible_amounts = [], } = data; return ( - +
diff --git a/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.js b/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.js new file mode 100644 index 0000000000..5fe3874422 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CivCargoHoldTerminal.js @@ -0,0 +1,103 @@ +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { AnimatedNumber, Box, Button, Flex, LabeledList, NoticeBox, Section } from '../components'; +import { Window } from '../layouts'; + +export const CivCargoHoldTerminal = (props, context) => { + const { act, data } = useBackend(context); + const { + pad, + sending, + status_report, + id_inserted, + id_bounty_info, + id_bounty_value, + id_bounty_num, + } = data; + const in_text = "Welcome valued employee."; + const out_text = "To begin, insert your ID into the console."; + return ( + + + + + + {id_inserted ? in_text : out_text} + +
+ + + {pad ? "Online" : "Not Found"} + + + {status_report} + + +
+ +
+ + +
+ + ); +}; -const AreaCharge = props => { +export const AreaCharge = props => { const { charging, charge } = props; return ( diff --git a/tgui/packages/tgui/interfaces/ProbingConsole.js b/tgui/packages/tgui/interfaces/ProbingConsole.js new file mode 100644 index 0000000000..4115be4e4b --- /dev/null +++ b/tgui/packages/tgui/interfaces/ProbingConsole.js @@ -0,0 +1,83 @@ +import { useBackend } from '../backend'; +import { Button, LabeledList, NoticeBox, Section } from '../components'; +import { Window } from '../layouts'; + +export const ProbingConsole = (props, context) => { + const { act, data } = useBackend(context); + const { + open, + feedback, + occupant, + occupant_name, + occupant_status, + } = data; + return ( + + +
+ + + {feedback} + + +
+
act('door')} /> + )}> + {occupant && ( + + + {occupant_name} + + + {occupant_status === 3 + ? 'Deceased' + : occupant_status === 2 + ? 'Unconcious' + : 'Concious'} + + +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ProximitySensor.js b/tgui/packages/tgui/interfaces/ProximitySensor.js index 8227d04802..c8dd04c3ee 100644 --- a/tgui/packages/tgui/interfaces/ProximitySensor.js +++ b/tgui/packages/tgui/interfaces/ProximitySensor.js @@ -12,7 +12,9 @@ export const ProximitySensor = (props, context) => { sensitivity, } = data; return ( - +
diff --git a/tgui/packages/tgui/interfaces/Radio.js b/tgui/packages/tgui/interfaces/Radio.js index 68b48d0230..738826ba63 100644 --- a/tgui/packages/tgui/interfaces/Radio.js +++ b/tgui/packages/tgui/interfaces/Radio.js @@ -25,8 +25,20 @@ export const Radio = (props, context) => { name: key, status: !!value, }))(data.channels); + // Calculate window height + let height = 106; + if (subspace) { + if (channels.length > 0) { + height += channels.length * 21 + 6; + } + else { + height += 24; + } + } return ( - +
diff --git a/tgui/packages/tgui/interfaces/RadioactiveMicrolaser.js b/tgui/packages/tgui/interfaces/RadioactiveMicrolaser.js index 40b6794d61..2fd20aa658 100644 --- a/tgui/packages/tgui/interfaces/RadioactiveMicrolaser.js +++ b/tgui/packages/tgui/interfaces/RadioactiveMicrolaser.js @@ -14,7 +14,11 @@ export const RadioactiveMicrolaser = (props, context) => { cooldown, } = data; return ( - +
diff --git a/tgui/packages/tgui/interfaces/RapidPipeDispenser.js b/tgui/packages/tgui/interfaces/RapidPipeDispenser.js index 54b664c57d..0380b7c4a3 100644 --- a/tgui/packages/tgui/interfaces/RapidPipeDispenser.js +++ b/tgui/packages/tgui/interfaces/RapidPipeDispenser.js @@ -72,7 +72,10 @@ export const RapidPipeDispenser = (props, context) => { .find(category => category.cat_name === categoryName) || categories[0]; return ( - +
diff --git a/tgui/packages/tgui/interfaces/RemoteRobotControl.js b/tgui/packages/tgui/interfaces/RemoteRobotControl.js index eb24bf22f8..91e0fa5ddf 100644 --- a/tgui/packages/tgui/interfaces/RemoteRobotControl.js +++ b/tgui/packages/tgui/interfaces/RemoteRobotControl.js @@ -6,7 +6,11 @@ import { Window } from '../layouts'; export const RemoteRobotControl = (props, context) => { return ( - + @@ -19,7 +23,6 @@ export const RemoteRobotControlContent = (props, context) => { const { robots = [], } = data; - if (!robots.length) { return (
@@ -29,7 +32,6 @@ export const RemoteRobotControlContent = (props, context) => {
); } - return robots.map(robot => { return (
{ + return ( + + + + + + ); +}; + +export const RequestKioskContent = (props, context) => { + const { act, data } = useBackend(context); + const { + accountName, + requests = [], + applicants = [], + bountyValue, + bountyText, + } = data; + const color = 'rgba(13, 13, 213, 0.7)'; + const backColor = 'rgba(0, 0, 69, 0.5)'; + return ( + +
+ + act('clear')} /> + )}> + {accountName || 'N/A'} + + +
+ + + {requests?.map(request => ( + +
+ + + {request.owner} + + + {formatMoney(request.value) + ' cr'} + + +
+
+ + ))} + + + +
+