diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 403526d30b0..a553cef999c 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -210,6 +210,8 @@ This prevents nesting levels from getting deeper then they need to be. * Do not divide when you can easily convert it to a multiplication. (ie `4/2` should be done as `4*0.5`) +* Do not use the shorthand sql insert format (where no column names are specified) because it unnecessarily breaks all queries on minor column changes and prevents using these tables for tracking outside related info such as in a connected site/forum. + #### Enforced not enforced The following different coding styles are not only not enforced, but it is generally frowned upon to change them over from one to the other for little reason: diff --git a/_maps/RandomZLevels/research.dmm b/_maps/RandomZLevels/research.dmm index 8ff8431a911..9bf2719f218 100644 --- a/_maps/RandomZLevels/research.dmm +++ b/_maps/RandomZLevels/research.dmm @@ -198,8 +198,8 @@ /area/awaymission/research/interior/engineering) "aL" = ( /obj/structure/table, -/obj/item/weapon/c4, -/obj/item/weapon/c4, +/obj/item/weapon/grenade/plastic/c4, +/obj/item/weapon/grenade/plastic/c4, /obj/item/weapon/storage/toolbox/syndicate, /turf/open/floor/mineral/plastitanium, /area/awaymission/research/interior/engineering) diff --git a/_maps/RandomZLevels/snowdin.dmm b/_maps/RandomZLevels/snowdin.dmm index a895d214ae3..bac50dd89ac 100644 --- a/_maps/RandomZLevels/snowdin.dmm +++ b/_maps/RandomZLevels/snowdin.dmm @@ -3948,7 +3948,7 @@ }, /area/awaymission/snowdin) "kB" = ( -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 2; pixel_y = 1 }, diff --git a/_maps/RandomZLevels/spacebattle.dmm b/_maps/RandomZLevels/spacebattle.dmm index 3ffc9f9a6ab..699229e6dbf 100644 --- a/_maps/RandomZLevels/spacebattle.dmm +++ b/_maps/RandomZLevels/spacebattle.dmm @@ -357,7 +357,7 @@ /area/awaymission/spacebattle/syndicate2) "bl" = ( /obj/structure/table/reinforced, -/obj/item/weapon/c4, +/obj/item/weapon/grenade/plastic/c4, /turf/open/floor/mineral/plastitanium, /area/awaymission/spacebattle/syndicate3) "bm" = ( @@ -366,7 +366,7 @@ /area/awaymission/spacebattle/syndicate1) "bn" = ( /obj/structure/table/reinforced, -/obj/item/weapon/c4, +/obj/item/weapon/grenade/plastic/c4, /turf/open/floor/mineral/plastitanium, /area/awaymission/spacebattle/syndicate1) "bo" = ( diff --git a/_maps/map_files/Cerestation/cerestation.dmm b/_maps/map_files/Cerestation/cerestation.dmm index c93ce6a262d..9765dc77a4c 100644 --- a/_maps/map_files/Cerestation/cerestation.dmm +++ b/_maps/map_files/Cerestation/cerestation.dmm @@ -1073,23 +1073,23 @@ /area/ai_monitored/turret_protected/ai) "acq" = ( /obj/structure/table, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 2; pixel_y = -5 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = -3; pixel_y = 3 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 2; pixel_y = -3 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = -2; pixel_y = -1 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 3; pixel_y = 3 }, @@ -40086,7 +40086,7 @@ }, /area/medical/virology) "brX" = ( -/obj/machinery/smartfridge/chemistry/virology, +/obj/machinery/smartfridge/chemistry/virology/preloaded, /turf/open/floor/plasteel/whitegreen/side{ tag = "icon-whitegreen (EAST)"; icon_state = "whitegreen"; @@ -47853,7 +47853,7 @@ }, /area/medical/chemistry) "bEN" = ( -/obj/machinery/smartfridge/chemistry, +/obj/machinery/smartfridge/chemistry/preloaded, /turf/open/floor/plating{ baseturf = /turf/open/floor/plating/asteroid/airless }, @@ -76429,7 +76429,7 @@ }, /area/toxins/xenobiology) "cCm" = ( -/obj/machinery/smartfridge/extract, +/obj/machinery/smartfridge/extract/preloaded, /obj/structure/cable/orange{ d1 = 4; d2 = 8; diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 34ff6f13ff5..4a91a5b85fb 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -10753,7 +10753,7 @@ /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, /turf/open/floor/plasteel/loadingarea{ - baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; + baseturf = /turf/open/space; dir = 8 }, /area/quartermaster/storage) @@ -11240,7 +11240,7 @@ /area/quartermaster/storage) "awJ" = ( /turf/open/floor/plasteel/loadingarea{ - baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; + baseturf = /turf/open/space; dir = 8 }, /area/quartermaster/storage) @@ -31236,7 +31236,7 @@ icon_state = "1-4" }, /turf/open/floor/plasteel/loadingarea{ - baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; + baseturf = /turf/open/space; dir = 8 }, /area/quartermaster/miningdock{ @@ -34793,7 +34793,6 @@ dir = 4 }, /turf/open/floor/plasteel{ - desc = ""; icon_state = "L14" }, /area/hallway/primary/central) @@ -52476,7 +52475,7 @@ name = "Queue Shutters" }, /turf/open/floor/plasteel/loadingarea{ - baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; + baseturf = /turf/open/space; dir = 8 }, /area/hallway/primary/central) @@ -70988,7 +70987,6 @@ "cDW" = ( /obj/machinery/atmospherics/pipe/manifold/supply/hidden, /turf/open/floor/plasteel{ - desc = ""; icon_state = "L13"; name = "floor" }, @@ -79514,8 +79512,13 @@ name = "Medbay Central" }) "cUf" = ( -/obj/machinery/smartfridge/chemistry, -/turf/closed/wall, +/obj/structure/grille, +/obj/machinery/door/poddoor/shutters/preopen{ + id = "chemisttop"; + name = "Chemisty Lobby Shutters" + }, +/obj/structure/window/reinforced/fulltile, +/turf/open/floor/plating, /area/medical/medbay{ name = "Medbay Central" }) @@ -79538,14 +79541,9 @@ name = "Medbay Central" }) "cUh" = ( -/obj/structure/grille, -/obj/structure/window/reinforced/fulltile, -/obj/machinery/door/poddoor/shutters/preopen{ - id = "chemisttop"; - name = "Chemisty Lobby Shutters" - }, /obj/structure/disposalpipe/segment, -/turf/open/floor/plating, +/obj/machinery/smartfridge/chemistry/preloaded, +/turf/closed/wall, /area/medical/medbay{ name = "Medbay Central" }) @@ -81191,7 +81189,7 @@ /turf/open/floor/plating, /area/toxins/xenobiology) "cXc" = ( -/obj/machinery/smartfridge/extract, +/obj/machinery/smartfridge/extract/preloaded, /obj/machinery/light_switch{ pixel_x = -26 }, @@ -82191,6 +82189,7 @@ /obj/item/weapon/grenade/chem_grenade, /obj/item/weapon/grenade/chem_grenade, /obj/item/weapon/grenade/chem_grenade, +/obj/item/weapon/screwdriver, /turf/open/floor/plasteel/whiteyellow/corner{ icon_state = "whiteyellowcorner"; dir = 8 @@ -84275,7 +84274,7 @@ /turf/open/floor/plasteel/whiteyellow/corner, /area/medical/chemistry) "ddg" = ( -/obj/machinery/smartfridge/chemistry, +/obj/machinery/smartfridge/chemistry/preloaded, /turf/closed/wall, /area/medical/chemistry) "ddh" = ( @@ -96355,7 +96354,6 @@ }, /area/medical/morgue) "dAn" = ( -/turf/open/floor/plating, /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/manifold/supply/hidden{ icon_state = "manifold"; @@ -104162,7 +104160,7 @@ name = "emergency shower" }, /turf/open/floor/plasteel/whiteblue/side{ - baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; + baseturf = /turf/open/space; dir = 4 }, /area/shuttle/escape) @@ -104907,7 +104905,7 @@ /obj/structure/cable/white{ icon_state = "4-8" }, -/obj/machinery/smartfridge/chemistry/virology, +/obj/machinery/smartfridge/chemistry/virology/preloaded, /obj/effect/turf_decal/stripes/line{ dir = 4 }, diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index f6fe1c8188f..44421252ee4 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -27963,7 +27963,6 @@ dir = 4 }, /turf/open/floor/plasteel{ - desc = ""; icon_state = "L13"; name = "floor" }, @@ -28813,7 +28812,6 @@ icon_state = "4-8" }, /turf/open/floor/plasteel{ - desc = ""; icon_state = "L14" }, /area/hallway/primary/central) @@ -63164,7 +63162,7 @@ /turf/open/floor/plating, /area/medical/chemistry) "cfY" = ( -/obj/machinery/smartfridge/chemistry, +/obj/machinery/smartfridge/chemistry/preloaded, /turf/closed/wall, /area/medical/chemistry) "cfZ" = ( @@ -77232,7 +77230,7 @@ }, /area/medical/virology) "cDH" = ( -/obj/machinery/smartfridge/chemistry/virology, +/obj/machinery/smartfridge/chemistry/virology/preloaded, /obj/machinery/airalarm{ frequency = 1439; pixel_y = 23 @@ -91081,7 +91079,7 @@ d2 = 8; icon_state = "2-8" }, -/obj/machinery/smartfridge/extract, +/obj/machinery/smartfridge/extract/preloaded, /obj/structure/cable/yellow{ d1 = 4; d2 = 8; diff --git a/_maps/map_files/Mining/Lavaland.dmm b/_maps/map_files/Mining/Lavaland.dmm index 778fb50c88d..c37c4ca3a3a 100644 --- a/_maps/map_files/Mining/Lavaland.dmm +++ b/_maps/map_files/Mining/Lavaland.dmm @@ -513,7 +513,7 @@ /obj/structure/cable{ d1 = 1; d2 = 4; - icon_state = "1-4"; + icon_state = "1-4" }, /obj/structure/cable{ d1 = 1; @@ -1040,7 +1040,7 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plasteel/brown{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; - dir = 1; + dir = 1 }, /area/mine/living_quarters) "cg" = ( @@ -1074,7 +1074,7 @@ /obj/structure/cable{ d1 = 1; d2 = 4; - icon_state = "1-4"; + icon_state = "1-4" }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface @@ -1497,10 +1497,21 @@ /area/mine/living_quarters) "dd" = ( /obj/structure/table, -/obj/item/weapon/reagent_containers/food/drinks/beer, +/obj/item/weapon/reagent_containers/food/drinks/beer{ + pixel_x = 7; + pixel_y = 5 + }, /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 5 }, +/obj/item/weapon/reagent_containers/food/drinks/beer{ + pixel_x = -1; + pixel_y = 9 + }, +/obj/item/weapon/reagent_containers/food/drinks/beer{ + pixel_x = -8; + pixel_y = 0 + }, /turf/open/floor/plasteel/bar{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface }, @@ -1772,6 +1783,9 @@ pixel_x = -5; pixel_y = 30 }, +/obj/machinery/shower{ + dir = 8 + }, /turf/open/floor/plasteel/purple/corner{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface }, @@ -2192,7 +2206,7 @@ }, /turf/open/floor/plasteel/vault{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; - dir = 8; + dir = 8 }, /area/mine/maintenance) "ez" = ( @@ -2214,13 +2228,13 @@ /obj/machinery/iv_drip, /turf/open/floor/plasteel/whiteblue/side{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; - dir = 1; + dir = 1 }, /area/mine/living_quarters) "eB" = ( /turf/open/floor/plasteel/whiteblue/side{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; - dir = 1; + dir = 1 }, /area/mine/living_quarters) "eC" = ( @@ -2231,7 +2245,7 @@ }, /turf/open/floor/plasteel/whiteblue/side{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; - dir = 5; + dir = 5 }, /area/mine/living_quarters) "eD" = ( @@ -2398,7 +2412,7 @@ /obj/item/weapon/storage/firstaid/regular, /turf/open/floor/plasteel/whiteblue/side{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; - dir = 4; + dir = 4 }, /area/mine/living_quarters) "eU" = ( @@ -2512,6 +2526,7 @@ dir = 2; network = list("MINE") }, +/obj/structure/reagent_dispensers/watertank, /turf/open/floor/plasteel/brown{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; dir = 5 @@ -2520,7 +2535,7 @@ "fe" = ( /turf/open/floor/plasteel/brown/corner{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; - dir = 1; + dir = 1 }, /area/mine/production) "ff" = ( @@ -2578,7 +2593,7 @@ }, /turf/open/floor/plasteel/brown{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; - dir = 1; + dir = 1 }, /area/mine/living_quarters) "fm" = ( @@ -2600,7 +2615,7 @@ /obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plasteel/brown{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; - dir = 1; + dir = 1 }, /area/mine/living_quarters) "fp" = ( @@ -2674,7 +2689,7 @@ /obj/structure/cable{ d1 = 1; d2 = 4; - icon_state = "1-4"; + icon_state = "1-4" }, /turf/open/floor/plasteel{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface @@ -2712,6 +2727,10 @@ /obj/machinery/light/small{ dir = 1 }, +/obj/structure/extinguisher_cabinet{ + pixel_x = -5; + pixel_y = 30 + }, /turf/open/floor/plasteel/purple/corner{ baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; dir = 4 @@ -2794,6 +2813,41 @@ /turf/open/floor/plating/asteroid/basalt/lava_land_surface, /turf/closed/indestructible/riveted, /area/space) +"fQ" = ( +/obj/machinery/shower{ + dir = 8 + }, +/obj/effect/turf_decal/delivery, +/turf/open/floor/plasteel/white, +/area/mine/laborcamp) +"fR" = ( +/obj/structure/sink/kitchen{ + dir = 4; + icon_state = "sink_alt"; + pixel_x = -13; + tag = "icon-sink_alt (EAST)" + }, +/turf/open/floor/plasteel{ + baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface + }, +/area/mine/laborcamp) +"fS" = ( +/obj/structure/reagent_dispensers/watertank, +/turf/open/floor/plasteel/purple/corner{ + baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface + }, +/area/mine/production) +"fT" = ( +/obj/structure/sink{ + dir = 4; + icon_state = "sink"; + pixel_x = 11; + tag = "icon-sink (EAST)" + }, +/turf/open/floor/plasteel/bar{ + baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface + }, +/area/mine/living_quarters) (1,1,1) = {" aa @@ -8919,7 +8973,7 @@ aA at at at -at +fR at au fH @@ -9175,7 +9229,7 @@ an an at at -at +fQ at dr an @@ -14594,8 +14648,8 @@ fj ee cT cZ -dg dk +fT ee al al @@ -18433,7 +18487,7 @@ af ab ab dx -dC +fS dK bI bS diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm index e8cb9e2187f..16269074a47 100644 --- a/_maps/map_files/OmegaStation/OmegaStation.dmm +++ b/_maps/map_files/OmegaStation/OmegaStation.dmm @@ -7303,7 +7303,6 @@ icon_state = "1-2" }, /turf/open/floor/plasteel{ - desc = ""; icon_state = "L14" }, /area/hallway/primary/central{ @@ -9155,7 +9154,7 @@ }, /obj/effect/landmark/start/shaft_miner, /turf/open/floor/plasteel/loadingarea{ - baseturf = /turf/open/floor/plating/lava/smooth/lava_land_surface; + baseturf = /turf/open/space; dir = 8 }, /area/quartermaster/miningdock) @@ -25976,7 +25975,6 @@ icon_state = "4-8" }, /turf/open/floor/plasteel{ - desc = ""; icon_state = "L13"; name = "floor" }, @@ -27036,7 +27034,7 @@ /turf/open/floor/plating, /area/medical/chemistry) "aRh" = ( -/obj/machinery/smartfridge/chemistry, +/obj/machinery/smartfridge/chemistry/preloaded, /turf/closed/wall, /area/medical/chemistry) "aRi" = ( @@ -37373,7 +37371,7 @@ /turf/open/floor/plasteel, /area/toxins/xenobiology) "bjb" = ( -/obj/machinery/smartfridge/extract, +/obj/machinery/smartfridge/extract/preloaded, /obj/machinery/light{ dir = 1 }, diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index 56bcff286e9..bd71cc47948 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -30296,7 +30296,7 @@ }, /area/medical/chemistry) "bjk" = ( -/obj/machinery/smartfridge/chemistry, +/obj/machinery/smartfridge/chemistry/preloaded, /turf/closed/wall, /area/medical/chemistry) "bjl" = ( @@ -30534,7 +30534,7 @@ /turf/open/floor/plasteel/white, /area/toxins/xenobiology) "bjH" = ( -/obj/machinery/smartfridge/extract, +/obj/machinery/smartfridge/extract/preloaded, /turf/open/floor/plasteel/whitepurple/side{ dir = 4 }, @@ -40057,9 +40057,8 @@ /turf/open/floor/plasteel/white, /area/medical/virology) "bBf" = ( -/obj/machinery/smartfridge/chemistry/virology, +/obj/machinery/smartfridge/chemistry/virology/preloaded, /turf/open/floor/plasteel/whitegreen/side{ - tag = "icon-whitegreen (EAST)"; icon_state = "whitegreen"; dir = 4 }, @@ -50530,7 +50529,7 @@ /turf/open/floor/plasteel/white, /area/medical/genetics) "bXR" = ( -/obj/machinery/smartfridge/chemistry, +/obj/machinery/smartfridge/chemistry/preloaded, /turf/open/floor/plasteel/black, /area/medical/chemistry) "bXS" = ( diff --git a/_maps/map_files/TgStation/tgstation.2.1.3.dmm b/_maps/map_files/TgStation/tgstation.2.1.3.dmm index 5504f4d54a3..fafc5fe5441 100644 --- a/_maps/map_files/TgStation/tgstation.2.1.3.dmm +++ b/_maps/map_files/TgStation/tgstation.2.1.3.dmm @@ -17429,7 +17429,6 @@ /area/hallway/primary/central) "aMf" = ( /turf/open/floor/plasteel{ - desc = ""; icon_state = "L13"; name = "floor" }, @@ -17935,7 +17934,6 @@ /area/hallway/primary/central) "aNB" = ( /turf/open/floor/plasteel{ - desc = ""; icon_state = "L14" }, /area/hallway/primary/central) @@ -27262,7 +27260,7 @@ /turf/open/floor/plasteel, /area/assembly/chargebay) "bjQ" = ( -/obj/machinery/smartfridge/chemistry, +/obj/machinery/smartfridge/chemistry/preloaded, /turf/open/floor/plating, /area/medical/chemistry) "bjR" = ( @@ -41268,7 +41266,7 @@ /turf/open/floor/plasteel/white, /area/toxins/xenobiology) "bMo" = ( -/obj/machinery/smartfridge/extract, +/obj/machinery/smartfridge/extract/preloaded, /turf/open/floor/plasteel/white, /area/toxins/xenobiology) "bMp" = ( @@ -45237,7 +45235,7 @@ /turf/open/floor/plating, /area/maintenance/asmaint) "bVa" = ( -/obj/machinery/smartfridge/chemistry/virology, +/obj/machinery/smartfridge/chemistry/virology/preloaded, /turf/open/floor/plasteel/whitegreen/side{ dir = 8 }, @@ -53951,23 +53949,23 @@ /area/maintenance/asmaint2) "cnI" = ( /obj/structure/table, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 2; pixel_y = -5 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = -3; pixel_y = 3 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 2; pixel_y = -3 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = -2; pixel_y = -1 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 3; pixel_y = 3 }, diff --git a/_maps/map_files/generic/Centcomm.dmm b/_maps/map_files/generic/Centcomm.dmm index e3870a49f88..ba62ea23579 100644 --- a/_maps/map_files/generic/Centcomm.dmm +++ b/_maps/map_files/generic/Centcomm.dmm @@ -8913,10 +8913,10 @@ /area/centcom/ferry) "vZ" = ( /obj/structure/table/reinforced, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 6 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = -4 }, /obj/machinery/firealarm{ diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index d30a2d098fa..433f44e0954 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -1,3 +1,4 @@ -#define ANTAG_DATUM_CULT /datum/antagonist/cult -#define ANTAG_DATUM_CLOCKCULT /datum/antagonist/clockcult -#define ANTAG_DATUM_CLOCKCULT_SILENT /datum/antagonist/clockcult/silent \ No newline at end of file +#define ANTAG_DATUM_CULT /datum/antagonist/cult +#define ANTAG_DATUM_CULT_MASTER /datum/antagonist/cult/master +#define ANTAG_DATUM_CLOCKCULT /datum/antagonist/clockcult +#define ANTAG_DATUM_CLOCKCULT_SILENT /datum/antagonist/clockcult/silent \ No newline at end of file diff --git a/code/__DEFINES/menu.dm b/code/__DEFINES/menu.dm new file mode 100644 index 00000000000..2730adf87c7 --- /dev/null +++ b/code/__DEFINES/menu.dm @@ -0,0 +1,3 @@ +#define CHECKBOX_NONE 0 +#define CHECKBOX_GROUP 1 +#define CHECKBOX_TOGGLE 2 \ No newline at end of file diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index e66d528a5af..42151510587 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -457,6 +457,7 @@ #define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null) #define LAZYLEN(L) length(L) #define LAZYCLEARLIST(L) if(L) L.Cut() +#define SANITIZE_LIST(L) ( islist(L) ? L : list() ) /* Definining a counter as a series of key -> numeric value entries diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index abfaec29a73..e93de5dccc1 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -6,6 +6,7 @@ ) #define Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL)) +#define CULT_POLL_WAIT 2400 /proc/get_area(atom/A) if (!istype(A)) @@ -414,68 +415,83 @@ return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) -/proc/showCandidatePollWindow(mob/dead/observer/G, poll_time, Question, list/candidates, ignore_category, time_passed, flashwindow = TRUE) +/proc/showCandidatePollWindow(mob/M, poll_time, Question, list/candidates, ignore_category, time_passed, flashwindow = TRUE) set waitfor = 0 - G << 'sound/misc/notice2.ogg' //Alerting them to their consideration + M << 'sound/misc/notice2.ogg' //Alerting them to their consideration if(flashwindow) - window_flash(G.client) - switch(ignore_category ? askuser(G,Question,"Please answer in [poll_time/10] seconds!","Yes","No","Never for this round", StealFocus=0, Timeout=poll_time) : askuser(G,Question,"Please answer in [poll_time/10] seconds!","Yes","No", StealFocus=0, Timeout=poll_time)) + window_flash(M.client) + switch(ignore_category ? askuser(M,Question,"Please answer in [poll_time/10] seconds!","Yes","No","Never for this round", StealFocus=0, Timeout=poll_time) : askuser(M,Question,"Please answer in [poll_time/10] seconds!","Yes","No", StealFocus=0, Timeout=poll_time)) if(1) - to_chat(G, "Choice registered: Yes.") + to_chat(M, "Choice registered: Yes.") if((world.time-time_passed)>poll_time) - to_chat(G, "Sorry, you were too late for the consideration!") - G << 'sound/machines/buzz-sigh.ogg' + to_chat(M, "Sorry, you answered too late to be considered!") + M << 'sound/machines/buzz-sigh.ogg' + candidates -= M else - candidates += G + candidates += M if(2) - to_chat(G, "Choice registered: No.") + to_chat(M, "Choice registered: No.") + candidates -= M if(3) var/list/L = GLOB.poll_ignore[ignore_category] if(!L) GLOB.poll_ignore[ignore_category] = list() - GLOB.poll_ignore[ignore_category] += G.ckey - to_chat(G, "Choice registered: Never for this round.") + GLOB.poll_ignore[ignore_category] += M.ckey + to_chat(M, "Choice registered: Never for this round.") + candidates -= M + else + candidates -= M -/proc/pollCandidates(var/Question, var/jobbanType, var/datum/game_mode/gametypeCheck, var/be_special_flag = 0, var/poll_time = 300, var/ignore_category = null, flashwindow = TRUE) - var/list/mob/dead/observer/candidates = list() +/proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE) + var/list/candidates = list() + + for(var/mob/dead/observer/G in GLOB.player_list) + candidates += G + + pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates) + + return candidates + +/proc/pollCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null) var/time_passed = world.time if (!Question) Question = "Would you like to be a special role?" - for(var/mob/dead/observer/G in GLOB.player_list) - if(!G.key || !G.client || (ignore_category && GLOB.poll_ignore[ignore_category] && G.ckey in GLOB.poll_ignore[ignore_category])) + for(var/m in group) + var/mob/M = m + if(!M.key || !M.client || (ignore_category && GLOB.poll_ignore[ignore_category] && M.ckey in GLOB.poll_ignore[ignore_category])) continue if(be_special_flag) - if(!(G.client.prefs) || !(be_special_flag in G.client.prefs.be_special)) + if(!(M.client.prefs) || !(be_special_flag in M.client.prefs.be_special)) continue - if (gametypeCheck) - if(!gametypeCheck.age_check(G.client)) + if(gametypeCheck) + if(!gametypeCheck.age_check(M.client)) continue - if (jobbanType) - if(jobban_isbanned(G, jobbanType) || jobban_isbanned(G, "Syndicate")) + if(jobbanType) + if(jobban_isbanned(M, jobbanType) || jobban_isbanned(M, "Syndicate")) continue - showCandidatePollWindow(G, poll_time, Question, candidates, ignore_category, time_passed, flashwindow) + showCandidatePollWindow(M, poll_time, Question, group, ignore_category, time_passed, flashwindow) sleep(poll_time) - //Check all our candidates, to make sure they didn't log off during the wait period. - for(var/mob/dead/observer/G in candidates) - if(!G.key || !G.client) - candidates.Remove(G) + //Check all our candidates, to make sure they didn't log off or get deleted during the wait period. + for(var/mob/M in group) + if(!M.key || !M.client) + group -= M - listclearnulls(candidates) + listclearnulls(group) - return candidates + return group /proc/pollCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null) - var/list/L = pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) + var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) if(!M || QDELETED(M) || !M.loc) return list() return L /proc/pollCandidatesForMobs(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, list/mobs, ignore_category = null) - var/list/L = pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) + var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) var/i=1 for(var/v in mobs) var/atom/A = v @@ -485,6 +501,55 @@ ++i return L +/proc/pollCultists(var/mob/living/Nominee) // Cult Master Poll + if(world.time < CULT_POLL_WAIT) + to_chat(Nominee, "It would be premature to select a leader while everyone is still settling in, try again in [round((CULT_POLL_WAIT-world.time)/10)] seconds.") + return + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current) + B.current.verbs -= /mob/living/proc/cult_master + if(!B.current.incapacitated()) + B.current << 'sound/hallucinations/im_here1.ogg' + to_chat(B.current, "Acolyte [Nominee] has asserted that they are worthy of leading the cult. A vote will be called shortly.") + sleep(250) + var/list/asked_cultists = list() + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current && B.current != Nominee && !B.current.incapacitated()) + B.current << 'sound/magic/exit_blood.ogg' + asked_cultists += B.current + var/list/yes_voters = pollCandidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 1200, group = asked_cultists) + sleep(300) + if(QDELETED(Nominee) || Nominee.incapacitated()) + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current) + B.current.verbs += /mob/living/proc/cult_master + if(!B.current.incapacitated()) + to_chat(B.current,"[Nominee] has died in the process of attempting to win the cult's support!") + return FALSE + if(!Nominee.mind) + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current) + B.current.verbs += /mob/living/proc/cult_master + if(!B.current.incapacitated()) + to_chat(B.current,"[Nominee] has gone insane and catatonic in the process of attempting to win the cult's support!") + return FALSE + if(LAZYLEN(yes_voters) <= LAZYLEN(asked_cultists) * 0.5) + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current) + B.current.verbs += /mob/living/proc/cult_master + if(!B.current.incapacitated()) + to_chat(B.current, "[Nominee] could not win the cult's support and shall continue to serve as an acolyte.") + return FALSE + SSticker.mode.remove_cultist(Nominee.mind, FALSE) + Nominee.mind.add_antag_datum(ANTAG_DATUM_CULT_MASTER) + GLOB.cult_mastered = TRUE + for(var/datum/mind/B in SSticker.mode.cult) + if(!B.current.incapacitated()) + to_chat(B.current,"[Nominee] has won the cult's support and is now their master. Follow [Nominee.p_their()] orders to the best of your ability!") + return TRUE + +/proc/poll_helper(var/mob/living/M) + /proc/makeBody(mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character if(!G_found || !G_found.key) return diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm index 14399b340a2..44c5315dbf4 100644 --- a/code/__HELPERS/matrices.dm +++ b/code/__HELPERS/matrices.dm @@ -24,7 +24,6 @@ //doesn't have an object argument because this is "Stacking" with the animate call above //3 billion% intentional - //Dumps the matrix data in format a-f /matrix/proc/tolist() . = list() diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 06bfc61ccb7..a7d371c0df7 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1342,6 +1342,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) //This prevents RCEs from badmins //kevinz000 if you touch this I will hunt you down GLOBAL_VAR_INIT(valid_HTTPSGet, FALSE) +GLOBAL_PROTECT(valid_HTTPSGet) /proc/HTTPSGet(url) if(findtext(url, "\"")) GLOB.valid_HTTPSGet = FALSE diff --git a/code/_globalvars/game_modes.dm b/code/_globalvars/game_modes.dm index 1b17c5477be..f1e5b479ae0 100644 --- a/code/_globalvars/game_modes.dm +++ b/code/_globalvars/game_modes.dm @@ -3,3 +3,12 @@ GLOBAL_VAR_INIT(secret_force_mode, "secret") // if this is anything but "secret" GLOBAL_VAR_INIT(wavesecret, 0) // meteor mode, delays wave progression, terrible name GLOBAL_DATUM(start_state, /datum/station_state) // Used in round-end report + +// Cult, needs to be global so admin cultists are functional +GLOBAL_VAR_INIT(blood_target, null) // Cult Master's target or Construct's Master +GLOBAL_DATUM(blood_target_image, /image) +GLOBAL_DATUM(sac_mind, /datum/mind) +GLOBAL_VAR_INIT(sac_image, null) +GLOBAL_VAR_INIT(cult_mastered, FALSE) +GLOBAL_VAR_INIT(reckoning_complete, FALSE) +GLOBAL_VAR_INIT(sac_complete, FALSE) \ No newline at end of file diff --git a/code/_globalvars/lists/poll_ignore.dm b/code/_globalvars/lists/poll_ignore.dm index ff6a4edd403..fadd68526d1 100644 --- a/code/_globalvars/lists/poll_ignore.dm +++ b/code/_globalvars/lists/poll_ignore.dm @@ -5,5 +5,6 @@ #define POLL_IGNORE_POSSESSED_BLADE "possessed_blade" #define POLL_IGNORE_ALIEN_LARVA "alien_larva" #define POLL_IGNORE_CLOCKWORK_MARAUDER "clockwork_marauder" +#define POLL_IGNORE_SYNDICATE "syndicate" GLOBAL_LIST_EMPTY(poll_ignore) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 9bda2364623..0c40be56ac5 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -107,64 +107,112 @@ var/obj/item/W = get_active_held_item() - if(W == A) W.attack_self(src) update_inv_hands() return - - // operate three levels deep here (item in backpack in src; item in box in backpack in src, not any deeper) - if(A.ClickAccessible(src, depth=INVENTORY_DEPTH)) - // No adjacency needed + + //These are always reachable. + //User itself, current loc, and user inventory + if(DirectAccess(A)) if(W) - melee_item_attack_chain(src, W, A, params) + melee_item_attack_chain(src,W,A,params) else if(ismob(A)) changeNext_move(CLICK_CD_MELEE) UnarmedAttack(A) return - - if(!isturf(loc)) // This is going to stop you from telekinesing from inside a closet, but I don't shed many tears for that + + //Can't reach anything else in lockers or other weirdness + if(!loc.AllowClick()) return - // Allows you to click on a box's contents, if that box is on the ground, but no deeper than that - if(isturf(A) || isturf(A.loc) || (A.loc && isturf(A.loc.loc))) - if(Adjacent(A) || (W && CheckReach(src, A, W.reach))) //Adjacent or reaching attacks - if(W) - melee_item_attack_chain(src, W, A, params) - else - if(ismob(A)) - changeNext_move(CLICK_CD_MELEE) - UnarmedAttack(A, 1) - return - else // non-adjacent click - if(W) - W.afterattack(A,src,0,params) // 0: not Adjacent - else - RangedAttack(A, params) + //Standard reach turf to turf or reaching inside storage + if(CanReach(A,W)) + if(W) + melee_item_attack_chain(src,W,A,params) + else + if(ismob(A)) + changeNext_move(CLICK_CD_MELEE) + UnarmedAttack(A,1) + else + if(W) + W.afterattack(A,src,0,params) + else + RangedAttack(A,params) -/proc/CheckReach(atom/movable/here, atom/movable/there, reach) +/atom/movable/proc/CanReach(atom/target,obj/item/tool,view_only = FALSE) + if(isturf(target) || isturf(target.loc) || DirectAccess(target)) //Directly accessible atoms + if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks + return TRUE + else + //Things inside storage insde another storage + //Eg Contents of a box in a backpack + var/atom/outer_storage = get_atom_on_turf(target) + if(outer_storage == target) //whatever that is we don't want infinite loop. + return FALSE + if(outer_storage && CanReach(outer_storage,tool) && outer_storage.CanReachStorage(target,src,view_only ? STORAGE_VIEW_DEPTH : INVENTORY_DEPTH)) + return TRUE + return FALSE + +//Can [target] in this container be reached by [user], can't be more than [depth] levels deep +/atom/proc/CanReachStorage(atom/target,user,depth) + return FALSE + +/obj/item/weapon/storage/CanReachStorage(atom/target,user,depth) + while(target && depth > 0) + target = target.loc + depth-- + if(target == src) + return TRUE + return FALSE + +/atom/movable/proc/DirectAccess(atom/target) + if(target == src) + return TRUE + if(target == loc) + return TRUE + +/mob/DirectAccess(atom/target) + if(..()) + return TRUE + if(target in contents) //This could probably use moving down and restricting to inventory only + return TRUE + return FALSE + +/mob/living/DirectAccess(atom/target) + if(..()) //Lightweight checks first + return TRUE + if(target in GetAllContents()) + return TRUE + +/atom/proc/AllowClick() + return FALSE + +/turf/AllowClick() + return TRUE + +/proc/CheckToolReach(atom/movable/here, atom/movable/there, reach) if(!here || !there) return switch(reach) if(0) - return here.loc == there.loc + return FALSE if(1) - return here.Adjacent(there) + return FALSE //here.Adjacent(there) if(2 to INFINITY) - var/obj/dummy = new(get_turf(here)) //We'll try to move this every tick, failing if we can't + var/obj/dummy = new(get_turf(here)) dummy.pass_flags |= PASSTABLE + dummy.invisibility = INVISIBILITY_ABSTRACT for(var/i in 1 to reach) //Limit it to that many tries var/turf/T = get_step(dummy, get_dir(dummy, there)) - if(dummy.loc == there.loc) + if(dummy.CanReach(there)) qdel(dummy) - return 1 - if(there.density && dummy in range(1, there)) //For windows and suchlike - qdel(dummy) - return 1 + return TRUE if(!dummy.Move(T)) //we're blocked! qdel(dummy) return + qdel(dummy) // Default behavior: ignore double clicks (the second click that makes the doubleclick call already calls for a normal click) /mob/proc/DblClickOn(atom/A, params) @@ -304,21 +352,6 @@ /atom/proc/CtrlShiftClick(mob/user) return -/* - Helper to check can the mob click/access an item. - Used by mob inventory and storage items. -*/ -/atom/proc/ClickAccessible(mob/user, depth=1) - if(src == user.loc || (src in user.contents)) - return TRUE - - if(loc && depth > 1) - return loc.ClickAccessible(user, depth-1) - -/turf/ClickAccessible(mob/user, depth=1) - return - - /* Misc helpers diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index b376706a42b..7cbd997db68 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -42,6 +42,7 @@ thealert.override_alerts = override if(override) thealert.timeout = null + thealert.mob_viewer = src if(new_master) var/old_layer = new_master.layer @@ -96,6 +97,7 @@ var/severity = 0 var/alerttooltipstyle = "" var/override_alerts = FALSE //If it is overriding other alerts of the same type + var/mob/mob_viewer //the mob viewing this alert /obj/screen/alert/MouseEntered(location,control,params) @@ -256,6 +258,102 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." icon_state = "blobbernaut_nofactory" alerttooltipstyle = "blob" +// BLOODCULT + +/obj/screen/alert/bloodsense + name = "Blood Sense" + desc = "Allows you to sense blood that is manipulated by dark magicks." + icon_state = "cult_sense" + alerttooltipstyle = "cult" + var/static/image/narnar + var/angle = 0 + var/mob/living/simple_animal/hostile/construct/Cviewer = null + +/obj/screen/alert/bloodsense/Initialize() + . = ..() + if(!narnar) + narnar = new('icons/mob/screen_alert.dmi', "mini_nar") + START_PROCESSING(SSprocessing, src) + +/obj/screen/alert/bloodsense/Destroy() + Cviewer = null + STOP_PROCESSING(SSprocessing, src) + return ..() + +/obj/screen/alert/bloodsense/process() + var/atom/blood_target + if(GLOB.blood_target) + if(!get_turf(GLOB.blood_target)) + GLOB.blood_target = null + else + blood_target = GLOB.blood_target + if(Cviewer) + if(Cviewer.seeking && Cviewer.master) + blood_target = Cviewer.master + if(!blood_target && !GLOB.sac_complete) + if(icon_state == "runed_sense0") + return + animate(src, transform = null, time = 1, loop = 0) + angle = 0 + cut_overlays() + icon_state = "runed_sense0" + desc = "Nar-Sie demands that [GLOB.sac_mind] be sacrificed before the summoning ritual can begin." + add_overlay(GLOB.sac_image) + return + if(!blood_target && GLOB.sac_complete) + if(icon_state == "runed_sense1") + return + animate(src, transform = null, time = 1, loop = 0) + angle = 0 + cut_overlays() + icon_state = "runed_sense1" + desc = "The sacrifice is complete, prepare to summon Nar-Sie!" + add_overlay(narnar) + return + if(!blood_target) + return + var/turf/P = get_turf(blood_target) + var/turf/Q = get_turf(mob_viewer) + var/area/A = get_area(P) + if(P.z != Q.z) //The target is on a different Z level, we cannot sense that far. + return + desc = "You are currently tracking [blood_target] in [A.name]." + var/target_angle = Get_Angle(Q, P) + var/target_dist = get_dist(P, Q) + cut_overlays() + switch(target_dist) + if(0 to 1) + icon_state = "runed_sense2" + if(2 to 8) + icon_state = "arrow8" + if(9 to 15) + icon_state = "arrow7" + if(16 to 22) + icon_state = "arrow6" + if(23 to 29) + icon_state = "arrow5" + if(30 to 36) + icon_state = "arrow4" + if(37 to 43) + icon_state = "arrow3" + if(44 to 50) + icon_state = "arrow2" + if(51 to 57) + icon_state = "arrow1" + if(58 to 64) + icon_state = "arrow0" + if(65 to 400) + icon_state = "arrow" + var/difference = target_angle - angle + angle = target_angle + if(!difference) + return + var/matrix/final = matrix(transform) + final.Turn(difference) + animate(src, transform = final, time = 5, loop = 0) + + + // CLOCKCULT /obj/screen/alert/clockwork alerttooltipstyle = "clockcult" diff --git a/code/datums/antagonists/datum_cult.dm b/code/datums/antagonists/datum_cult.dm index 30c0aa84507..346c88e6b1b 100644 --- a/code/datums/antagonists/datum_cult.dm +++ b/code/datums/antagonists/datum_cult.dm @@ -2,9 +2,55 @@ var/datum/action/innate/cultcomm/communion = new /datum/antagonist/cult/Destroy() - qdel(communion) + QDEL_NULL(communion) return ..() +/datum/antagonist/cult/proc/add_objectives() + var/list/target_candidates = list() + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(player.mind && !player.mind.has_antag_datum(ANTAG_DATUM_CULT) && !is_convertable_to_cult(player) && (player != owner) && player.stat != DEAD) + target_candidates += player.mind + if(target_candidates.len == 0) + message_admins("Cult Sacrifice: Could not find unconvertable target, checking for convertable target.") + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(player.mind && !player.mind.has_antag_datum(ANTAG_DATUM_CULT) && (player != owner) && player.stat != DEAD) + target_candidates += player.mind + listclearnulls(target_candidates) + if(LAZYLEN(target_candidates)) + GLOB.sac_mind = pick(target_candidates) + if(!GLOB.sac_mind) + message_admins("Cult Sacrifice: ERROR - Null target chosen!") + else + var/datum/job/sacjob = SSjob.GetJob(GLOB.sac_mind.assigned_role) + var/datum/preferences/sacface = GLOB.sac_mind.current.client.prefs + var/icon/reshape = get_flat_human_icon(null, sacjob, sacface) + reshape.Shift(SOUTH, 4) + reshape.Shift(EAST, 1) + reshape.Crop(7,4,26,31) + reshape.Crop(-5,-3,26,30) + GLOB.sac_image = reshape + else + message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!") + GLOB.sac_complete = TRUE + SSticker.mode.cult_objectives += "sacrifice" + SSticker.mode.cult_objectives += "eldergod" + +/datum/antagonist/cult/proc/cult_memorization(datum/mind/cult_mind) + var/mob/living/current = cult_mind.current + for(var/obj_count = 1,obj_count <= SSticker.mode.cult_objectives.len,obj_count++) + var/explanation + switch(SSticker.mode.cult_objectives[obj_count]) + if("sacrifice") + if(GLOB.sac_mind) + explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it." + else + explanation = "The veil has already been weakened here, proceed to the final objective." + if("eldergod") + explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie' with nine acolytes on it. You must do this after sacrificing your target." + if(!silent) + to_chat(current, "Objective #[obj_count]: [explanation]") + cult_mind.memory += "Objective #[obj_count]: [explanation]
" + /datum/antagonist/cult/can_be_owned(datum/mind/new_owner) . = ..() if(.) @@ -12,14 +58,17 @@ /datum/antagonist/cult/on_gain() . = ..() - SSticker.mode.cult += owner + var/mob/living/current = owner.current + if(!LAZYLEN(SSticker.mode.cult_objectives)) + add_objectives() + SSticker.mode.cult += owner // Only add after they've been given objectives + cult_memorization(owner) + if(jobban_isbanned(current, ROLE_CULTIST)) + addtimer(CALLBACK(SSticker.mode, /datum/game_mode.proc/replace_jobbaned_player, current, ROLE_CULTIST, ROLE_CULTIST), 0) SSticker.mode.update_cult_icons_added(owner) - if(istype(SSticker.mode, /datum/game_mode/cult)) - var/datum/game_mode/cult/C = SSticker.mode - C.memorize_cult_objectives(owner) - if(jobban_isbanned(owner.current, ROLE_CULTIST)) - addtimer(CALLBACK(SSticker.mode, /datum/game_mode.proc/replace_jobbaned_player, owner.current, ROLE_CULTIST, ROLE_CULTIST), 0) - owner.current.log_message("Has been converted to the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG) + current.log_message("Has been converted to the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG) + if(GLOB.blood_target && GLOB.blood_target_image && current.client) + current.client.images += GLOB.blood_target_image /datum/antagonist/cult/apply_innate_effects(mob/living/mob_override) . = ..() @@ -27,8 +76,12 @@ if(mob_override) current = mob_override current.faction |= "cult" + current.grant_language(/datum/language/narsie) current.verbs += /mob/living/proc/cult_help + if(!GLOB.cult_mastered) + current.verbs += /mob/living/proc/cult_master communion.Grant(current) + current.throw_alert("bloodsense", /obj/screen/alert/bloodsense) /datum/antagonist/cult/remove_innate_effects(mob/living/mob_override) . = ..() @@ -36,15 +89,62 @@ if(mob_override) current = mob_override current.faction -= "cult" + current.remove_language(/datum/language/narsie) current.verbs -= /mob/living/proc/cult_help communion.Remove(current) + owner.current.verbs -= /mob/living/proc/cult_master + for(var/datum/action/innate/cultmast/H in owner.current.actions) + qdel(H) + current.clear_alert("bloodsense") /datum/antagonist/cult/on_removal() owner.wipe_memory() SSticker.mode.cult -= owner SSticker.mode.update_cult_icons_removed(owner) - to_chat(owner, "An unfamiliar white light flashes through your mind, cleansing the taint of the Dark One and all your memories as its servant.") - owner.current.log_message("Has renounced the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG) if(!silent) - owner.current.visible_message("[owner] looks like [owner.current.p_they()] just reverted to their old faith!") + to_chat(owner.current, "An unfamiliar white light flashes through your mind, cleansing the taint of the Geometer and all your memories as her servant.") + owner.current.log_message("Has renounced the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG) + owner.current.visible_message("[owner.current] looks like [owner.current.p_they()] just reverted to their old faith!") + if(GLOB.blood_target && GLOB.blood_target_image && owner.current.client) + owner.current.client.images -= GLOB.blood_target_image . = ..() + +/datum/antagonist/cult/master + var/datum/action/innate/cultmast/finalreck/reckoning = new + var/datum/action/innate/cultmast/cultmark/bloodmark = new + +/datum/antagonist/cult/master/Destroy() + QDEL_NULL(reckoning) + QDEL_NULL(bloodmark) + return ..() + +/datum/antagonist/cult/master/on_gain() + . = ..() + var/mob/living/current = owner.current + SSticker.mode.set_antag_hud(current, "cultmaster") + +/datum/antagonist/cult/master/greet() + to_chat(owner.current, "You are the cult's Master. As the cult's Master, you have a unique title and loud voice when communicating, are capable of marking \ + targets, such as a location or a noncultist, to direct the cult to them, and, finally, you are capable of summoning the entire living cult to your location once.") + to_chat(owner.current, "Use these abilities to direct the cult to victory at any cost.") + +/datum/antagonist/cult/master/apply_innate_effects(mob/living/mob_override) + . = ..() + var/mob/living/current = owner.current + if(mob_override) + current = mob_override + if(!GLOB.reckoning_complete) + reckoning.Grant(current) + bloodmark.Grant(current) + current.update_action_buttons_icon() + current.apply_status_effect(/datum/status_effect/cult_master) + +/datum/antagonist/cult/master/remove_innate_effects(mob/living/mob_override) + . = ..() + var/mob/living/current = owner.current + if(mob_override) + current = mob_override + reckoning.Remove(current) + bloodmark.Remove(current) + current.update_action_buttons_icon() + current.remove_status_effect(/datum/status_effect/cult_master) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 9147d14e26d..538c9243ada 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -63,7 +63,7 @@ var/mob/living/enslaved_to //If this mind's master is another mob (i.e. adamantine golems) - var/datum/language_holder + var/datum/language_holder/language_holder /datum/mind/New(var/key) src.key = key @@ -90,7 +90,8 @@ SStgui.on_transfer(current, new_character) if(!language_holder) - language_holder = new_character.language_holder.copy(src) + var/datum/language_holder/mob_holder = new_character.get_language_holder(shadow = FALSE) + language_holder = mob_holder.copy(src) if(key) if(new_character.key != key) //if we're transfering into a body with a key associated which is not ours @@ -1447,17 +1448,10 @@ if(!(src in SSticker.mode.cult)) SSticker.mode.add_cultist(src,FALSE) special_role = "Cultist" - to_chat(current, "You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of Nar-Sie.") - to_chat(current, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.") - var/datum/game_mode/cult/cult = SSticker.mode - - if (istype(cult)) - cult.memorize_cult_objectives(src) - else - var/explanation = "Summon Nar-Sie via the use of the appropriate rune (Hell join self). It will only work if nine cultists stand on and around it." - to_chat(current, "Objective #1: [explanation]") - memory += "Objective #1: [explanation]
" - + to_chat(current, "You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy your world is, you see that it should be open to the knowledge of Nar-Sie.") + to_chat(current, "Assist your new bretheren in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.") + var/datum/antagonist/cult/C + C.cult_memorization(src) var/mob/living/carbon/human/H = current if (!SSticker.mode.equip_cultist(current)) to_chat(H, "Spawning an amulet from your Master failed.") diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm index 3a54db4e0a9..011363f0019 100644 --- a/code/datums/ruins/lavaland.dm +++ b/code/datums/ruins/lavaland.dm @@ -42,6 +42,7 @@ However, all the inhabitants seem to do is grow drugs and guns." suffix = "lavaland_surface_seed_vault.dmm" cost = 10 + allow_duplicates = FALSE /datum/map_template/ruin/lavaland/ash_walker name = "Ash Walker Nest" diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index 7b00a17e41c..a0338f72b0d 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -228,3 +228,32 @@ name = "Wish Granter's Immortality" desc = "You are being resurrected!" icon_state = "wish_granter" + +/datum/status_effect/cult_master + id = "The Cult Master" + duration = -1 + tick_interval = 100 + alert_type = null + var/alive = TRUE + +/datum/status_effect/cult_master/proc/deathrattle() + var/area/A = get_area(owner) + for(var/datum/mind/B in SSticker.mode.cult) + if(isliving(B.current)) + var/mob/living/M = B.current + M << 'sound/hallucinations/veryfar_noise.ogg' + to_chat(M, "The Cult's Master, [owner], has fallen in the [A]!") + + +/datum/status_effect/cult_master/tick() + if(owner.stat != DEAD && !alive) + alive = TRUE + return + if(owner.stat == DEAD && alive) + alive = FALSE + deathrattle() + +/datum/status_effect/cult_master/on_remove() + deathrattle() + . = ..() + diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index 3e40aa3072d..f0e2c63c6e4 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -10,6 +10,7 @@ var/status_type = STATUS_EFFECT_UNIQUE //How many of the effect can be on one mob, and what happens when you try to add another var/on_remove_on_mob_delete = FALSE //if we call on_remove() when the mob is deleted var/alert_type = /obj/screen/alert/status_effect //the alert thrown by the status effect, contains name and description + var/obj/screen/alert/status_effect/linked_alert = null //the alert itself, if it exists /datum/status_effect/New(mob/living/new_owner) if(new_owner) @@ -39,6 +40,7 @@ if(alert_type) var/obj/screen/alert/status_effect/A = owner.throw_alert(id, alert_type) A.attached_effect = src //so the alert can reference us, if it needs to + linked_alert = A //so we can reference the alert, if we need to START_PROCESSING(SSfastprocess, src) /datum/status_effect/process() diff --git a/code/datums/wires/explosive.dm b/code/datums/wires/explosive.dm index 2ab4eb09b9b..e65d64f36c6 100644 --- a/code/datums/wires/explosive.dm +++ b/code/datums/wires/explosive.dm @@ -13,16 +13,16 @@ /datum/wires/explosive/c4 - holder_type = /obj/item/weapon/c4 + holder_type = /obj/item/weapon/grenade/plastic/c4 randomize = TRUE //Same behaviour since no wire actually disarms it /datum/wires/explosive/c4/interactable(mob/user) - var/obj/item/weapon/c4/P = holder + var/obj/item/weapon/grenade/plastic/c4/P = holder if(P.open_panel) return TRUE /datum/wires/explosive/c4/explode() - var/obj/item/weapon/c4/P = holder + var/obj/item/weapon/grenade/plastic/c4/P = holder P.explode() diff --git a/code/game/gamemodes/antag_spawner.dm b/code/game/gamemodes/antag_spawner.dm index 2cb5d16503a..cba7ded6316 100644 --- a/code/game/gamemodes/antag_spawner.dm +++ b/code/game/gamemodes/antag_spawner.dm @@ -155,7 +155,8 @@ if(!(check_usability(user))) return - var/list/nuke_candidates = pollCandidatesForMob("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 150, src) + to_chat(user, "You activate [src] and wait for confirmation.") + var/list/nuke_candidates = pollCandidatesForMob("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 150, POLL_IGNORE_SYNDICATE, src) if(nuke_candidates.len) if(!(check_usability(user))) return diff --git a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm index abd5866442f..cf709f09b0d 100644 --- a/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm +++ b/code/game/gamemodes/clock_cult/clock_effects/clock_sigils.dm @@ -57,7 +57,7 @@ alpha = 75 color = "#FAE48C" light_range = 1.4 - light_power = 0.4 + light_power = 1 light_color = "#FAE48C" sigil_name = "Sigil of Transgression" @@ -96,10 +96,6 @@ sigil_name = "Sigil of Submission" var/glow_type -/obj/effect/clockwork/sigil/submission/New() - ..() - update_light() - /obj/effect/clockwork/sigil/submission/proc/post_channel(mob/living/L) /obj/effect/clockwork/sigil/submission/sigil_effects(mob/living/L) @@ -285,7 +281,7 @@ if(!power_charge) set_light(0) else - set_light(round(alpha*0.02, 1), round(alpha*0.005, 1)) + set_light(max(alpha*0.02, 1.4), max(alpha*0.01, 0.1)) //Vitality Matrix: Drains health from non-servants to heal or even revive servants. /obj/effect/clockwork/sigil/vitality diff --git a/code/game/gamemodes/clock_cult/clock_effects/spatial_gateway.dm b/code/game/gamemodes/clock_cult/clock_effects/spatial_gateway.dm index 1c49d366930..5c1d3fb5fbc 100644 --- a/code/game/gamemodes/clock_cult/clock_effects/spatial_gateway.dm +++ b/code/game/gamemodes/clock_cult/clock_effects/spatial_gateway.dm @@ -180,17 +180,26 @@ return procure_gateway(invoker, time_duration, gateway_uses, two_way) var/istargetobelisk = istype(target, /obj/structure/destructible/clockwork/powered/clockwork_obelisk) var/issrcobelisk = istype(src, /obj/structure/destructible/clockwork/powered/clockwork_obelisk) - if(issrcobelisk && !anchored) - to_chat(invoker, "[src] is no longer secured!") - return FALSE + if(issrcobelisk) + if(!anchored) + to_chat(invoker, "[src] is no longer secured!") + return FALSE + var/obj/structure/destructible/clockwork/powered/clockwork_obelisk/CO = src //foolish as I am, how I set this proc up makes substypes unfeasible + if(CO.active) + to_chat(invoker, "[src] is now sustaining a gateway!") + return FALSE if(istargetobelisk) if(!target.anchored) to_chat(invoker, "That [target.name] is no longer secured!") return procure_gateway(invoker, time_duration, gateway_uses, two_way) var/obj/structure/destructible/clockwork/powered/clockwork_obelisk/CO = target + if(CO.active) + to_chat(invoker, "That [target.name] is sustaining a gateway, and cannot recieve another!") + return procure_gateway(invoker, time_duration, gateway_uses, two_way) var/efficiency = CO.get_efficiency_mod() gateway_uses = round(gateway_uses * (2 * efficiency), 1) time_duration = round(time_duration * (2 * efficiency), 1) + CO.active = TRUE //you'd be active in a second but you should update immediately invoker.visible_message("The air in front of [invoker] ripples before suddenly tearing open!", \ "With a word, you rip open a [two_way ? "two-way":"one-way"] rift to [input_target_key]. It will last for [time_duration / 10] seconds and has [gateway_uses] use[gateway_uses > 1 ? "s" : ""].") var/obj/effect/clockwork/spatial_gateway/S1 = new(issrcobelisk ? get_turf(src) : get_step(get_turf(invoker), invoker.dir)) diff --git a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm index aa1a07ac2f2..21da8fab004 100644 --- a/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm +++ b/code/game/gamemodes/clock_cult/clock_scriptures/scripture_applications.dm @@ -135,7 +135,7 @@ if(!check_special_requirements()) return FALSE to_chat(invoker, "The tendril shivers slightly as it selects a marauder...") - var/list/marauder_candidates = pollCandidates("Do you want to play as the clockwork marauder of [invoker.real_name]?", ROLE_SERVANT_OF_RATVAR, null, FALSE, 50, POLL_IGNORE_CLOCKWORK_MARAUDER) + var/list/marauder_candidates = pollGhostCandidates("Do you want to play as the clockwork marauder of [invoker.real_name]?", ROLE_SERVANT_OF_RATVAR, null, FALSE, 50, POLL_IGNORE_CLOCKWORK_MARAUDER) if(!check_special_requirements()) return FALSE if(!marauder_candidates.len) diff --git a/code/game/gamemodes/clock_cult/clock_structures/clockwork_obelisk.dm b/code/game/gamemodes/clock_cult/clock_structures/clockwork_obelisk.dm index 2bebb4c9a5c..580f5fbed5a 100644 --- a/code/game/gamemodes/clock_cult/clock_structures/clockwork_obelisk.dm +++ b/code/game/gamemodes/clock_cult/clock_structures/clockwork_obelisk.dm @@ -58,7 +58,7 @@ var/input = stripped_input(usr, "Please choose a message to send over the Hierophant Network.", "Hierophant Broadcast", "") if(!is_servant_of_ratvar(user) || !input || !user.canUseTopic(src, !issilicon(user))) return - if(anchored) + if(!anchored) to_chat(user, "[src] is no longer secured!") return FALSE if(active) @@ -84,7 +84,8 @@ return if(procure_gateway(user, round(100 * get_efficiency_mod(), 1), round(5 * get_efficiency_mod(), 1), 1)) process() - if(!active) + if(!active) //we won't be active if nobody has sent a gateway to us + active = TRUE clockwork_say(user, text2ratvar("Spatial Gateway, activate!")) return return_power(gateway_cost) //if we didn't return above, ie, successfully create a gateway, we give the power back @@ -92,7 +93,8 @@ /obj/structure/destructible/clockwork/powered/clockwork_obelisk/process() if(!anchored) return - if(locate(/obj/effect/clockwork/spatial_gateway) in loc) + var/obj/effect/clockwork/spatial_gateway/SG = locate(/obj/effect/clockwork/spatial_gateway) + if(SG && SG.timerid) //it's a valid gateway, we're active icon_state = active_icon density = 0 active = TRUE diff --git a/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm b/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm index 0529b67ffdd..a8dfb2dd4ae 100644 --- a/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm +++ b/code/game/gamemodes/clock_cult/clock_structures/ratvar_the_clockwork_justicar.dm @@ -28,7 +28,7 @@ var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/clockwork_effects.dmi', "ratvar_alert") var/area/A = get_area(src) notify_ghosts("The Justiciar's light calls to you! Reach out to Ratvar in [A.name] to be granted a shell to spread his glory!", null, source = src, alert_overlay = alert_overlay) - INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency..proc/request, null, 0) + INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency..proc/request, null, 0, 0) /obj/structure/destructible/clockwork/massive/ratvar/Destroy() GLOB.ratvar_awakens-- diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index 7780ae5db9d..3b30d3e44a6 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -1,34 +1,31 @@ - - /datum/game_mode var/list/datum/mind/cult = list() var/list/cult_objectives = list() + var/eldergod = 1 //for the summon god objective /proc/iscultist(mob/living/M) return istype(M) && M.mind && M.mind.has_antag_datum(ANTAG_DATUM_CULT) /proc/is_sacrifice_target(datum/mind/mind) - if(SSticker.mode.name == "cult") - var/datum/game_mode/cult/cult_mode = SSticker.mode - if(mind == cult_mode.sacrifice_target) - return 1 - return 0 + if(mind == GLOB.sac_mind) + return TRUE + return FALSE /proc/is_convertable_to_cult(mob/living/M) if(!istype(M)) - return 0 + return FALSE if(M.mind) if(ishuman(M) && (M.mind.assigned_role in list("Captain", "Chaplain"))) - return 0 + return FALSE if(is_sacrifice_target(M.mind)) - return 0 + return FALSE if(M.mind.enslaved_to && !iscultist(M.mind.enslaved_to)) - return 0 + return FALSE else - return 0 + return FALSE if(M.isloyal() || issilicon(M) || isbot(M) || isdrone(M) || is_servant_of_ratvar(M)) - return 0 //can't convert machines, shielded, or ratvar's dogs - return 1 + return FALSE //can't convert machines, shielded, or ratvar's dogs + return TRUE /datum/game_mode/cult name = "cult" @@ -47,14 +44,13 @@ Crew: Prevent the cult from expanding and drive it out." var/finished = 0 - var/eldergod = 1 //for the summon god objective var/acolytes_needed = 10 //for the survive objective var/acolytes_survived = 0 - var/datum/mind/sacrifice_target = null//The target to be sacrificed var/list/cultists_to_cult = list() //the cultists we'll convert + /datum/game_mode/cult/pre_setup() cult_objectives += "sacrifice" cult_objectives += "eldergod" @@ -82,22 +78,6 @@ return (cultists_to_cult.len>=required_enemies) -/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind) - for(var/obj_count = 1,obj_count <= cult_objectives.len,obj_count++) - var/explanation - switch(cult_objectives[obj_count]) - if("survive") - explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station." - if("sacrifice") - if(sacrifice_target) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it." - else - explanation = "Free objective." - if("eldergod") - explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie' with nine acolytes on it. You must do this after sacrificing your target." - to_chat(cult_mind.current, "Objective #[obj_count]: [explanation]") - cult_mind.memory += "Objective #[obj_count]: [explanation]
" - /datum/game_mode/cult/post_setup() modePlayer += cultists_to_cult if("sacrifice" in cult_objectives) @@ -108,9 +88,18 @@ if(player.mind && !(player.mind in cultists_to_cult)) possible_targets += player.mind if(possible_targets.len > 0) - sacrifice_target = pick(possible_targets) - if(!sacrifice_target) + GLOB.sac_mind = pick(possible_targets) + if(!GLOB.sac_mind) message_admins("Cult Sacrifice: ERROR - Null target chosen!") + else + var/datum/job/sacjob = SSjob.GetJob(GLOB.sac_mind.assigned_role) + var/datum/preferences/sacface = GLOB.sac_mind.current.client.prefs + var/icon/reshape = get_flat_human_icon(null, sacjob, sacface) + reshape.Shift(SOUTH, 4) + reshape.Shift(EAST, 1) + reshape.Crop(7,4,26,31) + reshape.Crop(-5,-3,26,30) + GLOB.sac_image = reshape else message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!") for(var/datum/mind/cult_mind in cultists_to_cult) @@ -198,7 +187,7 @@ if(cult_objectives.Find("eldergod")) cult_fail += eldergod //1 by default, 0 if the elder god has been summoned at least once if(cult_objectives.Find("sacrifice")) - if(sacrifice_target && !GLOB.sacrificed.Find(sacrifice_target)) //if the target has been GLOB.sacrificed, ignore this step. otherwise, add 1 to cult_fail + if(GLOB.sac_mind && GLOB.sac_complete) //if the target has been GLOB.sacrificed, ignore this step. otherwise, add 1 to cult_fail cult_fail++ return cult_fail //if any objectives aren't met, failure @@ -243,16 +232,12 @@ SSblackbox.add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]") SSticker.news_report = CULT_FAILURE if("sacrifice") - if(sacrifice_target) - if(sacrifice_target in GLOB.sacrificed) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Success!" - SSblackbox.add_details("cult_objective","cult_sacrifice|SUCCESS") - else if(sacrifice_target && sacrifice_target.current) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail." - SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL") - else - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail (Gibbed)." - SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL|GIBBED") + if(GLOB.sac_complete) + explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Success!" + SSblackbox.add_details("cult_objective","cult_sacrifice|SUCCESS") + else + explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Fail." + SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL") if("eldergod") if(!eldergod) explanation = "Summon Nar-Sie. Success!" @@ -269,12 +254,45 @@ return 1 -/datum/game_mode/proc/auto_declare_completion_cult() - if( cult.len || (SSticker && istype(SSticker.mode,/datum/game_mode/cult)) ) - var/text = "
The cultists were:" - for(var/datum/mind/cultist in cult) - text += printplayer(cultist) - - text += "
" - - to_chat(world, text) +/datum/game_mode/proc/datum_cult_completion() + var/text = "" + var/acolytes_survived = 0 + for(var/datum/mind/cult_mind in cult) + if (cult_mind.current && cult_mind.current.stat != DEAD) + if(cult_mind.current.onCentcom() || cult_mind.current.onSyndieBase()) + acolytes_survived++ + var/cult_fail = 0 + cult_fail += eldergod + if(!GLOB.sac_complete) + cult_fail++ + if(!cult_fail) + SSblackbox.set_details("round_end_result","win - cult win") + SSblackbox.set_val("round_end_result",acolytes_survived) + to_chat(world, "The cult has succeeded! Nar-sie has snuffed out another torch in the void!") + else + SSblackbox.set_details("round_end_result","loss - staff stopped the cult") + SSblackbox.set_val("round_end_result",acolytes_survived) + to_chat(world, "The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!") + if(cult_objectives.len) + text += "
The cultists' objectives were:" + for(var/obj_count in 1 to 2) + var/explanation + switch(cult_objectives[obj_count]) + if("sacrifice") + if(GLOB.sac_complete) + explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Success!" + SSblackbox.add_details("cult_objective","cult_sacrifice|SUCCESS") + else + explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role]. Fail." + SSblackbox.add_details("cult_objective","cult_sacrifice|FAIL") + if("eldergod") + if(!eldergod) + explanation = "Summon Nar-Sie. Success!" + SSblackbox.add_details("cult_objective","cult_narsie|SUCCESS") + SSticker.news_report = CULT_SUMMON + else + explanation = "Summon Nar-Sie. Fail." + SSblackbox.add_details("cult_objective","cult_narsie|FAIL") + SSticker.news_report = CULT_FAILURE + text += "
Objective #[obj_count]: [explanation]" + to_chat(world, text) diff --git a/code/game/gamemodes/cult/cult_comms.dm b/code/game/gamemodes/cult/cult_comms.dm index 40c7bbece89..ffaaf46b802 100644 --- a/code/game/gamemodes/cult/cult_comms.dm +++ b/code/game/gamemodes/cult/cult_comms.dm @@ -1,3 +1,5 @@ +// Contains cult communion, guide, and cult master abilities +#define MARK_COOLDOWN /datum/action/innate/cultcomm name = "Communion" @@ -19,11 +21,22 @@ cultist_commune(usr, input) /proc/cultist_commune(mob/living/user, message) + var/my_message if(!message) return - user.whisper("O bidai nabora se[pick("'","`")]sma!") + user.whisper("O bidai nabora se[pick("'","`")]sma!", language = /datum/language/common) user.whisper(html_decode(message)) - var/my_message = "[(ishuman(user) ? "Acolyte" : "Construct")] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]: [message]" + var/title = "Acolyte" + var/span = "cultitalic" + if(user.mind && user.mind.has_antag_datum(ANTAG_DATUM_CULT_MASTER)) + span = "cultlarge" + if(ishuman(user)) + title = "Master" + else + title = "Lord" + else if(!ishuman(user)) + title = "Construct" + my_message = "[title] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]: [message]" for(var/mob/M in GLOB.mob_list) if(iscultist(M)) to_chat(M, my_message) @@ -65,3 +78,159 @@ popup.set_content(text) popup.open() return 1 + +/mob/living/proc/cult_master() + set category = "Cultist" + set name = "Assert Leadership" + pollCultists(src) // This proc handles the distribution of cult master actions + +/datum/action/innate/cultmast + background_icon_state = "bg_demon" + buttontooltipstyle = "cult" + check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_CONSCIOUS + +/datum/action/innate/cultmast/IsAvailable() + if(!owner.mind || !owner.mind.has_antag_datum(ANTAG_DATUM_CULT_MASTER)) + return 0 + return ..() + +/datum/action/innate/cultmast/finalreck + name = "Final Reckoning" + desc = "A single-use spell that brings the entire cult to the master's location." + button_icon_state = "sintouch" + +/datum/action/innate/cultmast/finalreck/Activate() + for(var/i in 1 to 4) + chant(i) + var/list/destinations = list() + for(var/turf/T in orange(1, owner)) + if(!is_blocked_turf(T, TRUE)) + destinations += T + if(!LAZYLEN(destinations)) + to_chat(owner, "You need more space to summon the cult!") + return + if(do_after(owner, 30, target = owner)) + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current && B.current.stat != DEAD) + var/turf/mobloc = get_turf(B.current) + switch(i) + if(1) + new /obj/effect/overlay/temp/cult/sparks(mobloc, B.current.dir) + playsound(mobloc, "sparks", 50, 1) + if(2) + new /obj/effect/overlay/temp/dir_setting/cult/phase/out(mobloc, B.current.dir) + playsound(mobloc, "sparks", 75, 1) + if(3) + new /obj/effect/overlay/temp/dir_setting/cult/phase(mobloc, B.current.dir) + playsound(mobloc, "sparks", 100, 1) + if(4) + playsound(mobloc, 'sound/magic/exit_blood.ogg', 100, 1) + if(B.current != owner) + B.current.setDir(SOUTH) + var/turf/final = pick(destinations) + new /obj/effect/overlay/temp/cult/blood(final) + addtimer(CALLBACK(B.current, /mob/.proc/reckon, final), 10) + else + return + GLOB.reckoning_complete = TRUE + Remove(owner) + +/mob/proc/reckon(turf/final) + new /obj/effect/overlay/temp/cult/blood/out(get_turf(src)) + forceMove(final) + +/datum/action/innate/cultmast/finalreck/proc/chant(chant_number) + switch(chant_number) + if(1) + owner.say("C'arta forbici!", language = /datum/language/common) + if(2) + owner.say("Pleggh e'ntrath!", language = /datum/language/common) + playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 50, 1) + if(3) + owner.say("Barhah hra zar'garis!", language = /datum/language/common) + playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 75, 1) + if(4) + owner.say("N'ath reth sh'yro eth d'rekkathnor!!!", language = /datum/language/common) + playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 100, 1) + +/datum/action/innate/cultmast/cultmark + name = "Mark Target" + desc = "Marks a target for the cult." + button_icon_state = "cult_mark" + var/obj/effect/proc_holder/cultmark/CM + var/cooldown = 0 + var/base_cooldown = 1200 + +/datum/action/innate/cultmast/cultmark/New() + CM = new() + CM.attached_action = src + ..() + +/datum/action/innate/cultmast/cultmark/IsAvailable() + if(!owner.mind || !owner.mind.has_antag_datum(ANTAG_DATUM_CULT_MASTER)) + return 0 + if(cooldown > world.time) + if(!CM.active) + owner << "You need to wait [round((cooldown - world.time) * 0.1)] seconds before you can mark another target!" + return 0 + return ..() + +/datum/action/innate/cultmast/cultmark/Destroy() + QDEL_NULL(CM) + return ..() + +/datum/action/innate/cultmast/cultmark/Activate() + CM.toggle(owner) //the important bit + return TRUE + +/obj/effect/proc_holder/cultmark + active = FALSE + ranged_mousepointer = 'icons/effects/cult_target.dmi' + var/datum/action/innate/cultmast/cultmark/attached_action + +/obj/effect/proc_holder/cultmark/Destroy() + attached_action = null + return ..() + +/obj/effect/proc_holder/cultmark/proc/toggle(mob/user) + if(active) + remove_ranged_ability("You cease the marking ritual.") + else + add_ranged_ability(user, "You prepare to mark a target for your cult...") + +/obj/effect/proc_holder/cultmark/InterceptClickOn(mob/living/caller, params, atom/target) + if(..()) + return + if(ranged_ability_user.incapacitated()) + remove_ranged_ability() + return + var/turf/T = get_turf(ranged_ability_user) + if(!isturf(T)) + return FALSE + if(target in view(7, get_turf(ranged_ability_user))) + GLOB.blood_target = target + var/area/A = get_area(target) + attached_action.cooldown = world.time + attached_action.base_cooldown + addtimer(CALLBACK(attached_action.owner, /mob.proc/update_action_buttons_icon), attached_action.base_cooldown) + GLOB.blood_target_image = image('icons/effects/cult_target.dmi', target, "glow", ABOVE_MOB_LAYER) + GLOB.blood_target_image.appearance_flags = RESET_COLOR + GLOB.blood_target_image.pixel_x = -target.pixel_x + GLOB.blood_target_image.pixel_y = -target.pixel_y + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current && B.current.stat != DEAD && B.current.client) + to_chat(B.current, "Master [ranged_ability_user] has marked [GLOB.blood_target] in the [A.name] as the cult's top priority, get there immediately!") + B.current << pick(sound('sound/hallucinations/over_here2.ogg',0,1,75), sound('sound/hallucinations/over_here3.ogg',0,1,75)) + B.current.client.images += GLOB.blood_target_image + attached_action.owner.update_action_buttons_icon() + remove_ranged_ability("The marking rite is complete! It will last for 90 seconds.") + addtimer(CALLBACK(GLOBAL_PROC, .proc/reset_blood_target), 900, TIMER_OVERRIDE) + return TRUE + return FALSE + +/proc/reset_blood_target() + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current && B.current.stat != DEAD && B.current.client) + if(GLOB.blood_target) + to_chat(B.current,"The blood mark has expired!") + B.current.client.images -= GLOB.blood_target_image + QDEL_NULL(GLOB.blood_target) \ No newline at end of file diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index a90aeb78bc2..6058e8f605d 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -213,7 +213,7 @@ flags_inv = HIDEJUMPSUIT allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade) body_parts_covered = CHEST|GROIN|LEGS|ARMS - armor = list(melee = -50, bullet = -50, laser = -100,energy = -50, bomb = -50, bio = -50, rad = -50, fire = 0, acid = 0) + armor = list(melee = -50, bullet = -50, laser = -50,energy = -50, bomb = -50, bio = -50, rad = -50, fire = 0, acid = 0) slowdown = -1 hoodtype = /obj/item/clothing/head/hooded/berserkerhood diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index ce4abced722..069a4224800 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -197,35 +197,30 @@ This file contains the arcane tome files. if(!src || QDELETED(src) || !Adjacent(user) || user.incapacitated() || !check_rune_turf(Turf, user)) return if(ispath(rune_to_scribe, /obj/effect/rune/narsie)) - if(SSticker.mode.name == "cult") - var/datum/game_mode/cult/cult_mode = SSticker.mode - if(!("eldergod" in cult_mode.cult_objectives)) - to_chat(user, "Nar-Sie does not wish to be summoned!") - return - if(cult_mode.sacrifice_target && !(cult_mode.sacrifice_target in GLOB.sacrificed)) - to_chat(user, "The sacrifice is not complete. The portal would lack the power to open if you tried!") - return - if(!cult_mode.eldergod) - to_chat(user, "\"I am already here. There is no need to try to summon me now.\"") - return - if((loc.z && loc.z != ZLEVEL_STATION) || !A.blob_allowed) - to_chat(user, "The Geometer is not interested in lesser locations; the station is the prize!") - return - var/confirm_final = alert(user, "This is the FINAL step to summon Nar-Sie, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar-Sie!", "No") - if(confirm_final == "No") - to_chat(user, "You decide to prepare further before scribing the rune.") - return - Turf = get_turf(user) - A = get_area(src) - if(!check_rune_turf(Turf, user) || (loc.z && loc.z != ZLEVEL_STATION)|| !A.blob_allowed) - return - priority_announce("Figments from an eldritch god are being summoned by [user] into [A.map_name] from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensionsal Affairs", 'sound/AI/spanomalies.ogg') - for(var/B in spiral_range_turfs(1, user, 1)) - var/obj/structure/emergency_shield/sanguine/N = new(B) - shields += N - else + if(!("eldergod" in SSticker.mode.cult_objectives)) to_chat(user, "Nar-Sie does not wish to be summoned!") return + if(!GLOB.sac_complete) + to_chat(user, "The sacrifice is not complete. The portal would lack the power to open if you tried!") + return + if(!SSticker.mode.eldergod) + to_chat(user, "\"I am already here. There is no need to try to summon me now.\"") + return + if((loc.z && loc.z != ZLEVEL_STATION) || !A.blob_allowed) + to_chat(user, "The Geometer is not interested in lesser locations; the station is the prize!") + return + var/confirm_final = alert(user, "This is the FINAL step to summon Nar-Sie; it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar-Sie!", "No") + if(confirm_final == "No") + to_chat(user, "You decide to prepare further before scribing the rune.") + return + Turf = get_turf(user) + A = get_area(src) + if(!check_rune_turf(Turf, user) || (loc.z && loc.z != ZLEVEL_STATION)|| !A.blob_allowed) + return + priority_announce("Figments from an eldritch god are being summoned by [user] into [A.map_name] from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg') + for(var/B in spiral_range_turfs(1, user, 1)) + var/obj/structure/emergency_shield/sanguine/N = new(B) + shields += N user.visible_message("[user] [user.blood_volume ? "cuts open their arm and begins writing in their own blood":"begins sketching out a strange design"]!", \ "You [user.blood_volume ? "slice open your arm and ":""]begin drawing a sigil of the Geometer.") if(user.blood_volume) diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index 892ba114c53..ca8b51111b7 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -136,7 +136,7 @@ structure_check() searches for nearby cultist structures required for the invoca if(invocation) for(var/M in invokers) var/mob/living/L = M - L.say(invocation) + L.say(invocation, language = /datum/language/common) do_invoke_glow() /obj/effect/rune/proc/do_invoke_glow() @@ -398,23 +398,23 @@ structure_check() searches for nearby cultist structures required for the invoca return 1 /obj/effect/rune/convert/proc/do_sacrifice(mob/living/sacrificial, list/invokers) + var/big_sac = FALSE if((((ishuman(sacrificial) || iscyborg(sacrificial)) && sacrificial.stat != DEAD) || is_sacrifice_target(sacrificial.mind)) && invokers.len < 3) for(var/M in invokers) to_chat(M, "[sacrificial] is too greatly linked to the world! You need three acolytes!") log_game("Offer rune failed - not enough acolytes and target is living or sac target") return FALSE - var/sacrifice_fulfilled = FALSE - if(sacrificial.mind) GLOB.sacrificed += sacrificial.mind if(is_sacrifice_target(sacrificial.mind)) - sacrifice_fulfilled = TRUE + GLOB.sac_complete = TRUE + big_sac = TRUE else GLOB.sacrificed += sacrificial new /obj/effect/overlay/temp/cult/sac(get_turf(src)) for(var/M in invokers) - if(sacrifice_fulfilled) + if(big_sac) to_chat(M, "\"Yes! This is the one I desire! You have done well.\"") else if(ishuman(sacrificial) || iscyborg(sacrificial)) @@ -451,7 +451,7 @@ structure_check() searches for nearby cultist structures required for the invoca scribe_delay = 450 //how long the rune takes to create scribe_damage = 40.1 //how much damage you take doing it var/used - var/ignore_gamemode = FALSE + var/ignore_gamemode = TRUE /obj/effect/rune/narsie/Initialize(mapload, set_keyword) . = ..() @@ -490,7 +490,7 @@ structure_check() searches for nearby cultist structures required for the invoca //BEGIN THE SUMMONING used = 1 ..() - send_to_playing_players('sound/effects/dimensional_rend.ogg') //There used to be a message for this but every time it was changed it got edgier so I removed it + send_to_playing_players('sound/effects/dimensional_rend.ogg') var/turf/T = get_turf(src) sleep(40) if(src) @@ -516,7 +516,7 @@ structure_check() searches for nearby cultist structures required for the invoca /obj/effect/rune/raise_dead cultist_name = "Resurrect Cultist" cultist_desc = "requires the corpse of a cultist placed upon the rune. Provided there have been sufficient sacrifices, they will be revived." - invocation = null //Depends on the name of the user - see below + invocation = "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" //Depends on the name of the user - see below icon_state = "1" color = "#C80000" var/static/revives_used = 0 @@ -556,9 +556,9 @@ structure_check() searches for nearby cultist structures required for the invoca return rune_in_use = 1 if(user.name == "Herbert West") - user.say("To life, to life, I bring them!") + invocation = "To life, to life, I bring them!" else - user.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!") + invocation = initial(invocation) ..() revives_used++ mob_to_revive.revive(1, 1) //This does remove disabilities and such, but the rune might actually see some use because of it! diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm index 9645b4bbce8..cd3431406cf 100644 --- a/code/game/gamemodes/cult/talisman.dm +++ b/code/game/gamemodes/cult/talisman.dm @@ -28,7 +28,7 @@ . = successfuluse if(successfuluse) //if the calling whatever says we succeed, do the fancy stuff if(invocation) - user.whisper(invocation) + user.whisper(invocation, language = /datum/language/common) if(health_cost && iscarbon(user)) var/mob/living/carbon/C = user C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm")) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index fa93dbf61cf..348526abdec 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -257,6 +257,8 @@ if(escaped_total > 0) SSblackbox.set_val("escaped_total",escaped_total) send2irc("Server", "Round just ended.") + if(cult.len && !istype(SSticker.mode,/datum/game_mode/cult)) + datum_cult_completion() return 0 diff --git a/code/game/gamemodes/gang/gang.dm b/code/game/gamemodes/gang/gang.dm index 9c31edf55fb..190b5d0bc72 100644 --- a/code/game/gamemodes/gang/gang.dm +++ b/code/game/gamemodes/gang/gang.dm @@ -249,7 +249,8 @@ GLOBAL_LIST_INIT(gang_colors_pool, list("red","orange","yellow","green","blue"," return gang_bosses /proc/determine_domination_time(var/datum/gang/G) - return max(180,900 - (round((G.territory.len/GLOB.start_state.num_territories)*100, 1) * 12)) + return max(180,480 - (round((G.territory.len/GLOB.start_state.num_territories)*100, 1) * 9)) + ////////////////////////////////////////////////////////////////////// //Announces the end of the game with all relavent information stated// diff --git a/code/game/gamemodes/gang/gang_items.dm b/code/game/gamemodes/gang/gang_items.dm index a8732c10469..c0bb1824b2d 100644 --- a/code/game/gamemodes/gang/gang_items.dm +++ b/code/game/gamemodes/gang/gang_items.dm @@ -108,10 +108,16 @@ /datum/gang_item/weapon/ammo/get_cost_display(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool) return " ↳" + ..() //this is pretty hacky but it looks nice on the popup +/datum/gang_item/weapon/shuriken + name = "Shuriken" + id = "shuriken" + cost = 3 + item_path = /obj/item/weapon/throwing_star + /datum/gang_item/weapon/switchblade name = "Switchblade" id = "switchblade" - cost = 10 + cost = 5 item_path = /obj/item/weapon/switchblade /datum/gang_item/weapon/pistol @@ -125,6 +131,18 @@ id = "pistol_ammo" cost = 10 item_path = /obj/item/ammo_box/magazine/m10mm + +/datum/gang_item/weapon/sniper + name = ".50cal Sniper Rifle" + id = "sniper" + cost = 40 + item_path = /obj/item/weapon/gun/ballistic/automatic/sniper_rifle + +/datum/gang_item/weapon/ammo/sniper_ammo + name = "Standard .50cal Sniper Rounds" + id = "sniper_ammo" + cost = 15 + item_path = /obj/item/ammo_box/magazine/sniper_rounds /datum/gang_item/weapon/uzi name = "Uzi SMG" @@ -138,30 +156,8 @@ id = "uzi_ammo" cost = 40 item_path = /obj/item/ammo_box/magazine/uzim9mm - -//SLEEPING CARP - -/datum/gang_item/weapon/bostaff - name = "Bo Staff" - id = "bostaff" - cost = 10 - item_path = /obj/item/weapon/twohanded/bostaff - -/datum/gang_item/weapon/sleeping_carp_scroll - name = "Sleeping Carp Scroll (one-use)" - id = "sleeping_carp_scroll" - cost = 30 - item_path = /obj/item/weapon/sleeping_carp_scroll - spawn_msg = "Anyone who reads the sleeping carp scroll will learn secrets of the sleeping carp martial arts style." - -/datum/gang_item/weapon/wrestlingbelt - name = "Wrestling Belt" - id = "wrastling_belt" - cost = 20 - item_path = /obj/item/weapon/storage/belt/champion/wrestling - spawn_msg = "Anyone wearing the wresting belt will know how to be effective with wrestling." - - + + /////////////////// //EQUIPMENT /////////////////// @@ -175,6 +171,12 @@ id = "spraycan" cost = 5 item_path = /obj/item/toy/crayon/spraycan/gang + +/datum/gang_item/equipment/sharpener + name = "Sharpener" + id = "whetstone" + cost = 3 + item_path = /obj/item/weapon/sharpener /datum/gang_item/equipment/necklace name = "Gold Necklace" @@ -182,12 +184,31 @@ cost = 1 item_path = /obj/item/clothing/neck/necklace/dope + +/datum/gang_item/equipment/emp + name = "EMP Grenade" + id = "EMP" + cost = 5 + item_path = /obj/item/weapon/grenade/empgrenade + /datum/gang_item/equipment/c4 name = "C4 Explosive" id = "c4" - cost = 10 + cost = 7 item_path = /obj/item/weapon/grenade/plastic/c4 +/datum/gang_item/equipment/frag + name = "Fragmentation Grenade" + id = "frag nade" + cost = 10 + item_path = /obj/item/weapon/grenade/syndieminibomb/concussion/frag + +/datum/gang_item/equipment/stimpack + name = "Black Market Stimulants" + id = "stimpack" + cost = 15 + item_path = /obj/item/weapon/reagent_containers/syringe/stimulants + /datum/gang_item/equipment/implant_breaker name = "Implant Breaker" id = "implant_breaker" diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index a4296294844..45f7c5262a7 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -17,6 +17,7 @@ var/atom/movable/constant_target = null //The thing we're always focused on, if we're in the right mode var/target_x = 0 //The target coordinates if we're tracking those var/target_y = 0 + var/minimum_range = 0 //at what range the pinpointer declares you to be at your destination var/nuke_warning = FALSE // If we've set off a miniature alarm about an armed nuke var/mode = TRACK_NUKE_DISK //What are we looking for? @@ -129,7 +130,7 @@ if(here.z != there.z) icon_state = "pinon[nuke_warning ? "alert" : ""]null" return - if(here == there) + if(get_dist_euclidian(here,there)<=minimum_range) icon_state = "pinon[nuke_warning ? "alert" : ""]direct" else setDir(get_dir(here, there)) @@ -171,3 +172,6 @@ desc = "An integrated tracking device, jury-rigged to search for living Syndicate operatives." mode = TRACK_OPERATIVES flags = NODROP + + + diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 6ac0ab8c7c6..039b4cc3a45 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -67,7 +67,14 @@ /datum/objective/proc/update_explanation_text() //Default does nothing, override where needed -/datum/objective/proc/give_special_equipment() +/datum/objective/proc/give_special_equipment(special_equipment) + if(owner && owner.current) + if(ishuman(owner.current)) + var/mob/living/carbon/human/H = owner.current + var/list/slots = list ("backpack" = slot_in_backpack) + for(var/eq_path in special_equipment) + var/obj/O = new eq_path + H.equip_in_one_of_slots(O, slots) /datum/objective/assassinate var/target_role_type=0 @@ -94,6 +101,14 @@ else explanation_text = "Free Objective" +/datum/objective/assassinate/internal + var/stolen = 0 //Have we already eliminated this target? + +/datum/objective/assassinate/internal/update_explanation_text() + ..() + if(target && !target.current) + explanation_text = "Assassinate [target.name], who was obliterated" + /datum/objective/mutiny var/target_role_type=0 @@ -468,7 +483,7 @@ GLOBAL_LIST_EMPTY(possible_items) steal_target = targetinfo.targetitem explanation_text = "Steal [targetinfo.name]." dangerrating = targetinfo.difficulty - give_special_equipment() + give_special_equipment(targetinfo.special_equipment) return steal_target else explanation_text = "Free objective" @@ -511,15 +526,6 @@ GLOBAL_LIST_EMPTY(possible_items) return 1 return 0 -/datum/objective/steal/give_special_equipment() - if(owner && owner.current && targetinfo) - if(ishuman(owner.current)) - var/mob/living/carbon/human/H = owner.current - var/list/slots = list ("backpack" = slot_in_backpack) - for(var/eq_path in targetinfo.special_equipment) - var/obj/O = new eq_path - H.equip_in_one_of_slots(O, slots) - GLOBAL_LIST_EMPTY(possible_items_special) /datum/objective/steal/special //ninjas are so special they get their own subtype good for them @@ -695,6 +701,9 @@ GLOBAL_LIST_EMPTY(possible_items_special) explanation_text = "Destroy [target.name], the experimental AI." else explanation_text = "Free Objective" + +/datum/objective/destroy/internal + var/stolen = FALSE //Have we already eliminated this target? /datum/objective/steal_five_of_type explanation_text = "Steal at least five items!" diff --git a/code/game/gamemodes/traitor/double_agents.dm b/code/game/gamemodes/traitor/double_agents.dm index 5de3c2b418a..c869b7f9421 100644 --- a/code/game/gamemodes/traitor/double_agents.dm +++ b/code/game/gamemodes/traitor/double_agents.dm @@ -1,3 +1,7 @@ +#define PINPOINTER_MINIMUM_RANGE 15 +#define PINPOINTER_EXTRA_RANDOM_RANGE 10 +#define PINPOINTER_PING_TIME 40 + /datum/game_mode/traitor/internal_affairs name = "Internal Affairs" config_tag = "internal_affairs" @@ -18,15 +22,189 @@ var/list/target_list = list() var/list/late_joining_list = list() + /datum/game_mode/traitor/internal_affairs/post_setup() var/i = 0 for(var/datum/mind/traitor in traitors) i++ if(i + 1 > traitors.len) i = 0 - target_list[traitor] = traitors[i + 1] + target_list[traitor] = traitors[i+1] ..() + +/datum/status_effect/agent_pinpointer + id = "agent_pinpointer" + duration = -1 + tick_interval = PINPOINTER_PING_TIME + alert_type = /obj/screen/alert/status_effect/agent_pinpointer + var/minimum_range = PINPOINTER_MINIMUM_RANGE + var/mob/scan_target = null + +/obj/screen/alert/status_effect/agent_pinpointer + name = "Internal Affairs Integrated Pinpointer" + desc = "Even stealthier than a normal implant." + icon = 'icons/obj/device.dmi' + icon_state = "pinon" + +/datum/status_effect/agent_pinpointer/proc/point_to_target() //If we found what we're looking for, show the distance and direction + if(!scan_target) + linked_alert.icon_state = "pinonnull" + return + var/turf/here = get_turf(owner) + var/turf/there = get_turf(scan_target) + if(here.z != there.z) + linked_alert.icon_state = "pinonnull" + return + if(get_dist_euclidian(here,there)<=minimum_range + rand(0, PINPOINTER_EXTRA_RANDOM_RANGE)) + linked_alert.icon_state = "pinondirect" + else + linked_alert.setDir(get_dir(here, there)) + switch(get_dist(here, there)) + if(1 to 8) + linked_alert.icon_state = "pinonclose" + if(9 to 16) + linked_alert.icon_state = "pinonmedium" + if(16 to INFINITY) + linked_alert.icon_state = "pinonfar" + + +/datum/status_effect/agent_pinpointer/proc/scan_for_target() + scan_target = null + if(owner) + if(owner.mind) + if(owner.mind.objectives) + for(var/datum/objective/objective_ in owner.mind.objectives) + if(!is_internal_objective(objective_)) + continue + var/datum/objective/assassinate/internal/objective = objective_ + var/mob/current = objective.target.current + if(current&¤t.stat!=DEAD) + scan_target = current + break + + +/datum/status_effect/agent_pinpointer/tick() + if(!owner) + qdel(src) + return + scan_for_target() + point_to_target() + +/proc/give_pinpointer(datum/mind/owner) + if(owner && owner.current) + owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer) + + +/datum/internal_agent_state + var/traitored = FALSE + var/datum/mind/owner = null + var/list/datum/mind/targets_stolen = list() + +/proc/is_internal_objective(datum/objective/O) + return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal)) + +/proc/replace_escape_objective(datum/mind/owner) + if(!owner||!owner.objectives) + return + for (var/objective_ in owner.objectives) + if(!(istype(objective_, /datum/objective/escape)||istype(objective_,/datum/objective/survive))) + continue + owner.objectives -= objective_ + var/datum/objective/martyr/martyr_objective = new + martyr_objective.owner = owner + owner.objectives += martyr_objective + +/proc/reinstate_escape_objective(datum/mind/owner) + if(!owner||!owner.objectives) + return + for (var/objective_ in owner.objectives) + if(!istype(objective_, /datum/objective/martyr)) + continue + owner.objectives -= objective_ + if(issilicon(owner)) + var/datum/objective/survive/survive_objective = new + survive_objective.owner = owner + owner.objectives += survive_objective + else + var/datum/objective/escape/escape_objective = new + escape_objective.owner = owner + owner.objectives += escape_objective + +/datum/internal_agent_state/proc/steal_targets(datum/mind/victim) + if(!owner.current||owner.current.stat==DEAD) //Should already be guaranteed if this is only called from steal_targets_timer_func, but better to be safe code than sorry code + return + var/already_traitored = traitored + to_chat(owner.current, " Target eliminated: [victim.name]") + for(var/objective_ in victim.objectives) + if(istype(objective_, /datum/objective/assassinate/internal)) + var/datum/objective/assassinate/internal/objective = objective_ + if(objective.target==owner) + traitored = TRUE + else if(targets_stolen.Find(objective.target) == 0) + var/datum/objective/assassinate/internal/new_objective = new + new_objective.owner = owner + new_objective.target = objective.target + new_objective.update_explanation_text() + owner.objectives += new_objective + targets_stolen += objective.target + var/status_text = objective.check_completion() ? "neutralised" : "active" + to_chat(owner.current, " New target added to database: [objective.target.name] ([status_text]) ") + else if(istype(objective_, /datum/objective/destroy/internal)) + var/datum/objective/destroy/internal/objective = objective_ + var/datum/objective/destroy/internal/new_objective = new + if(objective.target==owner) + traitored = TRUE + else if(targets_stolen.Find(objective.target) == 0) + new_objective.owner = owner + new_objective.target = objective.target + new_objective.update_explanation_text() + owner.objectives += new_objective + targets_stolen += objective.target + var/status_text = objective.check_completion() ? "neutralised" : "active" + to_chat(owner.current, " New target added to database: [objective.target.name] ([status_text]) ") + if(traitored&&!already_traitored) + for(var/objective_ in owner.objectives) + if(!is_internal_objective(objective_)) + continue + var/datum/objective/assassinate/internal/objective = objective_ + if(!objective.check_completion()) + traitored = FALSE + return + to_chat(owner.current," All the other agents are dead, and you're the last loose end. Stage a Syndicate terrorist attack to cover up for today's events. You no longer have any limits on collateral damage.") + replace_escape_objective(owner) + + + +/datum/internal_agent_state/proc/steal_targets_timer_func() + if(owner&&owner.current&&owner.current.stat!=DEAD) + for(var/objective_ in owner.objectives) + if(!is_internal_objective(objective_)) + continue + var/datum/objective/assassinate/internal/objective = objective_ + if(!objective.target) + continue + if(objective.check_completion()) + if(objective.stolen) + continue + else + steal_targets(objective.target) + objective.stolen = TRUE + else + if(objective.stolen) + var/fail_msg = "Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! " + if(traitored) + fail_msg += " The truth could still slip out! Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to your contract being terminated." + reinstate_escape_objective(owner) + traitored = FALSE + to_chat(owner.current, fail_msg) + objective.stolen = FALSE + add_steal_targets_timer(owner) + +/datum/internal_agent_state/proc/add_steal_targets_timer() + var/datum/callback/C = new(src, .steal_targets_timer_func) + addtimer(C, 30) + /datum/game_mode/traitor/internal_affairs/forge_traitor_objectives(datum/mind/traitor) if(target_list.len && target_list[traitor]) // Is a double agent @@ -34,13 +212,13 @@ // Assassinate var/datum/mind/target_mind = target_list[traitor] if(issilicon(target_mind.current)) - var/datum/objective/destroy/destroy_objective = new + var/datum/objective/destroy/internal/destroy_objective = new destroy_objective.owner = traitor destroy_objective.target = target_mind destroy_objective.update_explanation_text() traitor.objectives += destroy_objective else - var/datum/objective/assassinate/kill_objective = new + var/datum/objective/assassinate/internal/kill_objective = new kill_objective.owner = traitor kill_objective.target = target_mind kill_objective.update_explanation_text() @@ -55,6 +233,11 @@ var/datum/objective/escape/escape_objective = new escape_objective.owner = traitor traitor.objectives += escape_objective + var/datum/internal_agent_state/state = new + state.owner=traitor + state.add_steal_targets_timer() + if(!issilicon(traitor.current)) + give_pinpointer(traitor) else ..() // Give them standard objectives. @@ -106,14 +289,18 @@ /datum/game_mode/traitor/internal_affairs/greet_traitor(datum/mind/traitor) var/crime = pick("distribution of contraband" , "unauthorized erotic action on duty", "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "recieving bribes", "malpractice", "worship of prohbited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence") - to_chat(traitor.current, "You are the [traitor_name].") - to_chat(traitor.current, "Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial.") + to_chat(traitor.current, "You are the [traitor_name].") + to_chat(traitor.current, "Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial.") to_chat(traitor.current, "While you have a license to kill, unneeded property damage or loss of employee life will lead to your contract being terminated.") - to_chat(traitor.current, "For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink.") - to_chat(traitor.current, "Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.") + to_chat(traitor.current, "For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink.") + to_chat(traitor.current, "Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.") traitor.announce_objectives() /datum/game_mode/traitor/internal_affairs/give_codewords(mob/living/traitor_mob) - return \ No newline at end of file + return + +#undef PINPOINTER_EXTRA_RANDOM_RANGE +#undef PINPOINTER_MINIMUM_RANGE +#undef PINPOINTER_PING_TIME diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm index 7ccf7a6b7e1..09412a79ac7 100644 --- a/code/game/gamemodes/wizard/soulstone.dm +++ b/code/game/gamemodes/wizard/soulstone.dm @@ -54,7 +54,7 @@ if(!ishuman(M))//If target is not a human. return ..() if(iscultist(M)) - to_chat(user, "\"Come now, do not capture your fellow's soul.\"") + to_chat(user, "\"Come now, do not capture your bretheren's soul.\"") return add_logs(user, M, "captured [M.name]'s soul", src) @@ -132,11 +132,11 @@ if("VICTIM") var/mob/living/carbon/human/T = target - if(SSticker.mode.name == "cult" && T.mind == SSticker.mode:sacrifice_target) + if(is_sacrifice_target(T.mind)) if(iscultist(user)) to_chat(user, "\"This soul is mine. SACRIFICE THEM!\"") else - to_chat(user, "The soulstone doesn't work for no apparent reason.") + to_chat(user, "The soulstone seems to reject this soul.") return 0 if(contents.len) to_chat(user, "Capture failed!: The soulstone is full! Free an existing soul to make room.") @@ -188,7 +188,10 @@ else makeNewConstruct(/mob/living/simple_animal/hostile/construct/builder/noncult, A, user, 0, T.loc) - + for(var/datum/mind/B in SSticker.mode.cult) + if(B == A.mind) + SSticker.mode.cult -= A.mind + SSticker.mode.update_cult_icons_removed(A.mind) qdel(T) user.drop_item() qdel(src) @@ -200,6 +203,9 @@ var/mob/living/simple_animal/hostile/construct/newstruct = new ctype((loc_override) ? (loc_override) : (get_turf(target))) if(stoner) newstruct.faction |= "\ref[stoner]" + newstruct.master = stoner + var/datum/action/innate/seek_master/SM = new() + SM.Grant(newstruct) newstruct.key = target.key if(newstruct.mind && ((stoner && iscultist(stoner)) || cultoverride) && SSticker && SSticker.mode) SSticker.mode.add_cultist(newstruct.mind, 0) @@ -207,6 +213,9 @@ to_chat(newstruct, "You are still bound to serve the cult[stoner ? " and [stoner]":""], follow their orders and help them complete their goals at all costs.") else if(stoner) to_chat(newstruct, "You are still bound to serve your creator, [stoner], follow their orders and help them complete their goals at all costs.") + newstruct.throw_alert("bloodsense", /obj/screen/alert/bloodsense) + var/obj/screen/alert/bloodsense/BS = newstruct.alerts["bloodsense"] + BS.Cviewer = newstruct newstruct.cancel_camera() @@ -244,7 +253,7 @@ break if(!chosen_ghost) //Failing that, we grab a ghost - var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", "Cultist", null, ROLE_CULTIST, poll_time = 50) + var/list/consenting_candidates = pollGhostCandidates("Would you like to play as a Shade?", "Cultist", null, ROLE_CULTIST, poll_time = 50) if(consenting_candidates.len) chosen_ghost = pick(consenting_candidates) if(!T) diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 21fe22ee58a..fed1b370288 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -159,10 +159,6 @@ name = "Turbine Computer (Computer Board)" build_path = /obj/machinery/computer/turbine_computer origin_tech = "programming=4;engineering=4;powerstorage=4" -/obj/item/weapon/circuitboard/computer/telesci_console - name = "Telescience Console (Computer Board)" - build_path = /obj/machinery/computer/telescience - origin_tech = "programming=3;bluespace=3;plasmatech=4" /obj/item/weapon/circuitboard/computer/message_monitor name = "Message Monitor (Computer Board)" build_path = /obj/machinery/computer/message_monitor diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 209f311a47d..c08debe15bf 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -56,6 +56,7 @@ var/dish_quants = list() //used by the snack machine's custom compartment to count dishes. var/obj/item/weapon/vending_refill/refill_canister = null //The type of refill canisters used by this machine. + var/refill_count = 3 //The number of canisters the vending machine uses /obj/machinery/vending/Initialize() ..() @@ -88,7 +89,9 @@ /obj/machinery/vending/cola = "Robust Softdrinks", /obj/machinery/vending/cigarette = "ShadyCigs Deluxe", /obj/machinery/vending/autodrobe = "AutoDrobe", - /obj/machinery/vending/clothing = "ClothesMate") + /obj/machinery/vending/clothing = "ClothesMate", + /obj/machinery/vending/medical = "NanoMed Plus", + /obj/machinery/vending/wallmed = "NanoMed") /obj/item/weapon/circuitboard/machine/vendor/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/weapon/screwdriver)) @@ -104,7 +107,7 @@ /obj/item/weapon/circuitboard/machine/vendor/proc/set_type(var/obj/machinery/vending/typepath) build_path = typepath name = "[names_paths[build_path]] Vendor (Machine Board)" - req_components = list(initial(typepath.refill_canister) = 3) + req_components = list(initial(typepath.refill_canister) = initial(typepath.refill_count)) /obj/item/weapon/circuitboard/machine/vendor/apply_default_parts(obj/machinery/M) for(var/typepath in names_paths) @@ -113,7 +116,6 @@ break ..() - /obj/machinery/vending/Destroy() qdel(wires) wires = null @@ -877,6 +879,7 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C premium = list(/obj/item/weapon/storage/box/hug/medical = 1,/obj/item/weapon/reagent_containers/hypospray/medipen = 3, /obj/item/weapon/storage/belt/medical = 3, /obj/item/weapon/wrench/medical = 1) armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) resistance_flags = FIRE_PROOF + refill_canister = /obj/item/weapon/vending_refill/medical //This one's from bay12 /obj/machinery/vending/plasmaresearch @@ -899,6 +902,8 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C contraband = list(/obj/item/weapon/reagent_containers/pill/tox = 2,/obj/item/weapon/reagent_containers/pill/morphine = 2) armor = list(melee = 100, bullet = 100, laser = 100, energy = 100, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 50) resistance_flags = FIRE_PROOF + refill_canister = /obj/item/weapon/vending_refill/medical + refill_count = 1 /obj/machinery/vending/security name = "\improper SecTech" diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 20cb7aff0b8..1d7d9072c93 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -107,7 +107,7 @@ icon_state = "mecha_ion" origin_tech = "materials=4;engineering=4;combat=6;magnets=6" energy_drain = 500 - projectile = /obj/item/projectile/energy/tesla_cannon + projectile = /obj/item/projectile/energy/tesla/cannon fire_sound = 'sound/magic/lightningbolt.ogg' diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index a6517ccdb5a..e6af7398938 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -230,7 +230,15 @@ name = "blood sparks" icon_state = "bloodsparkles" -/obj/effect/overlay/temp/dir_setting/cult/phase +/obj/effect/overlay/temp/cult/blood // The traditional teleport + name = "blood jaunt" + duration = 12 + icon_state = "bloodin" + +/obj/effect/overlay/temp/cult/blood/out + icon_state = "bloodout" + +/obj/effect/overlay/temp/dir_setting/cult/phase // The veil shifter teleport name = "phase glow" duration = 7 icon_state = "cultin" diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm index b02f4219562..b9b899b4556 100644 --- a/code/game/objects/items/stacks/sheets/leather.dm +++ b/code/game/objects/items/stacks/sheets/leather.dm @@ -118,6 +118,9 @@ GLOBAL_LIST_INIT(xeno_recipes, list ( \ var/wetness = 30 //Reduced when exposed to high temperautres var/drying_threshold_temperature = 500 //Kelvin to start drying +/* + * Leather SHeet + */ /obj/item/stack/sheet/leather name = "leather" desc = "The by-product of mob grinding." @@ -125,6 +128,24 @@ GLOBAL_LIST_INIT(xeno_recipes, list ( \ icon_state = "sheet-leather" origin_tech = "materials=2" +GLOBAL_LIST_INIT(leather_recipes, list ( \ + new/datum/stack_recipe("wallet", /obj/item/weapon/storage/wallet, 1), \ + new/datum/stack_recipe("muzzle", /obj/item/clothing/mask/muzzle, 2), \ + new/datum/stack_recipe("botany gloves", /obj/item/clothing/gloves/botanic_leather, 3), \ + new/datum/stack_recipe("toolbelt", /obj/item/weapon/storage/belt/utility, 4), \ + new/datum/stack_recipe("leather satchel", /obj/item/weapon/storage/backpack/satchel, 5), \ + new/datum/stack_recipe("bandolier", /obj/item/weapon/storage/belt/bandolier, 5), \ + new/datum/stack_recipe("leather jacket", /obj/item/clothing/suit/jacket/leather, 7), \ + new/datum/stack_recipe("leather overcoat", /obj/item/clothing/suit/jacket/leather/overcoat, 10), \ +)) + +/obj/item/stack/sheet/leather/Initialize(mapload, new_amount, merge = TRUE) + recipes = GLOB.leather_recipes + return ..() + +/* + * Sinew + */ /obj/item/stack/sheet/sinew name = "watcher sinew" icon = 'icons/obj/mining.dmi' @@ -136,11 +157,12 @@ GLOBAL_LIST_INIT(xeno_recipes, list ( \ GLOBAL_LIST_INIT(sinew_recipes, list ( \ new/datum/stack_recipe("sinew restraints", /obj/item/weapon/restraints/handcuffs/sinew, 1, on_floor = 1), \ - )) +)) /obj/item/stack/sheet/sinew/Initialize(mapload, new_amount, merge = TRUE) recipes = GLOB.sinew_recipes return ..() + /* * Plates */ @@ -207,4 +229,4 @@ GLOBAL_LIST_INIT(sinew_recipes, list ( \ var/obj/item/stack/sheet/leather/HS = new(src.loc) HS.amount = 1 wetness = initial(wetness) - src.use(1) \ No newline at end of file + src.use(1) diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm deleted file mode 100644 index 404c40c4b6a..00000000000 --- a/code/game/objects/items/weapons/explosives.dm +++ /dev/null @@ -1,115 +0,0 @@ -//In this file: C4 - -/obj/item/weapon/c4 - name = "C-4" - desc = "Used to put holes in specific areas without too much extra hole." - gender = PLURAL - icon = 'icons/obj/grenade.dmi' - icon_state = "plastic-explosive0" - item_state = "plasticx" - flags = NOBLUDGEON - w_class = WEIGHT_CLASS_SMALL - origin_tech = "syndicate=1" - var/timer = 10 - var/open_panel = 0 - parent_type = /obj/item/weapon/grenade/plastic/c4 - -/obj/item/weapon/c4/New() - wires = new /datum/wires/explosive/c4(src) - plastic_overlay = mutable_appearance(icon, "plastic-explosive2") - ..() - -/obj/item/weapon/c4/Destroy() - qdel(wires) - wires = null - target = null - return ..() - -/obj/item/weapon/c4/suicide_act(mob/user) - user.visible_message("[user] activates the [src.name] and holds it above [user.p_their()] head! It looks like [user.p_theyre()] going out with a bang!") - var/message_say = "FOR NO RAISIN!" - if(user.mind) - if(user.mind.special_role) - var/role = lowertext(user.mind.special_role) - if(role == "traitor" || role == "syndicate") - message_say = "FOR THE SYNDICATE!" - else if(role == "changeling") - message_say = "FOR THE HIVE!" - else if(role == "cultist") - message_say = "FOR NAR-SIE!" - else if(role == "revolutionary" || role == "head revolutionary") - message_say = "VIVA LA REVOLUTION!" - else if(user.mind.gang_datum) - message_say = "[uppertext(user.mind.gang_datum.name)] RULES!" - user.say(message_say) - target = user - message_admins("[ADMIN_LOOKUPFLW(user)] suicided with [name] at [ADMIN_COORDJMP(src)]",0,1) - message_admins("[key_name(user)] suicided with [name] at ([x],[y],[z])") - sleep(10) - explode(get_turf(user)) - user.gib(1, 1) - -/obj/item/weapon/c4/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/weapon/screwdriver)) - open_panel = !open_panel - to_chat(user, "You [open_panel ? "open" : "close"] the wire panel.") - else if(is_wire_tool(I)) - wires.interact(user) - else - return ..() - -/obj/item/weapon/c4/attack_self(mob/user) - var/newtime = input(usr, "Please set the timer.", "Timer", 10) as num - if(user.get_active_held_item() == src) - newtime = Clamp(newtime, 10, 60000) - timer = newtime - to_chat(user, "Timer set for [timer] seconds.") - -/obj/item/weapon/c4/afterattack(atom/movable/AM, mob/user, flag) - if (!flag) - return - if (ismob(AM)) - return - if(loc == AM) - return - if((istype(AM, /obj/item/weapon/storage/)) && !((istype(AM, /obj/item/weapon/storage/secure)) || (istype(AM, /obj/item/weapon/storage/lockbox)))) //If its storage but not secure storage OR a lockbox, then place it inside. - return - if((istype(AM,/obj/item/weapon/storage/secure)) || (istype(AM, /obj/item/weapon/storage/lockbox))) - var/obj/item/weapon/storage/secure/S = AM - if(!S.locked) //Literal hacks, this works for lockboxes despite incorrect type casting, because they both share the locked var. But if its unlocked, place it inside, otherwise PLANTING C4! - return - - to_chat(user, "You start planting the bomb...") - - if(do_after(user, 50, target = AM)) - if(!user.temporarilyRemoveItemFromInventory(src)) - return - src.target = AM - forceMove(null) - - var/message = "[ADMIN_LOOKUPFLW(user)] planted [name] on [target.name] at [ADMIN_COORDJMP(target)] with [timer] second fuse" - GLOB.bombers += message - message_admins(message,0,1) - log_game("[key_name(user)] planted [name] on [target.name] at [COORD(target)] with [timer] second fuse") - - target.add_overlay(plastic_overlay, 1) - to_chat(user, "You plant the bomb. Timer counting down from [timer].") - addtimer(CALLBACK(src, .proc/explode), timer * 10) - -/obj/item/weapon/c4/proc/explode() - if(QDELETED(src)) - return - var/turf/location - if(target) - if(!QDELETED(target)) - location = get_turf(target) - target.cut_overlay(plastic_overlay, TRUE) - else - location = get_turf(src) - if(location) - location.ex_act(2, target) - explosion(location,0,0,3) - qdel(src) - -/obj/item/weapon/c4/attack(mob/M, mob/user, def_zone) - return diff --git a/code/game/objects/items/weapons/grenades/plastic.dm b/code/game/objects/items/weapons/grenades/plastic.dm index fb5b92aee2e..3118d731f22 100644 --- a/code/game/objects/items/weapons/grenades/plastic.dm +++ b/code/game/objects/items/weapons/grenades/plastic.dm @@ -96,7 +96,7 @@ to_chat(user, "You start planting the [src]. The timer is set to [det_time]...") - if(do_after(user, 50, target = AM)) + if(do_after(user, 30, target = AM)) if(!user.temporarilyRemoveItemFromInventory(src)) return src.target = AM @@ -154,6 +154,115 @@ /obj/item/weapon/grenade/plastic/c4 name = "C4" desc = "Used to put holes in specific areas without too much extra hole. A saboteur's favorite." + gender = PLURAL + icon = 'icons/obj/grenade.dmi' + icon_state = "plastic-explosive0" + item_state = "plasticx" + flags = NOBLUDGEON + w_class = WEIGHT_CLASS_SMALL + origin_tech = "syndicate=1" + var/timer = 10 + var/open_panel = 0 + +/obj/item/weapon/grenade/plastic/c4/New() + wires = new /datum/wires/explosive/c4(src) + plastic_overlay = mutable_appearance(icon, "plastic-explosive2") + ..() + +/obj/item/weapon/grenade/plastic/c4/Destroy() + qdel(wires) + wires = null + target = null + return ..() + +/obj/item/weapon/grenade/plastic/c4/suicide_act(mob/user) + user.visible_message("[user] activates the [src.name] and holds it above [user.p_their()] head! It looks like [user.p_theyre()] going out with a bang!") + var/message_say = "FOR NO RAISIN!" + if(user.mind) + if(user.mind.special_role) + var/role = lowertext(user.mind.special_role) + if(role == "traitor" || role == "syndicate") + message_say = "FOR THE SYNDICATE!" + else if(role == "changeling") + message_say = "FOR THE HIVE!" + else if(role == "cultist") + message_say = "FOR NAR-SIE!" + else if(role == "revolutionary" || role == "head revolutionary") + message_say = "VIVA LA REVOLUTION!" + else if(user.mind.gang_datum) + message_say = "[uppertext(user.mind.gang_datum.name)] RULES!" + user.say(message_say) + target = user + message_admins("[ADMIN_LOOKUPFLW(user)] suicided with [name] at [ADMIN_COORDJMP(src)]",0,1) + message_admins("[key_name(user)] suicided with [name] at ([x],[y],[z])") + sleep(10) + explode(get_turf(user)) + user.gib(1, 1) + +/obj/item/weapon/grenade/plastic/c4/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/weapon/screwdriver)) + open_panel = !open_panel + to_chat(user, "You [open_panel ? "open" : "close"] the wire panel.") + else if(is_wire_tool(I)) + wires.interact(user) + else + return ..() + +/obj/item/weapon/grenade/plastic/c4/attack_self(mob/user) + var/newtime = input(usr, "Please set the timer.", "Timer", 10) as num + if(user.get_active_held_item() == src) + newtime = Clamp(newtime, 10, 60000) + timer = newtime + to_chat(user, "Timer set for [timer] seconds.") + +/obj/item/weapon/grenade/plastic/c4/afterattack(atom/movable/AM, mob/user, flag) + if (!flag) + return + if (ismob(AM)) + return + if(loc == AM) + return + if((istype(AM, /obj/item/weapon/storage/)) && !((istype(AM, /obj/item/weapon/storage/secure)) || (istype(AM, /obj/item/weapon/storage/lockbox)))) //If its storage but not secure storage OR a lockbox, then place it inside. + return + if((istype(AM,/obj/item/weapon/storage/secure)) || (istype(AM, /obj/item/weapon/storage/lockbox))) + var/obj/item/weapon/storage/secure/S = AM + if(!S.locked) //Literal hacks, this works for lockboxes despite incorrect type casting, because they both share the locked var. But if its unlocked, place it inside, otherwise PLANTING C4! + return + + to_chat(user, "You start planting the bomb...") + + if(do_after(user, 50, target = AM)) + if(!user.temporarilyRemoveItemFromInventory(src)) + return + src.target = AM + forceMove(null) + + var/message = "[ADMIN_LOOKUPFLW(user)] planted [name] on [target.name] at [ADMIN_COORDJMP(target)] with [timer] second fuse" + GLOB.bombers += message + message_admins(message,0,1) + log_game("[key_name(user)] planted [name] on [target.name] at [COORD(target)] with [timer] second fuse") + + target.add_overlay(plastic_overlay, 1) + to_chat(user, "You plant the bomb. Timer counting down from [timer].") + addtimer(CALLBACK(src, .proc/explode), timer * 10) + +/obj/item/weapon/grenade/plastic/c4/proc/explode() + if(QDELETED(src)) + return + var/turf/location + if(target) + if(!QDELETED(target)) + location = get_turf(target) + target.cut_overlay(plastic_overlay, TRUE) + else + location = get_turf(src) + if(location) + location.ex_act(2, target) + explosion(location,0,0,3) + qdel(src) + +/obj/item/weapon/grenade/plastic/c4/attack(mob/M, mob/user, def_zone) + return // X4 is an upgraded directional variant of c4 which is relatively safe to be standing next to. And much less safe to be standing on the other side of. // C4 is intended to be used for infiltration, and destroying tech. X4 is intended to be used for heavy breaching and tight spaces. @@ -165,4 +274,4 @@ icon_state = "plasticx40" item_state = "plasticx4" directional = TRUE - boom_sizes = list(0, 2, 5) \ No newline at end of file + boom_sizes = list(0, 2, 5) diff --git a/code/game/objects/items/weapons/his_grace.dm b/code/game/objects/items/weapons/his_grace.dm index be269d583d8..d8e7dabdf99 100644 --- a/code/game/objects/items/weapons/his_grace.dm +++ b/code/game/objects/items/weapons/his_grace.dm @@ -41,7 +41,7 @@ ..() /obj/item/weapon/his_grace/CtrlClick(mob/user) //you can't pull his grace - attack_hand(user) + return /obj/item/weapon/his_grace/examine(mob/user) ..() diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm index aa720f19795..4b5acd949d3 100644 --- a/code/game/objects/items/weapons/holy_weapons.dm +++ b/code/game/objects/items/weapons/holy_weapons.dm @@ -214,7 +214,7 @@ possessed = TRUE - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE) var/mob/dead/observer/theghost = null if(LAZYLEN(candidates)) diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index d4bfd8b0eac..7b8d9861c7d 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -296,7 +296,7 @@ /obj/item/weapon/lighter, /obj/item/device/multitool, /obj/item/weapon/reagent_containers/food/drinks/bottle/molotov, - /obj/item/weapon/c4, + /obj/item/weapon/grenade/plastic/c4, ) /obj/item/weapon/storage/belt/grenade/full/PopulateContents() new /obj/item/weapon/grenade/flashbang(src) diff --git a/code/game/objects/items/weapons/storage/internal.dm b/code/game/objects/items/weapons/storage/internal.dm index 540ff0f206c..cfdc6cc3547 100644 --- a/code/game/objects/items/weapons/storage/internal.dm +++ b/code/game/objects/items/weapons/storage/internal.dm @@ -5,10 +5,6 @@ w_class = WEIGHT_CLASS_BULKY rustle_jimmies = FALSE -/obj/item/weapon/storage/internal/ClickAccessible(mob/user, depth=1) - if(loc) - return loc.ClickAccessible(user, depth) - /obj/item/weapon/storage/internal/Adjacent(A) if(loc) return loc.Adjacent(A) diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index 8c4edf19bfc..9399622acdf 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -38,7 +38,7 @@ return // this must come before the screen objects only block, dunno why it wasn't before - if(over_object == M && (src.ClickAccessible(M, depth=STORAGE_VIEW_DEPTH) || Adjacent(M))) + if(over_object == M && M.CanReach(src,view_only = TRUE)) orient2hud(M) if(M.s_active) M.s_active.close(M) diff --git a/code/game/objects/items/weapons/vending_items.dm b/code/game/objects/items/weapons/vending_items.dm index 366c08cbf7e..5424e45044c 100644 --- a/code/game/objects/items/weapons/vending_items.dm +++ b/code/game/objects/items/weapons/vending_items.dm @@ -71,3 +71,9 @@ icon_state = "refill_clothes" charges = list(31, 4, 4)// of 101 standard, 12 contraband, 10 premium(?) init_charges = list(31, 4, 4) + +/obj/item/weapon/vending_refill/medical + machine_name = "NanoMed" + icon_state = "refill_medical" + charges = list(26, 5, 3)// of 76 standard, 13 contraband, 8 premium + init_charges = list(26, 5, 3) \ No newline at end of file diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index 3d4cfaee93d..597efa8e120 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -11,8 +11,9 @@ max_integrity = 200 integrity_failure = 50 var/obj/item/showpiece = null - var/alert = 0 - var/open = 0 + var/alert = TRUE + var/open = FALSE + var/openable = TRUE var/obj/item/weapon/electronics/airlock/electronics var/start_showpiece_type = null //add type for items on display @@ -113,7 +114,7 @@ return /obj/structure/displaycase/attackby(obj/item/weapon/W, mob/user, params) - if(W.GetID() && !broken) + if(W.GetID() && !broken && openable) if(allowed(user)) to_chat(user, "You [open ? "close":"open"] the [src]") toggle_lock(user) @@ -132,7 +133,7 @@ else to_chat(user, "[src] is already in good condition!") return - else if(!alert && istype(W,/obj/item/weapon/crowbar)) //Only applies to the lab cage and player made display cases + else if(!alert && istype(W,/obj/item/weapon/crowbar) && openable) //Only applies to the lab cage and player made display cases if(broken) if(showpiece) to_chat(user, "Remove the displayed object first.") @@ -259,6 +260,7 @@ alert = TRUE integrity_failure = 0 + openable = FALSE /obj/structure/displaycase/trophy/Initialize() . = ..() @@ -278,6 +280,8 @@ if(!user.Adjacent(src)) //no TK museology return + if(user.a_intent == INTENT_HARM) + return ..() if(user.is_holding_item_of_type(/obj/item/key/displaycase)) if(added_roundstart) diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm index d087b143905..e9993bf0064 100644 --- a/code/game/objects/structures/ghost_role_spawners.dm +++ b/code/game/objects/structures/ghost_role_spawners.dm @@ -48,6 +48,11 @@ /obj/effect/mob_spawn/human/ash_walker/special(mob/living/new_spawn) new_spawn.real_name = random_unique_lizard_name(gender) to_chat(new_spawn, "Drag the corpses of men and beasts to your nest. It will absorb them to create more of your kind. Glory to the Necropolis!") + + new_spawn.grant_language(/datum/language/draconic) + var/datum/language_holder/holder = new_spawn.get_language_holder() + holder.selected_default_language = /datum/language/draconic + if(ishuman(new_spawn)) var/mob/living/carbon/human/H = new_spawn H.underwear = "Nude" diff --git a/code/modules/admin/DB_ban/functions.dm b/code/modules/admin/DB_ban/functions.dm index 37f4ea8835d..86688d8ba7a 100644 --- a/code/modules/admin/DB_ban/functions.dm +++ b/code/modules/admin/DB_ban/functions.dm @@ -71,16 +71,13 @@ computerid = bancid ip = banip - var/datum/DBQuery/query_add_ban_get_id = SSdbcore.NewQuery("SELECT id FROM [format_table_name("player")] WHERE ckey = '[ckey]'") - if(!query_add_ban_get_id.warn_execute()) + var/datum/DBQuery/query_add_ban_get_ckey = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[ckey]'") + if(!query_add_ban_get_ckey.warn_execute()) return - var/validckey = 0 - if(query_add_ban_get_id.NextRow()) - validckey = 1 - if(!validckey) + if(!query_add_ban_get_ckey.NextRow()) if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key))) - message_admins("[key_name_admin(usr)] attempted to ban [ckey], but [ckey] has not been seen yet. Please only ban actual players.",1) - return + if(alert(usr, "[ckey] has not been seen before, are you sure you want to create a ban for them?", "Unknown ckey", "Yes", "No", "Cancel") != "Yes") + return var/a_ckey var/a_computerid diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index e8803868fef..13f7c799a5b 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -37,6 +37,10 @@ else body += " \[Heal\] " + if(M.client) + body += "
\[Player Age: [M.client.player_age]\]\[Byond Age: [M.client.account_age]\]" + + body += "

\[ " body += "VV - " body += "TP - " diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 469581f4b77..728db7372cb 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -4,18 +4,12 @@ GLOBAL_PROTECT(admin_verbs_default) GLOBAL_LIST_INIT(admin_verbs_default, world.AVerbsDefault()) /world/proc/AVerbsDefault() return list( - /client/proc/toggleadminhelpsound, /*toggles whether we hear a sound when adminhelps/PMs are used*/ - /client/proc/toggleannouncelogin, /*toggles if an admin's login is announced during a round*/ /client/proc/deadmin, /*destroys our own admin datum so we can play as a regular player*/ /client/proc/cmd_admin_say, /*admin-only ooc chat*/ /client/proc/hide_verbs, /*hides all our adminverbs*/ /client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/ /client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/ - /client/proc/deadchat, /*toggles deadchat on/off*/ /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/ - /client/proc/toggleprayers, /*toggles prayers on/off*/ - /client/verb/toggleprayersounds, /*Toggles prayer sounds (HALLELUJAH!)*/ - /client/proc/toggle_hear_radio, /*toggles whether we hear the radio*/ /client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/ /client/proc/secrets, /client/proc/reload_admins, @@ -29,7 +23,6 @@ GLOBAL_PROTECT(admin_verbs_admin) GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin()) /world/proc/AVerbsAdmin() return list( - /client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/ /client/proc/invisimin, /*allows our mob to go invisible/visible*/ // /datum/admins/proc/show_traitor_panel, /*interface which shows a mob's mind*/ -Removed due to rare practical use. Moved to debug verbs ~Errorage /datum/admins/proc/show_player_panel, /*shows an interface for individual players, with various links (links require additional flags*/ @@ -172,9 +165,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( /client/proc/set_ooc, /client/proc/reset_ooc, /client/proc/deadmin, - /client/proc/deadchat, - /client/proc/toggleprayers, - /client/proc/toggle_hear_radio, /datum/admins/proc/show_traitor_panel, /datum/admins/proc/toggleenter, /datum/admins/proc/toggleguests, @@ -387,13 +377,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( mob.invisibility = INVISIBILITY_OBSERVER to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") -/client/proc/player_panel_new() - set name = "Player Panel" - set category = "Admin" - if(holder) - holder.player_panel_new() - SSblackbox.add_details("admin_verb","Player Panel New") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - /client/proc/check_antagonists() set name = "Check Antagonists" set category = "Admin" diff --git a/code/modules/admin/adminmenu.dm b/code/modules/admin/adminmenu.dm new file mode 100644 index 00000000000..fc7a57e212b --- /dev/null +++ b/code/modules/admin/adminmenu.dm @@ -0,0 +1,11 @@ +/datum/menu/Admin/Generate_list(client/C) + if (C.holder) + . = ..() + +/datum/menu/Admin/verb/playerpanel() + set name = "Player Panel" + set desc = "Player Panel" + set category = "Admin" + usr.client.holder.player_panel_new() + SSblackbox.add_details("admin_verb","Player Panel New") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return \ No newline at end of file diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index d7b06ee9c87..87a9e5a040a 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -132,7 +132,7 @@ /datum/admins/proc/makeWizard() - var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard", null) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard", null) var/mob/dead/observer/selected = pick_n_take(candidates) @@ -215,7 +215,7 @@ /datum/admins/proc/makeNukeTeam() var/datum/game_mode/nuclear/temp = new - var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for a nuke team being sent in?", "operative", temp) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a nuke team being sent in?", "operative", temp) var/list/mob/dead/observer/chosen = list() var/mob/dead/observer/theghost = null @@ -288,7 +288,7 @@ // DEATH SQUADS /datum/admins/proc/makeDeathsquad() var/mission = input("Assign a mission to the deathsquad", "Assign Mission", "Leave no witnesses.") - var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for an elite Nanotrasen Strike Team?", "deathsquad", null) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for an elite Nanotrasen Strike Team?", "deathsquad", null) var/squadSpawned = 0 if(candidates.len >= 2) //Minimum 2 to be considered a squad @@ -396,7 +396,7 @@ /datum/admins/proc/makeOfficial() var/mission = input("Assign a task for the official", "Assign Task", "Conduct a routine preformance review of [station_name()] and its Captain.") - var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered to be a Centcom Official?", "deathsquad") + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered to be a Centcom Official?", "deathsquad") if(candidates.len) var/mob/dead/observer/chosen_candidate = pick(candidates) @@ -457,7 +457,7 @@ var/mission = input("Assign a mission to the Emergency Response Team", "Assign Mission", "Assist the station.") as null|text if(!mission) return - var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for a Code [alert] Nanotrasen Emergency Response Team?", "deathsquad", null) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a Code [alert] Nanotrasen Emergency Response Team?", "deathsquad", null) var/teamSpawned = 0 if(candidates.len > 0) diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index 1c6c0e3a1ad..a1cba47271a 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -227,7 +227,7 @@ GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation) if(check_exile_implant(C)) say("Rejecting [AM]: Exile implant detected in contained lifeform.") return - if(AM.buckled_mobs.len) + if(AM.has_buckled_mobs()) for(var/mob/living/carbon/C in AM.buckled_mobs) if(check_exile_implant(C)) say("Rejecting [AM]: Exile implant detected in close proximity lifeform.") diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index da947c6c5ca..895c042e000 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -828,6 +828,14 @@ /obj/item/weapon/defibrillator/loaded) crate_name = "defibrillator crate" +/datum/supply_pack/medical/vending + name = "Medical Vending Crate" + cost = 2000 + contains = list(/obj/item/weapon/vending_refill/medical, + /obj/item/weapon/vending_refill/medical, + /obj/item/weapon/vending_refill/medical) + crate_name = "medical vending crate" + ////////////////////////////////////////////////////////////////////////////// //////////////////////////// Science ///////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 307be5d85e4..f768eb7f61e 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -274,8 +274,8 @@ GLOBAL_LIST(external_rsc_urls) if((global.comms_key == "default_pwd" || length(global.comms_key) <= 6) && global.comms_allowed) //It's the default value or less than 6 characters long, but it somehow didn't disable comms. to_chat(src, "The server's API key is either too short or is the default value! Consider changing it immediately!") - add_verbs_from_config(tdata) - set_client_age_from_db() + add_verbs_from_config() + set_client_age_from_db(tdata) var/cached_player_age = player_age //we have to cache this because other shit may change it and we need it's current value now down below. if (isnum(cached_player_age) && cached_player_age == -1) //first connection player_age = 0 @@ -300,11 +300,9 @@ GLOBAL_LIST(external_rsc_urls) if(config.use_account_age_for_jobs && account_age >= 0) player_age = account_age if(account_age >= 0 && account_age < config.notify_new_player_account_age) - message_admins("[key_name_admin(src)] (IP: [address], ID: [computer_id]) is a new BYOND account day[(account_age==1?"":"s")] old, created on [account_join_date].") + message_admins("[key_name_admin(src)] (IP: [address], ID: [computer_id]) is a new BYOND account [account_age] day[(account_age==1?"":"s")] old, created on [account_join_date].") if (config.irc_first_connection_alert) - send2irc_adminless_only("new_byond_user", "[key_name(src)] (IP: [address], ID: [computer_id]) is a new BYOND account day[(account_age==1?"":"s")] old, created on [account_join_date].") - else //We failed to get an age for this user, let admins know they need to keep an eye on them - message_admins("Failed to get BYOND account age for [key_name_admin(src)]") + send2irc_adminless_only("new_byond_user", "[key_name(src)] (IP: [address], ID: [computer_id]) is a new BYOND account [account_age] day[(account_age==1?"":"s")] old, created on [account_join_date].") get_message_output("watchlist entry", ckey) check_ip_intel() @@ -338,6 +336,26 @@ GLOBAL_LIST(external_rsc_urls) if(!tooltips) tooltips = new /datum/tooltip(src) + var/list/topmenus = GLOB.menulist[/datum/menu] + for (var/thing in topmenus) + var/datum/menu/topmenu = thing + var/topmenuname = "[topmenu]" + if (topmenuname == "[topmenu.type]") + var/list/tree = splittext(topmenuname, "/") + topmenuname = tree[tree.len] + winset(src, "[topmenu.type]", "parent=menu;name=[url_encode(topmenuname)]") + var/list/entries = topmenu.Generate_list(src) + for (var/child in entries) + winset(src, "[url_encode(child)]", "[entries[child]]") + if (!ispath(child, /datum/menu)) + var/atom/verb/verbpath = child + if (copytext(verbpath.name,1,2) != "@") + new child(src) + + for (var/thing in prefs.menuoptions) + var/datum/menu/menuitem = GLOB.menulist[thing] + if (menuitem) + menuitem.Load_checked(src) ////////////// //DISCONNECT// @@ -412,19 +430,20 @@ GLOBAL_LIST(external_rsc_urls) if(!query_client_in_db.NextRow()) new_player = 1 account_join_date = sanitizeSQL(findJoinDate()) - var/datum/DBQuery/query_add_player = SSdbcore.NewQuery("INSERT INTO [format_table_name("player")] VALUES ('[sql_ckey]', Now(), Now(), INET_ATON('[sql_ip]'), '[sql_computerid]', '[sql_admin_rank]', [account_join_date ? "'[account_join_date]'" : "NULL"])") + var/datum/DBQuery/query_add_player = SSdbcore.NewQuery("INSERT INTO [format_table_name("player")] (`ckey`, `firstseen`, `lastseen`, `ip`, `computerid`, `lastadminrank`, `accountjoindate`) VALUES ('[sql_ckey]', Now(), Now(), INET_ATON('[sql_ip]'), '[sql_computerid]', '[sql_admin_rank]', [account_join_date ? "'[account_join_date]'" : "NULL"])") if(!query_add_player.Execute()) return if(!account_join_date) account_join_date = "Error" account_age = -1 - var/datum/DBQuery/query_get_client_age = SSdbcore.NewQuery("SELECT DATEDIFF(Now(),firstseen), DATEDIFF(Now(),accountjoindate) FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'") + var/datum/DBQuery/query_get_client_age = SSdbcore.NewQuery("SELECT DATEDIFF(Now(),firstseen), accountjoindate, DATEDIFF(Now(),accountjoindate) FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'") if(!query_get_client_age.Execute()) return if(query_get_client_age.NextRow()) player_age = text2num(query_get_client_age.item[1]) if(!account_join_date) - account_age = query_get_client_age.item[2] + account_join_date = query_get_client_age.item[2] + account_age = text2num(query_get_client_age.item[3]) if(!account_age) account_join_date = sanitizeSQL(findJoinDate()) if(!account_join_date) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 19368c7d110..33e2a63f835 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -102,6 +102,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/uplink_spawn_loc = UPLINK_PDA + var/list/menuoptions + /datum/preferences/New(client/C) parent = C custom_names["ai"] = pick(GLOB.ai_names) @@ -124,6 +126,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(!loaded_preferences_successfully) save_preferences() save_character() //let's save this new random character so it doesn't keep generating new ones. + menuoptions = list() return diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 6aaae5f029a..11ed30de4fd 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -1,5 +1,5 @@ //This is the lowest supported version, anything below this is completely obsolete and the entire savefile will be wiped. -#define SAVEFILE_VERSION_MIN 10 +#define SAVEFILE_VERSION_MIN 15 //This is the current version, anything below this will attempt to update (if it's not obsolete) #define SAVEFILE_VERSION_MAX 17 @@ -88,15 +88,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car /datum/preferences/proc/update_preferences(current_version, savefile/S) - if(current_version < 10) - toggles |= MEMBER_PUBLIC - if(current_version < 11) - chat_toggles = TOGGLES_DEFAULT_CHAT - toggles = TOGGLES_DEFAULT - if(current_version < 12) - ignoring = list() - if(current_version < 15) - toggles |= SOUND_ANNOUNCEMENTS //should this proc get fairly long (say 3 versions long), @@ -106,16 +97,6 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //It's only really meant to avoid annoying frequent players //if your savefile is 3 months out of date, then 'tough shit'. /datum/preferences/proc/update_character(current_version, savefile/S) - if(pref_species && !(pref_species.id in GLOB.roundstart_species)) - var/rando_race = pick(config.roundstart_races) - pref_species = new rando_race() - - if(current_version < 13 || !istext(backbag)) - switch(backbag) - if(2) - backbag = DSATCHEL - else - backbag = DBACKPACK if(current_version < 16) var/berandom S["userandomjob"] >> berandom @@ -156,12 +137,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["tgui_fancy"] >> tgui_fancy S["tgui_lock"] >> tgui_lock S["windowflash"] >> windowflashing + S["be_special"] >> be_special - if(islist(S["be_special"])) - S["be_special"] >> be_special - else //force update and store the old bitflag version of be_special - needs_update = 11 - S["be_special"] >> old_be_special S["default_slot"] >> default_slot S["chat_toggles"] >> chat_toggles @@ -177,6 +154,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["uses_glasses_colour"]>> uses_glasses_colour S["clientfps"] >> clientfps S["parallax"] >> parallax + S["menuoptions"] >> menuoptions //try to fix any outdated data if necessary if(needs_update >= 0) @@ -199,6 +177,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit)) ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION) ghost_others = sanitize_inlist(ghost_others, GLOB.ghost_others_options, GHOST_OTHERS_DEFAULT_OPTION) + menuoptions = SANITIZE_LIST(menuoptions) + be_special = SANITIZE_LIST(be_special) + return 1 @@ -235,6 +216,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["uses_glasses_colour"]<< uses_glasses_colour S["clientfps"] << clientfps S["parallax"] << parallax + S["menuoptions"] << menuoptions return 1 @@ -445,7 +427,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car #undef SAVEFILE_VERSION_MAX #undef SAVEFILE_VERSION_MIN -/* + +#ifdef TESTING //DEBUG //Some crude tools for testing savefiles //path is the savefile path @@ -456,4 +439,5 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car /client/verb/savefile_import(path as text) var/savefile/S = new /savefile(path) S.ImportText("/",file("[path].txt")) -*/ + +#endif \ No newline at end of file diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm index 22155a02039..5ed5980883f 100644 --- a/code/modules/client/preferences_toggles.dm +++ b/code/modules/client/preferences_toggles.dm @@ -1,162 +1,249 @@ +//this works as is to create a single checked item, but has no back end code for toggleing the check yet +#define TOGGLE_CHECKBOX(PARENT, CHILD) PARENT/CHILD/abstract = TRUE;PARENT/CHILD/checkbox = CHECKBOX_TOGGLE;PARENT/CHILD/verb/CHILD + +//Example usage TOGGLE_CHECKBOX(datum/menu/Settings/Ghost/chatterbox, toggle_ghost_ears)() + +//override because we don't want to save preferences twice. +/datum/menu/Settings/Set_checked(client/C, verbpath) + if (checkbox == CHECKBOX_GROUP) + C.prefs.menuoptions[type] = verbpath + else if (checkbox == CHECKBOX_TOGGLE) + var/checked = Get_checked(C) + C.prefs.menuoptions[type] = !checked + winset(C, "[verbpath]", "is-checked = [!checked]") + +/datum/menu/Settings/verb/setup_character() + set name = "Game Preferences" + set category = "Preferences" + set desc = "Open Game Preferences Window" + usr.client.prefs.current_tab = 1 + usr.client.prefs.ShowChoices(usr) + //toggles -/client/verb/toggle_ghost_ears() +/datum/menu/Settings/Ghost/chatterbox + name = "Chat Box Spam" + +TOGGLE_CHECKBOX(/datum/menu/Settings/Ghost/chatterbox, toggle_ghost_ears)() set name = "Show/Hide GhostEars" set category = "Preferences" - set desc = ".Toggle Between seeing all mob speech, and only speech of nearby mobs" - prefs.chat_toggles ^= CHAT_GHOSTEARS - to_chat(src, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTEARS) ? "see all speech in the world" : "only see speech from nearby mobs"].") - prefs.save_preferences() - SSblackbox.add_details("preferences_verb","Toggle Ghost Ears|[prefs.chat_toggles & CHAT_GHOSTEARS]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + set desc = "See All Speech" + usr.client.prefs.chat_toggles ^= CHAT_GHOSTEARS + to_chat(usr, "As a ghost, you will now [(usr.client.prefs.chat_toggles & CHAT_GHOSTEARS) ? "see all speech in the world" : "only see speech from nearby mobs"].") + usr.client.prefs.save_preferences() + SSblackbox.add_details("preferences_verb","Toggle Ghost Ears|[usr.client.prefs.chat_toggles & CHAT_GHOSTEARS]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/menu/Settings/Ghost/chatterbox/toggle_ghost_ears/Get_checked(client/C) + return C.prefs.chat_toggles & CHAT_GHOSTEARS -/client/verb/toggle_ghost_sight() +TOGGLE_CHECKBOX(/datum/menu/Settings/Ghost/chatterbox, toggle_ghost_sight)() set name = "Show/Hide GhostSight" set category = "Preferences" - set desc = ".Toggle Between seeing all mob emotes, and only emotes of nearby mobs" - prefs.chat_toggles ^= CHAT_GHOSTSIGHT - to_chat(src, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTSIGHT) ? "see all emotes in the world" : "only see emotes from nearby mobs"].") - prefs.save_preferences() - SSblackbox.add_details("preferences_verb","Toggle Ghost Sight|[prefs.chat_toggles & CHAT_GHOSTSIGHT]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + set desc = "See All Emotes" + usr.client.prefs.chat_toggles ^= CHAT_GHOSTSIGHT + to_chat(usr, "As a ghost, you will now [(usr.client.prefs.chat_toggles & CHAT_GHOSTSIGHT) ? "see all emotes in the world" : "only see emotes from nearby mobs"].") + usr.client.prefs.save_preferences() + SSblackbox.add_details("preferences_verb","Toggle Ghost Sight|[usr.client.prefs.chat_toggles & CHAT_GHOSTSIGHT]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/menu/Settings/Ghost/chatterbox/toggle_ghost_sight/Get_checked(client/C) + return C.prefs.chat_toggles & CHAT_GHOSTSIGHT -/client/verb/toggle_ghost_whispers() +TOGGLE_CHECKBOX(/datum/menu/Settings/Ghost/chatterbox, toggle_ghost_whispers)() set name = "Show/Hide GhostWhispers" set category = "Preferences" - set desc = ".Toggle between hearing all whispers, and only whispers of nearby mobs" - prefs.chat_toggles ^= CHAT_GHOSTWHISPER - to_chat(src, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTWHISPER) ? "see all whispers in the world" : "only see whispers from nearby mobs"].") - prefs.save_preferences() - SSblackbox.add_details("preferences_verb","Toggle Ghost Whispers|[prefs.chat_toggles & CHAT_GHOSTWHISPER]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + set desc = "See All Whispers" + usr.client.prefs.chat_toggles ^= CHAT_GHOSTWHISPER + to_chat(usr, "As a ghost, you will now [(usr.client.prefs.chat_toggles & CHAT_GHOSTWHISPER) ? "see all whispers in the world" : "only see whispers from nearby mobs"].") + usr.client.prefs.save_preferences() + SSblackbox.add_details("preferences_verb","Toggle Ghost Whispers|[usr.client.prefs.chat_toggles & CHAT_GHOSTWHISPER]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/menu/Settings/Ghost/chatterbox/toggle_ghost_whispers/Get_checked(client/C) + return C.prefs.chat_toggles & CHAT_GHOSTWHISPER -/client/verb/toggle_ghost_radio() +TOGGLE_CHECKBOX(/datum/menu/Settings/Ghost/chatterbox, toggle_ghost_radio)() set name = "Show/Hide GhostRadio" set category = "Preferences" - set desc = ".Enable or disable hearing radio chatter as a ghost" - prefs.chat_toggles ^= CHAT_GHOSTRADIO - to_chat(src, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTRADIO) ? "see radio chatter" : "not see radio chatter"].") - prefs.save_preferences() - SSblackbox.add_details("preferences_verb","Toggle Ghost Radio|[prefs.chat_toggles & CHAT_GHOSTRADIO]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! //social experiment, increase the generation whenever you copypaste this shamelessly GENERATION 1 + set desc = "See All Radio Chatter" + usr.client.prefs.chat_toggles ^= CHAT_GHOSTRADIO + to_chat(usr, "As a ghost, you will now [(usr.client.prefs.chat_toggles & CHAT_GHOSTRADIO) ? "see radio chatter" : "not see radio chatter"].") + usr.client.prefs.save_preferences() + SSblackbox.add_details("preferences_verb","Toggle Ghost Radio|[usr.client.prefs.chat_toggles & CHAT_GHOSTRADIO]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! //social experiment, increase the generation whenever you copypaste this shamelessly GENERATION 1 +/datum/menu/Settings/Ghost/chatterbox/toggle_ghost_radio/Get_checked(client/C) + return C.prefs.chat_toggles & CHAT_GHOSTRADIO -/client/verb/toggle_ghost_pda() +TOGGLE_CHECKBOX(/datum/menu/Settings/Ghost/chatterbox, toggle_ghost_pda)() set name = "Show/Hide GhostPDA" set category = "Preferences" - set desc = ".Toggle Between seeing all mob pda messages, and only pda messages of nearby mobs" - prefs.chat_toggles ^= CHAT_GHOSTPDA - to_chat(src, "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTPDA) ? "see all pda messages in the world" : "only see pda messages from nearby mobs"].") - prefs.save_preferences() - SSblackbox.add_details("preferences_verb","Toggle Ghost PDA|[prefs.chat_toggles & CHAT_GHOSTPDA]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + set desc = "See All PDA Messages" + usr.client.prefs.chat_toggles ^= CHAT_GHOSTPDA + to_chat(usr, "As a ghost, you will now [(usr.client.prefs.chat_toggles & CHAT_GHOSTPDA) ? "see all pda messages in the world" : "only see pda messages from nearby mobs"].") + usr.client.prefs.save_preferences() + SSblackbox.add_details("preferences_verb","Toggle Ghost PDA|[usr.client.prefs.chat_toggles & CHAT_GHOSTPDA]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/menu/Settings/Ghost/chatterbox/toggle_ghost_pda/Get_checked(client/C) + return C.prefs.chat_toggles & CHAT_GHOSTPDA + +/datum/menu/Settings/Ghost/chatterbox/Events + name = "Events" //please be aware that the following two verbs have inverted stat output, so that "Toggle Deathrattle|1" still means you activated it -/client/verb/toggle_deathrattle() +TOGGLE_CHECKBOX(/datum/menu/Settings/Ghost/chatterbox/Events, toggle_deathrattle)() set name = "Toggle Deathrattle" set category = "Preferences" - set desc = "Toggle recieving a message in deadchat when sentient mobs die." - prefs.toggles ^= DISABLE_DEATHRATTLE - prefs.save_preferences() - to_chat(usr, "You will [(prefs.toggles & DISABLE_DEATHRATTLE) ? "no longer" : "now"] get messages when a sentient mob dies.") - SSblackbox.add_details("preferences_verb", "Toggle Deathrattle|[!(prefs.toggles & DISABLE_DEATHRATTLE)]") //If you are copy-pasting this, maybe you should spend some time reading the comments. + set desc = "Death" + usr.client.prefs.toggles ^= DISABLE_DEATHRATTLE + usr.client.prefs.save_preferences() + to_chat(usr, "You will [(usr.client.prefs.toggles & DISABLE_DEATHRATTLE) ? "no longer" : "now"] get messages when a sentient mob dies.") + SSblackbox.add_details("preferences_verb", "Toggle Deathrattle|[!(usr.client.prefs.toggles & DISABLE_DEATHRATTLE)]") //If you are copy-pasting this, maybe you should spend some time reading the comments. +/datum/menu/Settings/Ghost/chatterbox/Events/toggle_deathrattle/Get_checked(client/C) + return !(C.prefs.toggles & DISABLE_DEATHRATTLE) -/client/verb/toggle_arrivalrattle() +TOGGLE_CHECKBOX(/datum/menu/Settings/Ghost/chatterbox/Events, toggle_arrivalrattle)() set name = "Toggle Arrivalrattle" set category = "Preferences" - set desc = "Toggle recieving a message in deadchat when someone joins the station." - prefs.toggles ^= DISABLE_ARRIVALRATTLE - to_chat(usr, "You will [(prefs.toggles & DISABLE_ARRIVALRATTLE) ? "no longer" : "now"] get messages when someone joins the station.") - prefs.save_preferences() - SSblackbox.add_details("preferences_verb", "Toggle Arrivalrattle|[!(prefs.toggles & DISABLE_ARRIVALRATTLE)]") //If you are copy-pasting this, maybe you should rethink where your life went so wrong. + set desc = "New Player Arrival" + usr.client.prefs.toggles ^= DISABLE_ARRIVALRATTLE + to_chat(usr, "You will [(usr.client.prefs.toggles & DISABLE_ARRIVALRATTLE) ? "no longer" : "now"] get messages when someone joins the station.") + usr.client.prefs.save_preferences() + SSblackbox.add_details("preferences_verb", "Toggle Arrivalrattle|[!(usr.client.prefs.toggles & DISABLE_ARRIVALRATTLE)]") //If you are copy-pasting this, maybe you should rethink where your life went so wrong. +/datum/menu/Settings/Ghost/chatterbox/Events/toggle_arrivalrattle/Get_checked(client/C) + return !(C.prefs.toggles & DISABLE_ARRIVALRATTLE) -/client/verb/togglemidroundantag() +TOGGLE_CHECKBOX(/datum/menu/Settings/Ghost, togglemidroundantag)() set name = "Toggle Midround Antagonist" set category = "Preferences" - set desc = "Toggles whether or not you will be considered for antagonist status given during a round." - prefs.toggles ^= MIDROUND_ANTAG - prefs.save_preferences() - to_chat(src, "You will [(prefs.toggles & MIDROUND_ANTAG) ? "now" : "no longer"] be considered for midround antagonist positions.") - SSblackbox.add_details("preferences_verb","Toggle Midround Antag|[prefs.toggles & MIDROUND_ANTAG]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + set desc = "Midround Antagonist" + usr.client.prefs.toggles ^= MIDROUND_ANTAG + usr.client.prefs.save_preferences() + to_chat(usr, "You will [(usr.client.prefs.toggles & MIDROUND_ANTAG) ? "now" : "no longer"] be considered for midround antagonist positions.") + SSblackbox.add_details("preferences_verb","Toggle Midround Antag|[usr.client.prefs.toggles & MIDROUND_ANTAG]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/menu/Settings/Ghost/togglemidroundantag/Get_checked(client/C) + return C.prefs.toggles & MIDROUND_ANTAG -/client/verb/toggletitlemusic() +TOGGLE_CHECKBOX(/datum/menu/Settings/Sound, toggletitlemusic)() set name = "Hear/Silence LobbyMusic" set category = "Preferences" - set desc = "Toggles hearing the GameLobby music" - prefs.toggles ^= SOUND_LOBBY - prefs.save_preferences() - if(prefs.toggles & SOUND_LOBBY) - to_chat(src, "You will now hear music in the game lobby.") - if(isnewplayer(mob)) - playtitlemusic() + set desc = "Hear Music In Lobby" + usr.client.prefs.toggles ^= SOUND_LOBBY + usr.client.prefs.save_preferences() + if(usr.client.prefs.toggles & SOUND_LOBBY) + to_chat(usr, "You will now hear music in the game lobby.") + if(isnewplayer(usr)) + usr.client.playtitlemusic() else - to_chat(src, "You will no longer hear music in the game lobby.") - mob.stop_sound_channel(CHANNEL_LOBBYMUSIC) - SSblackbox.add_details("preferences_verb","Toggle Lobby Music|[prefs.toggles & SOUND_LOBBY]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + to_chat(usr, "You will no longer hear music in the game lobby.") + usr.stop_sound_channel(CHANNEL_LOBBYMUSIC) + SSblackbox.add_details("preferences_verb","Toggle Lobby Music|[usr.client.prefs.toggles & SOUND_LOBBY]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/menu/Settings/Sound/toggletitlemusic/Get_checked(client/C) + return C.prefs.toggles & SOUND_LOBBY -/client/verb/togglemidis() + +TOGGLE_CHECKBOX(/datum/menu/Settings/Sound, togglemidis)() set name = "Hear/Silence Midis" set category = "Preferences" - set desc = "Toggles hearing sounds uploaded by admins" - prefs.toggles ^= SOUND_MIDI - prefs.save_preferences() - if(prefs.toggles & SOUND_MIDI) - to_chat(src, "You will now hear any sounds uploaded by admins.") + set desc = "Hear Admin Triggered Sounds (Midis)" + usr.client.prefs.toggles ^= SOUND_MIDI + usr.client.prefs.save_preferences() + if(usr.client.prefs.toggles & SOUND_MIDI) + to_chat(usr, "You will now hear any sounds uploaded by admins.") else - to_chat(src, "You will no longer hear sounds uploaded by admins") - mob.stop_sound_channel(CHANNEL_ADMIN) - SSblackbox.add_details("preferences_verb","Toggle Hearing Midis|[prefs.toggles & SOUND_MIDI]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + to_chat(usr, "You will no longer hear sounds uploaded by admins") + usr.stop_sound_channel(CHANNEL_ADMIN) + SSblackbox.add_details("preferences_verb","Toggle Hearing Midis|[usr.client.prefs.toggles & SOUND_MIDI]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/menu/Settings/Sound/togglemidis/Get_checked(client/C) + return C.prefs.toggles & SOUND_MIDI -/client/verb/stop_client_sounds() - set name = "Stop Sounds" - set category = "Preferences" - set desc = "Kills all currently playing sounds, use if admin taste in midis a shite" - src << sound(null) - SSblackbox.add_details("preferences_verb","Stop Self Sounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/verb/listen_ooc() - set name = "Show/Hide OOC" - set category = "Preferences" - set desc = "Toggles seeing OutOfCharacter chat" - prefs.chat_toggles ^= CHAT_OOC - prefs.save_preferences() - to_chat(src, "You will [(prefs.chat_toggles & CHAT_OOC) ? "now" : "no longer"] see messages on the OOC channel.") - SSblackbox.add_details("preferences_verb","Toggle Seeing OOC|[prefs.chat_toggles & CHAT_OOC]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/Toggle_Soundscape() //All new ambience should be added here so it works with this verb until someone better at things comes up with a fix that isn't awful - set name = "Hear/Silence Ambience" - set category = "Preferences" - set desc = "Toggles hearing ambient sound effects" - prefs.toggles ^= SOUND_AMBIENCE - prefs.save_preferences() - if(prefs.toggles & SOUND_AMBIENCE) - to_chat(src, "You will now hear ambient sounds.") - else - to_chat(src, "You will no longer hear ambient sounds.") - src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 1) - src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 2) - SSblackbox.add_details("preferences_verb","Toggle Ambience|[prefs.toggles & SOUND_AMBIENCE]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -// This needs a toggle because you people are awful and spammed terrible music -/client/verb/toggle_instruments() +TOGGLE_CHECKBOX(/datum/menu/Settings/Sound, toggle_instruments)() set name = "Hear/Silence Instruments" set category = "Preferences" - set desc = "Toggles hearing musical instruments like the violin and piano" - prefs.toggles ^= SOUND_INSTRUMENTS - prefs.save_preferences() - if(prefs.toggles & SOUND_INSTRUMENTS) - to_chat(src, "You will now hear people playing musical instruments.") + set desc = "Hear In-game Instruments" + usr.client.prefs.toggles ^= SOUND_INSTRUMENTS + usr.client.prefs.save_preferences() + if(usr.client.prefs.toggles & SOUND_INSTRUMENTS) + to_chat(usr, "You will now hear people playing musical instruments.") else - to_chat(src, "You will no longer hear musical instruments.") - SSblackbox.add_details("preferences_verb","Toggle Instruments|[prefs.toggles & SOUND_INSTRUMENTS]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + to_chat(usr, "You will no longer hear musical instruments.") + SSblackbox.add_details("preferences_verb","Toggle Instruments|[usr.client.prefs.toggles & SOUND_INSTRUMENTS]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/menu/Settings/Sound/toggle_instruments/Get_checked(client/C) + return C.prefs.toggles & SOUND_INSTRUMENTS -//Lots of people get headaches from the normal ship ambience, this is to prevent that -/client/verb/toggle_ship_ambience() + +TOGGLE_CHECKBOX(/datum/menu/Settings/Sound, Toggle_Soundscape)() + set name = "Hear/Silence Ambience" + set category = "Preferences" + set desc = "Hear Ambient Sound Effects" + usr.client.prefs.toggles ^= SOUND_AMBIENCE + usr.client.prefs.save_preferences() + if(usr.client.prefs.toggles & SOUND_AMBIENCE) + to_chat(usr, "You will now hear ambient sounds.") + else + to_chat(usr, "You will no longer hear ambient sounds.") + usr << sound(null, repeat = 0, wait = 0, volume = 0, channel = 1) + usr << sound(null, repeat = 0, wait = 0, volume = 0, channel = 2) + SSblackbox.add_details("preferences_verb","Toggle Ambience|[usr.client.prefs.toggles & SOUND_AMBIENCE]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/menu/Settings/Sound/Toggle_Soundscape/Get_checked(client/C) + return C.prefs.toggles & SOUND_AMBIENCE + + +TOGGLE_CHECKBOX(/datum/menu/Settings/Sound, toggle_ship_ambience)() set name = "Hear/Silence Ship Ambience" set category = "Preferences" - set desc = "Toggles hearing generalized ship ambience, no matter your area." - prefs.toggles ^= SOUND_SHIP_AMBIENCE - prefs.save_preferences() - if(prefs.toggles & SOUND_SHIP_AMBIENCE) - to_chat(src, "You will now hear ship ambience.") + set desc = "Hear Ship Ambience Roar" + usr.client.prefs.toggles ^= SOUND_SHIP_AMBIENCE + usr.client.prefs.save_preferences() + if(usr.client.prefs.toggles & SOUND_SHIP_AMBIENCE) + to_chat(usr, "You will now hear ship ambience.") else - to_chat(src, "You will no longer hear ship ambience.") - src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 2) - src.ambience_playing = 0 - SSblackbox.add_details("preferences_verb", "Toggle Ship Ambience|[prefs.toggles & SOUND_SHIP_AMBIENCE]") //If you are copy-pasting this, I bet you read this comment expecting to see the same thing :^) + to_chat(usr, "You will no longer hear ship ambience.") + usr << sound(null, repeat = 0, wait = 0, volume = 0, channel = 2) + usr.client.ambience_playing = 0 + SSblackbox.add_details("preferences_verb", "Toggle Ship Ambience|[usr.client.prefs.toggles & SOUND_SHIP_AMBIENCE]") //If you are copy-pasting this, I bet you read this comment expecting to see the same thing :^) +/datum/menu/Settings/Sound/toggle_ship_ambience/Get_checked(client/C) + return C.prefs.toggles & SOUND_SHIP_AMBIENCE + + +TOGGLE_CHECKBOX(/datum/menu/Settings/Sound, toggle_announcement_sound)() + set name = "Hear/Silence Announcements" + set category = "Preferences" + set desc = "Hear Announcement Sound" + usr.client.prefs.toggles ^= SOUND_ANNOUNCEMENTS + to_chat(usr, "You will now [(usr.client.prefs.toggles & SOUND_ANNOUNCEMENTS) ? "hear announcement sounds" : "no longer hear announcements"].") + usr.client.prefs.save_preferences() + SSblackbox.add_details("preferences_verb","Toggle Announcement Sound|[usr.client.prefs.toggles & SOUND_ANNOUNCEMENTS]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/menu/Settings/Sound/toggle_announcement_sound/Get_checked(client/C) + return C.prefs.toggles & SOUND_ANNOUNCEMENTS + + +TOGGLE_CHECKBOX(/datum/menu/Settings/Sound, toggleprayersounds)() + set name = "Hear/Silence Prayer Sounds" + set category = "Preferences" + set desc = "Hear Prayer Sounds" + usr.client.prefs.toggles ^= SOUND_PRAYERS + usr.client.prefs.save_preferences() + if(usr.client.prefs.toggles & SOUND_PRAYERS) + to_chat(usr, "You will now hear prayer sounds.") + else + to_chat(usr, "You will no longer prayer sounds.") + SSblackbox.add_details("admin_toggle", "Toggle Prayer Sounds|[usr.client.prefs.toggles & SOUND_PRAYERS]") +/datum/menu/Settings/Sound/toggleprayersounds/Get_checked(client/C) + return C.prefs.toggles & SOUND_PRAYERS + + +/datum/menu/Settings/Sound/verb/stop_client_sounds() + set name = "Stop Sounds" + set category = "Preferences" + set desc = "Stop Current Sounds" + usr << sound(null) + SSblackbox.add_details("preferences_verb","Stop Self Sounds") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +TOGGLE_CHECKBOX(/datum/menu/Settings, listen_ooc)() + set name = "Show/Hide OOC" + set category = "Preferences" + set desc = "Show OOC Chat" + usr.client.prefs.chat_toggles ^= CHAT_OOC + usr.client.prefs.save_preferences() + to_chat(usr, "You will [(usr.client.prefs.chat_toggles & CHAT_OOC) ? "now" : "no longer"] see messages on the OOC channel.") + SSblackbox.add_details("preferences_verb","Toggle Seeing OOC|[usr.client.prefs.chat_toggles & CHAT_OOC]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/datum/menu/Settings/listen_ooc/Get_checked(client/C) + return C.prefs.chat_toggles & CHAT_OOC + GLOBAL_LIST_INIT(ghost_forms, list("ghost","ghostking","ghostian2","skeleghost","ghost_red","ghost_black", \ "ghost_blue","ghost_yellow","ghost_green","ghost_pink", \ @@ -246,13 +333,6 @@ GLOBAL_LIST_INIT(ghost_orbits, list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOS prefs.save_preferences() SSblackbox.add_details("preferences_verb","Toggle Intent Selection|[prefs.toggles & INTENT_STYLE]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/verb/setup_character() - set name = "Game Preferences" - set category = "Preferences" - set desc = "Allows you to access the Setup Character screen. Changes to your character won't take effect until next round, but other changes will." - prefs.current_tab = 1 - prefs.ShowChoices(usr) - /client/verb/toggle_ghost_hud_pref() set name = "Toggle Ghost HUD" set category = "Preferences" @@ -278,15 +358,6 @@ GLOBAL_LIST_INIT(ghost_orbits, list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOS to_chat(src, "You will no longer examine things you click on.") SSblackbox.add_details("preferences_verb","Toggle Ghost Inquisitiveness|[prefs.inquisitive_ghost]") -/client/verb/toggle_announcement_sound() - set name = "Hear/Silence Announcements" - set category = "Preferences" - set desc = ".Toggles hearing Central Command, Captain, VOX, and other announcement sounds" - prefs.toggles ^= SOUND_ANNOUNCEMENTS - to_chat(src, "You will now [(prefs.toggles & SOUND_ANNOUNCEMENTS) ? "hear announcement sounds" : "no longer hear announcements"].") - prefs.save_preferences() - SSblackbox.add_details("preferences_verb","Toggle Announcement Sound|[prefs.toggles & SOUND_ANNOUNCEMENTS]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - //Admin Preferences /client/proc/toggleadminhelpsound() set name = "Hear/Silence Adminhelps" @@ -338,14 +409,3 @@ GLOBAL_LIST_INIT(ghost_orbits, list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOS to_chat(src, "You will [(prefs.chat_toggles & CHAT_PRAYER) ? "now" : "no longer"] see prayerchat.") SSblackbox.add_details("admin_toggle","Toggle Prayer Visibility|[prefs.chat_toggles & CHAT_PRAYER]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/verb/toggleprayersounds() - set name = "Hear/Silence Prayer Sounds" - set category = "Preferences" - set desc = "Toggles hearing pray sounds." - prefs.toggles ^= SOUND_PRAYERS - prefs.save_preferences() - if(prefs.toggles & SOUND_PRAYERS) - to_chat(src, "You will now hear prayer sounds.") - else - to_chat(src, "You will no longer prayer sounds.") - SSblackbox.add_details("admin_toggle", "Toggle Prayer Sounds|[prefs.toggles & SOUND_PRAYERS]") diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 0994c2f1777..f287870ef65 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -161,7 +161,9 @@ /obj/item/clothing/obj_break(damage_flag) if(!damaged_clothes) update_clothes_damaged_state(TRUE) - to_chat(usr, "Your [src] starts to fall apart!") + if(ismob(loc)) //It's not important enough to warrant a message if nobody's wearing it + var/mob/M = loc + M.visible_message("[M]'s [name] starts to fall apart!", "Your [name] starts to fall apart!") /obj/item/clothing/proc/update_clothes_damaged_state(damaging = TRUE) var/index = "\ref[initial(icon)]-[initial(icon_state)]" diff --git a/code/modules/clothing/spacesuits/flightsuit.dm b/code/modules/clothing/spacesuits/flightsuit.dm index 448bf8a0a31..6ed63a3eaf8 100644 --- a/code/modules/clothing/spacesuits/flightsuit.dm +++ b/code/modules/clothing/spacesuits/flightsuit.dm @@ -663,6 +663,9 @@ flight = FALSE if(suit.shoes) suit.shoes.toggle(FALSE) + if(isturf(wearer.loc)) + var/turf/T = wearer.loc + T.Entered(src) else if(override_safe) disable_flight(TRUE) diff --git a/code/modules/events/ghost_role.dm b/code/modules/events/ghost_role.dm index 93eff54b270..2468ddb5c6a 100644 --- a/code/modules/events/ghost_role.dm +++ b/code/modules/events/ghost_role.dm @@ -59,7 +59,7 @@ var/list/mob/dead/observer/regular_candidates // don't get their hopes up if(priority_candidates.len < minimum_required) - regular_candidates = pollCandidates("Do you wish to be considered for the special role of '[role_name]'?", jobban, gametypecheck, be_special) + regular_candidates = pollGhostCandidates("Do you wish to be considered for the special role of '[role_name]'?", jobban, gametypecheck, be_special) else regular_candidates = list() diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index 1b41cd45cd5..705c532260e 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -15,12 +15,21 @@ var/max_n_of_items = 1500 var/icon_on = "smartfridge" var/icon_off = "smartfridge-off" + var/list/initial_contents /obj/machinery/smartfridge/Initialize() ..() create_reagents() reagents.set_reacting(FALSE) + if(islist(initial_contents)) + for(var/typekey in initial_contents) + var/amount = initial_contents[typekey] + if(isnull(amount)) + amount = 1 + for(var/i in 1 to amount) + load(new typekey(src)) + var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/smartfridge(null) B.apply_default_parts(src) @@ -51,7 +60,7 @@ else return ..() -/obj/item/weapon/circuitboard/machine/smartfridge/examine/(mob/user) +/obj/item/weapon/circuitboard/machine/smartfridge/examine(mob/user) ..() to_chat(user, "[src] is set to [fridges[build_path]]. You can use a screwdriver to reconfigure it.") @@ -384,12 +393,8 @@ return TRUE return FALSE -/obj/machinery/smartfridge/extract/New() - ..() - var/obj/item/device/slime_scanner/I = new /obj/item/device/slime_scanner(src) - load(I) - var/obj/item/device/slime_scanner/T = new /obj/item/device/slime_scanner(src) - load(T) +/obj/machinery/smartfridge/extract/preloaded + initial_contents = list(/obj/item/device/slime_scanner = 2) // ----------------------------- // Chemistry Medical Smartfridge @@ -397,21 +402,6 @@ /obj/machinery/smartfridge/chemistry name = "smart chemical storage" desc = "A refrigerated storage unit for medicine storage." - var/list/spawn_meds = list( - /obj/item/weapon/reagent_containers/pill/epinephrine = 12, - /obj/item/weapon/reagent_containers/pill/charcoal = 5, - /obj/item/weapon/reagent_containers/glass/bottle/epinephrine = 1, - /obj/item/weapon/reagent_containers/glass/bottle/charcoal = 1) - -/obj/machinery/smartfridge/chemistry/New() - ..() - for(var/typekey in spawn_meds) - var/amount = spawn_meds[typekey] - if(isnull(amount)) amount = 1 - while(amount) - var/obj/item/I = new typekey(src) - load(I) - amount-- /obj/machinery/smartfridge/chemistry/accept_check(obj/item/O) if(istype(O,/obj/item/weapon/storage/pill_bottle)) @@ -431,13 +421,22 @@ return TRUE return FALSE +/obj/machinery/smartfridge/chemistry/preloaded + initial_contents = list( + /obj/item/weapon/reagent_containers/pill/epinephrine = 12, + /obj/item/weapon/reagent_containers/pill/charcoal = 5, + /obj/item/weapon/reagent_containers/glass/bottle/epinephrine = 1, + /obj/item/weapon/reagent_containers/glass/bottle/charcoal = 1) + // ---------------------------- // Virology Medical Smartfridge // ---------------------------- /obj/machinery/smartfridge/chemistry/virology name = "smart virus storage" desc = "A refrigerated storage unit for volatile sample storage." - spawn_meds = list( + +/obj/machinery/smartfridge/chemistry/virology/preloaded + initial_contents = list( /obj/item/weapon/reagent_containers/syringe/antiviral = 4, /obj/item/weapon/reagent_containers/glass/bottle/cold = 1, /obj/item/weapon/reagent_containers/glass/bottle/flu_virion = 1, diff --git a/code/modules/language/language.dm b/code/modules/language/language.dm index 2f01cd06e0a..55a57de9941 100644 --- a/code/modules/language/language.dm +++ b/code/modules/language/language.dm @@ -1,4 +1,4 @@ -#define SCRAMBLE_CACHE_LEN 20 +#define SCRAMBLE_CACHE_LEN 50 //maximum of 50 specific scrambled lines per language /* Datum based languages. Easily editable and modular. @@ -16,10 +16,10 @@ // If key is null, then the language isn't real or learnable. var/flags // Various language flags. var/list/syllables // Used when scrambling text for a non-speaker. - var/list/sentence_chance = 5 // Likelihood of making a new sentence after each syllable. - var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string + var/sentence_chance = 5 // Likelihood of making a new sentence after each syllable. + var/space_chance = 55 // Likelihood of getting a space in the random scramble string var/list/spans = list() - var/static/list/scramble_cache = list() + var/list/scramble_cache = list() var/default_priority = 0 // the language that an atom knows with the highest "default_priority" is selected by default. // if you are seeing someone speak popcorn language, then something is wrong. diff --git a/code/modules/language/language_holder.dm b/code/modules/language/language_holder.dm index cd0ce23b23f..c8ba5094000 100644 --- a/code/modules/language/language_holder.dm +++ b/code/modules/language/language_holder.dm @@ -87,6 +87,10 @@ languages = list(/datum/language/common, /datum/language/ratvar) only_speaks_language = /datum/language/ratvar +/datum/language_holder/construct + languages = list(/datum/language/common, /datum/language/narsie) + only_speaks_language = /datum/language/narsie + /datum/language_holder/drone languages = list(/datum/language/common, /datum/language/drone, /datum/language/machine) only_speaks_language = /datum/language/drone diff --git a/code/modules/language/narsian.dm b/code/modules/language/narsian.dm new file mode 100644 index 00000000000..70966ad8802 --- /dev/null +++ b/code/modules/language/narsian.dm @@ -0,0 +1,43 @@ +/datum/language/narsie + name = "Nar-Sian" + desc = "The ancient, blood-soaked, impossibly complex language of Nar-Sian cultists." + speech_verb = "intones" + ask_verb = "inquires" + exclaim_verb = "invokes" + key = "n" + sentence_chance = 8 + space_chance = 95 //very high due to the potential length of each syllable + var/static/list/base_syllables = list( + "h", "v", "c", "e", "g", "d", "r", "n", "h", "o", "p", + "ra", "so", "at", "il", "ta", "gh", "sh", "ya", "te", "sh", "ol", "ma", "om", "ig", "ni", "in", + "sha", "mir", "sas", "mah", "zar", "tok", "lyr", "nqa", "nap", "olt", "val", "qha", + "fwe", "ath", "yro", "eth", "gal", "gib", "bar", "jin", "kla", "atu", "kal", "lig", + "yoka", "drak", "loso", "arta", "weyh", "ines", "toth", "fara", "amar", "nyag", "eske", "reth", "dedo", "btoh", "nikt", "neth", + "kanas", "garis", "uloft", "tarat", "khari", "thnor", "rekka", "ragga", "rfikk", "harfr", "andid", "ethra", "dedol", "totum", + "ntrath", "keriam" + ) //the list of syllables we'll combine with itself to get a larger list of syllables + syllables = list( + "sha", "mir", "sas", "mah", "hra", "zar", "tok", "lyr", "nqa", "nap", "olt", "val", + "yam", "qha", "fel", "det", "fwe", "mah", "erl", "ath", "yro", "eth", "gal", "mud", + "gib", "bar", "tea", "fuu", "jin", "kla", "atu", "kal", "lig", + "yoka", "drak", "loso", "arta", "weyh", "ines", "toth", "fara", "amar", "nyag", "eske", "reth", "dedo", "btoh", "nikt", "neth", "abis", + "kanas", "garis", "uloft", "tarat", "khari", "thnor", "rekka", "ragga", "rfikk", "harfr", "andid", "ethra", "dedol", "totum", + "verbot", "pleggh", "ntrath", "barhah", "pasnar", "keriam", "usinar", "savrae", "amutan", "tannin", "remium", "barada", + "forbici" + ) //the base syllables, which include a few rare ones that won't appear in the mixed syllables + icon_state = "narsie" + default_priority = 10 + +/datum/language/narsie/New() + for(var/syllable in base_syllables) //we only do this once, since there's only ever a single one of each language datum. + for(var/target_syllable in base_syllables) + if(syllable != target_syllable) //don't combine with yourself + if(length(syllable) + length(target_syllable) > 8) //if the resulting syllable would be very long, don't put anything between it + syllables += "[syllable][target_syllable]" + else if(prob(80)) //we'll be minutely different each round. + syllables += "[syllable]'[target_syllable]" + else if(prob(25)) //5% chance of - instead of ' + syllables += "[syllable]-[target_syllable]" + else //15% chance of no ' or - at all + syllables += "[syllable][target_syllable]" + ..() diff --git a/code/modules/language/ratvar.dm b/code/modules/language/ratvarian.dm similarity index 51% rename from code/modules/language/ratvar.dm rename to code/modules/language/ratvarian.dm index cef4a3164ec..b924d2fc79d 100644 --- a/code/modules/language/ratvar.dm +++ b/code/modules/language/ratvarian.dm @@ -1,14 +1,19 @@ /datum/language/ratvar name = "Ratvarian" desc = "A timeless language full of power and incomprehensible to the unenlightened." - speech_verb = "clinks" - ask_verb = "clunks" - exclaim_verb = "clanks" + var/static/random_speech_verbs = list("clanks", "clinks", "clunks", "clangs") + ask_verb = "requests" + exclaim_verb = "proclaims" + whisper_verb = "imparts" key = "r" default_priority = 10 spans = list(SPAN_ROBOT) - icon_state = "ratvar" /datum/language/ratvar/scramble(var/input) . = text2ratvar(input) + +/datum/language/ratvar/get_spoken_verb(msg_end) + if(!msg_end) + return pick(random_speech_verbs) + return ..() \ No newline at end of file diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index d93bcb50286..f3b66d91724 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -84,7 +84,7 @@ if(D.make_reagents.len) return 0 - var/build_amount = 1 + var/build_amount = 0 for(var/mat_id in D.materials) var/M = D.materials[mat_id] @@ -93,7 +93,15 @@ if(!M || !redemption_mat) return 0 - build_amount = min(build_amount, round(redemption_mat.amount / M)) + var/smeltable_sheets = round(redemption_mat.amount / M) + + if(!smeltable_sheets) + return 0 + + if(!build_amount) + build_amount = smeltable_sheets + + build_amount = min(build_amount, smeltable_sheets) return build_amount @@ -152,6 +160,7 @@ if(exchange_parts(user, W)) return if(default_pry_open(W)) + materials.retrieve_all() return if(default_unfasten_wrench(user, W)) return diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index ca8b8f2d9d5..8d842d8a48d 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -31,8 +31,23 @@ if(bodytemperature >= 225 && !(disabilities & NOCLONE)) //cryosleep or husked people do not pump the blood. //Blood regeneration if there is some space - if(blood_volume < BLOOD_VOLUME_NORMAL) - blood_volume += 0.1 // regenerate blood VERY slowly + if(blood_volume < BLOOD_VOLUME_NORMAL && !(NOHUNGER in dna.species.species_traits)) + var/nutrition_ratio = 0 + switch(nutrition) + if(0 to NUTRITION_LEVEL_STARVING) + nutrition_ratio = 0.2 + if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) + nutrition_ratio = 0.4 + if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED) + nutrition_ratio = 0.6 + if(NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED) + nutrition_ratio = 0.8 + else + nutrition_ratio = 1 + if(satiety > 80) + nutrition_ratio *= 1.25 + nutrition = max(0, nutrition - nutrition_ratio * HUNGER_FACTOR) + blood_volume = min(BLOOD_VOLUME_NORMAL, blood_volume + 0.5 * nutrition_ratio) //Effects of bloodloss var/word = pick("dizzy","woozy","faint") diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index 7eb6d182ef4..86ea3d64e9e 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -69,7 +69,7 @@ bursting = TRUE - var/list/candidates = pollCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, null, ROLE_ALIEN, 100, POLL_IGNORE_ALIEN_LARVA) + var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, null, ROLE_ALIEN, 100, POLL_IGNORE_ALIEN_LARVA) if(QDELETED(src) || QDELETED(owner)) return diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index c18f6721edc..06cfbe16cb1 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -165,5 +165,8 @@ dna.species.mutant_bodyparts -= "wingsopen" dna.species.mutant_bodyparts |= "wings" update_body() + if(isturf(loc)) + var/turf/T = loc + T.Entered(src) //Ayy lmao diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 52145f609b6..8a9efde9662 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -646,3 +646,18 @@ if(P.is_hot()) visible_message("[src] bursts into flames!") fire_act() + +/datum/species/golem/plastic + name = "Plastic" + id = "plastic golem" + prefix = "Plastic" + fixed_mut_color = "fff" + info_text = "As a Plastic Golem, you are capable of ventcrawling, and passing through plastic flaps." + +/datum/species/golem/plastic/on_species_gain(mob/living/carbon/C, datum/species/old_species) + . = ..() + C.ventcrawler = VENTCRAWLER_NUDE + +/datum/species/golem/plastic/on_species_loss(mob/living/carbon/C) + . = ..() + C.ventcrawler = initial(C.ventcrawler) diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 099cb03260a..b739a2a6c5c 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -49,8 +49,8 @@ timeofdeath = world.time tod = worldtime2text() var/turf/T = get_turf(src) + var/area/A = get_area(T) if(mind && mind.name && mind.active && (!(T.flags & NO_DEATHRATTLE))) - var/area/A = get_area(T) var/rendered = "[mind.name] has died at [A.name]." deadchat_broadcast(rendered, follow_target = src, turf_target = T, message_type=DEADCHAT_DEATHRATTLE) if(mind) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 92725d73a65..4bbeb3bcfda 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -446,7 +446,7 @@ if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1)//separated from our puller and not in the middle of a diagonal move. pulledby.stop_pulling() - if (s_active && !(s_active.ClickAccessible(src, depth=STORAGE_VIEW_DEPTH) || s_active.Adjacent(src))) + if (s_active && !(CanReach(s_active,view_only = TRUE))) s_active.close(src) /mob/living/movement_delay(ignorewalk = 0) diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index d175054970c..568c9e87212 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -27,10 +27,13 @@ unique_name = 1 AIStatus = AI_OFF //normal constructs don't have AI loot = list(/obj/item/weapon/ectoplasm) - del_on_death = 1 + del_on_death = TRUE + initial_language_holder = /datum/language_holder/construct deathmessage = "collapses in a shattered heap." var/list/construct_spells = list() var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this." + var/master = null + var/seeking = FALSE /mob/living/simple_animal/hostile/construct/Initialize() @@ -39,6 +42,11 @@ for(var/spell in construct_spells) AddSpell(new spell(null)) +/mob/living/simple_animal/hostile/construct/Destroy() + for(var/X in actions) + var/datum/action/A = X + qdel(A) + ..() /mob/living/simple_animal/hostile/construct/Login() ..() @@ -278,6 +286,36 @@ environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP +///////////////////////Master-Tracker/////////////////////// + +/datum/action/innate/seek_master + name = "Seek your Master" + desc = "You and your master share a soul-link that informs you of their location" + background_icon_state = "bg_demon" + buttontooltipstyle = "cult" + button_icon_state = "cult_mark" + var/tracking = FALSE + var/mob/living/simple_animal/hostile/construct/the_construct + +/datum/action/innate/seek_master/Grant(var/mob/living/C) + the_construct = C + ..() + +/datum/action/innate/seek_master/Activate() + if(!the_construct.master) + to_chat(the_construct, "You have no master to seek!") + the_construct.seeking = FALSE + return + if(tracking) + tracking = FALSE + the_construct.seeking = FALSE + to_chat(the_construct, "You are no longer tracking your master.") + return + else + tracking = TRUE + the_construct.seeking = TRUE + to_chat(the_construct, "You are now tracking your master.") + /////////////////////////////ui stuff///////////////////////////// @@ -294,4 +332,5 @@ else if(health > maxHealth*0.2) hud_used.healths.icon_state = "[icon_state]_health5" else - hud_used.healths.icon_state = "[icon_state]_health6" \ No newline at end of file + hud_used.healths.icon_state = "[icon_state]_health6" + diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index be889a4183c..fe6e22bcab2 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -414,7 +414,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians var/mob/living/simple_animal/hostile/guardian/G = input(src, "Pick the guardian you wish to reset", "Guardian Reset") as null|anything in guardians if(G) to_chat(src, "You attempt to reset [G.real_name]'s personality...") - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", "pAI", null, FALSE, 100) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as [src.real_name]'s [G.real_name]?", "pAI", null, FALSE, 100) var/mob/dead/observer/new_stand = null if(candidates.len) new_stand = pick(candidates) @@ -489,7 +489,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians return used = TRUE to_chat(user, "[use_message]") - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_PAI, null, FALSE, 100) + var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_PAI, null, FALSE, 100) var/mob/dead/observer/theghost = null if(candidates.len) diff --git a/code/modules/mob/living/simple_animal/hostile/jungle_mobs.dm b/code/modules/mob/living/simple_animal/hostile/jungle_mobs.dm new file mode 100644 index 00000000000..de1bf0b3083 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/jungle_mobs.dm @@ -0,0 +1,85 @@ +/mob/living/simple_animal/hostile/jungle + vision_range = 5 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + faction = list("jungle") + weather_immunities = list("acid") + obj_damage = 30 + environment_smash = 2 + minbodytemp = 0 + maxbodytemp = 450 + response_help = "pokes" + response_disarm = "shoves" + response_harm = "strikes" + status_flags = 0 + a_intent = INTENT_HARM + see_in_dark = 4 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + mob_size = MOB_SIZE_LARGE + + + +//Mega arachnid + +/mob/living/simple_animal/hostile/jungle/mega_arachnid + name = "mega arachnid" + desc = "Though physically imposing, it prefers to ambush its prey, and it will only engage with an already crippled opponent." + melee_damage_lower = 30 + melee_damage_upper = 30 + maxHealth = 300 + health = 300 + speed = 1 + ranged = 1 + pixel_x = -16 + move_to_delay = 10 + aggro_vision_range = 9 + speak_emote = list("chitters") + attack_sound = 'sound/weapons/bladeslice.ogg' + ranged_cooldown_time = 60 + projectiletype = /obj/item/projectile/mega_arachnid + projectilesound = 'sound/weapons/pierce.ogg' + icon = 'icons/mob/jungle/arachnid.dmi' + icon_state = "arachnid" + icon_living = "arachnid" + icon_dead = "dead_purple" + alpha = 50 + +/mob/living/simple_animal/hostile/jungle/mega_arachnid/Life() + ..() + if(target && ranged_cooldown > world.time && iscarbon(target)) + var/mob/living/carbon/C = target + if(!C.legcuffed && C.health < 50) + retreat_distance = 9 + minimum_distance = 9 + alpha = 125 + return + retreat_distance = 0 + minimum_distance = 0 + alpha = 255 + + +/mob/living/simple_animal/hostile/jungle/mega_arachnid/Aggro() + ..() + alpha = 255 + +/mob/living/simple_animal/hostile/jungle/mega_arachnid/LoseAggro() + ..() + alpha = 50 + +/obj/item/projectile/mega_arachnid + name = "flesh snare" + nodamage = 1 + damage = 0 + icon_state = "tentacle_end" + +/obj/item/projectile/mega_arachnid/on_hit(atom/target, blocked = 0) + if(iscarbon(target) && blocked < 100) + var/obj/item/weapon/restraints/legcuffs/beartrap/mega_arachnid/B = new /obj/item/weapon/restraints/legcuffs/beartrap/mega_arachnid(get_turf(target)) + B.Crossed(target) + ..() + +/obj/item/weapon/restraints/legcuffs/beartrap/mega_arachnid + name = "fleshy restraints" + desc = "Used by mega arachnids to immobilize their prey." + flags = DROPDEL + icon_state = "tentacle_end" + icon = 'icons/obj/projectiles.dmi' \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index b426aa996cb..404ad8b0a57 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -28,7 +28,8 @@ status_flags = CANPUSH movement_type = FLYING loot = list(/obj/item/weapon/ectoplasm) - del_on_death = 1 + del_on_death = TRUE + initial_language_holder = /datum/language_holder/construct /mob/living/simple_animal/shade/death() deathmessage = "lets out a contented sigh as [p_their()] form unwinds." diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 352b7b3b5ef..a5facfba089 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -16,7 +16,7 @@ response_disarm = "shoos" response_harm = "stomps on" emote_see = list("jiggles", "bounces in place") - speak_emote = list("telepathically chirps") + speak_emote = list("blorbles") bubble_icon = "slime" initial_language_holder = /datum/language_holder/slime @@ -31,10 +31,10 @@ see_in_dark = 8 - verb_say = "telepathically chirps" - verb_ask = "telepathically asks" - verb_exclaim = "telepathically cries" - verb_yell = "telephatically cries" + verb_say = "blorbles" + verb_ask = "inquisitively blorbles" + verb_exclaim = "loudly blorbles" + verb_yell = "loudly blorbles" // canstun and canweaken don't affect slimes because they ignore stun and weakened variables // for the sake of cleanliness, though, here they are. diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index d3a002a4a56..e2b8d86e91e 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -567,6 +567,7 @@ var/datum/map_config/cached = SSmapping.next_map_config if(cached) stat(null, "Next Map: [cached.map_name]") + stat("Round ID:", "[GLOB.round_id ? GLOB.round_id : "NULL"]") stat(null, "Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss")]") stat(null, "Station Time: [worldtime2text()]") stat(null, "Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)") diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 7d0d4fdbb5f..fb2df4deebd 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -14,6 +14,7 @@ light_power = 0.7 light_range = 15 light_color = rgb(255, 0, 0) + gender = FEMALE var/clashing = FALSE //If Nar-Sie is fighting Ratvar /obj/singularity/narsie/large diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm index 4157462b77f..bf695cffedc 100644 --- a/code/modules/projectiles/ammunition/energy.dm +++ b/code/modules/projectiles/ammunition/energy.dm @@ -191,7 +191,7 @@ fire_sound = 'sound/magic/lightningbolt.ogg' e_cost = 200 select_name = "stun" - projectile_type = /obj/item/projectile/energy/tesla_revolver + projectile_type = /obj/item/projectile/energy/tesla/revolver /obj/item/ammo_casing/energy/gravityrepulse projectile_type = /obj/item/projectile/gravityrepulse diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index 7c845759ea0..bd5ad14c48f 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -159,44 +159,34 @@ /obj/item/projectile/energy/bolt/large damage = 20 -/obj/item/projectile/energy/tesla_revolver +/obj/item/projectile/energy/tesla name = "tesla bolt" icon_state = "tesla_projectile" impact_effect_type = /obj/effect/overlay/temp/impact_effect/blue_laser var/chain -/obj/item/projectile/energy/tesla_revolver/fire(setAngle) +/obj/item/projectile/energy/tesla/fire(setAngle) if(firer) chain = firer.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY) ..() -/obj/item/projectile/energy/tesla_revolver/on_hit(atom/target) +/obj/item/projectile/energy/tesla/Destroy() + qdel(chain) + return ..() + +/obj/item/projectile/energy/tesla/revolver + name = "energy orb" + +/obj/item/projectile/energy/tesla/revolver/on_hit(atom/target) . = ..() if(isliving(target)) - tesla_zap(src, 3, 10000) + tesla_zap(target, 3, 10000) qdel(src) -/obj/item/projectile/energy/tesla_revolver/Destroy() - qdel(chain) - return ..() +/obj/item/projectile/energy/tesla/cannon + name = "tesla orb" - -/obj/item/projectile/energy/tesla_cannon - name = "tesla bolt" - icon_state = "tesla_projectile" - impact_effect_type = /obj/effect/overlay/temp/impact_effect/blue_laser - var/chain - -/obj/item/projectile/energy/tesla_cannon/fire(setAngle) - if(firer) - chain = firer.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY) - ..() - -/obj/item/projectile/energy/tesla_cannon/on_hit(atom/target) +/obj/item/projectile/energy/tesla/cannon/on_hit(atom/target) . = ..() - tesla_zap(src, 3, 10000, explosive = FALSE, stun_mobs = FALSE) + tesla_zap(target, 3, 10000, explosive = FALSE, stun_mobs = FALSE) qdel(src) - -/obj/item/projectile/energy/tesla_cannon/Destroy() - qdel(chain) - return ..() diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index 23836e866d2..31ec27fb79c 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -966,7 +966,7 @@ All effects don't start immediately, but rather get worse over time; the rate is glass_desc = "Only for the experienced. You think you see sand floating in the glass." /datum/reagent/consumable/ethanol/bananahonk - name = "Banana Mama" + name = "Banana Honk" id = "bananahonk" description = "A drink from Clown Heaven." nutriment_factor = 1 * REAGENTS_METABOLISM diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 56a89fe389f..260d09a81a8 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -29,16 +29,11 @@ var/brute_heal = 1 var/burn_heal = 0 - var/blood_gain = 0.4 /datum/reagent/consumable/nutriment/on_mob_life(mob/living/M) if(prob(50)) M.heal_bodypart_damage(brute_heal,burn_heal, 0) . = 1 - if(iscarbon(M)) - var/mob/living/carbon/C = M - if(C.blood_volume < BLOOD_VOLUME_NORMAL) - C.blood_volume += blood_gain ..() /datum/reagent/consumable/nutriment/on_new(list/supplied_data) @@ -82,7 +77,6 @@ brute_heal = 1 burn_heal = 1 - blood_gain = 0.5 /datum/reagent/consumable/nutriment/vitamin/on_mob_life(mob/living/M) if(M.satiety < 600) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 434161fa517..6d38e85ca86 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -347,10 +347,9 @@ /datum/reagent/medicine/mine_salve/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message = 1) if(iscarbon(M) && M.stat != DEAD) if(method in list(INGEST, VAPOR, INJECT)) - M.Stun(4) - M.Weaken(4) + M.nutrition -= 5 if(show_message) - to_chat(M, "Your stomach agonizingly cramps!") + to_chat(M, "Your stomach feels empty and cramps!") else var/mob/living/carbon/C = M for(var/s in C.surgeries) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index b38d39e1392..415b316dd17 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -189,7 +189,7 @@ /datum/reagent/water/holywater/on_mob_life(mob/living/M) if(!data) data = 1 data++ - M.jitteriness = max(M.jitteriness-5,0) + M.jitteriness = min(M.jitteriness+4,10) if(data >= 30) // 12 units, 54 seconds @ metabolism 0.4 units & tick rate 1.8 sec if(!M.stuttering) M.stuttering = 1 @@ -212,9 +212,9 @@ SSticker.mode.remove_cultist(M.mind, 1, 1) else if(is_servant_of_ratvar(M)) remove_servant_of_ratvar(M) - holder.remove_reagent(id, volume) // maybe this is a little too perfect and a max() cap on the statuses would be better?? M.jitteriness = 0 M.stuttering = 0 + holder.remove_reagent(id, volume) // maybe this is a little too perfect and a max() cap on the statuses would be better?? return holder.remove_reagent(id, 0.4) //fixed consumption to prevent balancing going out of whack @@ -1570,6 +1570,13 @@ M.update_transform() ..() +/datum/reagent/plastic_polymers + name = "plastic polymers" + id = "plastic_polymers" + description = "the petroleum based components of plastic." + color = "#f7eded" + taste_description = "plastic" + /datum/reagent/glitter name = "generic glitter" id = "glitter" diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index 19bb28666d4..0bc843a6054 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -624,3 +624,14 @@ id = "laughter" results = list("laughter" = 10) // Fuck it. I'm not touching this one. required_reagents = list("sugar" = 1, "banana" = 1) + +/datum/chemical_reaction/plastic_polymers + name = "plastic polymers" + id = "plastic_polymers" + required_reagents = list("oil" = 5, "sodiumchloride" = 2, "ash" = 3) + required_temp = 374 //lazily consistent with soap & other crafted objects generically created with heat. + +/datum/chemical_reaction/plastic_polymers/on_reaction(datum/reagents/holder, created_volume) + var/location = get_turf(holder.my_atom) + for(var/i in 1 to 10) + new /obj/item/stack/sheet/plastic(location) diff --git a/code/modules/research/designs/biogenerator_designs.dm b/code/modules/research/designs/biogenerator_designs.dm index d2931012299..a3544a9ba95 100644 --- a/code/modules/research/designs/biogenerator_designs.dm +++ b/code/modules/research/designs/biogenerator_designs.dm @@ -115,28 +115,12 @@ build_path = /obj/item/stack/sheet/cloth category = list("initial","Leather and Cloth") -/datum/design/wallet - name = "Wallet" - id = "wallet" - build_type = BIOGENERATOR - materials = list(MAT_BIOMASS = 100) - build_path = /obj/item/weapon/storage/wallet - category = list("initial","Leather and Cloth") - -/datum/design/botany_gloves - name = "Botanical gloves" - id = "botany_gloves" +/datum/design/leather + name = "Sheet of Leather" + id = "leather" build_type = BIOGENERATOR materials = list(MAT_BIOMASS = 150) - build_path = /obj/item/clothing/gloves/botanic_leather - category = list("initial","Leather and Cloth") - -/datum/design/toolbelt - name = "Utility Belt" - id = "toolbelt" - build_type = BIOGENERATOR - materials = list(MAT_BIOMASS = 300) - build_path = /obj/item/weapon/storage/belt/utility + build_path = /obj/item/stack/sheet/leather category = list("initial","Leather and Cloth") /datum/design/secbelt @@ -163,14 +147,6 @@ build_path = /obj/item/weapon/storage/belt/janitor category = list("initial","Leather and Cloth") -/datum/design/bandolier - name = "Bandolier belt" - id = "bandolier" - build_type = BIOGENERATOR - materials = list(MAT_BIOMASS = 300) - build_path = /obj/item/weapon/storage/belt/bandolier - category = list("initial","Leather and Cloth") - /datum/design/s_holster name = "Shoulder holster" id = "s_holster" @@ -179,30 +155,6 @@ build_path = /obj/item/weapon/storage/belt/holster category = list("initial","Leather and Cloth") -/datum/design/leather_satchel - name = "Leather satchel" - id = "leather_satchel" - build_type = BIOGENERATOR - materials = list(MAT_BIOMASS = 400) - build_path = /obj/item/weapon/storage/backpack/satchel - category = list("initial","Leather and Cloth") - -/datum/design/leather_jacket - name = "Leather jacket" - id = "leather_jacket" - build_type = BIOGENERATOR - materials = list(MAT_BIOMASS = 500) - build_path = /obj/item/clothing/suit/jacket/leather - category = list("initial","Leather and Cloth") - -/datum/design/leather_overcoat - name = "Leather overcoat" - id = "leather_overcoat" - build_type = BIOGENERATOR - materials = list(MAT_BIOMASS = 1000) - build_path = /obj/item/clothing/suit/jacket/leather/overcoat - category = list("initial","Leather and Cloth") - /datum/design/rice_hat name = "Rice hat" id = "rice_hat" diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index 56fb705e578..e408333d286 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -90,14 +90,6 @@ build_path = /obj/item/weapon/circuitboard/machine/quantumpad category = list ("Teleportation Machinery") -/*/datum/design/board/telepad - name = "Machine Design (Telepad Board)" - desc = "The circuit board for a telescience telepad." - id = "telepad" - req_tech = list("programming" = 4, "bluespace" = 5, "plasmatech" = 4, "engineering" = 4) - build_path = /obj/item/weapon/circuitboard/machine/telesci_pad - category = list ("Teleportation Machinery") -*/ /datum/design/board/teleconsole name = "Computer Design (Teleporter Console)" desc = "Allows for the construction of circuit boards used to build a teleporter control console." @@ -106,14 +98,6 @@ build_path = /obj/item/weapon/circuitboard/computer/teleporter category = list("Teleportation Machinery") -/* /datum/design/board/telesci_console - name = "Computer Design (Telepad Control Console Board)" - desc = "Allows for the construction of circuit boards used to build a telescience console." - id = "telesci_console" - req_tech = list("programming" = 3, "bluespace" = 3, "plasmatech" = 4) - build_path = /obj/item/weapon/circuitboard/computer/telesci_console - category = list("Teleportation Machinery") -*/ /datum/design/board/sleeper name = "Machine Design (Sleeper Board)" desc = "The circuit board for a sleeper." diff --git a/code/modules/ruins/lavaland_ruin_code.dm b/code/modules/ruins/lavaland_ruin_code.dm index bdd85ae4d46..35ffdefce67 100644 --- a/code/modules/ruins/lavaland_ruin_code.dm +++ b/code/modules/ruins/lavaland_ruin_code.dm @@ -107,6 +107,9 @@ if(istype(O, /obj/item/stack/sheet/mineral/adamantine)) species = /datum/species/golem/adamantine + if(istype(O, /obj/item/stack/sheet/plastic)) + species = /datum/species/golem/plastic + if(species) if(O.use(10)) to_chat(user, "You finish up the golem shell with ten sheets of [O].") diff --git a/code/modules/shuttle/elevator.dm b/code/modules/shuttle/elevator.dm new file mode 100644 index 00000000000..12f9c24f9a4 --- /dev/null +++ b/code/modules/shuttle/elevator.dm @@ -0,0 +1,10 @@ +/obj/docking_port/mobile/elevator + name = "elevator" + id = "elevator" + dwidth = 3 + width = 7 + height = 7 + knockdown = FALSE + +/obj/docking_port/mobile/elevator/request(obj/docking_port/stationary/S) //No transit, no ignition, just a simple up/down platform + dock(S, TRUE) \ No newline at end of file diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 756fad25b36..b5571164ffd 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -3,7 +3,7 @@ return // Called when shuttle attempts to move an atom. -/atom/movable/proc/onShuttleMove(turf/T1, rotation) +/atom/movable/proc/onShuttleMove(turf/T1, rotation, knockdown = TRUE) if(rotation) shuttleRotate(rotation) loc = T1 @@ -47,11 +47,11 @@ else shake_camera(src, 7, 1) -/mob/living/carbon/onShuttleMove() +/mob/living/carbon/onShuttleMove(turf/T1, rotation, knockdown = TRUE) . = ..() if(!.) return - if(!buckled) + if(!buckled && knockdown) Weaken(3) /obj/effect/abstract/proximity_checker/onShuttleMove() diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 00ddecb7f82..656ba24da68 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -217,6 +217,8 @@ var/launch_status = NOLAUNCH + var/knockdown = TRUE //Will it knock down mobs when it docks? + // A timid shuttle will not register itself with the shuttle subsystem // All shuttle templates are timid var/timid = FALSE @@ -517,7 +519,7 @@ //move mobile to new location for(var/atom/movable/AM in T0) - if(AM.onShuttleMove(T1, rotation)) + if(AM.onShuttleMove(T1, rotation, knockdown)) moved_atoms += AM if(rotation) @@ -578,10 +580,7 @@ if(M.pulledby) M.pulledby.stop_pulling() M.stop_pulling() - M.visible_message("[M] is hit by \ - a hyperspace ripple!", - "You feel an immense \ - crushing pressure as the space around you ripples.") + M.visible_message("[src] slams into [M]!") if(M.key || M.get_ghost(TRUE)) SSblackbox.add_details("shuttle_gib", "[type]") else diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm index 82d5db8b209..e18b248e927 100644 --- a/code/modules/shuttle/supply.dm +++ b/code/modules/shuttle/supply.dm @@ -9,7 +9,6 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( /obj/singularity, /obj/machinery/teleport/station, /obj/machinery/teleport/hub, - /obj/machinery/telepad, /obj/machinery/quantumpad, /obj/machinery/clonepod, /obj/effect/mob_spawn, diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm index 6e3c224924d..9d88e491b2f 100644 --- a/code/modules/surgery/organ_manipulation.dm +++ b/code/modules/surgery/organ_manipulation.dm @@ -106,7 +106,7 @@ "You begin to mend the incision in [target]'s [parse_zone(target_zone)]...") else if(istype(tool, /obj/item/weapon/reagent_containers/food/snacks/organ)) - to_chat(user, "[tool] was biten by someone! It's too damaged to use!") + to_chat(user, "[tool] was bitten by someone! It's too damaged to use!") return -1 /datum/surgery_step/manipulate_organs/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index f3f9d7a8cd1..2636e2e5d55 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -15,6 +15,7 @@ /datum/language/common, /datum/language/draconic, /datum/language/monkey, + /datum/language/narsie, /datum/language/ratvar )) diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index e817557cc25..edf083cd3ff 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -51,7 +51,7 @@ if(C.getorganslot("adamantine_resonator")) to_chat(C, msg) if(isobserver(m)) - var/link = FOLLOW_LINK(m, src) + var/link = FOLLOW_LINK(m, owner) to_chat(m, "[link] [msg]") //Colossus drop, forces the listeners to obey certain commands diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm index 9d3377cc9c2..887c26038a2 100644 --- a/code/modules/surgery/tools.dm +++ b/code/modules/surgery/tools.dm @@ -111,17 +111,8 @@ if(contents.len) to_chat(user, "[src] already has something inside it.") return - if(isorgan(I)) - var/obj/item/organ/O = I - if(O.status != ORGAN_ORGANIC) - to_chat(user, "[src] can only hold organic body parts!") - return - else if(isbodypart(I)) - var/obj/item/bodypart/BP = I - if(BP.status != BODYPART_ORGANIC) - to_chat(user, "[src] can only hold organic body parts!") - return - else + if(!isorgan(I) && !isbodypart(I)) + to_chat(user, "[src] can only hold body parts!") return user.visible_message("[user] puts [I] into [src].", "You put [I] inside [src].") @@ -150,4 +141,4 @@ desc = "A container for holding body parts." else to_chat(user, "[src] is empty.") - return \ No newline at end of file + return diff --git a/code/modules/telesci/telepad.dm b/code/modules/telesci/telepad.dm deleted file mode 100644 index 918ec309f2b..00000000000 --- a/code/modules/telesci/telepad.dm +++ /dev/null @@ -1,172 +0,0 @@ -///SCI TELEPAD/// -/obj/machinery/telepad - name = "telepad" - desc = "A bluespace telepad used for teleporting objects to and from a location." - icon = 'icons/obj/telescience.dmi' - icon_state = "pad-idle" - anchored = 1 - use_power = 1 - idle_power_usage = 200 - active_power_usage = 5000 - var/efficiency - -/obj/machinery/telepad/Initialize() - . = ..() - var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/telesci_pad(null) - B.apply_default_parts(src) - -/obj/item/weapon/circuitboard/machine/telesci_pad - name = "Telepad (Machine Board)" - build_path = /obj/machinery/telepad - origin_tech = "programming=4;engineering=3;plasmatech=4;bluespace=4" - req_components = list( - /obj/item/weapon/ore/bluespace_crystal = 2, - /obj/item/weapon/stock_parts/capacitor = 1, - /obj/item/stack/cable_coil = 1, - /obj/item/weapon/stock_parts/console_screen = 1) - def_components = list(/obj/item/weapon/ore/bluespace_crystal = /obj/item/weapon/ore/bluespace_crystal/artificial) - -/obj/machinery/telepad/RefreshParts() - var/E - for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts) - E += C.rating - efficiency = E - -/obj/machinery/telepad/attackby(obj/item/I, mob/user, params) - if(default_deconstruction_screwdriver(user, "pad-idle-o", "pad-idle", I)) - return - - if(panel_open) - if(istype(I, /obj/item/device/multitool)) - var/obj/item/device/multitool/M = I - M.buffer = src - to_chat(user, "You save the data in the [I.name]'s buffer.") - return 1 - - if(exchange_parts(user, I)) - return - - if(default_deconstruction_crowbar(I)) - return - - return ..() - - -//CARGO TELEPAD// -/obj/machinery/telepad_cargo - name = "cargo telepad" - desc = "A telepad used by the Rapid Crate Sender." - icon = 'icons/obj/telescience.dmi' - icon_state = "pad-idle" - anchored = 1 - use_power = 1 - idle_power_usage = 20 - active_power_usage = 500 - var/stage = 0 -/obj/machinery/telepad_cargo/attackby(obj/item/weapon/W, mob/user, params) - if(istype(W, /obj/item/weapon/wrench)) - anchored = 0 - playsound(src, 'sound/items/Ratchet.ogg', 50, 1) - if(anchored) - anchored = 0 - to_chat(user, "\The [src] can now be moved.") - else if(!anchored) - anchored = 1 - to_chat(user, "\The [src] is now secured.") - else if(istype(W, /obj/item/weapon/screwdriver)) - if(stage == 0) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You unscrew the telepad's tracking beacon.") - stage = 1 - else if(stage == 1) - playsound(src, W.usesound, 50, 1) - to_chat(user, "You screw in the telepad's tracking beacon.") - stage = 0 - else if(istype(W, /obj/item/weapon/weldingtool) && stage == 1) - var/obj/item/weapon/weldingtool/WT = W - if(WT.remove_fuel(0,user)) - playsound(src.loc, 'sound/items/Welder2.ogg', 100, 1) - to_chat(user, "You start disassembling [src]...") - if(do_after(user,20*WT.toolspeed, target = src)) - if(!WT.isOn()) - return - to_chat(user, "You disassemble [src].") - new /obj/item/stack/sheet/metal(get_turf(src)) - new /obj/item/stack/sheet/glass(get_turf(src)) - qdel(src) - else - return ..() - -///TELEPAD CALLER/// -/obj/item/device/telepad_beacon - name = "telepad beacon" - desc = "Use to warp in a cargo telepad." - icon = 'icons/obj/radio.dmi' - icon_state = "beacon" - item_state = "beacon" - origin_tech = "bluespace=3" - -/obj/item/device/telepad_beacon/attack_self(mob/user) - if(user) - to_chat(user, "Locked In") - new /obj/machinery/telepad_cargo(user.loc) - playsound(src, 'sound/effects/pop.ogg', 100, 1, 1) - qdel(src) - return - -///HANDHELD TELEPAD USER/// -/obj/item/weapon/rcs - name = "rapid-crate-sender (RCS)" - desc = "Use this to send crates and closets to cargo telepads." - icon = 'icons/obj/telescience.dmi' - icon_state = "rcs" - flags = CONDUCT - force = 10 - throwforce = 10 - throw_speed = 2 - throw_range = 5 - var/rcharges = 10 - var/obj/machinery/pad = null - var/last_charge = 30 - var/mode = 0 - var/rand_x = 0 - var/rand_y = 0 - var/emagged = 0 - var/teleporting = 0 - -/obj/item/weapon/rcs/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/item/weapon/rcs/examine(mob/user) - ..() - to_chat(user, "There are [rcharges] charge\s left.") - -/obj/item/weapon/rcs/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() -/obj/item/weapon/rcs/process() - if(rcharges > 10) - rcharges = 10 - if(last_charge == 0) - rcharges++ - last_charge = 30 - else - last_charge-- - -/obj/item/weapon/rcs/attack_self(mob/user) - if(emagged) - if(mode == 0) - mode = 1 - playsound(src.loc, 'sound/effects/pop.ogg', 50, 0) - to_chat(user, "The telepad locator has become uncalibrated.") - else - mode = 0 - playsound(src.loc, 'sound/effects/pop.ogg', 50, 0) - to_chat(user, "You calibrate the telepad locator.") - -/obj/item/weapon/rcs/emag_act(mob/user) - if(!emagged) - emagged = TRUE - do_sparks(5, TRUE, src) - to_chat(user, "You emag the RCS. Click on it to toggle between modes.") diff --git a/code/modules/telesci/telesci_computer.dm b/code/modules/telesci/telesci_computer.dm deleted file mode 100644 index 6c80a3ee1f4..00000000000 --- a/code/modules/telesci/telesci_computer.dm +++ /dev/null @@ -1,363 +0,0 @@ -/obj/machinery/computer/telescience - name = "\improper Telepad Control Console" - desc = "Used to teleport objects to and from the telescience telepad." - icon_screen = "teleport" - icon_keyboard = "teleport_key" - circuit = /obj/item/weapon/circuitboard/computer/telesci_console - var/sending = 1 - var/obj/machinery/telepad/telepad = null - var/temp_msg = "Telescience control console initialized.
Welcome." - - // VARIABLES // - var/teles_left // How many teleports left until it becomes uncalibrated - var/datum/projectile_data/last_tele_data = null - var/z_co = 1 - var/power_off - var/rotation_off - //var/angle_off - var/last_target - - var/rotation = 0 - var/angle = 45 - var/power = 5 - - // Based on the power used - var/teleport_cooldown = 0 // every index requires a bluespace crystal - var/list/power_options = list(5, 10, 20, 25, 30, 40, 50, 80, 100) - var/teleporting = 0 - var/starting_crystals = 3 - var/max_crystals = 4 - var/list/crystals = list() - var/obj/item/device/gps/inserted_gps - -/obj/machinery/computer/telescience/Initialize() - recalibrate() - . = ..() - -/obj/machinery/computer/telescience/Destroy() - eject() - if(inserted_gps) - inserted_gps.loc = loc - inserted_gps = null - return ..() - -/obj/machinery/computer/telescience/examine(mob/user) - ..() - to_chat(user, "There are [crystals.len ? crystals.len : "no"] bluespace crystal\s in the crystal slots.") - -/obj/machinery/computer/telescience/Initialize(mapload) - . = ..() - if(mapload) - for(var/i = 1; i <= starting_crystals; i++) - crystals += new /obj/item/weapon/ore/bluespace_crystal/artificial(null) // starting crystals - -/obj/machinery/computer/telescience/attack_paw(mob/user) - to_chat(user, "You are too primitive to use this computer!") - return - -/obj/machinery/computer/telescience/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/weapon/ore/bluespace_crystal)) - if(crystals.len >= max_crystals) - to_chat(user, "There are not enough crystal slots.") - return - if(!user.drop_item()) - return - crystals += W - W.loc = null - user.visible_message("[user] inserts [W] into \the [src]'s crystal slot.", "You insert [W] into \the [src]'s crystal slot.") - updateDialog() - else if(istype(W, /obj/item/device/gps)) - if(!inserted_gps) - if(!user.transferItemToLoc(W, src)) - return - inserted_gps = W - user.visible_message("[user] inserts [W] into \the [src]'s GPS device slot.", "You insert [W] into \the [src]'s GPS device slot.") - else if(istype(W, /obj/item/device/multitool)) - var/obj/item/device/multitool/M = W - if(M.buffer && istype(M.buffer, /obj/machinery/telepad)) - telepad = M.buffer - M.buffer = null - to_chat(user, "You upload the data from the [W.name]'s buffer.") - else - return ..() - -/obj/machinery/computer/telescience/attack_ai(mob/user) - src.attack_hand(user) - -/obj/machinery/computer/telescience/attack_hand(mob/user) - if(..()) - return - interact(user) - -/obj/machinery/computer/telescience/interact(mob/user) - var/t - if(!telepad) - in_use = 0 //Yeah so if you deconstruct teleporter while its in the process of shooting it wont disable the console - t += "
No telepad located.
Please add telepad data.

" - else - if(inserted_gps) - t += "Eject GPS" - t += "Set GPS memory" - else - t += "Eject GPS" - t += "Set GPS memory" - t += "
[temp_msg]

" - t += "Set Bearing" - t += "
[rotation]°
" - t += "Set Elevation" - t += "
[angle]°
" - t += "Set Power" - t += "
" - - for(var/i = 1; i <= power_options.len; i++) - if(crystals.len + telepad.efficiency < i) - t += "[power_options[i]]" - continue - if(power == power_options[i]) - t += "[power_options[i]]" - continue - t += "[power_options[i]]" - t += "
" - - t += "Set Sector" - t += "
[z_co ? z_co : "NULL"]
" - - t += "
Send" - t += " Receive" - t += "
Recalibrate Crystals Eject Crystals" - - // Information about the last teleport - t += "
" - if(!last_tele_data) - t += "No teleport data found." - else - t += "Source Location: ([last_tele_data.src_x], [last_tele_data.src_y])
" - //t += "Distance: [round(last_tele_data.distance, 0.1)]m
" - t += "Time: [round(last_tele_data.time, 0.1)] secs
" - t += "
" - - var/datum/browser/popup = new(user, "telesci", name, 300, 500) - popup.set_content(t) - popup.open() - -/obj/machinery/computer/telescience/proc/sparks() - if(telepad) - do_sparks(5, TRUE, get_turf(telepad)) - -/obj/machinery/computer/telescience/proc/telefail() - sparks() - visible_message("The telepad weakly fizzles.") - -/obj/machinery/computer/telescience/proc/doteleport(mob/user) - - if(teleport_cooldown > world.time) - temp_msg = "Telepad is recharging power.
Please wait [round((teleport_cooldown - world.time) / 10)] seconds." - return - - if(teleporting) - temp_msg = "Telepad is in use.
Please wait." - return - - if(telepad) - - var/truePower = Clamp(power + power_off, 1, 1000) - var/trueRotation = rotation + rotation_off - var/trueAngle = Clamp(angle, 1, 90) - - var/datum/projectile_data/proj_data = projectile_trajectory(telepad.x, telepad.y, trueRotation, trueAngle, truePower) - last_tele_data = proj_data - - var/trueX = Clamp(round(proj_data.dest_x, 1), 1, world.maxx) - var/trueY = Clamp(round(proj_data.dest_y, 1), 1, world.maxy) - var/spawn_time = round(proj_data.time) * 10 - - var/turf/target = locate(trueX, trueY, z_co) - last_target = target - var/area/A = get_area(target) - flick("pad-beam", telepad) - - if(spawn_time > 15) // 1.5 seconds - playsound(telepad.loc, 'sound/weapons/flash.ogg', 25, 1) - // Wait depending on the time the projectile took to get there - teleporting = 1 - temp_msg = "Powering up bluespace crystals.
Please wait." - - - spawn(round(proj_data.time) * 10) // in seconds - if(!telepad) - return - if(telepad.stat & NOPOWER) - return - teleporting = 0 - teleport_cooldown = world.time + (power * 2) - teles_left -= 1 - - // use a lot of power - use_power(power * 10) - - do_sparks(5, TRUE, get_turf(telepad)) - - temp_msg = "Teleport successful.
" - if(teles_left < 10) - temp_msg += "
Calibration required soon." - else - temp_msg += "Data printed below." - - do_sparks(5, TRUE, get_turf(target)) - - var/turf/source = target - var/turf/dest = get_turf(telepad) - var/log_msg = "" - log_msg += ": [key_name(user)] has teleported " - - if(sending) - source = dest - dest = target - - flick("pad-beam", telepad) - playsound(telepad.loc, 'sound/weapons/emitter2.ogg', 25, 1, extrarange = 3, falloff = 5) - for(var/atom/movable/ROI in source) - // if is anchored, don't let through - if(ROI.anchored) - if(isliving(ROI)) - var/mob/living/L = ROI - if(L.buckled) - // TP people on office chairs - if(L.buckled.anchored) - continue - - log_msg += "[key_name(L)] (on a chair), " - else - continue - else if(!isobserver(ROI)) - continue - if(ismob(ROI)) - var/mob/T = ROI - log_msg += "[key_name(T)], " - else - log_msg += "[ROI.name]" - if (istype(ROI, /obj/structure/closet)) - var/obj/structure/closet/C = ROI - log_msg += " (" - for(var/atom/movable/Q as mob|obj in C) - if(ismob(Q)) - log_msg += "[key_name(Q)], " - else - log_msg += "[Q.name], " - if (dd_hassuffix(log_msg, "(")) - log_msg += "empty)" - else - log_msg = dd_limittext(log_msg, length(log_msg) - 2) - log_msg += ")" - log_msg += ", " - do_teleport(ROI, dest) - - if (dd_hassuffix(log_msg, ", ")) - log_msg = dd_limittext(log_msg, length(log_msg) - 2) - else - log_msg += "nothing" - log_msg += " [sending ? "to" : "from"] [trueX], [trueY], [z_co] ([A ? A.name : "null area"])" - investigate_log(log_msg, "telesci") - updateDialog() - -/obj/machinery/computer/telescience/proc/teleport(mob/user) - if(rotation == null || angle == null || z_co == null) - temp_msg = "ERROR!
Set a angle, rotation and sector." - return - if(power <= 0) - telefail() - temp_msg = "ERROR!
No power selected!" - return - if(angle < 1 || angle > 90) - telefail() - temp_msg = "ERROR!
Elevation is less than 1 or greater than 90." - return - if(z_co == ZLEVEL_CENTCOM || z_co < 1 || z_co > ZLEVEL_SPACEMAX) - telefail() - temp_msg = "ERROR! Sector is outside known time and space!" - return - if(teles_left > 0) - doteleport(user) - else - telefail() - temp_msg = "ERROR!
Calibration required." - return - return - -/obj/machinery/computer/telescience/proc/eject() - for(var/obj/item/I in crystals) - I.loc = src.loc - crystals -= I - power = 0 - -/obj/machinery/computer/telescience/Topic(href, href_list) - if(..()) - return - if(!telepad) - updateDialog() - return - if(telepad.panel_open) - temp_msg = "Telepad undergoing physical maintenance operations." - - if(href_list["setrotation"]) - var/new_rot = input("Please input desired bearing in degrees.", name, rotation) as num - if(..()) // Check after we input a value, as they could've moved after they entered something - return - rotation = Clamp(new_rot, -900, 900) - rotation = round(rotation, 0.01) - - if(href_list["setangle"]) - var/new_angle = input("Please input desired elevation in degrees.", name, angle) as num - if(..()) - return - angle = Clamp(round(new_angle, 0.1), 1, 9999) - - if(href_list["setpower"]) - var/index = href_list["setpower"] - index = text2num(index) - if(index != null && power_options[index]) - if(crystals.len + telepad.efficiency >= index) - power = power_options[index] - - if(href_list["setz"]) - var/new_z = input("Please input desired sector.", name, z_co) as num - if(..()) - return - z_co = Clamp(round(new_z), 1, 10) - - if(href_list["ejectGPS"]) - if(inserted_gps) - inserted_gps.loc = loc - inserted_gps = null - - if(href_list["setMemory"]) - if(last_target && inserted_gps) - inserted_gps.locked_location = last_target - temp_msg = "Location saved." - else - temp_msg = "ERROR!
No data was stored." - - if(href_list["send"]) - sending = 1 - teleport(usr) - - if(href_list["receive"]) - sending = 0 - teleport(usr) - - if(href_list["recal"]) - recalibrate() - sparks() - temp_msg = "NOTICE:
Calibration successful." - - if(href_list["eject"]) - eject() - temp_msg = "NOTICE:
Bluespace crystals ejected." - - updateDialog() - -/obj/machinery/computer/telescience/proc/recalibrate() - teles_left = rand(30, 40) - //angle_off = rand(-25, 25) - power_off = rand(-4, 0) - rotation_off = rand(-10, 10) diff --git a/code/world.dm b/code/world.dm index 42326f6ba69..353d8198183 100644 --- a/code/world.dm +++ b/code/world.dm @@ -59,6 +59,7 @@ load_mode() load_motd() load_admins() + load_menu() if(config.usewhitelist) load_whitelist() LoadBans() @@ -341,5 +342,6 @@ status = s + /world/proc/has_round_started() return SSticker.HasRoundStarted() diff --git a/config/admins.txt b/config/admins.txt index d90c33bcda7..b4485c08411 100644 --- a/config/admins.txt +++ b/config/admins.txt @@ -127,4 +127,5 @@ Sweaterkittens = Game Master Feemjmeem = Game Master JStheguy = Game Master excessiveuseofcobby = Game Master -Plizzard = Game Master \ No newline at end of file +Plizzard = Game Master +octareenroon91 = Game Master diff --git a/html/changelog.html b/html/changelog.html index 222dbcc4afd..0b7aa971681 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -55,6 +55,124 @@ -->
+

08 May 2017

+

Dorsisdwarf updated:

+
    +
  • Fixed being unable to hit museum cases in melee
  • +
+ +

07 May 2017

+

Anonmare updated:

+
    +
  • Removes organic check from mediborg storage container
  • +
+

Joan updated:

+
    +
  • Cultists of Nar-sie have their own language. The language's key is ",n".
  • +
  • Sigils of Transgression are slightly brighter.
  • +
+

Moonlighting Mac updated:

+
    +
  • Due to budget cuts and oil synthesis replacing expensive processes of digging up haunted primeval ashwalker burial grounds, Nanotransen has taught chemists how to make plastic from chemicals in the hopes that new plastic products can reduce expenditure on metal and glass.
  • +
  • you can now make plastic sheets from chemistry out of heated up crude oil, ash & sodium chloride
  • +
+

MrStonedOne updated:

+
    +
  • fixed bug that caused an infinite connection loop when connecting on a new computer or computer with recently changed hardware.
  • +
  • Moved to a new system to make top menu items easier to edit.
  • +
  • Moved icon size stuff to a sub menu
  • +
  • Added option to change icon scaling mode.
  • +
  • Byond added fancy shader supported scaling(Point Sampling), this sucks because it's blurry, the default for /tg/station has changed back to the old nearest neighbor scaling.
  • +
+

Penguaro updated:

+
    +
  • Adjusted the Examine description of the right-most tiles of the Space Station 13 sign to be consistent with the rest of the tiles.
  • +
  • Anywhere there was lava below a tile on the station is now space.
  • +
+

Robustin updated:

+
    +
  • c4 planting is now 40% faster
  • +
+

coiax updated:

+
    +
  • Smartfridges no longer magically gain medicines if deconstructed and rebuilt.
  • +
  • Ash walkers now know and speak Draconic by default, but still know Galactic Common. Remember, Galcom's language key is ",0" and you can review your known languages with the Language Menu.
  • +
+

octareenroon91 updated:

+
    +
  • Showers and sinks added to gulag and mining station for hygiene and fire safety.
  • +
  • Watertanks added to mining station for convenient fire extinguisher refill.
  • +
+ +

06 May 2017

+

4dplanner updated:

+
    +
  • Internal Affairs Agents now obtain the kill objectives of their targets when they die.
  • +
  • Internal Affairs Agents now have an integrated nanotrasen pinpointer that tracks their target at distances further than ten squares.
  • +
  • Internal Affairs Agents will now lose any restrictions on collateral damage and gain a "Die a glorious death" objective upon becoming the last man standing, and revert if any of their targets are cloned.
  • +
+

BeeSting12 updated:

+
    +
  • Pubbystation no longer has two airlocks stacked on top of each other leading into xenobiology's kill room.
  • +
  • Deltastation's chemistry now has a screwdriver.
  • +
  • Deltastation's morgue no longer has a blue tile.
  • +
  • Deltastation's chemistry smart fridge is no longer blocked by a disposal unit.
  • +
+

Cyberboss updated:

+
    +
  • Teslas now give off light
  • +
+

Joan updated:

+
    +
  • Sigils of Transgression are slightly more visible and glow very faintly.
  • +
+

LanCartwright updated:

+
    +
  • Reduction to nutrition when consuming Miner's salve.
  • +
  • Stun/weaken that bypasses bugged chemical protection when consuming Miner's salve.
  • +
+

Lzimann updated:

+
    +
  • Telescience is no more.
  • +
+

PKPenguin321 updated:

+
    +
  • Undid the gang gamemode removal.
  • +
+

QualityVan updated:

+
    +
  • Cargo can order restocking units for NanoMed vending machines
  • +
  • NanoMed vending machines can be build and unbuilt
  • +
+

Robustin updated:

+
    +
  • Blood Cultists can now attempt to claim the position of Cult Master with the approval of a majority of their brethren.
  • +
  • The Cult Master has access to unique blood magic that will aid them in leading the cult to victory:
  • +
  • The Cult Master can mark targets, allowing the entire cult to track them down.
  • +
  • The Cult Master has single-use, noisy, and overt channeled spell that will summon the entire cult to their location.
  • +
  • All cultists gain a HUD alert that will assist them in completing their objectives.
  • +
  • Constructs created by soul shards will now be able to track their master.
  • +
  • Cultists created outside of cult mode will now get a working sacrifice and summon objective and the summon rune will no longer fail outside of cult mode.
  • +
  • Gang Dominator max time is now 8 minutes down from 15m
  • +
  • Gang Tagging now reduces the timer by 9 seconds per territory, down from 12 seconds.
  • +
+

coiax updated:

+
    +
  • Adamantine golems have special vocal cords that allow them to send one-way messages to all golems, due to fragments of resonating adamantine in their heads. Both of these are organs, and can be removed and put in other species.
  • +
  • You can use adamantine vocal cords by prefixing your message with ":x".
  • +
  • Xenobiology is no longer capable of making golem runes with plasma. Instead, inject plasma into the adamantine slime core to get bars of adamantine which you can then craft into an incomplete golem shell. Add 10 sheets of suitable material to finish the shell.
  • +
  • The metal adamantine is also quite valuable when sold via cargo.
  • +
  • If you know more than the usual number of languages, you'll now remember them after you get cloned.
  • +
  • Humans turning into monkeys will not suddenly be able to speak the monkey language, and will continue to understand and speak human.
  • +
  • Ghosts can now modify their own understood languages with the language menu.
  • +
  • Any mob can also open their language menu with the IC->Open Language Menu verb.
  • +
  • Silicons can now understand Draconic as part of their internal databases.
  • +
  • Golems no longer have underwear, undershorts or socks.
  • +
  • The lavaland Seed Vault ruin can spawn only once.
  • +
  • It is now possible to make plastic golems, who are made of a material flexible enough to crawl through vents (while not carrying equipment). They also can pass through plastic flaps.
  • +
  • Fixed adamantine vocal cord (F) links not working for observers.
  • +
+

05 May 2017

Incoming updated:

    @@ -1266,79 +1384,6 @@
    • Wannabe ninjas have been found carrying an experimental chameleon belt. The Spider clan has disavowed any involvement.
    - -

    06 March 2017

    -

    Cyberboss updated:

    -
      -
    • Map rotation has been made smoother
    • -
    -

    Gun Hog updated:

    -
      -
    • The Aux Base Construction Console now directs to the correct Base Management Console.
    • -
    • The missing Science Department access has been added to the Auxiliary Base Management Console.
    • -
    -

    Hyena updated:

    -
      -
    • Space bar is out of bussiness
    • -
    -

    MrStonedOne updated:

    -
      -
    • patched a hacky workaround for /vg/lights memory leaking crashing the server
    • -
    -

    Penguaro updated:

    -
      -
    • Changed DIR of Gas Filter for O2 in Waste Loop from 1 to 4
    • -
    -

    Sligneris updated:

    -
      -
    • 'xeno queen' AI hologram now actually uses the xeno queen sprite as a reference
    • -
    -

    Tofa01 updated:

    -
      -
    • [Omega] Fixes missing walls and wires new dock to the powergrid
    • -
    -

    XDTM updated:

    -
      -
    • Changelings can now click their fake clothing to remove it, without needing to drop the full disguise.
    • -
    -

    coiax updated:

    -
      -
    • The Bardrone and Barmaid are neutral, even in the face of reality altering elder gods.
    • -
    - -

    04 March 2017

    -

    Cyberboss updated:

    -
      -
    • You can build lattice in space again
    • -
    -

    Hyena updated:

    -
      -
    • Detective revolver/ammo now starts in their shoulder holster
    • -
    -

    Joan updated:

    -
      -
    • Weaker cult talismans take less time to imbue.
    • -
    -

    PJB3005 updated:

    -
      -
    • Rebased to /vg/station lighting code.
    • -
    -

    Supermichael777 updated:

    -
      -
    • Grey security uniforms have unique names and descriptions
    • -
    -

    Tofa01 updated:

    -
      -
    • Adds the new Centcomm Raven Battlecruiser to the purchasable shuttle list buy now get one free!
    • -
    -

    coiax updated:

    -
      -
    • CTF players start with their helmet toggled off, better to see the whites of their opponents eyes. Very briefly.
    • -
    • Existing CTF barricades are repaired between rounds, and deploy instantly when replaced.
    • -
    • Healing non-critical CTF damage is faster. Remember though, if you drop into crit, YOU DIE.
    • -
    • Admin ghosts can just click directly on the CTF controller to enable them, in addition to using the Secrets panel.
    • -
    • Cyborg radios can no longer have their inaccessible wires pulsed by EMPs.
    • -
GoonStation 13 Development Team diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 2193515d58f..7ff5e81cff2 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -10906,3 +10906,117 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. cause vomiting of organs. - tweak: Committing suicide with a gas pump can now shoot out random organs. - bugfix: Toxic vomit now shows up as intended. +2017-05-06: + 4dplanner: + - rscadd: Internal Affairs Agents now obtain the kill objectives of their targets + when they die. + - rscadd: Internal Affairs Agents now have an integrated nanotrasen pinpointer that + tracks their target at distances further than ten squares. + - rscadd: Internal Affairs Agents will now lose any restrictions on collateral damage + and gain a "Die a glorious death" objective upon becoming the last man standing, + and revert if any of their targets are cloned. + BeeSting12: + - bugfix: Pubbystation no longer has two airlocks stacked on top of each other leading + into xenobiology's kill room. + - rscadd: Deltastation's chemistry now has a screwdriver. + - bugfix: Deltastation's morgue no longer has a blue tile. + - bugfix: Deltastation's chemistry smart fridge is no longer blocked by a disposal + unit. + Cyberboss: + - tweak: Teslas now give off light + Joan: + - balance: Sigils of Transgression are slightly more visible and glow very faintly. + LanCartwright: + - rscadd: Reduction to nutrition when consuming Miner's salve. + - rscdel: Stun/weaken that bypasses bugged chemical protection when consuming Miner's + salve. + Lzimann: + - rscdel: Telescience is no more. + PKPenguin321: + - rscadd: Undid the gang gamemode removal. + QualityVan: + - rscadd: Cargo can order restocking units for NanoMed vending machines + - rscadd: NanoMed vending machines can be build and unbuilt + Robustin: + - rscadd: Blood Cultists can now attempt to claim the position of Cult Master with + the approval of a majority of their brethren. + - rscadd: 'The Cult Master has access to unique blood magic that will aid them in + leading the cult to victory:' + - rscadd: The Cult Master can mark targets, allowing the entire cult to track them + down. + - rscadd: The Cult Master has single-use, noisy, and overt channeled spell that + will summon the entire cult to their location. + - rscadd: All cultists gain a HUD alert that will assist them in completing their + objectives. + - rscadd: Constructs created by soul shards will now be able to track their master. + - rscadd: Cultists created outside of cult mode will now get a working sacrifice + and summon objective and the summon rune will no longer fail outside of cult + mode. + - tweak: Gang Dominator max time is now 8 minutes down from 15m + - tweak: Gang Tagging now reduces the timer by 9 seconds per territory, down from + 12 seconds. + coiax: + - rscadd: Adamantine golems have special vocal cords that allow them to send one-way + messages to all golems, due to fragments of resonating adamantine in their heads. + Both of these are organs, and can be removed and put in other species. + - rscadd: You can use adamantine vocal cords by prefixing your message with ":x". + - rscdel: Xenobiology is no longer capable of making golem runes with plasma. Instead, + inject plasma into the adamantine slime core to get bars of adamantine which + you can then craft into an incomplete golem shell. Add 10 sheets of suitable + material to finish the shell. + - experiment: The metal adamantine is also quite valuable when sold via cargo. + - bugfix: If you know more than the usual number of languages, you'll now remember + them after you get cloned. + - rscdel: Humans turning into monkeys will not suddenly be able to speak the monkey + language, and will continue to understand and speak human. + - rscadd: Ghosts can now modify their own understood languages with the language + menu. + - rscadd: Any mob can also open their language menu with the IC->Open Language Menu + verb. + - rscadd: Silicons can now understand Draconic as part of their internal databases. + - bugfix: Golems no longer have underwear, undershorts or socks. + - tweak: The lavaland Seed Vault ruin can spawn only once. + - rscadd: It is now possible to make plastic golems, who are made of a material + flexible enough to crawl through vents (while not carrying equipment). They + also can pass through plastic flaps. + - bugfix: Fixed adamantine vocal cord (F) links not working for observers. +2017-05-07: + Anonmare: + - tweak: Removes organic check from mediborg storage container + Joan: + - rscadd: Cultists of Nar-sie have their own language. The language's key is ",n". + - balance: Sigils of Transgression are slightly brighter. + Moonlighting Mac: + - experiment: Due to budget cuts and oil synthesis replacing expensive processes + of digging up haunted primeval ashwalker burial grounds, Nanotransen has taught + chemists how to make plastic from chemicals in the hopes that new plastic products + can reduce expenditure on metal and glass. + - rscadd: you can now make plastic sheets from chemistry out of heated up crude + oil, ash & sodium chloride + MrStonedOne: + - bugfix: fixed bug that caused an infinite connection loop when connecting on a + new computer or computer with recently changed hardware. + - rscadd: Moved to a new system to make top menu items easier to edit. + - tweak: Moved icon size stuff to a sub menu + - rscadd: Added option to change icon scaling mode. + - bugfix: Byond added fancy shader supported scaling(Point Sampling), this sucks + because it's blurry, the default for /tg/station has changed back to the old + nearest neighbor scaling. + Penguaro: + - bugfix: Adjusted the Examine description of the right-most tiles of the Space + Station 13 sign to be consistent with the rest of the tiles. + - bugfix: Anywhere there was lava below a tile on the station is now space. + Robustin: + - tweak: c4 planting is now 40% faster + coiax: + - bugfix: Smartfridges no longer magically gain medicines if deconstructed and rebuilt. + - rscadd: Ash walkers now know and speak Draconic by default, but still know Galactic + Common. Remember, Galcom's language key is ",0" and you can review your known + languages with the Language Menu. + octareenroon91: + - rscadd: Showers and sinks added to gulag and mining station for hygiene and fire + safety. + - rscadd: Watertanks added to mining station for convenient fire extinguisher refill. +2017-05-08: + Dorsisdwarf: + - bugfix: Fixed being unable to hit museum cases in melee diff --git a/html/changelogs/AutoChangeLog-pr-26772.yml b/html/changelogs/AutoChangeLog-pr-26772.yml deleted file mode 100644 index 608a0902493..00000000000 --- a/html/changelogs/AutoChangeLog-pr-26772.yml +++ /dev/null @@ -1,14 +0,0 @@ -author: "coiax" -delete-after: True -changes: - - rscadd: "Adamantine golems have special vocal cords that allow them to send -one-way messages to all golems, due to fragments of resonating -adamantine in their heads. Both of these are organs, and can be removed -and put in other species." - - rscadd: "You can use adamantine vocal cords by prefixing your message with -\":x\"." - - rscdel: "Xenobiology is no longer capable of making golem runes with plasma. -Instead, inject plasma into the adamantine slime core to get bars of adamantine -which you can then craft into an incomplete golem shell. Add 10 sheets of -suitable material to finish the shell." - - experiment: "The metal adamantine is also quite valuable when sold via cargo." diff --git a/html/changelogs/AutoChangeLog-pr-26776.yml b/html/changelogs/AutoChangeLog-pr-26776.yml deleted file mode 100644 index bf9411c7a16..00000000000 --- a/html/changelogs/AutoChangeLog-pr-26776.yml +++ /dev/null @@ -1,9 +0,0 @@ -author: "coiax" -delete-after: True -changes: - - bugfix: "If you know more than the usual number of languages, you'll now remember them after you get cloned." - - rscdel: "Humans turning into monkeys will not suddenly be able to speak the monkey language, and will continue to understand and speak human." - - rscadd: "Ghosts can now modify their own understood languages with the -language menu." - - rscadd: "Any mob can also open their language menu with the IC->Open Language Menu verb." - - rscadd: "Silicons can now understand Draconic as part of their internal databases." diff --git a/html/changelogs/AutoChangeLog-pr-26854.yml b/html/changelogs/AutoChangeLog-pr-26854.yml new file mode 100644 index 00000000000..8aeb9ff2957 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-26854.yml @@ -0,0 +1,8 @@ +author: "Moonlighting Mac says" +delete-after: True +changes: + - tweak: "After a recent trade fair, employees have took it upon themselves to learn how to craft leather objects out of finished leather sheets, for novices they are pretty good at it! Try it yourself!" + - rscadd: "Muzzles to silence people are now craftable out of leather sheets." + - rscdel: "You can no longer print off designs from a bio-generator that can now be crafted out of leather sheets." + - tweak: "Complete sheets of leather can be printed from the bio-generator for 150 biomass to accommodate for a loss of designs." + - experiment: "Specialist objects from the leather & cloth category bio-generator category were not removed as to encourage interdepartmental interaction & balance." diff --git a/html/changelogs/AutoChangeLog-pr-26872.yml b/html/changelogs/AutoChangeLog-pr-26872.yml deleted file mode 100644 index 031ec641511..00000000000 --- a/html/changelogs/AutoChangeLog-pr-26872.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "BeeSting12" -delete-after: True -changes: - - bugfix: "Pubbystation no longer has two airlocks stacked on top of each other leading into xenobiology's kill room." diff --git a/html/changelogs/AutoChangeLog-pr-26894.yml b/html/changelogs/AutoChangeLog-pr-26894.yml deleted file mode 100644 index b9cfd4624f5..00000000000 --- a/html/changelogs/AutoChangeLog-pr-26894.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "coiax" -delete-after: True -changes: - - bugfix: "Golems no longer have underwear, undershorts or socks." diff --git a/html/changelogs/AutoChangeLog-pr-26895.yml b/html/changelogs/AutoChangeLog-pr-26895.yml deleted file mode 100644 index e3777f40640..00000000000 --- a/html/changelogs/AutoChangeLog-pr-26895.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Joan" -delete-after: True -changes: - - balance: "Sigils of Transgression are slightly more visible and glow very faintly." diff --git a/html/changelogs/AutoChangeLog-pr-26899.yml b/html/changelogs/AutoChangeLog-pr-26899.yml deleted file mode 100644 index cd4b8f7864f..00000000000 --- a/html/changelogs/AutoChangeLog-pr-26899.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Cyberboss" -delete-after: True -changes: - - tweak: "Teslas now give off light" diff --git a/html/changelogs/AutoChangeLog-pr-26915.yml b/html/changelogs/AutoChangeLog-pr-26915.yml deleted file mode 100644 index fa85366e4cc..00000000000 --- a/html/changelogs/AutoChangeLog-pr-26915.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "PKPenguin321" -delete-after: True -changes: - - rscadd: "Undid the gang gamemode removal." diff --git a/html/changelogs/AutoChangeLog-pr-27014.yml b/html/changelogs/AutoChangeLog-pr-27014.yml new file mode 100644 index 00000000000..0c1695adbcb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-27014.yml @@ -0,0 +1,4 @@ +author: "ninjanomnom" +delete-after: True +changes: + - tweak: "Highlander no longer breaks your speakers" diff --git a/icons/effects/64x64.dmi b/icons/effects/64x64.dmi index 347bccc9425..361cfff40c0 100644 Binary files a/icons/effects/64x64.dmi and b/icons/effects/64x64.dmi differ diff --git a/icons/effects/cult_target.dmi b/icons/effects/cult_target.dmi new file mode 100644 index 00000000000..650feb33613 Binary files /dev/null and b/icons/effects/cult_target.dmi differ diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index d684794ee85..fe537a8e739 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/misc/language.dmi b/icons/misc/language.dmi index e961c351912..081bbf1aa3c 100644 Binary files a/icons/misc/language.dmi and b/icons/misc/language.dmi differ diff --git a/icons/mob/actions.dmi b/icons/mob/actions.dmi index 988ae8bdc83..58c877d4d95 100644 Binary files a/icons/mob/actions.dmi and b/icons/mob/actions.dmi differ diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi index 29c527b123d..6d05b890398 100644 Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ diff --git a/icons/mob/jungle/arachnid.dmi b/icons/mob/jungle/arachnid.dmi new file mode 100644 index 00000000000..a1db8e351f0 Binary files /dev/null and b/icons/mob/jungle/arachnid.dmi differ diff --git a/icons/mob/screen_alert.dmi b/icons/mob/screen_alert.dmi index 46666cdd5fc..13f16aae59a 100644 Binary files a/icons/mob/screen_alert.dmi and b/icons/mob/screen_alert.dmi differ diff --git a/icons/obj/vending_restock.dmi b/icons/obj/vending_restock.dmi index aa9f2873f02..eac7b8a2668 100644 Binary files a/icons/obj/vending_restock.dmi and b/icons/obj/vending_restock.dmi differ diff --git a/interface/menu.dm b/interface/menu.dm new file mode 100644 index 00000000000..4bebd5b2023 --- /dev/null +++ b/interface/menu.dm @@ -0,0 +1,203 @@ +/* +/datum/menu/Example/verb/Example() + set name = "" //if this starts with @ the verb is not created and name becomes the command to invoke. + set desc = "" //desc is the text given to this entry in the menu + //You can not use src in these verbs. It will be the menu at compile time, but the client at runtime. +*/ + +GLOBAL_LIST_EMPTY(menulist) + +/world/proc/load_menu() + for (var/typepath in subtypesof(/datum/menu)) + new typepath() + +/datum/menu + var/name + var/list/children + var/datum/menu/myparent + var/list/verblist + var/checkbox = CHECKBOX_NONE //checkbox type. + var/default //default checked type. + //Set to true to append our children to our parent, + //Rather then add us as a node (used for having more then one checkgroups in the same menu) + var/abstract = FALSE + +/datum/menu/New() + var/ourentry = GLOB.menulist[type] + children = list() + verblist = list() + if (ourentry) + if (islist(ourentry)) //some of our childern already loaded + Add_children(ourentry) + else + stack_trace("Menu item double load: [type]") + qdel(src) + return + + GLOB.menulist[type] = src + + Load_verbs(type, typesof("[type]/verb")) + + var/datum/menu/parent = GLOB.menulist[parent_type] + if (!parent) + GLOB.menulist[parent_type] = list(src) + else if (islist(parent)) + parent += src + else + parent.Add_children(list(src)) + +/datum/menu/proc/Set_parent(datum/menu/parent) + myparent = parent + if (abstract) + myparent.Add_children(children) + var/list/verblistoftypes = list() + for(var/thing in verblist) + LAZYADD(verblistoftypes[verblist[thing]], thing) + + for(var/verbparenttype in verblistoftypes) + myparent.Load_verbs(verbparenttype, verblistoftypes[verbparenttype]) + +/datum/menu/proc/Add_children(list/kids) + if (abstract && myparent) + myparent.Add_children(kids) + return + + for(var/thing in kids) + var/datum/menu/menuitem = thing + menuitem.Set_parent(src) + if (!menuitem.abstract) + children += menuitem + +/datum/menu/proc/Load_verbs(verb_parent_type, list/verbs) + if (abstract && myparent) + myparent.Load_verbs(verb_parent_type, verbs) + return + + for (var/verbpath in verbs) + verblist[verbpath] = verb_parent_type + +/datum/menu/proc/Generate_list(client/C) + . = list() + if (length(children)) + for (var/thing in children) + var/datum/menu/child = thing + var/list/childlist = child.Generate_list(C) + if (childlist) + var/childname = "[child]" + if (childname == "[child.type]") + var/list/tree = splittext(childname, "/") + childname = tree[tree.len] + .[child.type] = "parent=[url_encode(type)];name=[url_encode(childname)]" + . += childlist + + + + for (var/thing in verblist) + var/atom/verb/verbpath = thing + if (!verbpath) + stack_trace("Bad VERB in [type] verblist: [english_list(verblist)]") + var/list/entry = list() + entry["parent"] = "[type]" + entry["name"] = verbpath.desc + if (copytext(verbpath.name,1,2) == "@") + entry["command"] = copytext(verbpath.name,2) + else + entry["command"] = replacetext(verbpath.name, " ", "-") + var/datum/menu/verb_true_parent = GLOB.menulist[verblist[verbpath]] + var/true_checkbox = verb_true_parent.checkbox + if (true_checkbox != CHECKBOX_NONE) + var/checkedverb = verb_true_parent.Get_checked(C) + if (true_checkbox == CHECKBOX_GROUP) + if (verbpath == checkedverb) + entry["is-checked"] = TRUE + else + entry["is-checked"] = FALSE + else if (true_checkbox == CHECKBOX_TOGGLE) + entry["is-checked"] = checkedverb + + entry["command"] = ".updatemenuchecked \"[verb_true_parent.type]\" \"[verbpath]\"\n[entry["command"]]" + entry["can-check"] = TRUE + entry["group"] = "[verb_true_parent.type]" + .[verbpath] = list2params(entry) + +/datum/menu/proc/Get_checked(client/C) + return C.prefs.menuoptions[type] || default || FALSE + +/datum/menu/proc/Load_checked(client/C) //Loads the checked menu item into a new client. Used by icon menus to invoke the checked item. + return + +/datum/menu/proc/Set_checked(client/C, verbpath) + if (checkbox == CHECKBOX_GROUP) + C.prefs.menuoptions[type] = verbpath + C.prefs.save_preferences() + else if (checkbox == CHECKBOX_TOGGLE) + var/checked = Get_checked(C) + C.prefs.menuoptions[type] = !checked + C.prefs.save_preferences() + winset(C, "[verbpath]", "is-checked = [!checked]") + +/client/verb/updatemenuchecked(menutype as text, verbpath as text) + set name = ".updatemenuchecked" + menutype = text2path(menutype) + verbpath = text2path(verbpath) + if (!menutype || !verbpath) + return + var/datum/menu/M = GLOB.menulist[menutype] + if (!M) + return + if (!(verbpath in typesof("[menutype]/verb"))) + return + M.Set_checked(src, verbpath) + + +/datum/menu/Icon/Load_checked(client/C) //So we can be lazy, we invoke the "checked" menu item on menu load. + var/atom/verb/verbpath = Get_checked(C) + if (!verbpath || !(verbpath in typesof("[type]/verb"))) + return + if (copytext(verbpath.name,1,2) == "@") + winset(C, null, "command = [copytext(verbpath.name,2)]") + else + winset(C, null, "command = [replacetext(verbpath.name, " ", "-")]") + +/datum/menu/Icon/Size + checkbox = CHECKBOX_GROUP + default = /datum/menu/Icon/Size/verb/iconstretchtofit + +/datum/menu/Icon/Size/verb/iconstretchtofit() + set name = "@.winset \"mapwindow.map.icon-size=0\"" + set desc = "&Auto (stretch-to-fit)" + +/datum/menu/Icon/Size/verb/icon96() + set name = "@.winset \"mapwindow.map.icon-size=96\"" + set desc = "&96x96 (3x)" + +/datum/menu/Icon/Size/verb/icon64() + set name = "@.winset \"mapwindow.map.icon-size=64\"" + set desc = "&64x64 (2x)" + +/datum/menu/Icon/Size/verb/icon48() + set name = "@.winset \"mapwindow.map.icon-size=48\"" + set desc = "&48x48 (1.5x)" + +/datum/menu/Icon/Size/verb/icon32() + set name = "@.winset \"mapwindow.map.icon-size=32\"" + set desc = "&32x32 (1x)" + + +/datum/menu/Icon/Scaling + checkbox = CHECKBOX_GROUP + name = "Scaling Mode" + default = /datum/menu/Icon/Scaling/verb/NN + +/datum/menu/Icon/Scaling/verb/NN() + set name = "@.winset \"mapwindow.map.zoom-mode=distort\"" + set desc = "Nearest Neighbor" + +/datum/menu/Icon/Scaling/verb/PS() + set name = "@.winset \"mapwindow.map.zoom-mode=normal\"" + set desc = "Point Sampling" + +/datum/menu/Icon/Scaling/verb/BL() + set name = "@.winset \"mapwindow.map.zoom-mode=blur\"" + set desc = "Bilinear" + diff --git a/interface/skin.dmf b/interface/skin.dmf index 45fce1f4173..20cc91bb0c8 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -1038,60 +1038,6 @@ menu "menu" group = "" is-disabled = false saved-params = "is-checked" - elem - name = "&Icons" - command = "" - category = "" - is-checked = false - can-check = false - group = "" - is-disabled = false - saved-params = "is-checked" - elem "auto" - name = "&Auto (stretch-to-fit)" - command = ".winset \"mapwindow.map.icon-size=0\"" - category = "&Icons" - is-checked = true - can-check = true - group = "size" - is-disabled = false - saved-params = "is-checked" - elem "icon96" - name = "&96x96 (3x)" - command = ".winset \"mapwindow.map.icon-size=96\"" - category = "&Icons" - is-checked = false - can-check = true - group = "size" - is-disabled = false - saved-params = "is-checked" - elem "icon64" - name = "&64x64 (2x)" - command = ".winset \"mapwindow.map.icon-size=64\"" - category = "&Icons" - is-checked = false - can-check = true - group = "size" - is-disabled = false - saved-params = "is-checked" - elem "icon48" - name = "&48x48 (1.5x)" - command = ".winset \"mapwindow.map.icon-size=48\"" - category = "&Icons" - is-checked = false - can-check = true - group = "size" - is-disabled = false - saved-params = "is-checked" - elem "icon32" - name = "&32x32" - command = ".winset \"mapwindow.map.icon-size=32\"" - category = "&Icons" - is-checked = false - can-check = true - group = "size" - is-disabled = false - saved-params = "is-checked" elem name = "&Help" command = "" @@ -1359,6 +1305,7 @@ window "mapwindow" on-show = "" on-hide = "" style = "" + zoom-mode = "distort" window "infowindow" elem "infowindow" diff --git a/sound/misc/highlander.ogg b/sound/misc/highlander.ogg index 2116f8a9724..01148b55dbf 100644 Binary files a/sound/misc/highlander.ogg and b/sound/misc/highlander.ogg differ diff --git a/sound/misc/highlander_delayed.ogg b/sound/misc/highlander_delayed.ogg index d1cce707811..26453496628 100644 Binary files a/sound/misc/highlander_delayed.ogg and b/sound/misc/highlander_delayed.ogg differ diff --git a/sound/misc/highlander_only_one.ogg b/sound/misc/highlander_only_one.ogg index 3b69d26ade5..6458d10ab65 100644 Binary files a/sound/misc/highlander_only_one.ogg and b/sound/misc/highlander_only_one.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 2462a5985d9..ef14eeafbba 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -45,6 +45,7 @@ #include "code\__DEFINES\maps.dm" #include "code\__DEFINES\math.dm" #include "code\__DEFINES\MC.dm" +#include "code\__DEFINES\menu.dm" #include "code\__DEFINES\misc.dm" #include "code\__DEFINES\mobs.dm" #include "code\__DEFINES\monkeys.dm" @@ -774,7 +775,6 @@ #include "code\game\objects\items\weapons\defib.dm" #include "code\game\objects\items\weapons\dice.dm" #include "code\game\objects\items\weapons\dna_injector.dm" -#include "code\game\objects\items\weapons\explosives.dm" #include "code\game\objects\items\weapons\extinguisher.dm" #include "code\game\objects\items\weapons\flamethrower.dm" #include "code\game\objects\items\weapons\gift.dm" @@ -968,6 +968,7 @@ #include "code\modules\admin\admin_investigate.dm" #include "code\modules\admin\admin_ranks.dm" #include "code\modules\admin\admin_verbs.dm" +#include "code\modules\admin\adminmenu.dm" #include "code\modules\admin\banjob.dm" #include "code\modules\admin\create_mob.dm" #include "code\modules\admin\create_object.dm" @@ -1386,7 +1387,8 @@ #include "code\modules\language\language_menu.dm" #include "code\modules\language\machine.dm" #include "code\modules\language\monkey.dm" -#include "code\modules\language\ratvar.dm" +#include "code\modules\language\narsian.dm" +#include "code\modules\language\ratvarian.dm" #include "code\modules\language\slime.dm" #include "code\modules\language\swarmer.dm" #include "code\modules\language\xenocommon.dm" @@ -1669,6 +1671,7 @@ #include "code\modules\mob\living\simple_animal\hostile\hivebot.dm" #include "code\modules\mob\living\simple_animal\hostile\hostile.dm" #include "code\modules\mob\living\simple_animal\hostile\illusion.dm" +#include "code\modules\mob\living\simple_animal\hostile\jungle_mobs.dm" #include "code\modules\mob\living\simple_animal\hostile\killertomato.dm" #include "code\modules\mob\living\simple_animal\hostile\mecha_pilot.dm" #include "code\modules\mob\living\simple_animal\hostile\mimic.dm" @@ -1962,6 +1965,7 @@ #include "code\modules\shuttle\arrivals.dm" #include "code\modules\shuttle\assault_pod.dm" #include "code\modules\shuttle\computer.dm" +#include "code\modules\shuttle\elevator.dm" #include "code\modules\shuttle\emergency.dm" #include "code\modules\shuttle\ferry.dm" #include "code\modules\shuttle\manipulator.dm" @@ -2059,8 +2063,6 @@ #include "code\modules\surgery\organs\organ_internal.dm" #include "code\modules\surgery\organs\tongue.dm" #include "code\modules\surgery\organs\vocal_cords.dm" -#include "code\modules\telesci\telepad.dm" -#include "code\modules\telesci\telesci_computer.dm" #include "code\modules\tgui\external.dm" #include "code\modules\tgui\states.dm" #include "code\modules\tgui\subsystem.dm" @@ -2096,6 +2098,7 @@ #include "code\modules\zombie\items.dm" #include "code\modules\zombie\organs.dm" #include "interface\interface.dm" +#include "interface\menu.dm" #include "interface\stylesheet.dm" #include "interface\skin.dmf" // END_INCLUDE