diff --git a/.gitignore b/.gitignore index e80270c70c..172b470be4 100644 --- a/.gitignore +++ b/.gitignore @@ -202,6 +202,3 @@ tools/MapAtmosFixer/MapAtmosFixer/bin/* #Jukebox audio files /config/jukebox_music/**/* -_maps/map_files/nanostation/nanostation.dmm -_maps/nanostation.dm -_maps/nanotstation.json diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_puzzle.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_puzzle.dmm new file mode 100644 index 0000000000..911ee904fe --- /dev/null +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_puzzle.dmm @@ -0,0 +1,47 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/template_noop, +/area/lavaland/surface/outdoors) +"b" = ( +/obj/effect/sliding_puzzle/lavaland, +/turf/open/floor/plating/asteroid/basalt/lava_land_surface, +/area/lavaland/surface/outdoors) +"c" = ( +/turf/open/floor/plating/asteroid/basalt/lava_land_surface, +/area/lavaland/surface/outdoors) + +(1,1,1) = {" +a +a +a +a +a +"} +(2,1,1) = {" +a +c +c +c +a +"} +(3,1,1) = {" +a +c +b +c +a +"} +(4,1,1) = {" +a +c +c +c +a +"} +(5,1,1) = {" +a +a +a +a +a +"} diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index e00b845e55..6b0804c3ee 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -7198,7 +7198,7 @@ dir = 10 }, /turf/open/floor/plasteel/dark, -/area/bridge) +/area/ai_monitored/turret_protected/aisat_interior) "atN" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 5 @@ -7278,7 +7278,7 @@ req_access_txt = "10;13" }, /turf/open/floor/plating, -/area/bridge) +/area/ai_monitored/turret_protected/aisat_interior) "aua" = ( /obj/structure/closet/secure_closet/freezer/money, /obj/item/reagent_containers/food/drinks/bottle/vodka/badminka, @@ -8052,7 +8052,7 @@ /turf/open/floor/plasteel/vault{ dir = 5 }, -/area/bridge) +/area/ai_monitored/turret_protected/aisat_interior) "avR" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 @@ -8136,7 +8136,7 @@ req_access_txt = "10;13" }, /turf/open/floor/plating, -/area/bridge) +/area/ai_monitored/turret_protected/aisat_interior) "awd" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -8967,7 +8967,7 @@ }, /obj/machinery/door/firedoor, /turf/open/floor/plasteel/dark, -/area/bridge) +/area/ai_monitored/turret_protected/aisat_interior) "ayf" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 @@ -16023,22 +16023,13 @@ }, /turf/open/floor/plasteel, /area/hallway/secondary/exit/departure_lounge) -"aQA" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ - dir = 8 - }, -/turf/open/floor/plasteel, -/area/hallway/primary/central) "aQB" = ( -/obj/structure/disposalpipe/junction/flip{ - dir = 8 - }, /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 8 }, +/obj/structure/disposalpipe/segment{ + dir = 6 + }, /turf/open/floor/plasteel, /area/hallway/primary/central) "aQC" = ( @@ -24471,18 +24462,11 @@ }, /area/medical/medbay/central) "bmB" = ( -/obj/item/twohanded/required/kirbyplants{ - icon_state = "plant-21"; - pixel_y = 3 - }, /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 5 }, -/obj/structure/sign/poster/official/random{ - pixel_y = -32 - }, -/turf/open/floor/plasteel/yellow/corner{ - dir = 1 +/turf/open/floor/plasteel/blue/corner{ + dir = 8 }, /area/hallway/primary/central) "bmC" = ( @@ -26314,39 +26298,27 @@ "brl" = ( /obj/item/reagent_containers/glass/beaker/large, /obj/item/reagent_containers/glass/beaker/large, -/obj/structure/table/glass, /obj/item/reagent_containers/glass/beaker/large, /obj/item/reagent_containers/glass/beaker/large, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/dropper, +/obj/structure/table, /turf/open/floor/plasteel/whiteyellow/side{ dir = 1 }, /area/medical/chemistry) "brm" = ( /obj/machinery/chem_master, -/obj/machinery/button/door{ - id = "chemistry_shutters"; - name = "Shutters Control"; - pixel_x = 26; - pixel_y = 4; - req_access_txt = "5; 33" +/obj/machinery/requests_console{ + department = "Chemistry"; + departmentType = 2; + pixel_x = 32; + receive_ore_updates = 1 }, /turf/open/floor/plasteel/whiteyellow/side{ dir = 5 }, /area/medical/chemistry) -"bro" = ( -/obj/structure/plasticflaps/opaque, -/obj/effect/turf_decal/delivery, -/turf/open/floor/plasteel, -/area/maintenance/department/engine) -"brp" = ( -/obj/item/storage/toolbox/mechanical, -/turf/open/floor/plasteel/whitepurple/side{ - dir = 1 - }, -/area/science/lab) "brq" = ( /obj/structure/sink/kitchen{ desc = "A sink used for washing one's hands and face. It looks rusty and home-made"; @@ -26354,6 +26326,7 @@ pixel_y = 28 }, /obj/item/stack/cable_coil/orange, +/obj/item/storage/toolbox/mechanical, /turf/open/floor/plasteel/whitepurple/side{ dir = 1 }, @@ -26917,7 +26890,7 @@ /area/medical/chemistry) "bsL" = ( /obj/item/storage/box/beakers, -/obj/structure/table/glass, +/obj/structure/table, /turf/open/floor/plasteel/white, /area/medical/chemistry) "bsM" = ( @@ -27453,7 +27426,6 @@ /turf/open/floor/plasteel/whiteblue/corner, /area/medical/medbay/central) "buj" = ( -/obj/machinery/chem_heater, /obj/machinery/light{ dir = 8 }, @@ -27464,21 +27436,22 @@ c_tag = "Chemistry"; dir = 4 }, +/obj/structure/disposalpipe/trunk{ + dir = 4 + }, +/obj/machinery/disposal/bin, /turf/open/floor/plasteel/whiteyellow/corner{ dir = 1 }, /area/medical/chemistry) "buk" = ( /obj/structure/disposalpipe/segment{ - dir = 6 + dir = 10 }, /turf/open/floor/plasteel/white, /area/medical/chemistry) "bul" = ( -/obj/structure/disposalpipe/trunk{ - dir = 8 - }, -/obj/machinery/disposal/bin, +/obj/machinery/chem_heater, /turf/open/floor/plasteel/white, /area/medical/chemistry) "bum" = ( @@ -27486,8 +27459,12 @@ /turf/open/floor/plasteel/white, /area/medical/chemistry) "bun" = ( -/turf/open/floor/plasteel/whiteyellow/corner{ - dir = 4 +/obj/machinery/firealarm{ + dir = 4; + pixel_x = 28 + }, +/turf/open/floor/plasteel/whiteyellow/side{ + dir = 5 }, /area/medical/chemistry) "bup" = ( @@ -27894,11 +27871,13 @@ /turf/open/floor/plasteel/white, /area/medical/chemistry) "bvu" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ + dir = 1 }, -/turf/closed/wall, -/area/maintenance/department/engine) +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "bvw" = ( /obj/machinery/computer/rdconsole/core{ dir = 4 @@ -28464,18 +28443,12 @@ /obj/item/hand_labeler, /obj/item/clothing/glasses/science, /obj/item/clothing/glasses/science, -/obj/machinery/light_switch{ - pixel_x = 25 - }, /turf/open/floor/plasteel/whiteyellow/side{ dir = 6 }, /area/medical/chemistry) "bxa" = ( /obj/structure/table/glass, -/obj/structure/extinguisher_cabinet{ - pixel_x = -24 - }, /obj/item/book/manual/research_and_development, /obj/item/disk/tech_disk, /obj/item/disk/design_disk, @@ -29174,14 +29147,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/plasteel/white, /area/medical/chemistry) -"byy" = ( -/obj/structure/chair/office/light{ - dir = 4 - }, -/turf/open/floor/plasteel/whiteyellow/side{ - dir = 4 - }, -/area/medical/chemistry) "byz" = ( /obj/structure/window/reinforced{ dir = 8; @@ -29215,28 +29180,34 @@ /turf/open/floor/plasteel, /area/hallway/primary/aft) "byD" = ( -/obj/structure/disposalpipe/segment{ +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 6 }, /obj/structure/cable{ icon_state = "2-4" }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ +/obj/machinery/door/airlock/research{ + name = "R&D Lab"; + req_access_txt = "0"; + req_one_access_txt = "7;29;30" + }, +/obj/effect/turf_decal/delivery, +/obj/structure/disposalpipe/segment{ dir = 6 }, -/turf/open/floor/plasteel/dark, +/turf/open/floor/plasteel, /area/science/lab) "byE" = ( /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/structure/cable{ - icon_state = "4-8" - }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, -/turf/open/floor/plasteel/whitepurple/side, +/obj/structure/cable{ + icon_state = "4-8" + }, +/turf/open/floor/plasteel/dark, /area/science/lab) "byF" = ( /obj/structure/chair/office/light{ @@ -29905,21 +29876,14 @@ }, /obj/item/stack/cable_coil/random, /obj/item/book/manual/wiki/chemistry, -/turf/open/floor/plasteel/whiteyellow/side, -/area/medical/chemistry) -"bAh" = ( -/obj/structure/table/glass, +/obj/item/grenade/chem_grenade, +/obj/item/grenade/chem_grenade, /obj/item/stack/sheet/mineral/plasma{ amount = 2; layer = 2.9 }, -/obj/item/grenade/chem_grenade, -/obj/item/grenade/chem_grenade, -/obj/item/grenade/chem_grenade, /obj/item/screwdriver, -/turf/open/floor/plasteel/whiteyellow/side{ - dir = 6 - }, +/turf/open/floor/plasteel/whiteyellow/side, /area/medical/chemistry) "bAi" = ( /obj/machinery/smoke_machine, @@ -29935,24 +29899,16 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 }, -/obj/structure/sign/poster/random{ - pixel_y = 32 - }, /turf/open/floor/plasteel, /area/hallway/primary/aft) "bAm" = ( -/obj/machinery/door/airlock/maintenance{ - name = "Research Lab Maintenance"; - req_access_txt = "0"; - req_one_access_txt = "7;29" - }, /obj/structure/disposalpipe/segment, /obj/structure/cable{ icon_state = "1-2" }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/turf/open/floor/plating, -/area/maintenance/department/engine) +/turf/closed/wall/r_wall, +/area/hallway/primary/aft) "bAo" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -30263,15 +30219,20 @@ /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ dir = 8 }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, /turf/open/floor/plasteel/whiteblue/side{ dir = 8 }, /area/medical/medbay/central) "bBf" = ( -/obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 8 }, +/obj/structure/disposalpipe/junction/flip{ + dir = 1 + }, /turf/open/floor/plasteel/white, /area/medical/medbay/central) "bBg" = ( @@ -31333,8 +31294,10 @@ /obj/machinery/light/small{ dir = 4 }, -/turf/open/floor/plating, -/area/maintenance/department/engine) +/turf/open/floor/plasteel/purple/side{ + icon_state = "purplecorner" + }, +/area/hallway/primary/aft) "bDB" = ( /obj/machinery/airalarm/unlocked{ dir = 4; @@ -31946,8 +31909,10 @@ }, /obj/item/stock_parts/matter_bin, /obj/item/stock_parts/matter_bin, -/turf/open/floor/plating, -/area/maintenance/department/engine) +/turf/open/floor/plasteel/purple/side{ + icon_state = "purplecorner" + }, +/area/hallway/primary/aft) "bER" = ( /obj/machinery/computer/mecha{ dir = 4 @@ -33900,6 +33865,7 @@ /obj/structure/sign/departments/engineering{ pixel_y = -32 }, +/obj/machinery/door/firedoor, /turf/open/floor/plasteel/green/side, /area/hallway/primary/aft) "bJE" = ( @@ -46330,13 +46296,6 @@ /obj/item/flashlight/lantern, /turf/open/floor/plasteel/dark, /area/chapel/main/monastery) -"cwP" = ( -/obj/machinery/firealarm{ - dir = 4; - pixel_x = 28 - }, -/turf/closed/wall, -/area/medical/chemistry) "cwR" = ( /obj/structure/window/reinforced{ dir = 8; @@ -47248,6 +47207,16 @@ }, /turf/open/floor/plasteel, /area/science/explab) +"cKV" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on, +/obj/machinery/airalarm{ + dir = 4; + pixel_x = -23 + }, +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "cOp" = ( /obj/item/clothing/mask/gas, /obj/item/clothing/mask/gas, @@ -47386,8 +47355,8 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, -/turf/open/floor/plating, -/area/maintenance/department/engine) +/turf/open/floor/plasteel, +/area/hallway/primary/aft) "dgz" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -47415,8 +47384,14 @@ icon_state = "1-2" }, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/turf/open/floor/plating, -/area/maintenance/department/engine) +/obj/structure/chair{ + dir = 8; + name = "Defense" + }, +/turf/open/floor/plasteel/purple/side{ + icon_state = "purplecorner" + }, +/area/hallway/primary/aft) "dir" = ( /obj/machinery/atmospherics/pipe/simple/general/visible{ dir = 5 @@ -47607,6 +47582,22 @@ icon_state = "platingdmg1" }, /area/maintenance/department/engine) +"dHZ" = ( +/obj/structure/chair/office/light{ + dir = 1 + }, +/obj/effect/landmark/start/chemist, +/obj/machinery/button/door{ + id = "chemistry_shutters"; + name = "Shutters Control"; + pixel_x = 26; + pixel_y = 4; + req_access_txt = "5; 33" + }, +/turf/open/floor/plasteel/whiteyellow/side{ + dir = 1 + }, +/area/medical/chemistry) "dJm" = ( /obj/structure/cable{ icon_state = "4-8" @@ -47739,14 +47730,8 @@ /turf/open/floor/plasteel/white, /area/science/xenobiology) "eeF" = ( -/obj/structure/chair{ - dir = 8 - }, -/obj/structure/sign/poster/official/random{ - pixel_y = -32 - }, -/turf/open/floor/plasteel/yellow/corner{ - dir = 4 +/turf/open/floor/plasteel/purple/side{ + icon_state = "purplecorner" }, /area/hallway/primary/central) "eeQ" = ( @@ -47859,7 +47844,7 @@ dir = 1 }, /turf/open/floor/plasteel/neutral, -/area/maintenance/department/security/brig) +/area/hallway/secondary/exit/departure_lounge) "eCw" = ( /obj/structure/cable{ icon_state = "1-2" @@ -47966,6 +47951,11 @@ icon_state = "platingdmg3" }, /area/maintenance/department/security/brig) +"eRp" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/obj/machinery/door/firedoor, +/turf/open/floor/plasteel, +/area/hallway/primary/aft) "eSL" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /obj/item/beacon, @@ -48026,8 +48016,8 @@ /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{ dir = 1 }, -/turf/open/floor/plating, -/area/maintenance/department/engine) +/turf/open/floor/plasteel, +/area/hallway/primary/aft) "fdS" = ( /obj/machinery/door/window/southleft{ base_state = "left"; @@ -48423,8 +48413,8 @@ /obj/machinery/atmospherics/pipe/manifold/supply/hidden{ dir = 4 }, -/turf/open/floor/plating, -/area/maintenance/department/engine) +/turf/open/floor/plasteel, +/area/hallway/primary/aft) "gkX" = ( /obj/machinery/door/airlock/maintenance{ name = "Storage"; @@ -48557,6 +48547,15 @@ /obj/item/flashlight/lamp/green, /turf/open/floor/carpet, /area/lawoffice) +"gDR" = ( +/obj/machinery/camera{ + c_tag = "Central Primary Hallway Escape"; + dir = 4 + }, +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "gDZ" = ( /obj/effect/turf_decal/box/corners{ dir = 1 @@ -48601,12 +48600,6 @@ /turf/open/floor/plating, /area/maintenance/department/security/brig) "gGA" = ( -/obj/machinery/requests_console{ - department = "Chemistry"; - departmentType = 2; - pixel_x = 32; - receive_ore_updates = 1 - }, /obj/structure/table/glass, /obj/machinery/reagentgrinder, /obj/machinery/atmospherics/pipe/simple/supply/hidden{ @@ -48919,6 +48912,14 @@ }, /turf/open/floor/plating, /area/maintenance/department/security/brig) +"hIZ" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 1 + }, +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "hOx" = ( /obj/structure/cable{ icon_state = "2-4" @@ -49108,6 +49109,12 @@ /obj/machinery/processor/slime, /turf/open/floor/plasteel/white, /area/science/xenobiology) +"imE" = ( +/turf/open/floor/plasteel/purple/side{ + icon_state = "purple"; + dir = 4 + }, +/area/hallway/primary/aft) "ioj" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 @@ -49151,6 +49158,12 @@ dir = 8 }, /area/science/xenobiology) +"iwe" = ( +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden, +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "iyg" = ( /obj/structure/cable{ icon_state = "1-2" @@ -49537,12 +49550,6 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 6 }, -/obj/structure/sign/poster/random{ - pixel_y = 32 - }, -/obj/item/twohanded/required/kirbyplants{ - icon_state = "plant-17" - }, /obj/structure/sign/poster/official/random{ pixel_x = -32 }, @@ -49624,10 +49631,6 @@ }, /turf/open/floor/plating, /area/maintenance/department/science) -"jSA" = ( -/obj/structure/sign/departments/science, -/turf/closed/wall, -/area/maintenance/department/engine) "jTh" = ( /obj/structure/cable{ icon_state = "1-2" @@ -49693,6 +49696,10 @@ icon_state = "platingdmg3" }, /area/maintenance/department/engine) +"jZG" = ( +/obj/effect/spawner/structure/window, +/turf/open/floor/plating, +/area/medical/chemistry) "kfh" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 9 @@ -50059,6 +50066,14 @@ }, /turf/open/floor/plating/airless, /area/engine/engineering) +"kYM" = ( +/obj/structure/extinguisher_cabinet{ + pixel_x = 27 + }, +/turf/open/floor/plasteel/purple/side{ + icon_state = "purplecorner" + }, +/area/hallway/primary/aft) "lcU" = ( /obj/effect/turf_decal/stripes/line{ dir = 5 @@ -50127,6 +50142,11 @@ /obj/machinery/door/firedoor, /turf/open/floor/plasteel/dark, /area/library/lounge) +"ltE" = ( +/turf/open/floor/plasteel/purple/side{ + icon_state = "purplecorner" + }, +/area/hallway/primary/aft) "lzJ" = ( /obj/structure/closet/crate/bin, /turf/open/floor/carpet, @@ -50221,6 +50241,12 @@ /obj/item/wrench, /turf/open/floor/plating, /area/maintenance/department/science) +"lJI" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/structure/disposalpipe/segment, +/obj/machinery/door/firedoor, +/turf/open/floor/plasteel, +/area/hallway/primary/aft) "lKL" = ( /obj/machinery/door/airlock/abandoned{ name = "Starboard Emergency Storage"; @@ -50347,12 +50373,6 @@ dir = 1 }, /area/engine/engineering) -"mcf" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, -/turf/open/floor/plasteel/white, -/area/medical/chemistry) "mci" = ( /obj/machinery/portable_atmospherics/canister/toxins, /obj/structure/disposalpipe/segment{ @@ -50382,6 +50402,19 @@ }, /turf/open/floor/plating, /area/engine/engineering) +"mhn" = ( +/obj/machinery/door/firedoor, +/obj/structure/sign/poster/random{ + pixel_x = 32 + }, +/turf/open/floor/plasteel/purple/side{ + icon_state = "purplecorner" + }, +/area/hallway/primary/aft) +"mjn" = ( +/obj/machinery/jukebox, +/turf/open/floor/plasteel/dark, +/area/crew_quarters/bar) "mlr" = ( /obj/structure/lattice, /obj/structure/disposalpipe/junction{ @@ -50423,6 +50456,18 @@ dir = 1 }, /area/science/xenobiology) +"mqp" = ( +/obj/machinery/firealarm{ + dir = 8; + pixel_x = -28 + }, +/obj/machinery/light{ + dir = 8 + }, +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "msX" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable{ @@ -50696,9 +50741,6 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel/dark, /area/chapel/office) -"nnf" = ( -/turf/open/floor/plasteel/whiteyellow/side, -/area/medical/chemistry) "nnh" = ( /obj/structure/chair{ dir = 8 @@ -50981,6 +51023,11 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/wood, /area/lawoffice) +"nTr" = ( +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "nVU" = ( /obj/item/twohanded/spear, /turf/open/floor/plating, @@ -51518,8 +51565,8 @@ "phJ" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/open/floor/plating, -/area/maintenance/department/engine) +/turf/open/floor/plasteel, +/area/hallway/primary/aft) "pjH" = ( /obj/structure/cable{ icon_state = "1-2" @@ -51589,6 +51636,13 @@ }, /turf/open/space/basic, /area/space/nearstation) +"pxD" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/effect/spawner/structure/window, +/turf/open/floor/plating, +/area/medical/chemistry) "pBD" = ( /obj/structure/cable{ icon_state = "0-8" @@ -51691,12 +51745,6 @@ }, /turf/closed/wall/r_wall, /area/medical/virology) -"pSc" = ( -/obj/machinery/chem_heater, -/turf/open/floor/plasteel/whiteyellow/side{ - dir = 5 - }, -/area/medical/chemistry) "pVD" = ( /obj/machinery/light/small{ dir = 1 @@ -51767,6 +51815,9 @@ /obj/item/twohanded/required/kirbyplants{ icon_state = "plant-03" }, +/obj/structure/extinguisher_cabinet{ + pixel_x = -24 + }, /turf/open/floor/plasteel/dark, /area/science/lab) "pYC" = ( @@ -52027,6 +52078,15 @@ /obj/effect/turf_decal/stripes/corner, /turf/open/floor/plating/airless, /area/engine/engineering) +"qTV" = ( +/obj/item/radio/intercom{ + name = "Station Intercom (General)"; + pixel_x = -27 + }, +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "qUw" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plasteel/neutral, @@ -52683,6 +52743,16 @@ }, /turf/open/space, /area/engine/engineering) +"sYQ" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/machinery/door/firedoor, +/turf/open/floor/plasteel, +/area/hallway/primary/aft) "sZh" = ( /obj/structure/closet/crate, /turf/open/floor/plating, @@ -52843,6 +52913,14 @@ icon_state = "panelscorched" }, /area/maintenance/department/security/brig) +"tqO" = ( +/obj/structure/sign/poster/official/random{ + pixel_x = -32 + }, +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "tqX" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable{ @@ -52919,6 +52997,10 @@ }, /turf/open/floor/plating, /area/maintenance/department/security/brig) +"tzH" = ( +/obj/effect/spawner/structure/window, +/turf/open/floor/plating, +/area/science/lab) "tAK" = ( /obj/structure/table, /obj/structure/window/reinforced{ @@ -52951,6 +53033,11 @@ }, /turf/open/floor/plating/airless, /area/engine/engineering) +"tJr" = ( +/obj/structure/plasticflaps/opaque, +/obj/effect/turf_decal/delivery, +/turf/open/floor/plasteel, +/area/science/lab) "tPm" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -52970,8 +53057,8 @@ /area/maintenance/department/science) "tTl" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/open/floor/plating, -/area/maintenance/department/engine) +/turf/open/floor/plasteel, +/area/hallway/primary/aft) "tXn" = ( /obj/structure/sink{ dir = 4; @@ -53200,6 +53287,28 @@ /obj/machinery/field/generator, /turf/open/floor/plating, /area/maintenance/department/engine) +"uxP" = ( +/obj/structure/disposalpipe/segment{ + dir = 9 + }, +/obj/structure/cable{ + icon_state = "1-8" + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 9 + }, +/obj/structure/chair{ + dir = 8; + name = "Defense" + }, +/obj/machinery/camera{ + c_tag = "Atmospherics Starboard"; + dir = 8 + }, +/turf/open/floor/plasteel/purple/side{ + icon_state = "purplecorner" + }, +/area/hallway/primary/aft) "uzn" = ( /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden, /obj/structure/cable{ @@ -53440,6 +53549,20 @@ }, /turf/open/floor/plating, /area/maintenance/department/cargo) +"vsG" = ( +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/obj/machinery/door/airlock/maintenance{ + name = "Research Lab Maintenance"; + req_access_txt = "0"; + req_one_access_txt = "7;29" + }, +/turf/open/floor/plating, +/area/maintenance/department/engine) "vsJ" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel/neutral/corner{ @@ -53495,6 +53618,14 @@ }, /turf/open/floor/plasteel, /area/crew_quarters/fitness/recreation) +"vzA" = ( +/obj/machinery/light{ + dir = 8 + }, +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "vzP" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plating, @@ -53550,6 +53681,15 @@ }, /turf/open/floor/plating, /area/maintenance/department/security/brig) +"vKq" = ( +/obj/machinery/door/firedoor, +/obj/structure/sign/poster/official/random{ + pixel_x = -32 + }, +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "vMx" = ( /obj/structure/disposalpipe/segment{ dir = 6 @@ -54121,12 +54261,10 @@ /turf/open/floor/plasteel/dark, /area/library) "xje" = ( -/obj/machinery/door/airlock/maintenance{ - req_access_txt = "12" - }, /obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/open/floor/plating, -/area/maintenance/department/engine) +/obj/machinery/door/firedoor, +/turf/open/floor/plasteel, +/area/hallway/primary/aft) "xjK" = ( /obj/item/radio/intercom{ name = "Station Intercom (General)"; @@ -54276,11 +54414,13 @@ /obj/structure/sign/poster/official/random{ pixel_x = -32 }, -/obj/structure/chair{ - dir = 4 +/obj/machinery/light{ + dir = 8 }, -/turf/open/floor/plating, -/area/maintenance/department/engine) +/turf/open/floor/plasteel/blue/corner{ + dir = 8 + }, +/area/hallway/primary/aft) "xFl" = ( /obj/machinery/power/apc/highcap/five_k{ dir = 1; @@ -78201,7 +78341,7 @@ aAL aMR aAL aAL -aQA +aHE aAL aAL aTO @@ -85423,7 +85563,7 @@ bmz bnG boN bpW -brk +dHZ bsK buk bvs @@ -85942,8 +86082,8 @@ bsM bum bvt bwV -byy -bAh +byz +ooh bpY bCA bDy @@ -86174,7 +86314,7 @@ aSS aUg aVf aWi -bah +mjn aYe aZb bag @@ -86197,10 +86337,10 @@ bpV brm bsN bun -mcf -nnf -byz -ooh +gGA +bwW +byA +bAi bpY bCB bDz @@ -86452,14 +86592,14 @@ bmA bjd bpY bpY -cwP -pSc -gGA -bwW -byA -bAi bpY -pwj +bpY +pxD +jZG +bpY +bpY +bpY +vsG bva bva bva @@ -86707,19 +86847,19 @@ bls aBI aBI bmB -bva -bsn -bva -bva +vKq +nTr +gDR +vzA bvu -bva -bva -bva -bva -pwj -hVx +hIZ +tqO +mqp +cKV +iwe +qTV xDj -bva +blt jCv bOK bEN @@ -86976,7 +87116,7 @@ tTl dgg phJ phJ -xje +lJI bAk bIt bJB @@ -87221,19 +87361,19 @@ blu aDZ aDZ eeF -bBX -bBX -bBX -bro -bBX -bBX -bBX -bBX -bva +mhn +ltE +ltE +ltE +ltE +ltE +imE +kYM +bmC fdQ -npE -npE -bFZ +bmD +bmD +eRp bAl bIu bJC @@ -87479,20 +87619,20 @@ blt cqh bKM cCl -pYw -gFo -cSK -duF -bxa +cCl +cCl +tJr +tzH +tzH byD bAm dhz -bCD +uxP bDA bEQ -jSA -bHg -bIv +bGa +bHp +sYQ bJD bBo bBo @@ -87736,11 +87876,11 @@ bmC cqi boP bqb -brp -byF -cCt -byF -cCt +pYw +gFo +cSK +duF +bxa byE bBp bBp @@ -87994,9 +88134,9 @@ cqi boQ cCl brq +byF cCt -cCt -cCt +byF bxc nIU bAo diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index 255464d406..d43addf25a 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -70,6 +70,7 @@ #define ADMIN_PUNISHMENT_FIREBALL "Fireball" #define ADMIN_PUNISHMENT_ROD "Immovable Rod" #define ADMIN_PUNISHMENT_SUPPLYPOD "Supply Pod" +#define ADMIN_PUNISHMENT_MAZING "Puzzle" #define AHELP_ACTIVE 1 #define AHELP_CLOSED 2 diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index f1b62745ed..b2335129d7 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -23,6 +23,8 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s //define THIS_PROC_TYPE_WEIRD_STR "[THIS_PROC_TYPE_WEIRD]" //Included for completeness //define THIS_PROC_TYPE_WEIRD_STR_WITH_ARGS "[THIS_PROC_TYPE_WEIRD]([args.Join(",")])" //Ditto +#define NOT_IMPLEMENTED "NOT_IMPLEMENTED" + #define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day #define JANUARY 1 @@ -452,8 +454,13 @@ GLOBAL_LIST_INIT(pda_styles, list(MONO, VT, ORBITRON, SHARE)) #define PDAIMG(what) {""} //Filters -#define AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, border=4, color="#04080FAA") - +#define AMBIENT_OCCLUSION list("type"="drop_shadow","x"=0,"y"=-2,"size"=4,"border"=4,"color"="#04080FAA") +#define EYE_BLUR list("type"="blur", "size"=3) +#define GRAVITY_MOTION_BLUR list("type"="motion_blur","x"=0,"y"=0) #define STANDARD_GRAVITY 1 //Anything above this is high gravity, anything below no grav -#define GRAVITY_DAMAGE_TRESHOLD 3 //Starting with this value gravity will start to damage mobs \ No newline at end of file +#define GRAVITY_DAMAGE_TRESHOLD 3 //Starting with this value gravity will start to damage mobs + +#define CAMERA_NO_GHOSTS 0 +#define CAMERA_SEE_GHOSTS_BASIC 1 +#define CAMERA_SEE_GHOSTS_ORBIT 2 diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 9a8a249159..aed2e9c749 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -931,15 +931,6 @@ world flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. return flat_icon -//For photo camera. -/proc/build_composite_icon(atom/A) - var/icon/composite = icon(A.icon, A.icon_state, A.dir, 1) - for(var/O in A.overlays) - var/image/I = O - composite.Blend(icon(I.icon, I.icon_state, I.dir, 1), ICON_OVERLAY) - return composite - - //What the mob looks like as animated static //By vg's ComicIronic /proc/getStaticIcon(icon/A, safety = TRUE) diff --git a/code/__HELPERS/priority_announce.dm b/code/__HELPERS/priority_announce.dm index bbaf7b275d..8819eb4d04 100644 --- a/code/__HELPERS/priority_announce.dm +++ b/code/__HELPERS/priority_announce.dm @@ -19,7 +19,7 @@ announcement += "

[sender_override]

" if (title && length(title) > 0) announcement += "

[html_encode(title)]

" - + if(!sender_override) if(title == "") GLOB.news_network.SubmitArticle(text, "Central Command Update", "Station Announcements", null) @@ -46,7 +46,7 @@ var/datum/comm_message/M = new M.title = title M.content = text - + SScommunications.send_message(M) /proc/minor_announce(message, title = "Attention:", alert) diff --git a/code/__HELPERS/qdel.dm b/code/__HELPERS/qdel.dm new file mode 100644 index 0000000000..9bd0d4c95f --- /dev/null +++ b/code/__HELPERS/qdel.dm @@ -0,0 +1,10 @@ +#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE) +#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME) +#define QDEL_NULL(item) qdel(item); item = null +#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); } +#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE) +#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); } +#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); } + +/proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly. + QDEL_LIST(L) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 273e1cf1b7..f7aac6123e 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1293,19 +1293,6 @@ GLOBAL_REAL_VAR(list/stack_trace_storage) #define RANDOM_COLOUR (rgb(rand(0,255),rand(0,255),rand(0,255))) -#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE) -#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME) -#define QDEL_NULL(item) qdel(item); item = null -#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); } -#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE) -#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); } -#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); } - -/proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly. - QDEL_LIST(L) - - - /proc/random_nukecode() var/val = rand(0, 99999) var/str = "[val]" diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index 2951ddf165..8963b2d4b8 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -44,3 +44,13 @@ GLOBAL_LIST_EMPTY(adminlog) GLOBAL_PROTECT(adminlog) GLOBAL_LIST_EMPTY(active_turfs_startlist) + +/////Picture logging +GLOBAL_VAR(picture_log_directory) +GLOBAL_PROTECT(picture_log_directory) + +GLOBAL_VAR_INIT(picture_logging_id, 1) +GLOBAL_PROTECT(picture_logging_id) +GLOBAL_VAR(picture_logging_prefix) +GLOBAL_PROTECT(picture_logging_prefix) +///// diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index f98de83c23..b79e303d26 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -72,6 +72,9 @@ if(check_click_intercept(params,A)) return + if(notransform) + return + var/list/modifiers = params2list(params) if(modifiers["shift"] && modifiers["middle"]) ShiftMiddleClickOn(A) diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index 2bb514d008..5e2879a5d3 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -135,10 +135,10 @@ return if(isAI(usr)) var/mob/living/silicon/ai/AI = usr - AI.aicamera.toggle_camera_mode() + AI.aicamera.toggle_camera_mode(usr) else if(iscyborg(usr)) var/mob/living/silicon/robot/R = usr - R.aicamera.toggle_camera_mode() + R.aicamera.toggle_camera_mode(usr) /obj/screen/ai/image_view name = "View Images" @@ -149,10 +149,10 @@ return if(isAI(usr)) var/mob/living/silicon/ai/AI = usr - AI.aicamera.viewpictures() + AI.aicamera.viewpictures(usr) else if(iscyborg(usr)) var/mob/living/silicon/robot/R = usr - R.aicamera.viewpictures() + R.aicamera.viewpictures(usr) /obj/screen/ai/sensors name = "Sensor Augmentation" diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index edc15bb984..4e0fd0b976 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -123,11 +123,6 @@ /obj/screen/fullscreen/impaired icon_state = "impairedoverlay" -/obj/screen/fullscreen/blurry - icon = 'icons/mob/screen_gen.dmi' - screen_loc = "WEST,SOUTH to EAST,NORTH" - icon_state = "blurry" - /obj/screen/fullscreen/flash icon = 'icons/mob/screen_gen.dmi' screen_loc = "WEST,SOUTH to EAST,NORTH" diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm index d4559ed66d..b72dd948d7 100644 --- a/code/_onclick/hud/plane_master.dm +++ b/code/_onclick/hud/plane_master.dm @@ -29,9 +29,11 @@ blend_mode = BLEND_OVERLAY /obj/screen/plane_master/game_world/backdrop(mob/mymob) - filters = list() if(istype(mymob) && mymob.client && mymob.client.prefs && mymob.client.prefs.ambientocclusion) - filters += AMBIENT_OCCLUSION + add_filter("ambient_occlusion", 0, AMBIENT_OCCLUSION) + else + remove_filter("ambient_occlusion") + update_filters() /obj/screen/plane_master/lighting name = "lighting plane master" diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 1d41e9a765..f05a42e72c 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -410,3 +410,7 @@ /datum/config_entry/string/default_view config_entry_value = "15x15" + +/datum/config_entry/flag/log_pictures + +/datum/config_entry/flag/picture_logging_camera diff --git a/code/controllers/subsystem/npcpool.dm b/code/controllers/subsystem/npcpool.dm index 67e31b6931..d93f1f2407 100644 --- a/code/controllers/subsystem/npcpool.dm +++ b/code/controllers/subsystem/npcpool.dm @@ -23,7 +23,7 @@ SUBSYSTEM_DEF(npcpool) var/mob/living/simple_animal/SA = currentrun[currentrun.len] --currentrun.len - if(!SA.ckey) + if(!SA.ckey && !SA.notransform) if(SA.stat != DEAD) SA.handle_automated_movement() if(SA.stat != DEAD) diff --git a/code/controllers/subsystem/pathfinder.dm b/code/controllers/subsystem/pathfinder.dm index 871eba49ad..fee93d14b2 100644 --- a/code/controllers/subsystem/pathfinder.dm +++ b/code/controllers/subsystem/pathfinder.dm @@ -9,7 +9,7 @@ SUBSYSTEM_DEF(pathfinder) /datum/controller/subsystem/pathfinder/Initialize() space_type_cache = typecacheof(/turf/open/space) mobs = new(10) - circuits = new(3) + circuits = new(3) return ..() /datum/flowcache diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm index 14db2f1607..b844a1fc1a 100644 --- a/code/controllers/subsystem/persistence.dm +++ b/code/controllers/subsystem/persistence.dm @@ -15,6 +15,9 @@ SUBSYSTEM_DEF(persistence) var/list/spawned_objects = list() var/list/antag_rep = list() var/list/antag_rep_change = list() + var/list/picture_logging_information = list() + var/list/obj/structure/sign/picture_frame/photo_frames + var/list/obj/item/storage/photo_album/photo_albums /datum/controller/subsystem/persistence/Initialize() LoadSatchels() @@ -22,6 +25,7 @@ SUBSYSTEM_DEF(persistence) LoadChiselMessages() LoadTrophies() LoadRecentModes() + LoadPhotoPersistence() if(CONFIG_GET(flag/use_antag_rep)) LoadAntagReputation() ..() @@ -194,15 +198,74 @@ SUBSYSTEM_DEF(persistence) T.placer_key = chosen_trophy["placer_key"] T.update_icon() - /datum/controller/subsystem/persistence/proc/CollectData() CollectChiselMessages() CollectSecretSatchels() CollectTrophies() CollectRoundtype() + SavePhotoPersistence() //THIS IS PERSISTENCE, NOT THE LOGGING PORTION. if(CONFIG_GET(flag/use_antag_rep)) CollectAntagReputation() +/datum/controller/subsystem/persistence/proc/LoadPhotoPersistence() + var/album_path = file("data/photo_albums.json") + var/frame_path = file("data/photo_frames.json") + if(fexists(album_path)) + var/list/json = json_decode(file2text(album_path)) + if(json.len) + for(var/i in photo_albums) + var/obj/item/storage/photo_album/A = i + if(!A.persistence_id) + continue + if(json[A.persistence_id]) + A.populate_from_id_list(json[A.persistence_id]) + + if(fexists(frame_path)) + var/list/json = json_decode(file2text(frame_path)) + if(json.len) + for(var/i in photo_frames) + var/obj/structure/sign/picture_frame/PF = i + if(!PF.persistence_id) + continue + if(json[PF.persistence_id]) + PF.load_from_id(json[PF.persistence_id]) + +/datum/controller/subsystem/persistence/proc/SavePhotoPersistence() + var/album_path = file("data/photo_albums.json") + var/frame_path = file("data/photo_frames.json") + + var/list/frame_json = list() + var/list/album_json = list() + + if(fexists(album_path)) + album_json = json_decode(file2text(album_path)) + fdel(album_path) + + for(var/i in photo_albums) + var/obj/item/storage/photo_album/A = i + if(!istype(A) || !A.persistence_id) + continue + var/list/L = A.get_picture_id_list() + album_json[A.persistence_id] = L + + album_json = json_encode(album_json) + + WRITE_FILE(album_path, album_json) + + if(fexists(frame_path)) + frame_json = json_decode(file2text(frame_path)) + fdel(frame_path) + + for(var/i in photo_frames) + var/obj/structure/sign/picture_frame/F = i + if(!istype(F) || !F.persistence_id) + continue + frame_json[F.persistence_id] = F.get_photo_id() + + frame_json = json_encode(frame_json) + + WRITE_FILE(frame_path, frame_json) + /datum/controller/subsystem/persistence/proc/CollectSecretSatchels() satchel_blacklist = typecacheof(list(/obj/item/stack/tile/plasteel, /obj/item/crowbar)) var/list/satchels_to_add = list() diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index c88a86c092..60bb24c8c2 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -214,13 +214,16 @@ if(!C) C = H.client var/image = get_id_photo(H, C, show_directions) - var/obj/item/photo/photo_front = new() - var/obj/item/photo/photo_side = new() - for(var/D in show_directions) - if(D == SOUTH) - photo_front.photocreate(null, icon(image, dir = D)) - if(D == WEST || D == EAST) - photo_side.photocreate(null, icon(image, dir = D)) + var/datum/picture/pf = new + var/datum/picture/ps = new + pf.picture_name = "[H]" + ps.picture_name = "[H]" + pf.picture_desc = "This is [H]." + ps.picture_desc = "This is [H]." + pf.picture_image = icon(image, dir = SOUTH) + ps.picture_image = icon(image, dir = WEST) + var/obj/item/photo/photo_front = new(null, pf) + var/obj/item/photo/photo_side = new(null, ps) //These records should ~really~ be merged or something //General Record diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 9e14704123..aba03c784c 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -83,3 +83,67 @@ /datum/proc/to_chat_check_changed_vars(target = world) to_chat(target, txt_changed_vars()) #endif + +//Return a LIST for serialize_datum to encode! Not the actual json! +/datum/proc/serialize_list(list/options) + return NOT_IMPLEMENTED + +//Accepts a LIST from deserialize_datum. Should return src or another datum. +/datum/proc/deserialize_list(json, list/options) + return NOT_IMPLEMENTED + +//Serializes into JSON. Does not encode type. +/datum/proc/serialize_json(list/options) + . = serialize_list(options) + if((. == NOT_IMPLEMENTED) || !islist(.)) + . = null + else + . = json_encode(.) + +//Deserializes from JSON. Does not parse type. +/datum/proc/deserialize_json(list/input, list/options) + var/list/jsonlist = json_decode(input) + . = deserialize_list(jsonlist) + if(!istype(., /datum)) + . = null + +/proc/json_serialize_datum(datum/D, list/options) + if(!istype(D)) + return + var/list/jsonlist = D.serialize_list(options) + if(islist(jsonlist)) + jsonlist["DATUM_TYPE"] = D.type + return json_encode(jsonlist) + +/proc/json_deserialize_datum(list/jsonlist, list/options, target_type, strict_target_type = FALSE) + if(!islist(jsonlist)) + if(!istext(jsonlist)) + CRASH("Invalid JSON") + return + jsonlist = json_decode(jsonlist) + if(!islist(jsonlist)) + CRASH("Invalid JSON") + return + if(!jsonlist["DATUM_TYPE"]) + return + if(!ispath(jsonlist["DATUM_TYPE"])) + if(!istext(jsonlist["DATUM_TYPE"])) + return + jsonlist["DATUM_TYPE"] = text2path(jsonlist["DATUM_TYPE"]) + if(!ispath(jsonlist["DATUM_TYPE"])) + return + if(target_type) + if(!ispath(target_type)) + return + if(strict_target_type) + if(target_type != jsonlist["DATUM_TYPE"]) + return + else if(!ispath(jsonlist["DATUM_TYPE"], target_type)) + return + var/typeofdatum = jsonlist["DATUM_TYPE"] //BYOND won't directly read if this is just put in the line below, and will instead runtime because it thinks you're trying to make a new list? + var/datum/D = new typeofdatum + var/datum/returned = D.deserialize_list(jsonlist, options) + if(!istype(returned, /datum)) + qdel(D) + else + return returned \ No newline at end of file diff --git a/code/datums/martial/boxing.dm b/code/datums/martial/boxing.dm index 68b021bc7d..20c3570bc1 100644 --- a/code/datums/martial/boxing.dm +++ b/code/datums/martial/boxing.dm @@ -34,8 +34,8 @@ D.apply_damage(damage, STAMINA, affecting, armor_block) add_logs(A, D, "punched (boxing) ") - if(D.getStaminaLoss() > 50) - var/knockout_prob = D.getStaminaLoss() + rand(-15,15) + if(D.getStaminaLoss() > 100) + var/knockout_prob = (D.getStaminaLoss() + rand(-15,15))*0.75 if((D.stat != DEAD) && prob(knockout_prob)) D.visible_message("[A] has knocked [D] out with a haymaker!", \ "[A] has knocked [D] out with a haymaker!") diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm index af299aaee9..4006c13a2c 100644 --- a/code/datums/ruins/lavaland.dm +++ b/code/datums/ruins/lavaland.dm @@ -215,3 +215,10 @@ suffix = "lavaland_surface_random_ripley.dmm" allow_duplicates = FALSE cost = 5 + +/datum/map_template/ruin/lavaland/puzzle + name = "Ancient Puzzle" + id = "puzzle" + description = "Mystery to be solved." + suffix = "lavaland_surface_puzzle.dmm" + cost = 5 \ No newline at end of file diff --git a/code/datums/shuttles.dm b/code/datums/shuttles.dm index 3517f92060..b53b334132 100644 --- a/code/datums/shuttles.dm +++ b/code/datums/shuttles.dm @@ -126,7 +126,6 @@ var/datum/supply_pack/P = SSshuttle.supply_packs[/datum/supply_pack/engineering/shuttle_engine] P.special_enabled = TRUE - /datum/map_template/shuttle/emergency/asteroid suffix = "asteroid" name = "Asteroid Station Emergency Shuttle" @@ -229,7 +228,7 @@ suffix = "scrapheap" name = "Standby Evacuation Vessel \"Scrapheap Challenge\"" credit_cost = -1000 - description = "Due to a lack of functional emergency shuttles, we bought this second hand from a scrapyard and pressed it into service. Please do not lean to heavily on the exterior windows, they are fragile." + description = "Due to a lack of functional emergency shuttles, we bought this second hand from a scrapyard and pressed it into service. Please do not lean to heavily on the exterior windows, they are fragile. VIRUS ALERT: This entry seems to be booby-trapped with a redirector trap. Buy this at your own risk." admin_notes = "An abomination with no functional medbay, sections missing, and some very fragile windows. Surprisingly airtight." /datum/map_template/shuttle/emergency/narnar diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm index 7bbcbe6171..2f2ee53e3e 100644 --- a/code/datums/traits/negative.dm +++ b/code/datums/traits/negative.dm @@ -12,7 +12,7 @@ var/mob/living/carbon/human/H = quirk_holder if(NOBLOOD in H.dna.species.species_traits) //can't lose blood if your species doesn't have any return - else + else quirk_holder.blood_volume -= 0.275 diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 32e982e8e6..68089905b6 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -608,4 +608,10 @@ /atom/movable/proc/get_filter(name) if(filter_data && filter_data[name]) - return filters[filter_data.Find(name)] \ No newline at end of file + return filters[filter_data.Find(name)] + +/atom/movable/proc/remove_filter(name) + if(filter_data[name]) + filter_data -= name + update_filters() + return TRUE \ No newline at end of file diff --git a/code/game/gamemodes/objective_items.dm b/code/game/gamemodes/objective_items.dm index d42c546990..6603bb3604 100644 --- a/code/game/gamemodes/objective_items.dm +++ b/code/game/gamemodes/objective_items.dm @@ -148,12 +148,12 @@ /datum/objective_item/steal/blueprints/check_special_completion(obj/item/I) if(istype(I, /obj/item/areaeditor/blueprints)) - return 1 + return TRUE if(istype(I, /obj/item/photo)) var/obj/item/photo/P = I - if(P.blueprints) //if the blueprints are in frame - return 1 - return 0 + if(P.picture.has_blueprints) //if the blueprints are in frame + return TRUE + return FALSE /datum/objective_item/steal/slime name = "an unused sample of slime extract." diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 0ee394d889..2f1c0020b9 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -108,10 +108,10 @@ if(active1 in GLOB.data_core.general) if(istype(active1.fields["photo_front"], /obj/item/photo)) var/obj/item/photo/P1 = active1.fields["photo_front"] - user << browse_rsc(P1.img, "photo_front") + user << browse_rsc(P1.picture.picture_image, "photo_front") if(istype(active1.fields["photo_side"], /obj/item/photo)) var/obj/item/photo/P2 = active1.fields["photo_side"] - user << browse_rsc(P2.img, "photo_side") + user << browse_rsc(P2.picture.picture_image, "photo_side") dat += "Name:[active1.fields["name"]]" dat += "" dat += "" diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 8a2a06bf1f..666966ee2e 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -182,10 +182,10 @@ if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) if(istype(active1.fields["photo_front"], /obj/item/photo)) var/obj/item/photo/P1 = active1.fields["photo_front"] - user << browse_rsc(P1.img, "photo_front") + user << browse_rsc(P1.picture.picture_image, "photo_front") if(istype(active1.fields["photo_side"], /obj/item/photo)) var/obj/item/photo/P2 = active1.fields["photo_side"] - user << browse_rsc(P2.img, "photo_side") + user << browse_rsc(P2.picture.picture_image, "photo_side") dat += {"
@@ -446,7 +446,7 @@ What a mess.*/ sleep(30) if((istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)))//make sure the record still exists. var/obj/item/photo/photo = active1.fields["photo_front"] - new /obj/item/poster/wanted(src.loc, photo.img, wanted_name, info) + new /obj/item/poster/wanted(loc, photo.picture.picture_image, wanted_name, info) printing = 0 //RECORD DELETE @@ -611,7 +611,7 @@ What a mess.*/ if(photo) qdel(active1.fields["photo_front"]) //Lets center it to a 32x32. - var/icon/I = photo.img + var/icon/I = photo.picture.picture_image var/w = I.Width() var/h = I.Height() var/dw = w - 32 @@ -622,7 +622,7 @@ What a mess.*/ if(active1.fields["photo_front"]) if(istype(active1.fields["photo_front"], /obj/item/photo)) var/obj/item/photo/P = active1.fields["photo_front"] - print_photo(P.img, active1.fields["name"]) + print_photo(P.picture.picture_image, active1.fields["name"]) if("show_photo_side") if(active1.fields["photo_side"]) if(istype(active1.fields["photo_side"], /obj/item/photo)) @@ -633,7 +633,7 @@ What a mess.*/ if(photo) qdel(active1.fields["photo_side"]) //Lets center it to a 32x32. - var/icon/I = photo.img + var/icon/I = photo.picture.picture_image var/w = I.Width() var/h = I.Height() var/dw = w - 32 @@ -644,7 +644,7 @@ What a mess.*/ if(active1.fields["photo_side"]) if(istype(active1.fields["photo_side"], /obj/item/photo)) var/obj/item/photo/P = active1.fields["photo_side"] - print_photo(P.img, active1.fields["name"]) + print_photo(P.picture.picture_image, active1.fields["name"]) if("mi_crim_add") if(istype(active1, /datum/data/record)) var/t1 = stripped_input(usr, "Please input minor crime names:", "Secure. records", "", null) @@ -759,10 +759,9 @@ What a mess.*/ var/obj/item/photo/P = null if(issilicon(user)) var/mob/living/silicon/tempAI = user - var/datum/picture/selection = tempAI.GetPhoto() + var/datum/picture/selection = tempAI.GetPhoto(user) if(selection) - P = new() - P.photocreate(selection.fields["icon"], selection.fields["img"], selection.fields["desc"]) + P = new(null, selection) else if(istype(user.get_active_held_item(), /obj/item/photo)) P = user.get_active_held_item() return P @@ -778,7 +777,7 @@ What a mess.*/ small_img.Scale(8, 8) ic.Blend(small_img,ICON_OVERLAY, 13, 13) P.icon = ic - P.img = temp + P.picture.picture_image = temp P.desc = "The photo on file for [name]." P.pixel_x = rand(-10, 10) P.pixel_y = rand(-10, 10) diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm index 9d522f8f1c..940734c0c2 100644 --- a/code/game/machinery/doppler_array.dm +++ b/code/game/machinery/doppler_array.dm @@ -129,4 +129,4 @@ GLOBAL_LIST_EMPTY(doppler_arrays) linked_techweb = SSresearch.science_tech /proc/techweb_scale_bomb(lightradius) - return (lightradius ** 0.5) * 3000 + return lightradius ** 1.36 diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index aa847a4624..bb4868d054 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -126,9 +126,9 @@ GLOBAL_LIST_EMPTY(allCasters) newMsg.is_admin_message = adminMessage newMsg.locked = !allow_comments if(photo) - newMsg.img = photo.img + newMsg.img = photo.picture.picture_image newMsg.caption = photo.scribble - newMsg.photo_file = save_photo(photo.img) + newMsg.photo_file = save_photo(photo.picture.picture_image) for(var/datum/newscaster/feed_channel/FC in network_channels) if(FC.channel_name == channel_name) FC.messages += newMsg @@ -145,8 +145,8 @@ GLOBAL_LIST_EMPTY(allCasters) wanted_issue.scannedUser = scanned_user wanted_issue.isAdminMsg = adminMsg if(photo) - wanted_issue.img = photo.img - wanted_issue.photo_file = save_photo(photo.img) + wanted_issue.img = photo.picture.picture_image + wanted_issue.photo_file = save_photo(photo.picture.picture_image) if(newMessage) for(var/obj/machinery/newscaster/N in GLOB.allCasters) N.newsAlert() @@ -794,10 +794,7 @@ GLOBAL_LIST_EMPTY(allCasters) if(photo && !user.transferItemToLoc(photo, src)) photo = null if(issilicon(user)) - var/list/nametemp = list() - var/find - var/datum/picture/selection - var/obj/item/camera/siliconcam/targetcam = null + var/obj/item/camera/siliconcam/targetcam if(isAI(user)) var/mob/living/silicon/ai/R = user targetcam = R.aicamera @@ -809,19 +806,12 @@ GLOBAL_LIST_EMPTY(allCasters) targetcam = R.aicamera else to_chat(user, "You cannot interface with silicon photo uploading!") - if(targetcam.aipictures.len == 0) + if(!targetcam.stored.len) to_chat(usr, "No images saved") return - for(var/datum/picture/t in targetcam.aipictures) - nametemp += t.fields["name"] - find = input("Select image (numbered in order taken)") in nametemp - var/obj/item/photo/P = new/obj/item/photo() - for(var/datum/picture/q in targetcam.aipictures) - if(q.fields["name"] == find) - selection = q - break - P.photocreate(selection.fields["icon"], selection.fields["img"], selection.fields["desc"]) - P.sillynewscastervar = 1 + var/datum/picture/selection = targetcam.selectpicture(user) + var/obj/item/photo/P = new(null, selection) + P.sillynewscastervar = TRUE photo = P qdel(P) diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm index 74881cb064..5996bcea6b 100644 --- a/code/game/machinery/telecomms/computers/message.dm +++ b/code/game/machinery/telecomms/computers/message.dm @@ -141,7 +141,7 @@ break // Del - Sender - Recepient - Message // X - Al Green - Your Mom - WHAT UP!? - dat += "" + dat += "" dat += "
Name: [active1.fields["name"]] 
ID: [active1.fields["id"]] 
X
[pda.sender][pda.recipient][pda.message][pda.photo ? " (Photo)":""]
X
[pda.sender][pda.recipient][pda.message][pda.picture ? " (Photo)":""]
" //Hacking screen. if(2) diff --git a/code/game/machinery/telecomms/machines/message_server.dm b/code/game/machinery/telecomms/machines/message_server.dm index 45661add0d..3b2dbed96f 100644 --- a/code/game/machinery/telecomms/machines/message_server.dm +++ b/code/game/machinery/telecomms/machines/message_server.dm @@ -124,7 +124,7 @@ var/sender = "Unspecified" var/recipient = "Unspecified" var/message = "Blank" // transferred message - var/icon/photo // attached photo + var/datum/picture/picture // attached photo /datum/data_pda_msg/New(param_rec, param_sender, param_message, param_photo) if(param_rec) @@ -134,17 +134,17 @@ if(param_message) message = param_message if(param_photo) - photo = param_photo + picture = param_photo /datum/data_pda_msg/Topic(href,href_list) ..() if(href_list["photo"]) var/mob/M = usr - M << browse_rsc(photo, "pda_photo.png") + M << browse_rsc(picture.picture_image, "pda_photo.png") M << browse("PDA Photo" \ + "" \ + "" \ - + "", "window=pdaphoto;size=192x192") + + "", "window=pdaphoto;size=[picture.psize_x]x[picture.psize_y]") onclose(M, "pdaphoto") /datum/data_rc_msg diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index e3a5c85719..dbcb8c987c 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -27,6 +27,7 @@ var/turf/open/atmos_destination //Atmos link destination var/allow_anchored = FALSE var/innate_accuracy_penalty = 0 + var/last_effect = 0 /obj/effect/portal/anom name = "wormhole" @@ -154,7 +155,12 @@ return if(ismegafauna(M)) message_admins("[M] has used a portal at [ADMIN_VERBOSEJMP(src)] made by [usr].") - if(do_teleport(M, real_target, innate_accuracy_penalty)) + var/no_effect = FALSE + if(last_effect == world.time) + no_effect = TRUE + else + last_effect = world.time + if(do_teleport(M, real_target, innate_accuracy_penalty, no_effects = no_effect)) if(istype(M, /obj/item/projectile)) var/obj/item/projectile/P = M P.ignore_source_check = TRUE diff --git a/code/game/objects/effects/temporary_visuals/projectiles/impact.dm b/code/game/objects/effects/temporary_visuals/projectiles/impact.dm index abda070f5e..875eaf5e60 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/impact.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/impact.dm @@ -33,3 +33,6 @@ /obj/effect/projectile/impact/heavy_laser name = "heavy laser impact" icon_state = "impact_beam_heavy" + +/obj/effect/projectile/impact/wormhole + icon_state = "wormhole_g" diff --git a/code/game/objects/effects/temporary_visuals/projectiles/muzzle.dm b/code/game/objects/effects/temporary_visuals/projectiles/muzzle.dm index b3c72b293b..ad6b23f504 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/muzzle.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/muzzle.dm @@ -25,3 +25,6 @@ /obj/effect/projectile/muzzle/heavy_laser icon_state = "muzzle_beam_heavy" + +/obj/effect/projectile/muzzle/wormhole + icon_state = "wormhole_g" diff --git a/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm b/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm index 4c0af15503..49cdc3667c 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/projectile_effects.dm @@ -51,6 +51,10 @@ pixel_x += round((sin(angle_override)+16*sin(angle_override)*2), 1) pixel_y += round((cos(angle_override)+16*cos(angle_override)*2), 1) -/obj/effect/projectile_lighting/Initialize(mapload, color, range, intensity) +/obj/effect/projectile_lighting + var/owner + +/obj/effect/projectile_lighting/Initialize(mapload, color, range, intensity, owner_key) . = ..() set_light(range, intensity, color) + owner = owner_key diff --git a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm index 9728c84942..42ca48cd6d 100644 --- a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm +++ b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm @@ -1,4 +1,4 @@ -/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, beam_type, color, qdel_in = 5, light_range = 2, light_color_override, light_intensity = 1) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported! +/proc/generate_tracer_between_points(datum/point/starting, datum/point/ending, beam_type, color, qdel_in = 5, light_range = 2, light_color_override, light_intensity = 1, instance_key) //Do not pass z-crossing points as that will not be properly (and likely will never be properly until it's absolutely needed) supported! if(!istype(starting) || !istype(ending) || !ispath(beam_type)) return var/datum/point/midpoint = point_midpoint_points(starting, ending) @@ -9,9 +9,13 @@ . = PB if(light_range > 0 && light_intensity > 0) var/list/turf/line = getline(starting.return_turf(), ending.return_turf()) - for(var/i in line) - var/turf/T = i - QDEL_IN(new /obj/effect/projectile_lighting(T, light_color_override, light_range, light_intensity), qdel_in > 0? qdel_in : 5) + tracing_line: + for(var/i in line) + var/turf/T = i + for(var/obj/effect/projectile_lighting/PL in T) + if(PL.owner == instance_key) + continue tracing_line + QDEL_IN(new /obj/effect/projectile_lighting(T, light_color_override, light_range, light_intensity, instance_key), qdel_in > 0? qdel_in : 5) line = null if(qdel_in) QDEL_IN(PB, qdel_in) @@ -57,3 +61,6 @@ /obj/effect/projectile/tracer/tracer/aiming icon_state = "pixelbeam_greyscale" + +/obj/effect/projectile/tracer/wormhole + icon_state = "wormhole_g" diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index a9b5924a71..a9e0560ff4 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -32,7 +32,7 @@ var/drawtype var/text_buffer = "" - var/list/graffiti = list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","body","cyka","arrow","star","poseur tag","prolizard","antilizard") + var/list/graffiti = list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","body","cyka","arrow","star","poseur tag","prolizard","antilizard", "tile") //cit edit var/list/letters = list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z") var/list/numerals = list("0","1","2","3","4","5","6","7","8","9") var/list/oriented = list("arrow","body") // These turn to face the same way as the drawer diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 389934b778..5b3f657180 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -72,7 +72,7 @@ GLOBAL_LIST_EMPTY(PDAs) var/obj/item/paicard/pai = null // A slot for a personal AI device - var/icon/photo //Scanned photo + var/datum/picture/picture //Scanned photo var/list/contained_item = list(/obj/item/pen, /obj/item/toy/crayon, /obj/item/lipstick, /obj/item/flashlight/pen, /obj/item/clothing/mask/cigarette) var/obj/item/inserted_item //Used for pen, crayon, and lipstick insertion or removal. Same as above. @@ -636,8 +636,8 @@ GLOBAL_LIST_EMPTY(PDAs) "message" = message, "targets" = string_targets )) - if (photo) - signal.data["photo"] = photo + if (picture) + signal.data["photo"] = picture signal.send_to_receivers() // If it didn't reach, note that fact @@ -657,7 +657,7 @@ GLOBAL_LIST_EMPTY(PDAs) log_talk(user, "[key_name(user)] (PDA: [initial(name)]) sent \"[message]\" to [target_text]", LOGPDA) to_chat(user, "Message sent to [target_text]: \"[message]\"") // Reset the photo - photo = null + picture = null last_text = world.time if (everyone) last_everyone = world.time @@ -811,7 +811,7 @@ GLOBAL_LIST_EMPTY(PDAs) update_icon() else if(istype(C, /obj/item/photo)) var/obj/item/photo/P = C - photo = P.img + picture = P.picture to_chat(user, "You scan \the [C].") else return ..() @@ -925,11 +925,11 @@ GLOBAL_LIST_EMPTY(PDAs) var/selected = plist[c] - if(aicamera.aipictures.len>0) + if(aicamera.stored.len) var/add_photo = input(user,"Do you want to attach a photo?","Photo","No") as null|anything in list("Yes","No") if(add_photo=="Yes") - var/datum/picture/Pic = aicamera.selectpicture(aicamera) - aiPDA.photo = Pic.fields["img"] + var/datum/picture/Pic = aicamera.selectpicture(user) + aiPDA.picture = Pic if(incapacitated()) return diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm index 689319e672..12d5d34fbb 100644 --- a/code/game/objects/items/storage/toolbox.dm +++ b/code/game/objects/items/storage/toolbox.dm @@ -79,10 +79,17 @@ has_latches = FALSE /obj/item/storage/toolbox/mechanical/old/heirloom - name = "toolbox" //this will be named "X family toolbox" + name = "old, robust toolbox" //this will be named "X family toolbox" desc = "It's seen better days." - force = 5 - w_class = WEIGHT_CLASS_NORMAL + force = 12 + total_mass = 2 + +/obj/item/storage/toolbox/mechanical/old/heirloom/afterattack(atom/A, mob/user, proximity) + . = ..() + if(proximity) + if(isobj(A)) + var/obj/O = A + O.take_damage(20) /obj/item/storage/toolbox/mechanical/old/heirloom/PopulateContents() return diff --git a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm index 7a79b7d2de..29096c6dd8 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/cargo.dm @@ -19,3 +19,4 @@ new /obj/item/export_scanner(src) new /obj/item/door_remote/quartermaster(src) new /obj/item/circuitboard/machine/techfab/department/cargo(src) + new /obj/item/storage/photo_album/QM(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm index 97a157e0be..45216a6d95 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm @@ -29,6 +29,7 @@ new /obj/item/inducer(src) new /obj/item/circuitboard/machine/techfab/department/engineering(src) new /obj/item/extinguisher/advanced(src) + new /obj/item/storage/photo_album/CE(src) /obj/structure/closet/secure_closet/engineering_electrical name = "electrical supplies locker" diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm index e2f0fb5f88..93da17b9b1 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm @@ -76,6 +76,7 @@ new /obj/item/pet_carrier(src) new /obj/item/wallframe/defib_mount(src) new /obj/item/circuitboard/machine/techfab/department/medical(src) + new /obj/item/storage/photo_album/CMO(src) /obj/structure/closet/secure_closet/animal name = "animal control" diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm index 6e5c425b2b..eb10b97bc3 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm @@ -25,3 +25,4 @@ new /obj/item/laser_pointer(src) new /obj/item/door_remote/research_director(src) new /obj/item/circuitboard/machine/techfab/department/science(src) + new /obj/item/storage/photo_album/RD(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index 52032e9bb3..47226c6a61 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -33,6 +33,7 @@ new /obj/item/gun/energy/e_gun(src) new /obj/item/door_remote/captain(src) new /obj/item/card/id/captains_spare(src) + new /obj/item/storage/photo_album/Captain(src) /obj/structure/closet/secure_closet/hop name = "\proper head of personnel's locker" @@ -59,6 +60,7 @@ new /obj/item/pet_carrier(src) new /obj/item/door_remote/civillian(src) new /obj/item/circuitboard/machine/techfab/department/service(src) + new /obj/item/storage/photo_album/HoP(src) /obj/structure/closet/secure_closet/hos name = "\proper head of security's locker" @@ -91,6 +93,7 @@ new /obj/item/flashlight/seclite(src) new /obj/item/pinpointer/nuke(src) new /obj/item/circuitboard/machine/techfab/department/security(src) + new /obj/item/storage/photo_album/HoS(src) /obj/structure/closet/secure_closet/warden name = "\proper warden's locker" diff --git a/code/game/world.dm b/code/game/world.dm index 7d7b17b4ac..b5d0781dab 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -79,13 +79,24 @@ GLOBAL_PROTECT(security_mode) /world/proc/SetupLogs() var/override_dir = params[OVERRIDE_LOG_DIRECTORY_PARAMETER] if(!override_dir) - GLOB.log_directory = "data/logs/[time2text(world.realtime, "YYYY/MM/DD")]/round-" + var/realtime = world.realtime + var/texttime = time2text(realtime, "YYYY/MM/DD") + GLOB.log_directory = "data/logs/[texttime]/round-" + GLOB.picture_logging_prefix = "L_[time2text(realtime, "YYYYMMDD")]_" + GLOB.picture_log_directory = "data/picture_logs/[texttime]/round-" if(GLOB.round_id) GLOB.log_directory += "[GLOB.round_id]" + GLOB.picture_logging_prefix += "R_[GLOB.round_id]_" + GLOB.picture_log_directory += "[GLOB.round_id]" else - GLOB.log_directory += "[replacetext(time_stamp(), ":", ".")]" + var/timestamp = replacetext(time_stamp(), ":", ".") + GLOB.log_directory += "[timestamp]" + GLOB.picture_log_directory += "[timestamp]" + GLOB.picture_logging_prefix += "T_[timestamp]_" else GLOB.log_directory = "data/logs/[override_dir]" + GLOB.picture_logging_prefix = "O_[override_dir]_" + GLOB.picture_log_directory = "data/picture_logs/[override_dir]" GLOB.world_game_log = "[GLOB.log_directory]/game.log" GLOB.world_attack_log = "[GLOB.log_directory]/attack.log" diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 800a94c4ba..8c8b599862 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -47,7 +47,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list( /client/proc/stop_line_profiling, /client/proc/show_line_profiling, /client/proc/create_mapping_job_icons, - /client/proc/debug_z_levels, + /client/proc/debug_z_levels )) /obj/effect/debugging/mapfix_marker diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 0184c4b34f..de30b69f97 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1313,7 +1313,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits if(!check_rights(R_ADMIN)) return - var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_ROD, ADMIN_PUNISHMENT_SUPPLYPOD) + var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_ROD, ADMIN_PUNISHMENT_SUPPLYPOD, ADMIN_PUNISHMENT_MAZING) var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list @@ -1359,6 +1359,10 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits if(iscarbon(target)) target.Stun(10)//takes 0.53 seconds for CentCom pod to land new /obj/effect/DPtarget(get_turf(target), delivery, POD_CENTCOM) + if(ADMIN_PUNISHMENT_MAZING) + if(!puzzle_imprison(target)) + to_chat(usr,"Imprisonment failed!") + return var/msg = "[key_name_admin(usr)] punished [key_name_admin(target)] with [punishment]." message_admins(msg) diff --git a/code/modules/antagonists/abductor/machinery/experiment.dm b/code/modules/antagonists/abductor/machinery/experiment.dm index 5614fc9105..10581e7bbb 100644 --- a/code/modules/antagonists/abductor/machinery/experiment.dm +++ b/code/modules/antagonists/abductor/machinery/experiment.dm @@ -99,8 +99,9 @@ dat += "

Experiment

" if(occupant) var/obj/item/photo/P = new - P.photocreate(null, icon(dissection_icon(occupant), dir = SOUTH)) - user << browse_rsc(P.img, "dissection_img") + 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 += "" diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index b57aebb2cd..53a05acaf2 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -225,6 +225,7 @@ GLOBAL_LIST_EMPTY(asset_datums) var/res_name = "spritesheet_[name].css" var/fname = "data/spritesheets/[res_name]" + fdel(fname) text2file(generate_css(), fname) register_asset(res_name, file(fname)) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 2ae00f9348..8e3d89dbd0 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -729,6 +729,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) if (clicklimiter[SECOND_COUNT] > scl) to_chat(src, "Your previous click was ignored because you've done too many in a second") return + + if(ab) + return if (prefs.hotkeys) // If hotkey mode is enabled, then clicking the map will automatically diff --git a/code/modules/mapping/space_management/space_reservation.dm b/code/modules/mapping/space_management/space_reservation.dm index 83147d5df7..f1b3b1ccdc 100644 --- a/code/modules/mapping/space_management/space_reservation.dm +++ b/code/modules/mapping/space_management/space_reservation.dm @@ -3,6 +3,8 @@ //Yes, I'm sorry. /datum/turf_reservation var/list/reserved_turfs = list() + var/width = 0 + var/height = 0 var/bottom_left_coords[3] var/top_right_coords[3] var/wipe_reservation_on_release = TRUE @@ -59,6 +61,8 @@ SSmapping.unused_turfs["[T.z]"] -= T SSmapping.used_turfs[T] = src T.ChangeTurf(turf_type, turf_type) + src.width = width + src.height = height return TRUE /datum/turf_reservation/New() diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 002efdbf8d..5345410005 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -1315,3 +1315,18 @@ for(var/t in RANGE_TURFS(1, T)) var/obj/effect/temp_visual/hierophant/blast/B = new(t, user, friendly_fire_check) B.damage = 15 //keeps monster damage boost due to lower damage + + +//Just some minor stuff +/obj/structure/closet/crate/necropolis/puzzle + name = "puzzling chest" + +/obj/structure/closet/crate/necropolis/puzzle/PopulateContents() + var/loot = rand(1,3) + switch(loot) + if(1) + new /obj/item/soulstone/anybody(src) + if(2) + new /obj/item/wisp_lantern(src) + if(3) + new /obj/item/prisoncube(src) \ No newline at end of file diff --git a/code/modules/mob/dead/new_player/sprite_accessories.dm b/code/modules/mob/dead/new_player/sprite_accessories.dm index 13ede8ef04..45ff7e40c8 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories.dm @@ -64,6 +64,9 @@ ////////////////////// // Hair Definitions // ////////////////////// + +#define VHAIR(_name, new_state) /datum/sprite_accessory/hair/##new_state/icon_state=#new_state;/datum/sprite_accessory/hair/##new_state/name = "Virgo - " + #_name + /datum/sprite_accessory/hair icon = 'icons/mob/human_face.dmi' // default icon for all hairs @@ -147,7 +150,6 @@ name = "Ponytail 5" icon_state = "hair_ponytail5" - /datum/sprite_accessory/hair/sidetail name = "Side Pony" icon_state = "hair_sidetail" @@ -512,9 +514,172 @@ name = "Large Bun" icon_state = "hair_largebun" + +//VIRGO PORTED HAIRS +VHAIR(Short Hair Rosa, hair_rosa_s) +VHAIR(Short Hair 80s, hair_80s_s) +VHAIR(Long Bedhead, hair_long_bedhead_s) +VHAIR(Dave, hair_dave_s) +VHAIR(Country, hair_country_s) +VHAIR(Shy, hair_shy_s) +VHAIR(Unshaven Mohawk, hair_unshaven_mohawk_s) +VHAIR(Manbun, hair_manbun_s) +VHAIR(Longer Bedhead, hair_longer_bedhead_s) +VHAIR(Ponytail, hair_ponytail_s) +VHAIR(Ziegler, hair_ziegler_s) +VHAIR(Emo Fringe, hair_emofringe_s) +VHAIR(Very Short Over Eye Alt, hair_veryshortovereyealternate_s) +VHAIR(Shorthime, hair_shorthime_s) +VHAIR(High Tight, hair_hightight_s) +VHAIR(Thinning Front, hair_thinningfront_s) +VHAIR(Big Afro, hair_bigafro_s) +VHAIR(Afro, hair_afro_s) +VHAIR(High Braid, hair_hbraid_s) +VHAIR(Braid, hair_braid_s) +VHAIR(Sargeant, hair_sargeant_s) +VHAIR(Gelled, hair_gelled_s) +VHAIR(Kagami, hair_kagami_s) +VHAIR(ShortTail, hair_stail_s) +VHAIR(Gentle, hair_gentle_s) +VHAIR(Grande, hair_grande_s) +VHAIR(Bobcurl, hair_bobcurl_s) +VHAIR(Pompadeur, hair_pompadour_s) +VHAIR(Plait, hair_plait_s) +VHAIR(Long, hair_long_s) +VHAIR(Rattail, hair_rattail_s) +VHAIR(Tajspiky, hair_tajspiky_s) +VHAIR(Messy, hair_messy_s) +VHAIR(Bangs, hair_bangs_s) +VHAIR(TBraid, hair_tbraid_s) +VHAIR(Toriyama2, hair_toriyama2_s) +VHAIR(CIA, hair_cia_s) +VHAIR(Mulder, hair_mulder_s) +VHAIR(Scully, hair_scully_s) +VHAIR(Nitori, hair_nitori_s) +VHAIR(Joestar, hair_joestar_s) +VHAIR(Ponytail4, hair_ponytail4_s) +VHAIR(Ponytail5, hair_ponytail5_s) +VHAIR(Beehive2, hair_beehive2_s) +VHAIR(Short Braid, hair_shortbraid_s) +VHAIR(Reverse Mohawk, hair_reversemohawk_s) +VHAIR(SHort Bangs, hair_shortbangs_s) +VHAIR(Half Shaved, hair_halfshaved_s) +VHAIR(Longer Alt 2, hair_longeralt2_s) +VHAIR(Bun, hair_bun_s) +VHAIR(Curly, hair_curly_s) +VHAIR(Victory, hair_victory_s) +VHAIR(Ponytail6, hair_ponytail6_s) +VHAIR(Undercut3, hair_undercut3_s) +VHAIR(Bobcut Alt, hair_bobcultalt_s) +VHAIR(Fingerwave, hair_fingerwave_s) +VHAIR(Oxton, hair_oxton_s) +VHAIR(Poofy2, hair_poofy2_s) +VHAIR(Fringe Tail, hair_fringetail_s) +VHAIR(Bun3, hair_bun3_s) +VHAIR(Wisp, hair_wisp_s) +VHAIR(Undercut2, hair_undercut2_s) +VHAIR(TBob, hair_tbob_s) +VHAIR(Spiky Ponytail, hair_spikyponytail_s) +VHAIR(Rowbun, hair_rowbun_s) +VHAIR(Rowdualtail, hair_rowdualtail_s) +VHAIR(Rowbraid, hair_rowbraid_s) +VHAIR(Shaved Mohawk, hair_shavedmohawk_s) +VHAIR(Topknot, hair_topknot_s) +VHAIR(Ronin, hair_ronin_s) +VHAIR(Bowlcut2, hair_bowlcut2_s) +VHAIR(Thinning Rear, hair_thinningrear_s) +VHAIR(Thinning, hair_thinning_s) +VHAIR(Jade, hair_jade_s) +VHAIR(Bedhead, hair_bedhead_s) +VHAIR(Dreadlocks, hair_dreads_s) +VHAIR(Very Long, hair_vlong_s) +VHAIR(Jensen, hair_jensen_s) +VHAIR(Halfbang, hair_halfbang_s) +VHAIR(Kusangi, hair_kusangi_s) +VHAIR(Ponytail, hair_ponytail_s) +VHAIR(Ponytail3, hair_ponytail3_s) +VHAIR(Halfbang Alt, hair_halfbang_alt_s) +VHAIR(Bedhead V2, hair_bedheadv2_s) +VHAIR(Long Fringe, hair_longfringe_s) +VHAIR(Flair, hair_flair_s) +VHAIR(Bedhead V3, hair_bedheadv3_s) +VHAIR(Himecut, hair_himecut_s) +VHAIR(Curls, hair_curls_s) +VHAIR(Very Long Fringe, hair_vlongfringe_s) +VHAIR(Longest, hair_longest_s) +VHAIR(Father, hair_father_s) +VHAIR(Emo Long, hair_emolong_s) +VHAIR(Short Hair 3, hair_shorthair3_s) +VHAIR(Double Bun, hair_doublebun_s) +VHAIR(Sleeze, hair_sleeze_s) +VHAIR(Twintail, hair_twintail_s) +VHAIR(Emo 2, hair_emo2_s) +VHAIR(Low Fade, hair_lowfade_s) +VHAIR(Med Fade, hair_medfade_s) +VHAIR(High Fade, hair_highfade_s) +VHAIR(Bald Fade, hair_baldfade_s) +VHAIR(No Fade, hair_nofade_s) +VHAIR(Trim Flat, hair_trimflat_s) +VHAIR(Shaved, hair_shaved_s) +VHAIR(Trimmed, hair_trimmed_s) +VHAIR(Tight Bun, hair_tightbun_s) +VHAIR(Short Hair 4, hair_d_s) +VHAIR(Short Hair 5, hair_e_s) +VHAIR(Short Hair 6, hair_f_s) +VHAIR(Skinhead, hair_skinhead_s) +VHAIR(Afro2, hair_afro2_s) +VHAIR(Bobcut, hair_bobcut_s) +VHAIR(Emo, hair_emo_s) +VHAIR(Long Over Eye, hair_longovereye_s) +VHAIR(Feather, hair_feather_s) +VHAIR(Hitop, hair_hitop_s) +VHAIR(Short Over Eye, hair_shortoverye_s) +VHAIR(Straight, hair_straight_s) +VHAIR(Buzzcut, hair_buzzcut_s) +VHAIR(Combover, hair_combover_s) +VHAIR(Crewcut, hair_crewcut_s) +VHAIR(Devillock, hair_devilock_s) +VHAIR(Clean, hair_clean_s) +VHAIR(Shaggy, hair_shaggy_s) +VHAIR(Updo, hair_updo_s) +VHAIR(Mohawk, hair_mohawk_s) +VHAIR(Odango, hair_odango_s) +VHAIR(Ombre, hair_ombre_s) +VHAIR(Parted, hair_parted_s) +VHAIR(Quiff, hair_quiff_s) +VHAIR(Volaju, hair_volaju_s) +VHAIR(Bun2, hair_bun2_s) +VHAIR(Rows1, hair_rows1_s) +VHAIR(Rows2, hair_rows2_s) +VHAIR(Dandy Pompadour, hair_dandypompadour_s) +VHAIR(Poofy, hair_poofy_s) +VHAIR(Toriyama, hair_toriyama_s) +VHAIR(Drillruru, hair_drillruru_s) +VHAIR(Bowlcut, hair_bowlcut_s) +VHAIR(Coffee House, hair_coffeehouse_s) +VHAIR(Family Man, hair_thefamilyman_s) +VHAIR(Shaved Part, hair_shavedpart_s) +VHAIR(Modern, hair_modern_s) +VHAIR(One Shoulder, hair_oneshoulder_s) +VHAIR(Very Short Over Eye, hair_veryshortovereye_s) +VHAIR(Unkept, hair_unkept_s) +VHAIR(Wife, hair_wife_s) +VHAIR(Nia, hair_nia_s) +VHAIR(Undercut, hair_undercut_s) +VHAIR(Bobcut Alt, hair_bobcutalt_s) +VHAIR(Short Hair 4 alt, hair_shorthair4_s) +VHAIR(Tressshoulder, hair_tressshoulder_s) + +//END + +#undef VHAIR + ///////////////////////////// // Facial Hair Definitions // ///////////////////////////// + +#define VFACE(_name, new_state) /datum/sprite_accessory/facial_hair/##new_state/icon_state=#new_state;;/datum/sprite_accessory/facial_hair/##new_state/name="Virgo" + #_name + /datum/sprite_accessory/facial_hair icon = 'icons/mob/human_face.dmi' gender = MALE // barf (unless you're a dorf, dorfs dig chix w/ beards :P) @@ -600,6 +765,46 @@ name = "Broken Man" icon_state = "facial_brokenman" +VFACE(Watson, facial_watson_s) +VFACE(Chaplin, facial_chaplin_s) +VFACE(Fullbeard, facial_fullbeard_s) +VFACE(Vandyke, facial_vandyke_s) +VFACE(Elvis, facial_elvis_s) +VFACE(Abe, facial_abe_s) +VFACE(Chin, facial_chin_s) +VFACE(GT, facial_gt_s) +VFACE(Hip, facial_hip_s) +VFACE(Hogan, facial_hogan_s) +VFACE(Selleck, facial_selleck_s) +VFACE(Neckbeard, facial_neckbeard_s) +VFACE(Longbeard, facial_longbeard_s) +VFACE(Dwarf, facial_dwarf_s) +VFACE(Sideburn, facial_sideburn_s) +VFACE(Mutton, facial_mutton_s) +VFACE(Moustache, facial_moustache_s) +VFACE(Pencilstache, facial_pencilstache_s) +VFACE(Goatee, facial_goatee_s) +VFACE(Smallstache, facial_smallstache_s) +VFACE(Volaju, facial_volaju_s) +VFACE(3 O\'clock, facial_3oclock_s) +VFACE(5 O\'clock, facial_5oclock_s) +VFACE(7 O\'clock, facial_7oclock_s) +VFACE(5 O\'clock Moustache, facial_5oclockmoustache_s) +VFACE(7 O\'clock, facial_7oclockmoustache_s) +VFACE(Walrus, facial_walrus_s) +VFACE(Muttonmus, facial_muttonmus_s) +VFACE(Wise, facial_wise_s) +VFACE(Martial Artist, facial_martialartist_s) +VFACE(Dorsalfnil, facial_dorsalfnil_s) +VFACE(Hornadorns, facial_hornadorns_s) +VFACE(Spike, facial_spike_s) +VFACE(Chinhorns, facial_chinhorns_s) +VFACE(Cropped Fullbeard, facial_croppedfullbeard_s) +VFACE(Chinless Beard, facial_chinlessbeard_s) +VFACE(Moonshiner, facial_moonshiner_s) +VFACE(Tribearder, facial_tribearder_s) + +#undef VFACE /////////////////////////// // Underwear Definitions // diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index 7d6d5afca2..36d26f0faa 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -104,7 +104,6 @@ msg += "[t_He] look[p_s()] very happy.\n" if(MOOD_LEVEL_HAPPY4 to INFINITY) msg += "[t_He] look[p_s()] ecstatic.\n" - msg += "*---------*" to_chat(user, msg) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 817e3c3f3b..40b11c14ce 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -133,7 +133,7 @@ else if(eye_blurry) //blurry eyes heal slowly eye_blurry = max(eye_blurry-1, 0) if(client && !eye_blurry) - clear_fullscreen("blurry") + remove_eyeblur() /mob/living/proc/update_damage_hud() return @@ -148,7 +148,7 @@ /mob/living/proc/gravity_animate() if(!get_filter("gravity")) - add_filter("gravity",1,list("type"="motion_blur", "x"=0, "y"=0)) + add_filter("gravity",1, GRAVITY_MOTION_BLUR) INVOKE_ASYNC(src, .proc/gravity_pulse_animation) /mob/living/proc/gravity_pulse_animation() diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm index c03f68a648..f6143e9039 100644 --- a/code/modules/mob/living/silicon/robot/laws.dm +++ b/code/modules/mob/living/silicon/robot/laws.dm @@ -78,4 +78,5 @@ temp = master.supplied[index] if (length(temp) > 0) laws.supplied[index] = temp - return + + picturesync() diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 3a1abd1aa3..473fcb24df 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1205,4 +1205,12 @@ lawsync() lawupdate = 1 return TRUE + picturesync() return FALSE + +/mob/living/silicon/robot/proc/picturesync() + if(connected_ai && connected_ai.aicamera && aicamera) + for(var/i in aicamera.stored) + connected_ai.aicamera.stored[i] = TRUE + for(var/i in connected_ai.aicamera.stored) + aicamera.stored[i] = TRUE diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 3fadd0d6c8..0e69db8f51 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -385,9 +385,9 @@ add_sensors() to_chat(src, "Sensor overlay activated.") -/mob/living/silicon/proc/GetPhoto() +/mob/living/silicon/proc/GetPhoto(mob/user) if (aicamera) - return aicamera.selectpicture(aicamera) + return aicamera.selectpicture(user) /mob/living/silicon/update_transform() var/matrix/ntransform = matrix(transform) //aka transform.Copy() diff --git a/code/modules/mob/status_procs.dm b/code/modules/mob/status_procs.dm index 080307ee9d..f031f77351 100644 --- a/code/modules/mob/status_procs.dm +++ b/code/modules/mob/status_procs.dm @@ -205,25 +205,37 @@ var/old_eye_blurry = eye_blurry eye_blurry = max(amount, eye_blurry) if(!old_eye_blurry) - overlay_fullscreen("blurry", /obj/screen/fullscreen/blurry) + add_eyeblur() /mob/proc/adjust_blurriness(amount) var/old_eye_blurry = eye_blurry eye_blurry = max(eye_blurry+amount, 0) if(amount>0) if(!old_eye_blurry) - overlay_fullscreen("blurry", /obj/screen/fullscreen/blurry) + add_eyeblur() else if(old_eye_blurry && !eye_blurry) - clear_fullscreen("blurry") + remove_eyeblur() /mob/proc/set_blurriness(amount) var/old_eye_blurry = eye_blurry eye_blurry = max(amount, 0) if(amount>0) if(!old_eye_blurry) - overlay_fullscreen("blurry", /obj/screen/fullscreen/blurry) + add_eyeblur() else if(old_eye_blurry) - clear_fullscreen("blurry") + remove_eyeblur() + +/mob/proc/add_eyeblur() + var/obj/screen/plane_master/game_world/GW = locate(/obj/screen/plane_master/game_world) in client.screen + var/obj/screen/plane_master/floor/F = locate(/obj/screen/plane_master/floor) in client.screen + GW.add_filter("blurry_eyes", 2, EYE_BLUR) + F.add_filter("blurry_eyes", 2, EYE_BLUR) + +/mob/proc/remove_eyeblur() + var/obj/screen/plane_master/game_world/GW = locate(/obj/screen/plane_master/game_world) in client.screen + var/obj/screen/plane_master/floor/F = locate(/obj/screen/plane_master/floor) in client.screen + GW.remove_filter("blurry_eyes") + F.remove_filter("blurry_eyes") /////////////////////////////////// DRUGGY //////////////////////////////////// diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index e8a4c26d10..2a880b670c 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -94,30 +94,7 @@ else if(photocopy) for(var/i = 0, i < copies, i++) if(toner >= 5 && !busy && photocopy) //Was set to = 0, but if there was say 3 toner left and this ran, you would get -2 which would be weird for ink - var/obj/item/photo/p = new /obj/item/photo (loc) - var/icon/I = icon(photocopy.icon, photocopy.icon_state) - var/icon/img = icon(photocopy.img) - if(greytoggle == "Greyscale") - if(toner > 10) //plenty of toner, go straight greyscale - I.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0)) //I'm not sure how expensive this is, but given the many limitations of photocopying, it shouldn't be an issue. - img.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0)) - else //not much toner left, lighten the photo - I.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(100,100,100)) - img.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(100,100,100)) - toner -= 5 //photos use a lot of ink! - else if(greytoggle == "Color") - if(toner >= 10) - toner -= 10 //Color photos use even more ink! - else - continue - p.icon = I - p.img = img - p.name = photocopy.name - p.desc = photocopy.desc - p.scribble = photocopy.scribble - p.pixel_x = rand(-10, 10) - p.pixel_y = rand(-10, 10) - p.blueprints = photocopy.blueprints //a copy of a picture is still good enough for the syndicate + new /obj/item/photo (loc, photocopy.picture.Copy(greytoggle == "Greyscale"? TRUE : FALSE)) busy = TRUE sleep(15) busy = FALSE @@ -155,15 +132,10 @@ else break var/obj/item/photo/p = new /obj/item/photo (loc) - p.desc = "You see [ass]'s ass on the photo." p.pixel_x = rand(-10, 10) p.pixel_y = rand(-10, 10) - p.img = temp_img - var/icon/small_img = icon(temp_img) //Icon() is needed or else temp_img will be rescaled too >.> - var/icon/ic = icon('icons/obj/items_and_weapons.dmi',"photo") - small_img.Scale(8, 8) - ic.Blend(small_img,ICON_OVERLAY, 13, 13) - p.icon = ic + p.picture = new(null, "You see [ass]'s ass on the photo.", temp_img) + p.update_icon() toner -= 5 busy = TRUE sleep(15) @@ -196,29 +168,14 @@ if(!isAI(usr)) return if(toner >= 5 && !busy) - var/list/nametemp = list() - var/find - var/datum/picture/selection var/mob/living/silicon/ai/tempAI = usr - if(tempAI.aicamera.aipictures.len == 0) + if(tempAI.aicamera.stored.len == 0) to_chat(usr, "No images saved") return - for(var/datum/picture/t in tempAI.aicamera.aipictures) - nametemp += t.fields["name"] - find = input("Select image (numbered in order taken)") in nametemp - var/obj/item/photo/p = new /obj/item/photo (loc) - for(var/datum/picture/q in tempAI.aicamera.aipictures) - if(q.fields["name"] == find) - selection = q - break - var/icon/I = selection.fields["icon"] - var/icon/img = selection.fields["img"] - p.icon = I - p.img = img - p.desc = selection.fields["desc"] - p.blueprints = selection.fields["blueprints"] - p.pixel_x = rand(-10, 10) - p.pixel_y = rand(-10, 10) + var/datum/picture/selection = tempAI.aicamera.selectpicture(usr) + var/obj/item/photo = new(loc, selection) + photo.pixel_x = rand(-10, 10) + photo.pixel_y = rand(-10, 10) toner -= 5 //AI prints color pictures only, thus they can do it more efficiently busy = TRUE sleep(15) diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm deleted file mode 100644 index 0fa3a36d19..0000000000 --- a/code/modules/paperwork/photography.dm +++ /dev/null @@ -1,661 +0,0 @@ -/* Photography! - * Contains: - * Camera - * Camera Film - * Photos - * Photo Albums - * Picture Frames - * AI Photography - */ - -/* - * Film - */ -/obj/item/camera_film - name = "film cartridge" - icon = 'icons/obj/items_and_weapons.dmi' - desc = "A camera film cartridge. Insert it into a camera to reload it." - icon_state = "film" - item_state = "electropack" - lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' - w_class = WEIGHT_CLASS_TINY - resistance_flags = FLAMMABLE - materials = list(MAT_METAL = 10, MAT_GLASS = 10) - -/* - * Photo - */ -/obj/item/photo - name = "photo" - icon = 'icons/obj/items_and_weapons.dmi' - icon_state = "photo" - item_state = "paper" - w_class = WEIGHT_CLASS_TINY - resistance_flags = FLAMMABLE - max_integrity = 50 - grind_results = list("iodine" = 4) - var/icon/img //Big photo image - var/scribble //Scribble on the back. - var/blueprints = 0 //Does it include the blueprints? - var/sillynewscastervar //Photo objects with this set to 1 will not be ejected by a newscaster. Only gets set to 1 if a silicon puts one of their images into a newscaster - -/obj/item/photo/suicide_act(mob/living/carbon/user) - user.visible_message("[user] is taking one last look at \the [src]! It looks like [user.p_theyre()] giving in to death!")//when you wanna look at photo of waifu one last time before you die... - if (user.gender == MALE) - playsound(user, 'sound/voice/human/manlaugh1.ogg', 50, 1)//EVERY TIME I DO IT MAKES ME LAUGH - else if (user.gender == FEMALE) - playsound(user, 'sound/voice/human/womanlaugh.ogg', 50, 1) - return OXYLOSS - -/obj/item/photo/attack_self(mob/user) - user.examinate(src) - - -/obj/item/photo/attackby(obj/item/P, mob/user, params) - if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) - if(!user.is_literate()) - to_chat(user, "You scribble illegibly on [src]!") - return - var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text) - txt = copytext(txt, 1, 128) - if(user.canUseTopic(src, BE_CLOSE)) - scribble = txt - ..() - - -/obj/item/photo/examine(mob/user) - ..() - - if(in_range(src, user)) - show(user) - else - to_chat(user, "You need to get closer to get a good look at this photo!") - - -/obj/item/photo/proc/show(mob/user) - user << browse_rsc(img, "tmp_photo.png") - user << browse("[name]" \ - + "" \ - + "" \ - + "[scribble ? "
Written on the back:
[scribble]" : ""]"\ - + "", "window=book;size=192x[scribble ? 400 : 192]") - onclose(user, "[name]") - - -/obj/item/photo/verb/rename() - set name = "Rename photo" - set category = "Object" - set src in usr - - var/n_name = copytext(sanitize(input(usr, "What would you like to label the photo?", "Photo Labelling", null) as text), 1, MAX_NAME_LEN) - //loc.loc check is for making possible renaming photos in clipboards - if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == CONSCIOUS && usr.canmove && !usr.restrained()) - name = "photo[(n_name ? text("- '[n_name]'") : null)]" - add_fingerprint(usr) - -/obj/item/photo/proc/photocreate(inicon, inimg, indesc, inblueprints) - icon = inicon - img = inimg - desc = indesc - blueprints = inblueprints - -/* - * Photo album - */ -/obj/item/storage/photo_album - name = "photo album" - icon = 'icons/obj/items_and_weapons.dmi' - icon_state = "album" - item_state = "briefcase" - lefthand_file = 'icons/mob/inhands/equipment/briefcase_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/briefcase_righthand.dmi' - resistance_flags = FLAMMABLE - -/obj/item/storage/photo_album/Initialize() - . = ..() - GET_COMPONENT(STR, /datum/component/storage) - STR.can_hold = typecacheof(list(/obj/item/photo)) - -/* - * Camera - */ -/obj/item/camera - name = "camera" - icon = 'icons/obj/items_and_weapons.dmi' - desc = "A polaroid camera." - icon_state = "camera" - item_state = "electropack" - lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' - w_class = WEIGHT_CLASS_SMALL - flags_1 = CONDUCT_1 - slot_flags = ITEM_SLOT_BELT - materials = list(MAT_METAL = 50, MAT_GLASS = 150) - var/pictures_max = 10 - var/pictures_left = 10 - var/on = TRUE - var/blueprints = 0 //are blueprints visible in the current photo being created? - var/list/aipictures = list() //Allows for storage of pictures taken by AI, in a similar manner the datacore stores info. Keeping this here allows us to share some procs w/ regualar camera - var/see_ghosts = 0 //for the spoop of it - var/obj/item/disk/holodisk/disk - - -/obj/item/camera/CheckParts(list/parts_list) - ..() - var/obj/item/camera/C = locate(/obj/item/camera) in contents - if(C) - pictures_max = C.pictures_max - pictures_left = C.pictures_left - visible_message("[C] has been imbued with godlike power!") - qdel(C) - - -/obj/item/camera/spooky - name = "camera obscura" - desc = "A polaroid camera, some say it can see ghosts!" - see_ghosts = 1 - -/obj/item/camera/detective - name = "Detective's camera" - desc = "A polaroid camera with extra capacity for crime investigations." - pictures_max = 30 - pictures_left = 30 - - -/obj/item/camera/siliconcam //camera AI can take pictures with - name = "silicon photo camera" - var/in_camera_mode = 0 - -/obj/item/camera/siliconcam/ai_camera //camera AI can take pictures with - name = "AI photo camera" - -/obj/item/camera/siliconcam/robot_camera //camera cyborgs can take pictures with.. needs it's own because of verb CATEGORY >.> - name = "Cyborg photo camera" - -/obj/item/camera/siliconcam/robot_camera/verb/borgprinting() - set category ="Robot Commands" - set name = "Print Image" - set src in usr - - if(usr.stat == DEAD) - return //won't work if dead - borgprint() - -/obj/item/camera/attack(mob/living/carbon/human/M, mob/user) - return - - -/obj/item/camera/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/camera_film)) - if(pictures_left) - to_chat(user, "[src] still has some film in it!") - return - if(!user.temporarilyRemoveItemFromInventory(I)) - return - to_chat(user, "You insert [I] into [src].") - qdel(I) - pictures_left = pictures_max - return - if(istype(I, /obj/item/disk/holodisk)) - if (!disk) - if(!user.transferItemToLoc(I, src)) - to_chat(user, "[I] is stuck to your hand!") - return TRUE - to_chat(user, "You slide [I] into the back of [src].") - disk = I - else - to_chat(user, "There's already a disk inside [src].") - return TRUE //no afterattack - ..() - -/obj/item/camera/attack_self(mob/user) - if(!disk) - return - to_chat(user, "You eject [disk] out the back of [src].") - user.put_in_hands(disk) - disk = null - -/obj/item/camera/examine(mob/user) - ..() - to_chat(user, "It has [pictures_left] photo\s left.") - - -/obj/item/camera/proc/camera_get_icon(list/turfs, turf/center) - var/list/atoms = list() - for(var/turf/T in turfs) - atoms.Add(T) - for(var/atom/movable/A in T) - if(A.invisibility) - if(see_ghosts && isobserver(A)) - var/mob/dead/observer/O = A - if(O.orbiting) //so you dont see ghosts following people like antags, etc. - continue - else - continue - atoms.Add(A) - - var/list/sorted = sortTim(atoms,/proc/cmp_atom_layer_asc) - - var/icon/res = icon('icons/effects/96x96.dmi', "") - - for(var/atom/A in sorted) - var/icon/img = getFlatIcon(A, no_anim = TRUE) - if(isliving(A)) - var/mob/living/L = A - if(L.lying) - img.Turn(L.lying) - - var/offX = world.icon_size * (A.x - center.x) + A.pixel_x + 33 - var/offY = world.icon_size * (A.y - center.y) + A.pixel_y + 33 - if(ismovableatom(A)) - var/atom/movable/AM = A - offX += AM.step_x - offY += AM.step_y - - res.Blend(img, blendMode2iconMode(A.blend_mode), offX, offY) - - if(istype(A, /obj/item/areaeditor/blueprints)) - blueprints = 1 - - for(var/turf/T in turfs) - var/area/A = T.loc - if(A.icon_state)//There's actually something to blend in. - res.Blend(getFlatIcon(A,no_anim = TRUE), blendMode2iconMode(A.blend_mode), world.icon_size * (T.x - center.x) + 33, world.icon_size * (T.y - center.y) + 33) - - return res - - -/obj/item/camera/proc/camera_get_mobs(turf/the_turf) - var/mob_detail - for(var/mob/M in the_turf) - if(M.invisibility) - if(see_ghosts && isobserver(M)) - var/mob/dead/observer/O = M - if(O.orbiting) - continue - if(!mob_detail) - mob_detail = "You can see a g-g-g-g-ghooooost! " - else - mob_detail += "You can also see a g-g-g-g-ghooooost!" - else - continue - - var/list/holding = list() - - if(isliving(M)) - var/mob/living/L = M - - for(var/obj/item/I in L.held_items) - if(!holding) - holding += "[L.p_theyre(TRUE)] holding \a [I]" - else - holding += " and \a [I]" - holding = holding.Join() - - if(!mob_detail) - mob_detail = "You can see [L] on the photo[L.health < (L.maxHealth * 0.75) ? " - [L] looks hurt":""].[holding ? " [holding]":"."]. " - else - mob_detail += "You can also see [L] on the photo[L.health < (L.maxHealth * 0.75) ? " - [L] looks hurt":""].[holding ? " [holding]":"."]." - - - return mob_detail - - -/obj/item/camera/proc/captureimage(atom/target, mob/user, flag) //Proc for both regular and AI-based camera to take the image - var/mobs = "" - var/isAi = isAI(user) - var/list/seen - if(!isAi) //crappy check, but without it AI photos would be subject to line of sight from the AI Eye object. Made the best of it by moving the sec camera check inside - if(user.client) //To make shooting through security cameras possible - seen = get_hear(world.view, user.client.eye) //To make shooting through security cameras possible - else - seen = get_hear(world.view, user) - else - seen = get_hear(world.view, target) - - var/list/turfs = list() - for(var/turf/T in range(1, target)) - if(T in seen) - if(isAi && !GLOB.cameranet.checkTurfVis(T)) - continue - else - turfs += T - mobs += camera_get_mobs(T) - - var/icon/temp = icon('icons/effects/96x96.dmi',"") - temp.Blend("#000", ICON_OVERLAY) - temp.Blend(camera_get_icon(turfs, target), ICON_OVERLAY) - - if(!issilicon(user)) - printpicture(user, temp, mobs, flag) - else - aipicture(user, temp, mobs, isAi, blueprints) - - - - -/obj/item/camera/proc/printpicture(mob/user, icon/temp, mobs, flag) //Normal camera proc for creating photos - var/obj/item/photo/P = new/obj/item/photo(get_turf(src)) - if(in_range(src, user)) //needed because of TK - user.put_in_hands(P) - var/icon/small_img = icon(temp) - var/icon/ic = icon('icons/obj/items_and_weapons.dmi',"photo") - small_img.Scale(8, 8) - ic.Blend(small_img,ICON_OVERLAY, 13, 13) - P.icon = ic - P.img = temp - P.desc = mobs - P.pixel_x = rand(-10, 10) - P.pixel_y = rand(-10, 10) - - if(blueprints) - P.blueprints = 1 - blueprints = 0 - - -/obj/item/camera/proc/aipicture(mob/user, icon/temp, mobs, isAi) //instead of printing a picture like a regular camera would, we do this instead for the AI - - var/icon/small_img = icon(temp) - var/icon/ic = icon('icons/obj/items_and_weapons.dmi',"photo") - small_img.Scale(8, 8) - ic.Blend(small_img,ICON_OVERLAY, 13, 13) - var/icon = ic - var/img = temp - var/desc = mobs - var/pixel_x = rand(-10, 10) - var/pixel_y = rand(-10, 10) - - var/injectblueprints = 1 - if(blueprints) - injectblueprints = 1 - blueprints = 0 - - if(isAi) - injectaialbum(icon, img, desc, pixel_x, pixel_y, injectblueprints) - else - injectmasteralbum(icon, img, desc, pixel_x, pixel_y, injectblueprints) - - - -/datum/picture - var/name = "image" - var/list/fields = list() - - -/obj/item/camera/proc/injectaialbum(icon, img, desc, pixel_x, pixel_y, blueprintsinject) //stores image information to a list similar to that of the datacore - var/numberer = 1 - for(var/datum/picture in src.aipictures) - numberer++ - var/datum/picture/P = new() - P.fields["name"] = "Image [numberer] (taken by [src.loc.name])" - P.fields["icon"] = icon - P.fields["img"] = img - P.fields["desc"] = desc - P.fields["pixel_x"] = pixel_x - P.fields["pixel_y"] = pixel_y - P.fields["blueprints"] = blueprintsinject - - aipictures += P - to_chat(usr, "Image recorded") //feedback to the AI player that the picture was taken - -/obj/item/camera/proc/injectmasteralbum(icon, img, desc, pixel_x, pixel_y, blueprintsinject) //stores image information to a list similar to that of the datacore - var/numberer = 1 - var/mob/living/silicon/robot/C = src.loc - if(C.connected_ai) - for(var/datum/picture in C.connected_ai.aicamera.aipictures) - numberer++ - var/datum/picture/P = new() - P.fields["name"] = "Image [numberer] (taken by [src.loc.name])" - P.fields["icon"] = icon - P.fields["img"] = img - P.fields["desc"] = desc - P.fields["pixel_x"] = pixel_x - P.fields["pixel_y"] = pixel_y - P.fields["blueprints"] = blueprintsinject - - C.connected_ai.aicamera.aipictures += P - to_chat(usr, "Image recorded and saved to remote database") //feedback to the Cyborg player that the picture was taken - else - injectaialbum(icon, img, desc, pixel_x, pixel_y, blueprintsinject) - -/obj/item/camera/siliconcam/proc/selectpicture(obj/item/camera/siliconcam/targetloc) - var/list/nametemp = list() - var/find - if(targetloc.aipictures.len == 0) - to_chat(usr, "No images saved") - return - for(var/datum/picture/t in targetloc.aipictures) - nametemp += t.fields["name"] - find = input("Select image (numbered in order taken)") in nametemp - for(var/datum/picture/q in targetloc.aipictures) - if(q.fields["name"] == find) - return q - -/obj/item/camera/siliconcam/proc/viewpichelper(obj/item/camera/siliconcam/targetloc) - var/obj/item/photo/P = new/obj/item/photo() - var/datum/picture/selection = selectpicture(targetloc) - if(selection) - P.photocreate(selection.fields["icon"], selection.fields["img"], selection.fields["desc"]) - P.pixel_x = selection.fields["pixel_x"] - P.pixel_y = selection.fields["pixel_y"] - - P.show(usr) - to_chat(usr, P.desc) - qdel(P) //so 10 thousand picture items are not left in memory should an AI take them and then view them all - -/obj/item/camera/siliconcam/proc/viewpictures(user) - if(iscyborg(user)) // Cyborg - var/mob/living/silicon/robot/C = src.loc - var/obj/item/camera/siliconcam/Cinfo - if(C.connected_ai) - Cinfo = C.connected_ai.aicamera - viewpichelper(Cinfo) - else - Cinfo = C.aicamera - viewpichelper(Cinfo) - else // AI - var/Ainfo = src - viewpichelper(Ainfo) - -/obj/item/camera/afterattack(atom/target, mob/user, flag) - if(!on || !pictures_left || !isturf(target.loc)) - return - if (disk) - if(ismob(target)) - if (disk.record) - QDEL_NULL(disk.record) - - disk.record = new - var/mob/M = target - disk.record.caller_name = M.name - disk.record.set_caller_image(M) - else - return - else - captureimage(target, user, flag) - pictures_left-- - to_chat(user, "[pictures_left] photos left.") - - playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) - - icon_state = "camera_off" - on = FALSE - addtimer(CALLBACK(src, .proc/cooldown), 64) - -/obj/item/camera/proc/cooldown() - set waitfor = FALSE - icon_state = "camera" - on = TRUE - -/obj/item/camera/siliconcam/proc/toggle_camera_mode() - if(in_camera_mode) - camera_mode_off() - else - camera_mode_on() - -/obj/item/camera/siliconcam/proc/camera_mode_off() - src.in_camera_mode = 0 - to_chat(usr, "Camera Mode deactivated") - -/obj/item/camera/siliconcam/proc/camera_mode_on() - src.in_camera_mode = 1 - to_chat(usr, "Camera Mode activated") - -/obj/item/camera/siliconcam/robot_camera/proc/borgprint() - var/list/nametemp = list() - var/find - var/datum/picture/selection - var/mob/living/silicon/robot/C = src.loc - var/obj/item/camera/siliconcam/targetcam = null - if(C.toner < 20) - to_chat(usr, "Insufficent toner to print image.") - return - if(C.connected_ai) - targetcam = C.connected_ai.aicamera - else - targetcam = C.aicamera - if(targetcam.aipictures.len == 0) - to_chat(usr, "No images saved") - return - for(var/datum/picture/t in targetcam.aipictures) - nametemp += t.fields["name"] - find = input("Select image (numbered in order taken)") in nametemp - for(var/datum/picture/q in targetcam.aipictures) - if(q.fields["name"] == find) - selection = q - break - var/obj/item/photo/p = new /obj/item/photo(C.loc) - p.photocreate(selection.fields["icon"], selection.fields["img"], selection.fields["desc"], selection.fields["blueprints"]) - p.pixel_x = rand(-10, 10) - p.pixel_y = rand(-10, 10) - C.toner -= 20 //Cyborgs are very ineffeicient at printing an image - visible_message("[C.name] spits out a photograph from a narrow slot on its chassis.") - to_chat(usr, "You print a photograph.") - -// Picture frames - -/obj/item/wallframe/picture - name = "picture frame" - desc = "The perfect showcase for your favorite deathtrap memories." - icon = 'icons/obj/decals.dmi' - materials = list() - flags_1 = 0 - icon_state = "frame-empty" - result_path = /obj/structure/sign/picture_frame - var/obj/item/photo/displayed - -/obj/item/wallframe/picture/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/photo)) - if(!displayed) - if(!user.transferItemToLoc(I, src)) - return - displayed = I - update_icon() - else - to_chat(user, "\The [src] already contains a photo.") - ..() - -//ATTACK HAND IGNORING PARENT RETURN VALUE -/obj/item/wallframe/picture/attack_hand(mob/user) - if(user.get_inactive_held_item() != src) - ..() - return - if(contents.len) - var/obj/item/I = pick(contents) - user.put_in_hands(I) - to_chat(user, "You carefully remove the photo from \the [src].") - displayed = null - update_icon() - return ..() - -/obj/item/wallframe/picture/attack_self(mob/user) - user.examinate(src) - -/obj/item/wallframe/picture/examine(mob/user) - if(user.is_holding(src) && displayed) - displayed.show(user) - else - ..() - -/obj/item/wallframe/picture/update_icon() - cut_overlays() - if(displayed) - add_overlay(getFlatIcon(displayed)) - -/obj/item/wallframe/picture/after_attach(obj/O) - ..() - var/obj/structure/sign/picture_frame/PF = O - PF.copy_overlays(src) - if(displayed) - PF.framed = displayed - if(contents.len) - var/obj/item/I = pick(contents) - I.forceMove(PF) - - -/obj/structure/sign/picture_frame - name = "picture frame" - desc = "Every time you look it makes you laugh." - icon = 'icons/obj/decals.dmi' - icon_state = "frame-empty" - var/obj/item/photo/framed - -/obj/structure/sign/picture_frame/New(loc, dir, building) - ..() - if(dir) - setDir(dir) - if(building) - pixel_x = (dir & 3)? 0 : (dir == 4 ? -30 : 30) - pixel_y = (dir & 3)? (dir ==1 ? -30 : 30) : 0 - -/obj/structure/sign/picture_frame/examine(mob/user) - if(in_range(src, user) && framed) - framed.show(user) - else - ..() - -/obj/structure/sign/picture_frame/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/screwdriver) || istype(I, /obj/item/wrench)) - to_chat(user, "You start unsecuring [name]...") - if(I.use_tool(src, user, 30, volume=50)) - playsound(loc, 'sound/items/deconstruct.ogg', 50, 1) - to_chat(user, "You unsecure [name].") - deconstruct() - return - - else if(istype(I, /obj/item/photo)) - if(!framed) - var/obj/item/photo/P = I - if(!user.transferItemToLoc(P, src)) - return - framed = P - update_icon() - else - to_chat(user, "\The [src] already contains a photo.") - - ..() - -/obj/structure/sign/picture_frame/attack_hand(mob/user) - . = ..() - if(.) - return - if(framed) - framed.show(user) - -/obj/structure/sign/picture_frame/update_icon() - cut_overlays() - if(framed) - add_overlay(getFlatIcon(framed)) - -/obj/structure/sign/picture_frame/deconstruct(disassembled = TRUE) - if(!(flags_1 & NODECONSTRUCT_1)) - var/obj/item/wallframe/picture/F = new /obj/item/wallframe/picture(loc) - if(framed) - F.displayed = framed - framed = null - if(contents.len) - var/obj/item/I = pick(contents) - I.forceMove(F) - F.update_icon() - qdel(src) diff --git a/code/modules/photography/_pictures.dm b/code/modules/photography/_pictures.dm new file mode 100644 index 0000000000..24892cf541 --- /dev/null +++ b/code/modules/photography/_pictures.dm @@ -0,0 +1,163 @@ +/datum/picture + var/picture_name = "picture" + var/picture_desc = "This is a picture." + var/caption + var/icon/picture_image + var/icon/picture_icon + var/psize_x = 0 + var/psize_y = 0 + var/has_blueprints = FALSE + var/logpath //If the picture has been logged this is the path. + var/id //this var is NOT protected because the worst you can do with this that you couldn't do otherwise is overwrite photos, and photos aren't going to be used as attack logs/investigations anytime soon. + +/datum/picture/New(name = "picture", desc = "This is a picture. A bugged one. Report it to coderbus!", image, icon, size_x = 96, size_y = 96, bp = FALSE, caption_, autogenerate_icon = FALSE) + picture_name = name + picture_desc = desc + picture_image = image + picture_icon = icon + psize_x = size_x + psize_y = size_y + has_blueprints = bp + caption = caption_ + if(autogenerate_icon && !picture_icon && picture_image) + regenerate_small_icon() + +/datum/picture/proc/get_small_icon() + if(!picture_icon) + regenerate_small_icon() + return picture_icon + +/datum/picture/proc/regenerate_small_icon() + if(!picture_image) + return + var/icon/small_img = icon(picture_image) + var/icon/ic = icon('icons/obj/items_and_weapons.dmi', "photo") + small_img.Scale(8, 8) + ic.Blend(small_img,ICON_OVERLAY, 13, 13) + picture_icon = ic + +/datum/picture/serialize_list(list/options) + . = list() + .["id"] = id + .["desc"] = picture_desc + .["name"] = picture_name + .["caption"] = caption + .["pixel_size_x"] = psize_x + .["pixel_size_y"] = psize_y + .["blueprints"] = has_blueprints + .["logpath"] = logpath + +/datum/picture/deserialize_list(list/input, list/options) + if(!input["logpath"] || !fexists(input["logpath"]) || !input["id"] || !input["pixel_size_x"] || !input["pixel_size_y"]) + return + picture_image = icon(file(input["logpath"])) + logpath = input["logpath"] + id = input["id"] + psize_x = input["pixel_size_x"] + psize_y = input["pixel_size_y"] + if(input["blueprints"]) + has_blueprints = input["blueprints"] + if(input["caption"]) + caption = input["caption"] + if(input["desc"]) + picture_desc = input["desc"] + if(input["name"]) + picture_name = input["name"] + return src + +/proc/load_photo_from_disk(id, location) + var/datum/picture/P = load_picture_from_disk(id) + if(istype(P)) + var/obj/item/photo/p = new(location, P) + return p + +/proc/load_picture_from_disk(id) + var/pathstring = log_path_from_picture_ID(id) + if(!pathstring) + return + var/path = file(pathstring) + if(!fexists(path)) + return + var/dir_index = findlasttext(pathstring, "/") + var/dir = copytext(pathstring, 1, dir_index) + var/json_path = file("[dir]/metadata.json") + if(!fexists(json_path)) + return + var/list/json = json_decode(file2text(json_path)) + if(!json[id]) + return + var/datum/picture/P = new + P.deserialize_json(json[id]) + return P + +/proc/log_path_from_picture_ID(id) + if(!istext(id)) + return + . = "data/picture_logs/" + var/list/data = splittext(id, "_") + if(data.len < 3) + return null + var/mode = data[1] + switch(mode) + if("L") + if(data.len < 5) + return null + var/timestamp = data[2] + var/year = copytext(timestamp, 1, 5) + var/month = copytext(timestamp, 5, 7) + var/day = copytext(timestamp, 7, 9) + var/round = data[4] + . += "[year]/[month]/[day]/round-[round]" + if("O") + var/list/path = data.Copy(2, data.len) + . += path.Join("") + else + return null + var/n = data[data.len] + . += "/[n].png" + +//BE VERY CAREFUL WITH THIS PROC, TO AVOID DUPLICATION. +/datum/picture/proc/log_to_file() + if(!picture_image) + return + if(!CONFIG_GET(flag/log_pictures)) + return + if(logpath) + return //we're already logged + var/number = GLOB.picture_logging_id++ + var/finalpath = "[GLOB.picture_log_directory]/[number].png" + fcopy(icon(picture_image, dir = SOUTH, frame = 1), finalpath) + logpath = finalpath + id = "[GLOB.picture_logging_prefix][number]" + var/jsonpath = "[GLOB.picture_log_directory]/metadata.json" + jsonpath = file(jsonpath) + var/list/json + if(fexists(jsonpath)) + json = json_decode(file2text(jsonpath)) + fdel(jsonpath) + else + json = list() + json[id] = serialize_json() + WRITE_FILE(jsonpath, json_encode(json)) + +/datum/picture/proc/Copy(greyscale = FALSE, cropx = 0, cropy = 0) + var/datum/picture/P = new + P.picture_name = picture_name + P.picture_desc = picture_desc + if(picture_image) + P.picture_image = icon(picture_image) //Copy, not reference. + if(picture_icon) + P.picture_icon = icon(picture_icon) + P.psize_x = psize_x - cropx * 2 + P.psize_y = psize_y - cropy * 2 + P.has_blueprints = has_blueprints + if(greyscale) + if(picture_image) + P.picture_image.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0)) + if(picture_icon) + P.picture_icon.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0)) + if(cropx || cropy) + if(picture_image) + P.picture_image.Crop(cropx, cropy, psize_x - cropx, psize_y - cropy) + P.regenerate_small_icon() + return P diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm new file mode 100644 index 0000000000..db4f6fd863 --- /dev/null +++ b/code/modules/photography/camera/camera.dm @@ -0,0 +1,221 @@ + +#define CAMERA_PICTURE_SIZE_HARD_LIMIT 21 + +/obj/item/camera + name = "camera" + icon = 'icons/obj/items_and_weapons.dmi' + desc = "A polaroid camera." + icon_state = "camera" + item_state = "electropack" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + w_class = WEIGHT_CLASS_SMALL + flags_1 = CONDUCT_1 + slot_flags = ITEM_SLOT_BELT + materials = list(MAT_METAL = 50, MAT_GLASS = 150) + var/state_on = "camera" + var/state_off = "camera_off" + var/pictures_max = 10 + var/pictures_left = 10 + var/on = TRUE + var/cooldown = 64 + var/blending = FALSE //lets not take pictures while the previous is still processing! + var/see_ghosts = CAMERA_NO_GHOSTS //for the spoop of it + var/obj/item/disk/holodisk/disk + var/sound/custom_sound + var/silent = FALSE + var/picture_size_x = 1 + var/picture_size_y = 1 + var/picture_size_x_min = 0 + var/picture_size_y_min = 0 + var/picture_size_x_max = 3 + var/picture_size_y_max = 3 + +/obj/item/camera/CheckParts(list/parts_list) + ..() + var/obj/item/camera/C = locate(/obj/item/camera) in contents + if(C) + pictures_max = C.pictures_max + pictures_left = C.pictures_left + visible_message("[C] has been imbued with godlike power!") + qdel(C) + +/obj/item/camera/attack_self(mob/user) + if(!disk) + return + to_chat(user, "You eject [disk] out the back of [src].") + user.put_in_hands(disk) + disk = null + +/obj/item/camera/AltClick(mob/user) + var/desired_x = input(user, "How high do you want the camera to shoot, between [picture_size_x_min] and [picture_size_x_max]?", "Zoom", picture_size_x) as num + var/desired_y = input(user, "How wide do you want the camera to shoot, between [picture_size_y_min] and [picture_size_y_max]?", "Zoom", picture_size_y) as num + desired_x = min(CLAMP(desired_x, picture_size_x_min, picture_size_x_max), CAMERA_PICTURE_SIZE_HARD_LIMIT) + desired_y = min(CLAMP(desired_y, picture_size_y_min, picture_size_y_max), CAMERA_PICTURE_SIZE_HARD_LIMIT) + if(user.canUseTopic(src)) + picture_size_x = desired_x + picture_size_y = desired_y + +/obj/item/camera/attack(mob/living/carbon/human/M, mob/user) + return + +/obj/item/camera/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/camera_film)) + if(pictures_left) + to_chat(user, "[src] still has some film in it!") + return + if(!user.temporarilyRemoveItemFromInventory(I)) + return + to_chat(user, "You insert [I] into [src].") + qdel(I) + pictures_left = pictures_max + return + if(istype(I, /obj/item/disk/holodisk)) + if (!disk) + if(!user.transferItemToLoc(I, src)) + to_chat(user, "[I] is stuck to your hand!") + return TRUE + to_chat(user, "You slide [I] into the back of [src].") + disk = I + else + to_chat(user, "There's already a disk inside [src].") + return TRUE //no afterattack + ..() + +/obj/item/camera/examine(mob/user) + ..() + to_chat(user, "It has [pictures_left] photos left.") + +/obj/item/camera/proc/can_target(atom/target, mob/user, prox_flag) + if(!on || blending || !pictures_left || (!isturf(target) && !isturf(target.loc)) || !((isAI(user) && GLOB.cameranet.checkTurfVis(get_turf(target))) || ((user.client && (get_turf(target) in get_hear(user.client.view, user)) || (get_turf(target) in get_hear(world.view, user)))))) + return FALSE + return TRUE + +/obj/item/camera/afterattack(atom/target, mob/user, flag) + if (disk) + if(ismob(target)) + if (disk.record) + QDEL_NULL(disk.record) + + disk.record = new + var/mob/M = target + disk.record.caller_name = M.name + disk.record.set_caller_image(M) + else + to_chat(user, "Invalid holodisk target.") + return + + if(!can_target(target, user, flag)) + return + + on = FALSE + addtimer(CALLBACK(src, .proc/cooldown), cooldown) + icon_state = state_off + + INVOKE_ASYNC(src, .proc/captureimage, target, user, flag, picture_size_x, picture_size_y) + + +/obj/item/camera/proc/cooldown() + UNTIL(!blending) + icon_state = state_on + on = TRUE + +/obj/item/camera/proc/show_picture(mob/user, datum/picture/selection) + var/obj/item/photo/P = new(src, selection) + P.show(user) + to_chat(user, P.desc) + qdel(P) + +/obj/item/camera/proc/captureimage(atom/target, mob/user, flag, size_x = 1, size_y = 1) + blending = TRUE + var/turf/target_turf = get_turf(target) + if(!isturf(target_turf)) + blending = FALSE + return FALSE + size_x = CLAMP(size_x, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT) + size_y = CLAMP(size_y, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT) + var/list/desc = list() + var/ai_user = isAI(user) + var/list/seen + var/list/viewlist = (user && user.client)? getviewsize(user.client.view) : getviewsize(world.view) + var/viewr = max(viewlist[1], viewlist[2]) + max(size_x, size_y) + var/viewc = user.client? user.client.eye : target + seen = get_hear(viewr, viewc) + var/list/turfs = list() + var/list/mobs = list() + var/blueprints = FALSE + var/clone_area = SSmapping.RequestBlockReservation(size_x * 2 + 1, size_y * 2 + 1) + for(var/turf/T in block(locate(target_turf.x - size_x, target_turf.y - size_y, target_turf.z), locate(target_turf.x + size_x, target_turf.y + size_y, target_turf.z))) + if((ai_user && GLOB.cameranet.checkTurfVis(T)) || T in seen) + turfs += T + for(var/mob/M in T) + mobs += M + if(locate(/obj/item/areaeditor/blueprints) in T) + blueprints = TRUE + CHECK_TICK + for(var/i in mobs) + desc += camera_get_mobdesc(i) + + var/psize_x = (size_x * 2 + 1) * world.icon_size + var/psize_y = (size_y * 2 + 1) * world.icon_size + var/get_icon = camera_get_icon(turfs, target_turf, psize_x, psize_y, clone_area, size_x, size_y, (size_x * 2 + 1), (size_y * 2 + 1)) + qdel(clone_area) + var/icon/temp = icon('icons/effects/96x96.dmi',"") + temp.Blend("#000", ICON_OVERLAY) + temp.Scale(psize_x, psize_y) + temp.Blend(get_icon, ICON_OVERLAY) + + var/datum/picture/P = new("picture", desc.Join(""), temp, null, psize_x, psize_y, blueprints) + after_picture(user, P, flag) + blending = FALSE + +/obj/item/camera/proc/camera_get_mobdesc(mob/M) + var/list/mob_details = list() + if(M.invisibility) + if(see_ghosts && isobserver(M)) + mob_details += "You can also see a g-g-g-g-ghooooost! " + else + return mob_details + var/list/holding = list() + if(isliving(M)) + var/mob/living/L = M + var/len = length(L.held_items) + if(len) + for(var/obj/item/I in L.held_items) + if(!holding.len) + holding += "They are holding \a [I]" + else if(L.held_items.Find(I) == len) + holding += ", and \a [I]." + else + holding += ", \a [I]" + holding += "." + holding = holding.Join("") + mob_details += "You can also see [L] on the photo[L.health < (L.maxHealth * 0.75) ? ", looking a bit hurt":""][holding ? ". [holding]":"."]." + return mob_details.Join("") + +/obj/item/camera/proc/after_picture(mob/user, datum/picture/picture, proximity_flag) + printpicture(user, picture) + +/obj/item/camera/proc/printpicture(mob/user, datum/picture/picture) //Normal camera proc for creating photos + var/obj/item/photo/p = new(get_turf(src), picture) + if(in_range(src, user)) //needed because of TK + user.put_in_hands(p) + pictures_left-- + to_chat(user, "[pictures_left] photos left.") + var/customize = alert(user, "Do you want to customize the photo?", "Customization", "Yes", "No") + if(customize == "Yes") + var/name1 = input(user, "Set a name for this photo, or leave blank. 32 characters max.", "Name") as text|null + var/desc1 = input(user, "Set a description to add to photo, or leave blank. 128 characters max.", "Caption") as text|null + var/caption = input(user, "Set a caption for this photo, or leave blank. 256 characters max.", "Caption") as text|null + if(name1) + name1 = copytext(name1, 1, 33) + picture.picture_name = name1 + if(desc1) + desc1 = copytext(desc1, 1, 129) + picture.picture_desc = "[desc1] - [picture.picture_desc]" + if(caption) + caption = copytext(caption, 1, 257) + picture.caption = caption + p.set_picture(picture) + if(CONFIG_GET(flag/picture_logging_camera)) + picture.log_to_file() diff --git a/code/modules/photography/camera/camera_image_capturing.dm b/code/modules/photography/camera/camera_image_capturing.dm new file mode 100644 index 0000000000..7b9fe94f44 --- /dev/null +++ b/code/modules/photography/camera/camera_image_capturing.dm @@ -0,0 +1,88 @@ +/obj/effect/appearance_clone + +/obj/effect/appearance_clone/New(loc, atom/A) //Intentionally not Initialize(). + if(istype(A)) + appearance = A.appearance + dir = A.dir + if(ismovableatom(A)) + var/atom/movable/AM = A + step_x = AM.step_x + step_y = AM.step_y + . = ..() + +/obj/item/camera/proc/camera_get_icon(list/turfs, turf/center, psize_x = 96, psize_y = 96, datum/turf_reservation/clone_area, size_x, size_y, total_x, total_y) + var/list/atoms = list() + var/skip_normal = FALSE + var/wipe_atoms = FALSE + + if(istype(clone_area) && total_x == clone_area.width && total_y == clone_area.height && size_x >= 0 && size_y > 0) + var/cloned_center_x = round(clone_area.bottom_left_coords[1] + ((total_x - 1) / 2)) + var/cloned_center_y = round(clone_area.bottom_left_coords[2] + ((total_y - 1) / 2)) + for(var/t in turfs) + var/turf/T = t + var/offset_x = T.x - center.x + var/offset_y = T.y - center.y + var/turf/newT = locate(cloned_center_x + offset_x, cloned_center_y + offset_y, clone_area.bottom_left_coords[3]) + if(!(newT in clone_area.reserved_turfs)) //sanity check so we don't overwrite other areas somehow + continue + atoms += new /obj/effect/appearance_clone(newT, T) + if(T.loc.icon_state) + atoms += new /obj/effect/appearance_clone(newT, T.loc) + for(var/i in T.contents) + var/atom/A = i + if(!A.invisibility || (see_ghosts && isobserver(A))) + atoms += new /obj/effect/appearance_clone(newT, A) + skip_normal = TRUE + wipe_atoms = TRUE + center = locate(cloned_center_x, cloned_center_y, clone_area.bottom_left_coords[3]) + + if(!skip_normal) + for(var/i in turfs) + var/turf/T = i + atoms += T + for(var/atom/movable/A in T) + if(A.invisibility) + if(!(see_ghosts && isobserver(A))) + continue + atoms += A + CHECK_TICK + + var/icon/res = icon('icons/effects/96x96.dmi', "") + res.Scale(psize_x, psize_y) + + var/list/sorted = list() + var/j + for(var/i in 1 to atoms.len) + var/atom/c = atoms[i] + for(j = sorted.len, j > 0, --j) + var/atom/c2 = sorted[j] + if(c2.layer <= c.layer) + break + sorted.Insert(j+1, c) + CHECK_TICK + + var/xcomp = FLOOR(psize_x / 2, 1) - 15 + var/ycomp = FLOOR(psize_y / 2, 1) - 15 + + + for(var/atom/A in sorted) + var/xo = (A.x - center.x) * world.icon_size + A.pixel_x + xcomp + var/yo = (A.y - center.y) * world.icon_size + A.pixel_y + ycomp + if(ismovableatom(A)) + var/atom/movable/AM = A + xo += AM.step_x + yo += AM.step_y + var/icon/img = getFlatIcon(A) + res.Blend(img, blendMode2iconMode(A.blend_mode), xo, yo) + CHECK_TICK + + if(!silent) + if(istype(custom_sound)) //This is where the camera actually finishes its exposure. + playsound(loc, custom_sound, 75, 1, -3) + else + playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) + + if(wipe_atoms) + QDEL_LIST(atoms) + + return res diff --git a/code/modules/photography/camera/film.dm b/code/modules/photography/camera/film.dm new file mode 100644 index 0000000000..5d69824bad --- /dev/null +++ b/code/modules/photography/camera/film.dm @@ -0,0 +1,14 @@ +/* + * Film + */ +/obj/item/camera_film + name = "film cartridge" + icon = 'icons/obj/items_and_weapons.dmi' + desc = "A camera film cartridge. Insert it into a camera to reload it." + icon_state = "film" + item_state = "electropack" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + w_class = WEIGHT_CLASS_TINY + resistance_flags = FLAMMABLE + materials = list(MAT_METAL = 10, MAT_GLASS = 10) diff --git a/code/modules/photography/camera/other.dm b/code/modules/photography/camera/other.dm new file mode 100644 index 0000000000..ce2572db36 --- /dev/null +++ b/code/modules/photography/camera/other.dm @@ -0,0 +1,14 @@ +/obj/item/camera/spooky + name = "camera obscura" + desc = "A polaroid camera, some say it can see ghosts!" + see_ghosts = CAMERA_SEE_GHOSTS_BASIC + +/obj/item/camera/spooky/badmin + desc = "A polaroid camera, some say it can see ghosts! It seems to have an extra magnifier on the end." + see_ghosts = CAMERA_SEE_GHOSTS_ORBIT + +/obj/item/camera/detective + name = "Detective's camera" + desc = "A polaroid camera with extra capacity for crime investigations." + pictures_max = 30 + pictures_left = 30 diff --git a/code/modules/photography/camera/picture_frame.dm b/code/modules/photography/camera/picture_frame.dm new file mode 100644 index 0000000000..c31d888bd8 --- /dev/null +++ b/code/modules/photography/camera/picture_frame.dm @@ -0,0 +1,145 @@ +// Picture frames + +/obj/item/wallframe/picture + name = "picture frame" + desc = "The perfect showcase for your favorite deathtrap memories." + icon = 'icons/obj/decals.dmi' + materials = list() + flags_1 = 0 + icon_state = "frame-empty" + result_path = /obj/structure/sign/picture_frame + var/obj/item/photo/displayed + +/obj/item/wallframe/picture/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/photo)) + if(!displayed) + if(!user.transferItemToLoc(I, src)) + return + displayed = I + update_icon() + else + to_chat(user, "\The [src] already contains a photo.") + ..() + +//ATTACK HAND IGNORING PARENT RETURN VALUE +/obj/item/wallframe/picture/attack_hand(mob/user) + if(user.get_inactive_held_item() != src) + ..() + return + if(contents.len) + var/obj/item/I = pick(contents) + user.put_in_hands(I) + to_chat(user, "You carefully remove the photo from \the [src].") + displayed = null + update_icon() + return ..() + +/obj/item/wallframe/picture/attack_self(mob/user) + user.examinate(src) + +/obj/item/wallframe/picture/examine(mob/user) + if(user.is_holding(src) && displayed) + displayed.show(user) + else + ..() + +/obj/item/wallframe/picture/update_icon() + cut_overlays() + if(displayed) + add_overlay(getFlatIcon(displayed)) + +/obj/item/wallframe/picture/after_attach(obj/O) + ..() + var/obj/structure/sign/picture_frame/PF = O + PF.copy_overlays(src) + if(displayed) + PF.framed = displayed + if(contents.len) + var/obj/item/I = pick(contents) + I.forceMove(PF) + +/obj/structure/sign/picture_frame + name = "picture frame" + desc = "Every time you look it makes you laugh." + icon = 'icons/obj/decals.dmi' + icon_state = "frame-empty" + var/obj/item/photo/framed + var/persistence_id + +/obj/structure/sign/picture_frame/Initialize(mapload, dir, building) + . = ..() + LAZYADD(SSpersistence.photo_frames, src) + if(dir) + setDir(dir) + if(building) + pixel_x = (dir & 3)? 0 : (dir == 4 ? -30 : 30) + pixel_y = (dir & 3)? (dir ==1 ? -30 : 30) : 0 + +/obj/structure/sign/picture_frame/Destroy() + LAZYREMOVE(SSpersistence.photo_frames, src) + return ..() + +/obj/structure/sign/picture_frame/proc/get_photo_id() + if(istype(framed) && istype(framed.picture)) + return framed.picture.id + +/obj/structure/sign/picture_frame/proc/load_from_id(id) + var/obj/item/photo/P = load_photo_from_disk(id) + if(istype(P)) + if(istype(framed)) + framed.forceMove(drop_location()) + else + qdel(framed) + framed = P + update_icon() + +/obj/structure/sign/picture_frame/examine(mob/user) + if(in_range(src, user) && framed) + framed.show(user) + else + ..() + +/obj/structure/sign/picture_frame/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/screwdriver) || istype(I, /obj/item/wrench)) + to_chat(user, "You start unsecuring [name]...") + if(I.use_tool(src, user, 30, volume=50)) + playsound(loc, 'sound/items/deconstruct.ogg', 50, 1) + to_chat(user, "You unsecure [name].") + deconstruct() + return + + else if(istype(I, /obj/item/photo)) + if(!framed) + var/obj/item/photo/P = I + if(!user.transferItemToLoc(P, src)) + return + framed = P + update_icon() + else + to_chat(user, "\The [src] already contains a photo.") + + ..() + +/obj/structure/sign/picture_frame/attack_hand(mob/user) + . = ..() + if(.) + return + if(framed) + framed.show(user) + +/obj/structure/sign/picture_frame/update_icon() + cut_overlays() + if(framed) + add_overlay(getFlatIcon(framed)) + +/obj/structure/sign/picture_frame/deconstruct(disassembled = TRUE) + if(!(flags_1 & NODECONSTRUCT_1)) + var/obj/item/wallframe/picture/F = new /obj/item/wallframe/picture(loc) + if(framed) + F.displayed = framed + framed = null + if(contents.len) + var/obj/item/I = pick(contents) + I.forceMove(F) + F.update_icon() + qdel(src) diff --git a/code/modules/photography/camera/silicon_camera.dm b/code/modules/photography/camera/silicon_camera.dm new file mode 100644 index 0000000000..ad8542a5c1 --- /dev/null +++ b/code/modules/photography/camera/silicon_camera.dm @@ -0,0 +1,98 @@ + +/obj/item/camera/siliconcam + name = "silicon photo camera" + var/in_camera_mode = FALSE + var/list/datum/picture/stored = list() + +/obj/item/camera/siliconcam/ai_camera + name = "AI photo camera" + +/obj/item/camera/siliconcam/proc/toggle_camera_mode(mob/user) + if(in_camera_mode) + camera_mode_off(user) + else + camera_mode_on(user) + +/obj/item/camera/siliconcam/proc/camera_mode_off(mob/user) + in_camera_mode = FALSE + to_chat(user, "Camera Mode deactivated") + +/obj/item/camera/siliconcam/proc/camera_mode_on(mob/user) + in_camera_mode = TRUE + to_chat(user, "Camera Mode activated") + +/obj/item/camera/siliconcam/proc/selectpicture(mob/user) + var/list/nametemp = list() + var/find + if(!stored.len) + to_chat(usr, "No images saved") + return + var/list/temp = list() + for(var/i in stored) + var/datum/picture/p = i + nametemp += p.picture_name + temp[p.picture_name] = p + find = input(user, "Select image") in nametemp|null + if(!find) + return + return temp[find] + +/obj/item/camera/siliconcam/proc/viewpictures(mob/user) + var/datum/picture/selection = selectpicture(user) + if(istype(selection)) + show_picture(user, selection) + +/obj/item/camera/siliconcam/ai_camera/after_picture(mob/user, datum/picture/picture, proximity_flag) + var/number = stored.len + picture.picture_name = "Image [number] (taken by [loc.name])" + stored[picture] = TRUE + to_chat(usr, "Image recorded") + +/obj/item/camera/siliconcam/robot_camera + name = "Cyborg photo camera" + var/printcost = 2 + +/obj/item/camera/siliconcam/robot_camera/after_picture(mob/user, datum/picture/picture, proximity_flag) + var/mob/living/silicon/robot/C = loc + if(istype(C) && istype(C.connected_ai)) + var/number = C.connected_ai.aicamera.stored.len + picture.picture_name = "Image [number] (taken by [loc.name])" + C.connected_ai.aicamera.stored[picture] = TRUE + to_chat(usr, "Image recorded and saved to remote database") + else + var/number = stored.len + picture.picture_name = "Image [number] (taken by [loc.name])" + stored[picture] = TRUE + to_chat(usr, "Image recorded and saved to local storage. Upload will happen automatically if unit is lawsynced.") + +/obj/item/camera/siliconcam/robot_camera/selectpicture(mob/user) + var/mob/living/silicon/robot/R = loc + if(istype(R) && R.connected_ai) + R.picturesync() + return R.connected_ai.aicamera.selectpicture(user) + else + return ..() + +/obj/item/camera/siliconcam/robot_camera/verb/borgprinting() + set category ="Robot Commands" + set name = "Print Image" + set src in usr + if(usr.stat == DEAD) + return + borgprint(usr) + +/obj/item/camera/siliconcam/robot_camera/proc/borgprint(mob/user) + var/mob/living/silicon/robot/C = loc + if(!istype(C) || C.toner < 20) + to_chat(user, "Insufficent toner to print image.") + return + var/datum/picture/selection = selectpicture(user) + if(!istype(selection)) + to_chat(user, "Invalid Image.") + return + var/obj/item/photo/p = new /obj/item/photo(C.loc, selection) + p.pixel_x = rand(-10, 10) + p.pixel_y = rand(-10, 10) + C.toner -= printcost //All fun allowed. + visible_message("[C.name] spits out a photograph from a narrow slot on its chassis.") + to_chat(usr, "You print a photograph.") diff --git a/code/modules/photography/photo_album.dm b/code/modules/photography/photo_album.dm new file mode 100644 index 0000000000..cf4fd3d80b --- /dev/null +++ b/code/modules/photography/photo_album.dm @@ -0,0 +1,43 @@ +/* + * Photo album + */ +/obj/item/storage/photo_album + name = "photo album" + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "album" + item_state = "briefcase" + lefthand_file = 'icons/mob/inhands/equipment/briefcase_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/briefcase_righthand.dmi' + resistance_flags = FLAMMABLE + var/persistence_id + +/obj/item/storage/photo_album/Initialize() + . = ..() + GET_COMPONENT(STR, /datum/component/storage) + STR.can_hold = typecacheof(list(/obj/item/photo)) + LAZYADD(SSpersistence.photo_albums, src) + +/obj/item/storage/photo_album/Destroy() + LAZYREMOVE(SSpersistence.photo_albums, src) + return ..() + +/obj/item/storage/photo_album/proc/get_picture_id_list() + var/list/L = list() + for(var/i in contents) + if(istype(i, /obj/item/photo)) + L += i + if(!L.len) + return + . = list() + for(var/i in L) + var/obj/item/photo/P = i + if(!istype(P.picture)) + continue + . |= P.picture.id + +/obj/item/storage/photo_album/proc/populate_from_id_list(list/ids) + for(var/i in ids) + var/obj/item/photo/P = load_photo_from_disk(i) + if(istype(P)) + if(!SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, P, null, TRUE, TRUE)) + qdel(P) diff --git a/code/modules/photography/photo_item.dm b/code/modules/photography/photo_item.dm new file mode 100644 index 0000000000..d03fa08fbd --- /dev/null +++ b/code/modules/photography/photo_item.dm @@ -0,0 +1,86 @@ +/* + * Photo + */ +/obj/item/photo + name = "photo" + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "photo" + item_state = "paper" + w_class = WEIGHT_CLASS_TINY + resistance_flags = FLAMMABLE + max_integrity = 50 + grind_results = list("iodine" = 4) + var/datum/picture/picture + var/scribble //Scribble on the back. + var/sillynewscastervar //Photo objects with this set to 1 will not be ejected by a newscaster. Only gets set to 1 if a silicon puts one of their images into a newscaster + +/obj/item/photo/Initialize(mapload, datum/picture/P, datum_name = TRUE, datum_desc = TRUE) + set_picture(P, datum_name, datum_desc) + return ..() + +/obj/item/photo/proc/set_picture(datum/picture/P, setname, setdesc) + if(!istype(P)) + return + picture = P + update_icon() + if(P.caption) + scribble = P.caption + if(setname && P.picture_name) + name = P.picture_name + if(setdesc && P.picture_desc) + desc = P.picture_desc + +/obj/item/photo/update_icon() + var/icon/I = picture.get_small_icon() + if(I) + icon = I + +/obj/item/photo/suicide_act(mob/living/carbon/user) + user.visible_message("[user] is taking one last look at \the [src]! It looks like [user.p_theyre()] giving in to death!")//when you wanna look at photo of waifu one last time before you die... + if (user.gender == MALE) + playsound(user, 'sound/voice/human/manlaugh1.ogg', 50, 1)//EVERY TIME I DO IT MAKES ME LAUGH + else if (user.gender == FEMALE) + playsound(user, 'sound/voice/human/womanlaugh.ogg', 50, 1) + return OXYLOSS + +/obj/item/photo/attack_self(mob/user) + user.examinate(src) + +/obj/item/photo/attackby(obj/item/P, mob/user, params) + if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) + if(!user.is_literate()) + to_chat(user, "You scribble illegibly on [src]!") + return + var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text) + txt = copytext(txt, 1, 128) + if(user.canUseTopic(src, BE_CLOSE)) + scribble = txt + ..() + +/obj/item/photo/examine(mob/user) + ..() + + if(in_range(src, user)) + show(user) + else + to_chat(user, "You need to get closer to get a good look at this photo!") + +/obj/item/photo/proc/show(mob/user) + user << browse_rsc(picture.picture_image, "tmp_photo.png") + user << browse("[name]" \ + + "" \ + + "" \ + + "[scribble ? "
Written on the back:
[scribble]" : ""]"\ + + "", "window=photo_showing;size=[picture.psize_x + scribble? 128:0]x[picture.psize_y + scribble? 128:0]") + onclose(user, "[name]") + +/obj/item/photo/verb/rename() + set name = "Rename photo" + set category = "Object" + set src in usr + + var/n_name = copytext(sanitize(input(usr, "What would you like to label the photo?", "Photo Labelling", null) as text), 1, MAX_NAME_LEN) + //loc.loc check is for making possible renaming photos in clipboards + if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == CONSCIOUS && usr.canmove && !usr.restrained()) + name = "photo[(n_name ? text("- '[n_name]'") : null)]" + add_fingerprint(usr) diff --git a/code/modules/photography/photos/album.dm b/code/modules/photography/photos/album.dm new file mode 100644 index 0000000000..b4481eb008 --- /dev/null +++ b/code/modules/photography/photos/album.dm @@ -0,0 +1,57 @@ +/* + * Photo album + */ +/obj/item/storage/photo_album + name = "photo album" + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "album" + item_state = "briefcase" + lefthand_file = 'icons/mob/inhands/equipment/briefcase_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/briefcase_righthand.dmi' + resistance_flags = FLAMMABLE + var/persistence_id + +/obj/item/storage/photo_album/Initialize() + . = ..() + GET_COMPONENT(STR, /datum/component/storage) + STR.can_hold = typecacheof(list(/obj/item/photo)) + STR.max_combined_w_class = 42 + STR.max_items = 21 + LAZYADD(SSpersistence.photo_albums, src) + +/obj/item/storage/photo_album/Destroy() + LAZYREMOVE(SSpersistence.photo_albums, src) + return ..() + +/obj/item/storage/photo_album/proc/get_picture_id_list() + var/list/L = list() + for(var/i in contents) + if(istype(i, /obj/item/photo)) + L += i + if(!L.len) + return + . = list() + for(var/i in L) + var/obj/item/photo/P = i + if(!istype(P.picture)) + continue + . |= P.picture.id + +/obj/item/storage/photo_album/proc/populate_from_id_list(list/ids) + for(var/i in ids) + var/obj/item/photo/P = load_photo_from_disk(i) + if(istype(P)) + if(!SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, P, null, TRUE, TRUE)) + qdel(P) + +#define ALBUM_DEFINE(id) /obj/item/storage/photo_album/##id/persistence_id = #id + +ALBUM_DEFINE(HoS) +ALBUM_DEFINE(RD) +ALBUM_DEFINE(HoP) +ALBUM_DEFINE(Captain) +ALBUM_DEFINE(CMO) +ALBUM_DEFINE(QM) +ALBUM_DEFINE(CE) + +#undef ALBUM_DEFINE diff --git a/code/modules/photography/photos/frame.dm b/code/modules/photography/photos/frame.dm new file mode 100644 index 0000000000..c3b9530e07 --- /dev/null +++ b/code/modules/photography/photos/frame.dm @@ -0,0 +1,157 @@ +// Picture frames + +/obj/item/wallframe/picture + name = "picture frame" + desc = "The perfect showcase for your favorite deathtrap memories." + icon = 'icons/obj/decals.dmi' + materials = list() + flags_1 = 0 + icon_state = "frame-empty" + result_path = /obj/structure/sign/picture_frame + var/obj/item/photo/displayed + +/obj/item/wallframe/picture/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/photo)) + if(!displayed) + if(!user.transferItemToLoc(I, src)) + return + displayed = I + update_icon() + else + to_chat(user, "\The [src] already contains a photo.") + ..() + +//ATTACK HAND IGNORING PARENT RETURN VALUE +/obj/item/wallframe/picture/attack_hand(mob/user) + if(user.get_inactive_held_item() != src) + ..() + return + if(contents.len) + var/obj/item/I = pick(contents) + user.put_in_hands(I) + to_chat(user, "You carefully remove the photo from \the [src].") + displayed = null + update_icon() + return ..() + +/obj/item/wallframe/picture/attack_self(mob/user) + user.examinate(src) + +/obj/item/wallframe/picture/examine(mob/user) + if(user.is_holding(src) && displayed) + displayed.show(user) + else + ..() + +/obj/item/wallframe/picture/update_icon() + cut_overlays() + if(displayed) + add_overlay(getFlatIcon(displayed)) + +/obj/item/wallframe/picture/after_attach(obj/O) + ..() + var/obj/structure/sign/picture_frame/PF = O + PF.copy_overlays(src) + if(displayed) + PF.framed = displayed + if(contents.len) + var/obj/item/I = pick(contents) + I.forceMove(PF) + +/obj/structure/sign/picture_frame + name = "picture frame" + desc = "Every time you look it makes you laugh." + icon = 'icons/obj/decals.dmi' + icon_state = "frame-empty" + var/obj/item/photo/framed + var/persistence_id + var/can_decon = TRUE + +#define FRAME_DEFINE(id) /obj/structure/sign/picture_frame/##id/persistence_id = #id + +//Put default persistent frame defines here! + +#undef FRAME_DEFINE + +/obj/structure/sign/picture_frame/Initialize(mapload, dir, building) + . = ..() + LAZYADD(SSpersistence.photo_frames, src) + if(dir) + setDir(dir) + if(building) + pixel_x = (dir & 3)? 0 : (dir == 4 ? -30 : 30) + pixel_y = (dir & 3)? (dir ==1 ? -30 : 30) : 0 + +/obj/structure/sign/picture_frame/Destroy() + LAZYREMOVE(SSpersistence.photo_frames, src) + return ..() + +/obj/structure/sign/picture_frame/proc/get_photo_id() + if(istype(framed) && istype(framed.picture)) + return framed.picture.id + +/obj/structure/sign/picture_frame/proc/load_from_id(id) + var/obj/item/photo/P = load_photo_from_disk(id) + if(istype(P)) + if(istype(framed)) + framed.forceMove(drop_location()) + else + qdel(framed) + framed = P + update_icon() + +/obj/structure/sign/picture_frame/examine(mob/user) + if(in_range(src, user) && framed) + framed.show(user) + else + ..() + +/obj/structure/sign/picture_frame/attackby(obj/item/I, mob/user, params) + if(can_decon && (istype(I, /obj/item/screwdriver) || istype(I, /obj/item/wrench))) + to_chat(user, "You start unsecuring [name]...") + if(I.use_tool(src, user, 30, volume=50)) + playsound(loc, 'sound/items/deconstruct.ogg', 50, 1) + to_chat(user, "You unsecure [name].") + deconstruct() + + else if(istype(I, /obj/item/wirecutters) && framed) + framed.forceMove(drop_location()) + framed = null + user.visible_message("[user] cuts away [framed] from [src]!") + return + + else if(istype(I, /obj/item/photo)) + if(!framed) + var/obj/item/photo/P = I + if(!user.transferItemToLoc(P, src)) + return + framed = P + update_icon() + else + to_chat(user, "\The [src] already contains a photo.") + + ..() + +/obj/structure/sign/picture_frame/attack_hand(mob/user) + . = ..() + if(.) + return + if(framed) + framed.show(user) + +/obj/structure/sign/picture_frame/update_icon() + cut_overlays() + if(framed) + add_overlay(getFlatIcon(framed)) + +/obj/structure/sign/picture_frame/deconstruct(disassembled = TRUE) + if(!(flags_1 & NODECONSTRUCT_1)) + var/obj/item/wallframe/picture/F = new /obj/item/wallframe/picture(loc) + if(framed) + F.displayed = framed + framed = null + if(contents.len) + var/obj/item/I = pick(contents) + I.forceMove(F) + F.update_icon() + qdel(src) diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm new file mode 100644 index 0000000000..b7a37febab --- /dev/null +++ b/code/modules/photography/photos/photo.dm @@ -0,0 +1,91 @@ +/* + * Photo + */ +/obj/item/photo + name = "photo" + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "photo" + item_state = "paper" + w_class = WEIGHT_CLASS_TINY + resistance_flags = FLAMMABLE + max_integrity = 50 + grind_results = list("iodine" = 4) + var/datum/picture/picture + var/scribble //Scribble on the back. + var/sillynewscastervar //Photo objects with this set to 1 will not be ejected by a newscaster. Only gets set to 1 if a silicon puts one of their images into a newscaster + +/obj/item/photo/Initialize(mapload, datum/picture/P, datum_name = TRUE, datum_desc = TRUE) + set_picture(P, datum_name, datum_desc) + return ..() + +/obj/item/photo/proc/set_picture(datum/picture/P, setname, setdesc) + if(!istype(P)) + return + picture = P + update_icon() + if(P.caption) + scribble = P.caption + if(setname && P.picture_name) + name = P.picture_name + if(setdesc && P.picture_desc) + desc = P.picture_desc + +/obj/item/photo/update_icon() + if(!istype(picture) || !picture.picture_image) + return + var/icon/I = picture.get_small_icon() + if(I) + icon = I + +/obj/item/photo/suicide_act(mob/living/carbon/user) + user.visible_message("[user] is taking one last look at \the [src]! It looks like [user.p_theyre()] giving in to death!")//when you wanna look at photo of waifu one last time before you die... + if (user.gender == MALE) + playsound(user, 'sound/voice/human/manlaugh1.ogg', 50, 1)//EVERY TIME I DO IT MAKES ME LAUGH + else if (user.gender == FEMALE) + playsound(user, 'sound/voice/human/womanlaugh.ogg', 50, 1) + return OXYLOSS + +/obj/item/photo/attack_self(mob/user) + user.examinate(src) + +/obj/item/photo/attackby(obj/item/P, mob/user, params) + if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) + if(!user.is_literate()) + to_chat(user, "You scribble illegibly on [src]!") + return + var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text) + txt = copytext(txt, 1, 128) + if(user.canUseTopic(src, BE_CLOSE)) + scribble = txt + ..() + +/obj/item/photo/examine(mob/user) + ..() + + if(in_range(src, user)) + show(user) + else + to_chat(user, "You need to get closer to get a good look at this photo!") + +/obj/item/photo/proc/show(mob/user) + if(!istype(picture) || !picture.picture_image) + to_chat(user, "[src] seems to be blank...") + return + user << browse_rsc(picture.picture_image, "tmp_photo.png") + user << browse("[name]" \ + + "" \ + + "" \ + + "[scribble ? "
Written on the back:
[scribble]" : ""]"\ + + "", "window=photo_showing;size=480x608") + onclose(user, "[name]") + +/obj/item/photo/verb/rename() + set name = "Rename photo" + set category = "Object" + set src in usr + + var/n_name = copytext(sanitize(input(usr, "What would you like to label the photo?", "Photo Labelling", null) as text), 1, MAX_NAME_LEN) + //loc.loc check is for making possible renaming photos in clipboards + if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == CONSCIOUS && usr.canmove && !usr.restrained()) + name = "photo[(n_name ? text("- '[n_name]'") : null)]" + add_fingerprint(usr) diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index b6d51ff6ad..0e5ae56c81 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -179,6 +179,7 @@ icon_state = "wormhole_projector" var/obj/effect/portal/p_blue var/obj/effect/portal/p_orange + var/atmos_link = FALSE /obj/item/gun/energy/wormhole_projector/update_icon() icon_state = "[initial(icon_state)][select]" @@ -227,7 +228,7 @@ p_blue.link_portal(p_orange) /obj/item/gun/energy/wormhole_projector/proc/create_portal(obj/item/projectile/beam/wormhole/W, turf/target) - var/obj/effect/portal/P = new /obj/effect/portal(target, src, 300, null, FALSE, null) + var/obj/effect/portal/P = new /obj/effect/portal(target, src, 300, null, FALSE, null, atmos_link) if(istype(W, /obj/item/projectile/beam/wormhole/orange)) qdel(p_orange) p_orange = P diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 980a79c79c..027849c21d 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -597,8 +597,9 @@ if(!length(beam_segments)) return if(tracer_type) + var/tempref = REF(src) for(var/datum/point/p in beam_segments) - generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity) + generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempref) if(muzzle_type && duration > 0) var/datum/point/p = beam_segments[1] var/atom/movable/thing = new muzzle_type diff --git a/code/modules/projectiles/projectile/special/wormhole.dm b/code/modules/projectiles/projectile/special/wormhole.dm index 94ef5b9a23..53f766a7d5 100644 --- a/code/modules/projectiles/projectile/special/wormhole.dm +++ b/code/modules/projectiles/projectile/special/wormhole.dm @@ -2,9 +2,15 @@ name = "bluespace beam" icon_state = "spark" hitsound = "sparks" - damage = 3 + damage = 0 + nodamage = TRUE + hitscan = TRUE + pass_flags = PASSGLASS | PASSTABLE | PASSGRILLE | PASSMOB var/obj/item/gun/energy/wormhole_projector/gun color = "#33CCFF" + tracer_type = /obj/effect/projectile/tracer/wormhole + impact_type = /obj/effect/projectile/impact/wormhole + muzzle_type = /obj/effect/projectile/muzzle/wormhole /obj/item/projectile/beam/wormhole/orange name = "orange bluespace beam" @@ -16,10 +22,6 @@ gun = casing.gun /obj/item/projectile/beam/wormhole/on_hit(atom/target) - if(ismob(target)) - var/turf/portal_destination = pick(orange(6, src)) - do_teleport(target, portal_destination) - return ..() if(!gun) qdel(src) gun.create_portal(src, get_turf(src)) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 3e7cd539d0..a150e5c138 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -224,7 +224,7 @@ list_reagents = list("chloralhydrate" = 50) /obj/item/reagent_containers/syringe/lethal/execution - list_reagents = list("plasma" = 15, "formaldehyde" = 15, "cyanide" = 10, "facid" = 10) + list_reagents = list("amatoxin" = 15, "formaldehyde" = 15, "cyanide" = 10, "facid" = 10) /obj/item/reagent_containers/syringe/mulligan name = "Mulligan" diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index a65ad7de0a..fcf0b57506 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -501,15 +501,6 @@ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 -/datum/techweb_node/exp_flight - id = "exp_flight" - display_name = "Experimental Flight Equipment" - description = "Highly advanced construction tools." - design_ids = list("flightshoes", "flightpack", "flightsuit") - prereq_ids = list("adv_engi","integrated_HUDs", "adv_power" , "high_efficiency") - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000) - export_price = 5000 - /////////////////////////weaponry tech///////////////////////// /datum/techweb_node/weaponry id = "weaponry" diff --git a/code/modules/ruins/lavalandruin_code/puzzle.dm b/code/modules/ruins/lavalandruin_code/puzzle.dm new file mode 100644 index 0000000000..9e798507da --- /dev/null +++ b/code/modules/ruins/lavalandruin_code/puzzle.dm @@ -0,0 +1,351 @@ +/obj/effect/sliding_puzzle + name = "Sliding puzzle generator" + icon = 'icons/obj/items_and_weapons.dmi' //mapping + icon_state = "syndballoon" + invisibility = INVISIBILITY_ABSTRACT + anchored = TRUE + var/list/elements + var/floor_type = /turf/open/floor/vault + var/finished = FALSE + var/reward_type = /obj/item/reagent_containers/food/snacks/cookie + var/element_type = /obj/structure/puzzle_element + var/auto_setup = TRUE + var/empty_tile_id + +//Gets the turf where the tile with given id should be +/obj/effect/sliding_puzzle/proc/get_turf_for_id(id) + var/turf/center = get_turf(src) + switch(id) + if(1) + return get_step(center,NORTHWEST) + if(2) + return get_step(center,NORTH) + if(3) + return get_step(center,NORTHEAST) + if(4) + return get_step(center,WEST) + if(5) + return center + if(6) + return get_step(center,EAST) + if(7) + return get_step(center,SOUTHWEST) + if(8) + return get_step(center,SOUTH) + if(9) + return get_step(center,SOUTHEAST) + +/obj/effect/sliding_puzzle/Initialize(mapload) + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/effect/sliding_puzzle/LateInitialize() + if(auto_setup) + setup() + +/obj/effect/sliding_puzzle/proc/check_setup_location() + for(var/id in 1 to 9) + var/turf/T = get_turf_for_id(id) + if(!T) + return FALSE + if(istype(T,/turf/closed/indestructible)) + return FALSE + return TRUE + + +/obj/effect/sliding_puzzle/proc/validate() + if(finished) + return + + if(elements.len < 8) //Someone broke it + qdel(src) + + //Check if everything is in place + for(var/id in 1 to 9) + var/target_turf = get_turf_for_id(id) + var/obj/structure/puzzle_element/E = locate() in target_turf + if(id == empty_tile_id && !E) // This location should be empty. + continue + if(!E || E.id != id) //wrong tile or no tile at all + return + //Ding ding + finish() + +/obj/effect/sliding_puzzle/Destroy() + if(LAZYLEN(elements)) + for(var/obj/structure/puzzle_element/E in elements) + E.source = null + elements.Cut() + return ..() + +#define COLLAPSE_DURATION 7 + +/obj/effect/sliding_puzzle/proc/finish() + finished = TRUE + for(var/mob/M in range(7,src)) + shake_camera(M, COLLAPSE_DURATION , 1) + for(var/obj/structure/puzzle_element/E in elements) + E.collapse() + + dispense_reward() + +/obj/effect/sliding_puzzle/proc/dispense_reward() + new reward_type(get_turf(src)) + +/obj/effect/sliding_puzzle/proc/is_solvable() + var/list/current_ordering = list() + for(var/obj/structure/puzzle_element/E in elements_in_order()) + current_ordering += E.id + + var/swap_tally = 0 + for(var/i in 1 to current_ordering.len) + var/checked_value = current_ordering[i] + for(var/j in i to current_ordering.len) + if(current_ordering[j] < checked_value) + swap_tally++ + + return swap_tally % 2 == 0 + +//swap two tiles in same row +/obj/effect/sliding_puzzle/proc/make_solvable() + var/first_tile_id = 1 + var/other_tile_id = 2 + if(empty_tile_id == 1 || empty_tile_id == 2) //Can't swap with empty one so just grab some in second row + first_tile_id = 4 + other_tile_id = 5 + + var/turf/T1 = get_turf_for_id(first_tile_id) + var/turf/T2 = get_turf_for_id(other_tile_id) + + var/obj/structure/puzzle_element/E1 = locate() in T1 + var/obj/structure/puzzle_element/E2 = locate() in T2 + + E1.forceMove(T2) + E2.forceMove(T1) + +/proc/cmp_xy_desc(atom/movable/A,atom/movable/B) + if(A.y > B.y) + return -1 + if(A.y < B.y) + return 1 + if(A.x > B.x) + return 1 + if(A.x < B.x) + return -1 + return 0 + +/obj/effect/sliding_puzzle/proc/elements_in_order() + return sortTim(elements,cmp=/proc/cmp_xy_desc) + +/obj/effect/sliding_puzzle/proc/get_base_icon() + var/icon/I = new('icons/obj/puzzle.dmi') + var/list/puzzles = icon_states(I) + var/puzzle_state = pick(puzzles) + var/icon/P = new('icons/obj/puzzle.dmi',puzzle_state) + return P + +/obj/effect/sliding_puzzle/proc/setup() + //First we slice the 96x96 icon into 32x32 pieces + var/list/puzzle_pieces = list() //id -> icon list + + var/width = 3 + var/height = 3 + var/list/left_ids = list() + var/tile_count = width * height + + //Generate per tile icons + for(var/id in 1 to tile_count) + var/y = width - round((id - 1) / width) + var/x = ((id - 1) % width) + 1 + + var/x_start = 1 + (x - 1) * world.icon_size + var/x_end = x_start + world.icon_size - 1 + var/y_start = 1 + ((y - 1) * world.icon_size) + var/y_end = y_start + world.icon_size - 1 + + var/icon/T = get_base_icon() + T.Crop(x_start,y_start,x_end,y_end) + puzzle_pieces["[id]"] = T + left_ids += id + + //Setup random empty tile + empty_tile_id = pick_n_take(left_ids) + var/turf/empty_tile_turf = get_turf_for_id(empty_tile_id) + empty_tile_turf.PlaceOnTop(floor_type,null,CHANGETURF_INHERIT_AIR) + var/mutable_appearance/MA = new(puzzle_pieces["[empty_tile_id]"]) + MA.layer = empty_tile_turf.layer + 0.1 + empty_tile_turf.add_overlay(MA) + + elements = list() + var/list/empty_spots = left_ids.Copy() + for(var/spot_id in empty_spots) + var/turf/T = get_turf_for_id(spot_id) + T = T.PlaceOnTop(floor_type,null,CHANGETURF_INHERIT_AIR) + var/obj/structure/puzzle_element/E = new element_type(T) + elements += E + var/chosen_id = pick_n_take(left_ids) + E.puzzle_icon = puzzle_pieces["[chosen_id]"] + E.source = src + E.id = chosen_id + E.set_puzzle_icon() + + if(!is_solvable()) + make_solvable() + +/obj/structure/puzzle_element + name = "mysterious pillar" + desc = "puzzling..." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "puzzle_pillar" + anchored = FALSE + density = TRUE + var/id = 0 + var/obj/effect/sliding_puzzle/source + var/icon/puzzle_icon + +/obj/structure/puzzle_element/Move(nloc, dir) + if(!isturf(nloc) || moving_diagonally || get_dist(get_step(src,dir),get_turf(source)) > 1) + return 0 + else + return ..() + +/obj/structure/puzzle_element/proc/set_puzzle_icon() + cut_overlays() + if(puzzle_icon) + //Need to scale it down a bit to fit the static border + var/icon/C = new(puzzle_icon) + C.Scale(19,19) + var/mutable_appearance/puzzle_small = new(C) + puzzle_small.layer = layer + 0.1 + puzzle_small.pixel_x = 7 + puzzle_small.pixel_y = 7 + add_overlay(puzzle_small) + +/obj/structure/puzzle_element/Destroy() + if(source) + source.elements -= src + source.validate() + return ..() + +//Set the full image on the turf and delete yourself +/obj/structure/puzzle_element/proc/collapse() + var/turf/T = get_turf(src) + var/mutable_appearance/MA = new(puzzle_icon) + MA.layer = T.layer + 0.1 + T.add_overlay(MA) + //Some basic shaking animation + for(var/i in 1 to COLLAPSE_DURATION) + animate(src, pixel_x=rand(-5,5), pixel_y=rand(-2,2), time=1) + QDEL_IN(src,COLLAPSE_DURATION) + +/obj/structure/puzzle_element/Moved() + . = ..() + source.validate() + +//Admin abuse version so you can pick the icon before it sets up +/obj/effect/sliding_puzzle/admin + auto_setup = FALSE + var/icon/puzzle_icon + var/puzzle_state + +/obj/effect/sliding_puzzle/admin/get_base_icon() + var/icon/I = new(puzzle_icon,puzzle_state) + return I + +//Ruin version +/obj/effect/sliding_puzzle/lavaland + reward_type = /obj/structure/closet/crate/necropolis/puzzle + +/obj/effect/sliding_puzzle/lavaland/dispense_reward() + if(prob(25)) + //If it's not roaming somewhere else already. + var/mob/living/simple_animal/hostile/megafauna/bubblegum/B = locate() in GLOB.mob_list + if(!B) + reward_type = /mob/living/simple_animal/hostile/megafauna/bubblegum + return ..() + +//Prison cube version +/obj/effect/sliding_puzzle/prison + auto_setup = FALSE //This will be done by cube proc + var/mob/living/prisoner + element_type = /obj/structure/puzzle_element/prison + +/obj/effect/sliding_puzzle/prison/get_base_icon() + if(!prisoner) + CRASH("Prison cube without prisoner") + prisoner.setDir(SOUTH) + var/icon/I = getFlatIcon(prisoner) + I.Scale(96,96) + return I + +/obj/effect/sliding_puzzle/prison/Destroy() + if(prisoner) + to_chat(prisoner,"With the cube broken by force, you can feel your body falling apart.") + prisoner.death() + qdel(prisoner) + . = ..() + +/obj/effect/sliding_puzzle/prison/dispense_reward() + prisoner.forceMove(get_turf(src)) + prisoner.notransform = FALSE + prisoner = null + +//Some armor so it's harder to kill someone by mistake. EDITED - Hugboxed +/obj/structure/puzzle_element/prison + resistance_flags = INDESTRUCTIBLE | ACID_PROOF | FIRE_PROOF | LAVA_PROOF + +/obj/structure/puzzle_element/prison/relaymove(mob/user) + return + +/obj/item/prisoncube + name = "Prison Cube" + desc = "Dusty cube with humanoid imprint on it." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "prison_cube" + +/obj/item/prisoncube/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(!proximity_flag || !isliving(target)) + return + var/mob/living/victim = target + var/mob/living/carbon/carbon_victim = victim + //Handcuffed or unconcious + if(istype(carbon_victim) && carbon_victim.handcuffed || victim.stat != CONSCIOUS) + if(!puzzle_imprison(target)) + to_chat(user,"[src] does nothing.") + return + to_chat(user,"You trap [victim] in the prison cube!") + qdel(src) + else + to_chat(user,"[src] only accepts restrained or unconcious prisoners.") + +/proc/puzzle_imprison(mob/living/prisoner) + var/turf/T = get_turf(prisoner) + var/obj/effect/sliding_puzzle/prison/cube = new(T) + if(!cube.check_setup_location()) + qdel(cube) + return FALSE + + //First grab the prisoner and move them temporarily into the generator so they won't get thrown around. + prisoner.notransform = TRUE + prisoner.forceMove(cube) + to_chat(prisoner,"You're trapped by the prison cube! You will remain trapped until someone solves it.") + + //Clear the area from objects (and cube user) + var/list/things_to_throw = list() + for(var/atom/movable/AM in range(1,T)) + if(!AM.anchored) + things_to_throw += AM + + for(var/atom/movable/AM in things_to_throw) + var/throwtarget = get_edge_target_turf(T, get_dir(T, get_step_away(AM, T))) + AM.throw_at(throwtarget, 2, 3) + + //Create puzzle itself + cube.prisoner = prisoner + cube.setup() + + //Move them into random block + var/obj/structure/puzzle_element/E = pick(cube.elements) + prisoner.forceMove(E) + return TRUE diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index 5a12635963..9a18480920 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -21,7 +21,6 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C var/product_path = null var/amount = 0 var/max_amount = 0 - var/display_color = "blue" /obj/machinery/vending name = "\improper Vendomat" @@ -66,6 +65,8 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C var/scan_id = 1 var/obj/item/coin/coin var/obj/item/stack/spacecash/bill + + var/global/vending_cache = list() //used for storing the icons of items being vended var/dish_quants = list() //used by the snack machine's custom compartment to count dishes. @@ -158,7 +159,6 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C if(!start_empty) R.amount = amount R.max_amount = amount - R.display_color = pick("#ff8080","#80ff80","#8080ff") recordlist += R /obj/machinery/vending/proc/restock(obj/item/vending_refill/canister) @@ -317,7 +317,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C return return ..() -/obj/machinery/vending/interact(mob/user) +/obj/machinery/vending/ui_interact(mob/user) var/dat = "" dat += "

Select an item

" @@ -332,17 +332,16 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C display_records = product_records + coin_records if((coin || bill) && extended_inventory) display_records = product_records + hidden_records + coin_records - dat += "
    " + dat += "" for (var/datum/data/vending_product/R in display_records) - dat += "
  • " + dat += "
  • " + dat += "" if(R.amount > 0) - dat += "Vend " + dat += "" else - dat += "Sold out " - dat += "[sanitize(R.name)]:" - dat += " [R.amount]" - dat += "" - dat += "" + dat += "" + dat += "" + dat += "
    [sanitize(R.name)][R.amount] VendVend
    " dat += "" if(premium.len > 0) dat += "Change Return: " @@ -365,6 +364,14 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() +/obj/machinery/vending/proc/GetIconForProduct(datum/data/vending_product/P) + if(vending_cache[P.product_path]) + return vending_cache[P.product_path] + var/product = new P.product_path() + vending_cache[P.product_path] = icon2base64(getFlatIcon(product, no_anim = TRUE)) + qdel(product) + return vending_cache[P.product_path] + /obj/machinery/vending/Topic(href, href_list) if(..()) return @@ -461,10 +468,13 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C use_power(5) if(icon_vend) //Show the vending animation if needed flick(icon_vend,src) - new R.product_path(get_turf(src)) + var/vended = new R.product_path(get_turf(src)) + if(usr.put_in_hands(vended)) + to_chat(usr, "You take [R.name] out of the slot.") + else + to_chat(usr, "[capitalize(R.name)] falls onto the floor!") SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[R.product_path]")) vend_ready = 1 - return updateUsrDialog() return diff --git a/config/config.txt b/config/config.txt index fba903477c..411e8f979f 100644 --- a/config/config.txt +++ b/config/config.txt @@ -133,6 +133,12 @@ LOG_MANIFEST ## As an example of how this could be """useful""" look towards Poly (https://twitter.com/Poly_the_Parrot) # LOG_TWITTER +## Enable logging pictures +LOG_PICTURES + +##Log camera pictures - Must have picture logging enabled +PICTURE_LOGGING_CAMERA + ## period of time in seconds for players to be considered inactive # INACTIVITY_PERIOD 300 diff --git a/html/changelogs/AutoChangeLog-pr-7360.yml b/html/changelogs/AutoChangeLog-pr-7360.yml new file mode 100644 index 0000000000..9b65502bd0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-7360.yml @@ -0,0 +1,4 @@ +author: "Hatterhat" +delete-after: True +changes: + - tweak: "The plasma formerly present in some execution syringes was replaced with amatoxin." diff --git a/html/changelogs/AutoChangeLog-pr-7368.yml b/html/changelogs/AutoChangeLog-pr-7368.yml new file mode 100644 index 0000000000..bfb405f356 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-7368.yml @@ -0,0 +1,4 @@ +author: "deathride58" +delete-after: True +changes: + - rscadd: "Added Raiq's boombox as a donor item" diff --git a/html/changelogs/AutoChangeLog-pr-7386.yml b/html/changelogs/AutoChangeLog-pr-7386.yml new file mode 100644 index 0000000000..02e1da09ef --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-7386.yml @@ -0,0 +1,9 @@ +author: "kevinz000" +delete-after: True +changes: + - rscadd: "Cameras now can take pictures up to 7x7. Alt click them to change their width/height from the default 3x3." + - rscadd: "Photos are now logged to disk, assuming the host enabled the configuration option" + - rscadd: "If the above is enabled, albums/frames can now have their persistence IDs set in variables to allow for PHOTO PERSISTENCE" + - rscadd: "Every head of staff gets a unique album with its own ID in their locker. Let your successors know about your accomplishments, screwups, and everything in between!" + - rscadd: "All of this works or should work with newcasters/pdas/etc etc" + - rscadd: "Photo taking now clones the area into transit space and then operates on it, instead of on the spot, in theory making photographing things more accurate?" diff --git a/html/changelogs/AutoChangeLog-pr-7405.yml b/html/changelogs/AutoChangeLog-pr-7405.yml new file mode 100644 index 0000000000..5deecc21c5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-7405.yml @@ -0,0 +1,5 @@ +author: "deathride58" +delete-after: True +changes: + - balance: "The combat rework's training wheels have been removed. Knockdowns no longer have any noticeable stun. This indirectly means stunbatons and tasers will always deal their full stamloss on hit." + - balance: "The stamloss required for boxing gloves to throw a knockout punch has been increased from 50 to 100." diff --git a/html/changelogs/AutoChangeLog-pr-7409.yml b/html/changelogs/AutoChangeLog-pr-7409.yml new file mode 100644 index 0000000000..d20dd0bd90 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-7409.yml @@ -0,0 +1,6 @@ +author: "slate3" +delete-after: True +changes: + - rscadd: "ported a new alternative sprite for engineering borgs, mr. handy" + - rscadd: "ported a new alternative sprite for security borgs, a spider-like walker" + - rscadd: "ported a new alternative sprite for medical borgs, an eyebot" diff --git a/html/changelogs/AutoChangeLog-pr-7437.yml b/html/changelogs/AutoChangeLog-pr-7437.yml new file mode 100644 index 0000000000..8615f1d7f4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-7437.yml @@ -0,0 +1,5 @@ +author: "nicc" +delete-after: True +changes: + - tweak: "area alterations" + - rscadd: "escape pod" diff --git a/html/changelogs/AutoChangeLog-pr-7441.yml b/html/changelogs/AutoChangeLog-pr-7441.yml new file mode 100644 index 0000000000..a45a36872d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-7441.yml @@ -0,0 +1,4 @@ +author: "Flatty" +delete-after: True +changes: + - tweak: "The blurry eye overlay has been replaced with an actual blur." diff --git a/html/changelogs/AutoChangeLog-pr-7442.yml b/html/changelogs/AutoChangeLog-pr-7442.yml new file mode 100644 index 0000000000..983720e4cb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-7442.yml @@ -0,0 +1,4 @@ +author: "Flatty" +delete-after: True +changes: + - tweak: "The vending machine UI is now prettier." diff --git a/icons/effects/crayondecal.dmi b/icons/effects/crayondecal.dmi index 24bf84c97d..bb48832025 100644 Binary files a/icons/effects/crayondecal.dmi and b/icons/effects/crayondecal.dmi differ diff --git a/icons/mob/custom_w.dmi b/icons/mob/custom_w.dmi index 495b6580d1..d1f69fe649 100644 Binary files a/icons/mob/custom_w.dmi and b/icons/mob/custom_w.dmi differ diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi index bd4fcf9447..96df6caa8d 100644 Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ diff --git a/icons/mob/screen_gen.dmi b/icons/mob/screen_gen.dmi index 28e37d526c..82b207abb1 100644 Binary files a/icons/mob/screen_gen.dmi and b/icons/mob/screen_gen.dmi differ diff --git a/icons/obj/custom.dmi b/icons/obj/custom.dmi index 0bf8d120b2..75cdf427cd 100644 Binary files a/icons/obj/custom.dmi and b/icons/obj/custom.dmi differ diff --git a/icons/obj/lavaland/artefacts.dmi b/icons/obj/lavaland/artefacts.dmi index 0530f7bb3b..7f11ba29d4 100644 Binary files a/icons/obj/lavaland/artefacts.dmi and b/icons/obj/lavaland/artefacts.dmi differ diff --git a/icons/obj/projectiles_impact.dmi b/icons/obj/projectiles_impact.dmi index 42fe9621be..bac35e68e1 100644 Binary files a/icons/obj/projectiles_impact.dmi and b/icons/obj/projectiles_impact.dmi differ diff --git a/icons/obj/projectiles_muzzle.dmi b/icons/obj/projectiles_muzzle.dmi index 4799b11b2f..2f1d6d90c6 100644 Binary files a/icons/obj/projectiles_muzzle.dmi and b/icons/obj/projectiles_muzzle.dmi differ diff --git a/icons/obj/projectiles_tracer.dmi b/icons/obj/projectiles_tracer.dmi index 3270ac441b..56442452e7 100644 Binary files a/icons/obj/projectiles_tracer.dmi and b/icons/obj/projectiles_tracer.dmi differ diff --git a/icons/obj/puzzle.dmi b/icons/obj/puzzle.dmi new file mode 100644 index 0000000000..f623142beb Binary files /dev/null and b/icons/obj/puzzle.dmi differ diff --git a/modular_citadel/code/datums/status_effects/debuffs.dm b/modular_citadel/code/datums/status_effects/debuffs.dm index 4c201861a2..a57902d0cf 100644 --- a/modular_citadel/code/datums/status_effects/debuffs.dm +++ b/modular_citadel/code/datums/status_effects/debuffs.dm @@ -3,7 +3,7 @@ new_owner.resting = TRUE new_owner.adjustStaminaLoss(isnull(override_stam)? set_duration*0.25 : override_stam) if(isnull(override_duration) && (set_duration > 80)) - set_duration = set_duration*0.15 + set_duration = set_duration*0.01 return ..() else if(!isnull(override_duration)) set_duration = override_duration diff --git a/modular_citadel/code/game/objects/items/boombox.dm b/modular_citadel/code/game/objects/items/boombox.dm new file mode 100644 index 0000000000..10d12f1973 --- /dev/null +++ b/modular_citadel/code/game/objects/items/boombox.dm @@ -0,0 +1,61 @@ +/obj/item/boombox + name = "boombox" + desc = "A dusty, gray, bulky, battery-powered, auto-looping stereo cassette player. An ancient relic from prehistoric times on that one planet with humans and stuff. Yeah, that one." + icon = 'modular_citadel/icons/obj/boombox.dmi' + icon_state = "raiqbawks_off"//PLACEHOLDER UNTIL SOMEONE SPRITES PLAIN NON-FANCY BOOMBOXES + w_class = WEIGHT_CLASS_NORMAL + force = 3 + var/baseiconstate = "raiqbawks" + var/boomingandboxing = FALSE + var/list/availabletrackids + +/obj/item/boombox/attack_self(mob/user) + . = ..() + if(boomingandboxing) + SSjukeboxes.removejukebox(SSjukeboxes.findjukeboxindex(src)) + boomingandboxing = FALSE + to_chat(user, "You flip a switch on [src], and the music immediately stops.") + update_icon() + return + if(!availabletrackids || !availabletrackids.len) + to_chat(user, "[src] flashes as you prod it senselessly. It doesn't have any songs stored on it.") + return + if(!boomingandboxing) + var/list/tracklist = list() + for(var/datum/track/S in SSjukeboxes.songs) + if(istype(S) && S.song_associated_id in availabletrackids) + tracklist[S.song_name] = S + var/selected = input(user, "Play song", "Track:") as null|anything in tracklist + if(QDELETED(src) || !selected || !istype(tracklist[selected], /datum/track)) + return + var/jukeboxslottotake = SSjukeboxes.addjukebox(src, tracklist[selected]) + if(jukeboxslottotake) + boomingandboxing = TRUE + update_icon() + +/obj/item/boombox/Destroy(mob/user) + SSjukeboxes.removejukebox(SSjukeboxes.findjukeboxindex(src)) + . = ..() + +/obj/item/boombox/update_icon() + icon_state = "[baseiconstate]_[boomingandboxing ? "on" : "off"]" + return + +/obj/item/boombox/raiq + name = "Miami Boomer" + desc = "A shiny, fashionable boombox filled to the brim with neon lights, synthesizers, gang violence, and broken R keys. A worn-out sticker on the back states \"Includes three kickin' beats!\"" + icon_state = "raiqbawks_off" + baseiconstate = "raiqbawks" + availabletrackids = list("hotline.ogg","chiptune.ogg","genesis.ogg") + +/obj/item/boombox/raiq/update_icon() + . = ..() + if(boomingandboxing) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + set_light(0) + +/obj/item/boombox/raiq/process() + set_light(5,0.95,pick("#d87aff","#7a7aff","#89ecff","#b88eff","#ff59ad")) + return diff --git a/modular_citadel/code/modules/client/loadout/__donator.dm b/modular_citadel/code/modules/client/loadout/__donator.dm index df4d98f295..4e432c7672 100644 --- a/modular_citadel/code/modules/client/loadout/__donator.dm +++ b/modular_citadel/code/modules/client/loadout/__donator.dm @@ -42,10 +42,10 @@ path = /obj/item/clothing/accessory/medal/steele ckeywhitelist = list("inferno707") -/datum/gear/sexymimemask - name = "The hollow heart" +/datum/gear/hheart + name = "The Hollow Heart" category = SLOT_WEAR_MASK - path = /obj/item/clothing/mask/sexymime + path = /obj/item/clothing/mask/hheart ckeywhitelist = list("inferno707") /datum/gear/engravedzippo @@ -307,3 +307,22 @@ datum/gear/darksabresheath category = SLOT_IN_BACKPACK path = /obj/item/bedsheet/custom/flagcape ckeywhitelist = list("darnchacha") + +/datum/gear/luckyjack + name = "Lucky Jackboots" + category = SLOT_IN_BACKPACK + path = /obj/item/clothing/shoes/lucky + ckeywhitelist = list("donaldtrumpthecommunist") + +/datum/gear/raiqbawks + name = "Miami Boombox" + category = SLOT_HANDS + cost = 2 + path = /obj/item/boombox/raiq + ckeywhitelist = list("chefferz") + +/datum/gear/m41 + name = "Toy M41" + category = SLOT_IN_BACKPACK + path = /obj/item/toy/gun/m41 + ckeywhitelist = list("thalverscholen") diff --git a/modular_citadel/code/modules/custom_loadout/custom_items.dm b/modular_citadel/code/modules/custom_loadout/custom_items.dm index 9e465499db..394e058d78 100644 --- a/modular_citadel/code/modules/custom_loadout/custom_items.dm +++ b/modular_citadel/code/modules/custom_loadout/custom_items.dm @@ -171,13 +171,13 @@ item_state = "santa" slowdown = 0 -/obj/item/clothing/mask/sexymime +/obj/item/clothing/mask/hheart name = "The Hollow heart" desc = "Sometimes things are too much to hide." icon = 'icons/obj/custom.dmi' icon_override = 'icons/mob/custom_w.dmi' - icon_state = "sexymime" - item_state = "sexymime" + icon_state = "hheart" + item_state = "hheart" flags_inv = HIDEFACE|HIDEFACIALHAIR /obj/item/clothing/suit/trenchcoat/green @@ -388,3 +388,12 @@ alternate_worn_icon = 'icons/mob/custom_w.dmi' icon_state = "flagcape" item_state = "flagcape" + + +/obj/item/clothing/shoes/lucky + name = "Lucky Jackboots" + icon = 'icons/obj/custom.dmi' + icon_override = 'icons/mob/custom_w.dmi' + desc = "Comfy Lucky Jackboots with the word Luck on them." + item_state = "luckyjack" + icon_state = "luckyjack" diff --git a/modular_citadel/code/modules/mob/living/silicon/robot/robot_modules.dm b/modular_citadel/code/modules/mob/living/silicon/robot/robot_modules.dm index 1db0c51349..bcac8f8443 100644 --- a/modular_citadel/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/modular_citadel/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -66,6 +66,7 @@ /obj/item/storage/bag/borgdelivery, /obj/item/analyzer/nose, /obj/item/soap/tongue, + /obj/item/extinguisher/mini, /obj/item/healthanalyzer, /obj/item/dogborg/sleeper/medihound, /obj/item/reagent_containers/borghypo, @@ -93,6 +94,7 @@ /obj/item/analyzer/nose, /obj/item/soap/tongue/scrubpup, /obj/item/lightreplacer/cyborg, + /obj/item/extinguisher/mini, /obj/item/dogborg/sleeper/compactor) emag_modules = list(/obj/item/dogborg/pounce) ratvar_modules = list( @@ -127,6 +129,7 @@ /obj/item/analyzer/nose, /obj/item/soap/tongue, /obj/item/healthanalyzer, + /obj/item/extinguisher/mini, /obj/item/borg/cyborghug) emag_modules = list(/obj/item/dogborg/pounce) ratvar_modules = list( @@ -179,7 +182,7 @@ /obj/item/robot_module/medical/be_transformed_to(obj/item/robot_module/old_module) var/mob/living/silicon/robot/R = loc - var/borg_icon = input(R, "Select an icon!", "Robot Icon", null) as null|anything in list("Default", "Droid") + var/borg_icon = input(R, "Select an icon!", "Robot Icon", null) as null|anything in list("Default", "Droid", "Eyebot") if(!borg_icon) return FALSE switch(borg_icon) @@ -189,11 +192,15 @@ cyborg_base_icon = "medical" cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi' hat_offset = 4 + if("Eyebot") + cyborg_base_icon = "eyebotmed" + cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi' + special_light_key = "eyebotmed" return ..() /obj/item/robot_module/security/be_transformed_to(obj/item/robot_module/old_module) var/mob/living/silicon/robot/R = loc - var/borg_icon = input(R, "Select an icon!", "Robot Icon", null) as null|anything in list("Default", "Default - Treads", "Droid") + var/borg_icon = input(R, "Select an icon!", "Robot Icon", null) as null|anything in list("Default", "Default - Treads", "Droid", "Spider") if(!borg_icon) return FALSE switch(borg_icon) @@ -207,11 +214,15 @@ cyborg_base_icon = "Security" special_light_key = "service" hat_offset = 0 + if("Spider") + cyborg_base_icon = "spidersec" + special_light_key = "spidersec" + cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi' return ..() /obj/item/robot_module/engineering/be_transformed_to(obj/item/robot_module/old_module) var/mob/living/silicon/robot/R = loc - var/borg_icon = input(R, "Select an icon!", "Robot Icon", null) as null|anything in list("Default", "Default - Treads") + var/borg_icon = input(R, "Select an icon!", "Robot Icon", null) as null|anything in list("Default", "Default - Treads","Handy") if(!borg_icon) return FALSE switch(borg_icon) @@ -225,6 +236,10 @@ cyborg_base_icon = "loader" cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi' has_snowflake_deadsprite = TRUE + if("Handy") + cyborg_base_icon = "handyeng" + special_light_key = "handyeng" + cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi' return ..() /obj/item/robot_module/miner/be_transformed_to(obj/item/robot_module/old_module) diff --git a/modular_citadel/code/modules/projectiles/guns/toys.dm b/modular_citadel/code/modules/projectiles/guns/toys.dm index d6b80a95e5..a90ea999a4 100644 --- a/modular_citadel/code/modules/projectiles/guns/toys.dm +++ b/modular_citadel/code/modules/projectiles/guns/toys.dm @@ -57,4 +57,12 @@ desc = "An authentic cap-firing reproduction of a F3 Justicar big-bore revolver! Pretend to blow your friend's brains out with this 100% safe toy! Satisfaction guaranteed!" icon_state = "justicar" icon = 'modular_citadel/icons/obj/guns/toys.dmi' - materials = list(MAT_METAL=2000, MAT_GLASS=250) \ No newline at end of file + materials = list(MAT_METAL=2000, MAT_GLASS=250) + + +/obj/item/toy/gun/m41 + name = "Toy M41A Pulse Rifle" + desc = "A toy replica of the Corporate Mercenaries' standard issue rifle. For Avtomat is inscribed on the side." + icon_state = "toym41" + icon = 'modular_citadel/icons/obj/guns/toys.dmi' + materials = list(MAT_METAL=2000, MAT_GLASS=250) diff --git a/modular_citadel/code/modules/reagents/reagent container/hypospraymkii.dm b/modular_citadel/code/modules/reagents/reagent container/hypospraymkii.dm index f39896b953..0eb50836f6 100755 --- a/modular_citadel/code/modules/reagents/reagent container/hypospraymkii.dm +++ b/modular_citadel/code/modules/reagents/reagent container/hypospraymkii.dm @@ -1,15 +1,15 @@ #define HYPO_SPRAY 0 #define HYPO_INJECT 1 -#define WAIT_SPRAY 50 -#define WAIT_INJECT 50 -#define SELF_SPRAY 30 -#define SELF_INJECT 30 +#define WAIT_SPRAY 25 +#define WAIT_INJECT 25 +#define SELF_SPRAY 15 +#define SELF_INJECT 15 -#define DELUXE_WAIT_SPRAY 40 -#define DELUXE_WAIT_INJECT 40 -#define DELUXE_SELF_SPRAY 20 -#define DELUXE_SELF_INJECT 20 +#define DELUXE_WAIT_SPRAY 20 +#define DELUXE_WAIT_INJECT 20 +#define DELUXE_SELF_SPRAY 10 +#define DELUXE_SELF_INJECT 10 #define COMBAT_WAIT_SPRAY 0 #define COMBAT_WAIT_INJECT 0 @@ -284,4 +284,4 @@ to_chat(M, "[src] is now set to inject contents on application.") if(HYPO_INJECT) mode = HYPO_SPRAY - to_chat(M, "[src] is now set to spray contents on application.") \ No newline at end of file + to_chat(M, "[src] is now set to spray contents on application.") diff --git a/modular_citadel/code/modules/reagents/reagent container/hypovial.dm b/modular_citadel/code/modules/reagents/reagent container/hypovial.dm index 32ff1a8b80..a0ced60602 100755 --- a/modular_citadel/code/modules/reagents/reagent container/hypovial.dm +++ b/modular_citadel/code/modules/reagents/reagent container/hypovial.dm @@ -67,11 +67,11 @@ /obj/item/reagent_containers/glass/bottle/vial/small name = "hypovial" - volume = 30 + volume = 60 possible_transfer_amounts = list(5,10,15,30) /obj/item/reagent_containers/glass/bottle/vial/small/bluespace - volume = 60 + volume = 120 possible_transfer_amounts = list(5,10,15,20,30,60) name = "bluespace hypovial" icon_state = "hypovialbs" @@ -81,7 +81,7 @@ name = "large hypovial" desc = "A large hypovial, for deluxe hypospray models." icon_state = "hypoviallarge" - volume = 60 + volume = 120 possible_transfer_amounts = list(5,10,15,20,30,60) unique_reskin = list("large hypovial" = "hypoviallarge", "large red hypovial" = "hypoviallarge-b", @@ -116,7 +116,7 @@ /obj/item/reagent_containers/glass/bottle/vial/large/bluespace possible_transfer_amounts = list(5,10,15,20,25,30,60,120) name = "bluespace large hypovial" - volume = 120 + volume = 240 icon_state = "hypoviallargebs" unique_reskin = null @@ -194,4 +194,4 @@ /obj/item/reagent_containers/glass/bottle/vial/large/preloaded/combat name = "combat hypovial" icon_state = "hypoviallarge-t" - comes_with = list("epinephrine" = 3, "omnizine" = 19, "leporazine" = 19, "atropine" = 19) //Epinephrine's main effect here is to kill suff damage, so we don't need much given atropine \ No newline at end of file + comes_with = list("epinephrine" = 3, "omnizine" = 19, "leporazine" = 19, "atropine" = 19) //Epinephrine's main effect here is to kill suff damage, so we don't need much given atropine diff --git a/modular_citadel/game/objects/items/melee/misc.dm b/modular_citadel/game/objects/items/melee/misc.dm index e6dbf14c0d..ca800d57ea 100644 --- a/modular_citadel/game/objects/items/melee/misc.dm +++ b/modular_citadel/game/objects/items/melee/misc.dm @@ -3,8 +3,9 @@ var/stun_stam_cost_coeff = 1.25 var/hardstun_ds = 1 var/softstun_ds = 0 - var/stam_dmg = 40 + var/stam_dmg = 30 cooldown = 20 + total_mass = 3.75 /obj/item/melee/classic_baton/attack(mob/living/target, mob/living/user) if(!on) diff --git a/modular_citadel/icons/mob/robots.dmi b/modular_citadel/icons/mob/robots.dmi index 9da2a97cc6..230a01f4d4 100644 Binary files a/modular_citadel/icons/mob/robots.dmi and b/modular_citadel/icons/mob/robots.dmi differ diff --git a/modular_citadel/icons/obj/boombox.dmi b/modular_citadel/icons/obj/boombox.dmi new file mode 100644 index 0000000000..c68b8ced40 Binary files /dev/null and b/modular_citadel/icons/obj/boombox.dmi differ diff --git a/modular_citadel/icons/obj/guns/toys.dmi b/modular_citadel/icons/obj/guns/toys.dmi index 3c8595f405..4c58734754 100644 Binary files a/modular_citadel/icons/obj/guns/toys.dmi and b/modular_citadel/icons/obj/guns/toys.dmi differ diff --git a/modular_citadel/simplemob_vore_values.dm b/modular_citadel/simplemob_vore_values.dm index 65d347266d..ed1268a354 100644 --- a/modular_citadel/simplemob_vore_values.dm +++ b/modular_citadel/simplemob_vore_values.dm @@ -79,4 +79,7 @@ devourable = TRUE /mob/living/carbon/monkey - devourable = TRUE \ No newline at end of file + devourable = TRUE + +/mob/living/simple_animal/hostile/carp + devourable = TRUE diff --git a/tgstation.dme b/tgstation.dme index 69c9fbb3de..5f2fd794b0 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -116,6 +116,7 @@ #include "code\__HELPERS\names.dm" #include "code\__HELPERS\priority_announce.dm" #include "code\__HELPERS\pronouns.dm" +#include "code\__HELPERS\qdel.dm" #include "code\__HELPERS\radiation.dm" #include "code\__HELPERS\radio.dm" #include "code\__HELPERS\roundend.dm" @@ -2189,8 +2190,16 @@ #include "code\modules\paperwork\paperplane.dm" #include "code\modules\paperwork\pen.dm" #include "code\modules\paperwork\photocopier.dm" -#include "code\modules\paperwork\photography.dm" #include "code\modules\paperwork\stamps.dm" +#include "code\modules\photography\_pictures.dm" +#include "code\modules\photography\camera\camera.dm" +#include "code\modules\photography\camera\camera_image_capturing.dm" +#include "code\modules\photography\camera\film.dm" +#include "code\modules\photography\camera\other.dm" +#include "code\modules\photography\camera\silicon_camera.dm" +#include "code\modules\photography\photos\album.dm" +#include "code\modules\photography\photos\frame.dm" +#include "code\modules\photography\photos\photo.dm" #include "code\modules\power\apc.dm" #include "code\modules\power\cable.dm" #include "code\modules\power\cell.dm" @@ -2459,6 +2468,7 @@ #include "code\modules\research\xenobiology\crossbreeding\stabilized.dm" #include "code\modules\ruins\lavaland_ruin_code.dm" #include "code\modules\ruins\lavalandruin_code\biodome_clown_planet.dm" +#include "code\modules\ruins\lavalandruin_code\puzzle.dm" #include "code\modules\ruins\lavalandruin_code\sloth.dm" #include "code\modules\ruins\lavalandruin_code\surface.dm" #include "code\modules\ruins\objects_and_mobs\ash_walker_den.dm" @@ -2720,6 +2730,7 @@ #include "modular_citadel\code\game\objects\effects\temporary_visuals\projectiles\muzzle.dm" #include "modular_citadel\code\game\objects\effects\temporary_visuals\projectiles\tracer.dm" #include "modular_citadel\code\game\objects\items\balls.dm" +#include "modular_citadel\code\game\objects\items\boombox.dm" #include "modular_citadel\code\game\objects\items\handcuffs.dm" #include "modular_citadel\code\game\objects\items\holy_weapons.dm" #include "modular_citadel\code\game\objects\items\honk.dm"