diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_alien_nest.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_alien_nest.dmm
index 5c35c78274..6893a41517 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_alien_nest.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_alien_nest.dmm
@@ -34,11 +34,6 @@
/obj/structure/alien/weeds,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/xenonest)
-"ai" = (
-/obj/structure/alien/weeds,
-/obj/structure/alien/egg,
-/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
-/area/ruin/unpowered/xenonest)
"aj" = (
/obj/structure/alien/weeds,
/obj/structure/bed/nest,
@@ -1219,12 +1214,12 @@ an
ac
ac
aJ
-ag
+af
ag
ac
ac
ar
-ai
+ag
ac
an
an
@@ -1269,7 +1264,7 @@ an
an
ac
Dd
-ai
+ag
ak
ag
af
@@ -1298,10 +1293,10 @@ aa
ab
ac
ac
-ai
+ag
ag
at
-ai
+ag
ac
ag
at
@@ -1401,7 +1396,7 @@ ab
ac
af
ag
-af
+ag
ag
af
ac
@@ -1807,7 +1802,7 @@ bD
(25,1,1) = {"
ab
ac
-ai
+ag
ag
JM
ag
@@ -1816,7 +1811,7 @@ ac
ag
ar
ag
-ai
+ag
ac
ab
ab
@@ -1860,7 +1855,7 @@ ab
ac
ac
af
-ai
+ag
af
ag
aF
diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
index 7ad773e601..63d2acdf46 100644
--- a/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
@@ -173,6 +173,7 @@
/obj/structure/stone_tile{
dir = 4
},
+/obj/item/t_scanner/adv_mining_scanner/lesser,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/ash_walkers)
"aC" = (
@@ -186,6 +187,7 @@
},
/obj/structure/closet/crate/medical,
/obj/item/storage/firstaid/regular,
+/obj/item/storage/firstaid/ancient,
/obj/item/reagent_containers/blood/random,
/obj/item/reagent_containers/blood/lizard,
/obj/item/reagent_containers/blood/lizard,
@@ -269,6 +271,7 @@
/area/ruin/unpowered/ash_walkers)
"aP" = (
/obj/structure/stone_tile,
+/obj/item/shard,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/ash_walkers)
"aQ" = (
@@ -575,6 +578,7 @@
/obj/item/stack/sheet/mineral/wood,
/obj/item/stack/sheet/mineral/wood,
/obj/item/seeds/tower,
+/obj/item/seeds/tower,
/turf/open/indestructible/boss,
/area/ruin/unpowered/ash_walkers)
"bx" = (
@@ -1086,7 +1090,7 @@
dir = 8
},
/obj/structure/table/wood,
-/obj/item/twohanded/spear,
+/obj/item/kitchen/knife/combat/bone,
/turf/open/indestructible/boss,
/area/ruin/unpowered/ash_walkers)
"cN" = (
@@ -1142,6 +1146,7 @@
/obj/structure/stone_tile{
dir = 1
},
+/obj/item/surgical_drapes,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/ruin/unpowered/ash_walkers)
"cX" = (
@@ -1301,6 +1306,7 @@
},
/obj/structure/stone_tile/center,
/obj/effect/mapping_helpers/no_lava,
+/obj/item/hatchet,
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
"du" = (
@@ -1392,6 +1398,18 @@
},
/turf/closed/mineral/volcanic/lava_land_surface,
/area/lavaland/surface/outdoors)
+"nS" = (
+/obj/item/toy/cards/deck/syndicate,
+/turf/closed/mineral/volcanic/lava_land_surface,
+/area/lavaland/surface/outdoors)
+"Bv" = (
+/obj/structure/stone_tile{
+ dir = 1
+ },
+/obj/effect/mapping_helpers/no_lava,
+/obj/item/cultivator,
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
+/area/lavaland/surface/outdoors)
(1,1,1) = {"
aa
@@ -1407,7 +1425,7 @@ ah
ah
aa
aa
-ah
+nS
ah
ah
ah
@@ -1558,7 +1576,7 @@ ba
bn
bz
ak
-cb
+Bv
df
bX
co
diff --git a/_maps/RandomRuins/SpaceRuins/deepstorage.dmm b/_maps/RandomRuins/SpaceRuins/deepstorage.dmm
index 0ff30453f2..bf4f0dbfa6 100644
--- a/_maps/RandomRuins/SpaceRuins/deepstorage.dmm
+++ b/_maps/RandomRuins/SpaceRuins/deepstorage.dmm
@@ -170,6 +170,8 @@
/obj/item/storage/box/monkeycubes,
/obj/item/storage/toolbox/drone,
/obj/item/storage/toolbox/drone,
+/obj/item/storage/fancy/candle_box,
+/obj/item/storage/fancy/candle_box,
/obj/effect/turf_decal/delivery,
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plasteel,
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index 23c41131ea..3269d53588 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -29156,6 +29156,7 @@
name = "Station Intercom";
pixel_y = 26
},
+/obj/item/reagent_containers/glass/bottle/mutagen,
/turf/open/floor/plasteel,
/area/hydroponics)
"bbZ" = (
@@ -78869,7 +78870,6 @@
/turf/open/floor/plasteel,
/area/crew_quarters/locker)
"cCb" = (
-/obj/structure/closet/wardrobe/green,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
@@ -78880,6 +78880,7 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
+/obj/structure/closet/wardrobe/mixed,
/turf/open/floor/plasteel/dark,
/area/crew_quarters/locker)
"cCc" = (
@@ -81677,7 +81678,6 @@
/turf/open/floor/plating,
/area/crew_quarters/toilet/restrooms)
"cGL" = (
-/obj/machinery/vending/clothing,
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
@@ -81688,6 +81688,7 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
+/obj/structure/closet/wardrobe/green,
/turf/open/floor/plasteel/dark,
/area/crew_quarters/locker)
"cGM" = (
@@ -81719,6 +81720,7 @@
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
+/obj/machinery/vending/clothing,
/turf/open/floor/plasteel/dark,
/area/crew_quarters/locker)
"cGO" = (
diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm
index 5251bc4821..19df3bca53 100644
--- a/_maps/map_files/OmegaStation/OmegaStation.dmm
+++ b/_maps/map_files/OmegaStation/OmegaStation.dmm
@@ -1026,7 +1026,8 @@
/turf/closed/wall,
/area/hallway/primary/starboard/fore)
"abu" = (
-/turf/open/floor/plating/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating,
/area/asteroid/nearstation)
"abv" = (
/turf/closed/wall,
@@ -1271,7 +1272,8 @@
/area/asteroid/nearstation)
"abS" = (
/obj/item/stack/ore/glass,
-/turf/open/floor/plating/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating,
/area/asteroid/nearstation)
"abT" = (
/turf/closed/wall,
@@ -2090,7 +2092,8 @@
icon_state = "crateopen"
},
/obj/effect/spawner/lootdrop/maintenance,
-/turf/open/floor/plating/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating,
/area/asteroid/nearstation)
"adw" = (
/turf/open/floor/plating{
@@ -3353,11 +3356,13 @@
"afM" = (
/obj/item/stack/ore/iron,
/obj/structure/barricade/wooden,
-/turf/open/floor/plating/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating,
/area/asteroid/nearstation)
"afN" = (
/obj/effect/decal/cleanable/blood/old,
-/turf/open/floor/plating/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating,
/area/asteroid/nearstation)
"afP" = (
/obj/structure/table/wood,
@@ -3831,7 +3836,8 @@
"agE" = (
/obj/item/stack/ore/silver,
/obj/item/stack/ore/iron,
-/turf/open/floor/plating/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating,
/area/asteroid/nearstation)
"agF" = (
/turf/closed/wall/r_wall,
@@ -4771,7 +4777,8 @@
/area/quartermaster/storage)
"aik" = (
/obj/structure/barricade/wooden,
-/turf/open/floor/plating/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating,
/area/asteroid/nearstation)
"aim" = (
/obj/structure/rack,
@@ -5733,7 +5740,8 @@
/area/quartermaster/storage)
"ajQ" = (
/obj/item/stack/ore/iron,
-/turf/open/floor/plating/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating,
/area/asteroid/nearstation)
"ajR" = (
/obj/structure/rack,
@@ -9822,7 +9830,8 @@
/area/engine/atmos)
"aqG" = (
/obj/structure/door_assembly/door_assembly_mhatch,
-/turf/open/floor/plating/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating,
/area/maintenance/port/fore)
"aqI" = (
/obj/structure/closet/secure_closet/security/sec,
@@ -10343,7 +10352,8 @@
/turf/open/floor/plasteel,
/area/engine/atmos)
"arB" = (
-/turf/open/floor/plating/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating,
/area/maintenance/port/fore)
"arC" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
@@ -36271,7 +36281,8 @@
/turf/open/floor/plating,
/area/engine/supermatter)
"bvg" = (
-/turf/open/floor/plating/airless/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating/airless,
/area/asteroid/nearstation)
"bvh" = (
/obj/structure/closet/emcloset{
@@ -40507,7 +40518,8 @@
"sIA" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/structure/barricade/wooden,
-/turf/open/floor/plating/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating,
/area/engine/engineering)
"sIB" = (
/turf/closed/wall/rust,
@@ -41786,7 +41798,8 @@
dir = 8
},
/obj/effect/turf_decal/delivery,
-/turf/open/floor/plating/airless/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating/airless,
/area/ai_monitored/turret_protected/aisat_interior)
"sNj" = (
/obj/effect/turf_decal/bot,
@@ -42384,7 +42397,8 @@
/obj/structure/cable/white{
icon_state = "0-8"
},
-/turf/open/floor/plating/airless/astplate,
+/obj/effect/turf_decal/sand/plating,
+/turf/open/floor/plating/airless,
/area/asteroid/nearstation)
"sOK" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
diff --git a/_maps/shuttles/whiteship_box.dmm b/_maps/shuttles/whiteship_box.dmm
index 8dc007ec95..0404cede34 100644
--- a/_maps/shuttles/whiteship_box.dmm
+++ b/_maps/shuttles/whiteship_box.dmm
@@ -15,6 +15,7 @@
"ae" = (
/obj/machinery/door/airlock/external,
/obj/effect/mapping_helpers/airlock/cyclelink_helper,
+/obj/structure/fans/tiny,
/obj/docking_port/mobile{
callTime = 250;
dheight = 0;
@@ -148,6 +149,7 @@
/area/shuttle/abandoned/crew)
"ap" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 1
},
@@ -737,6 +739,7 @@
/obj/effect/turf_decal/bot,
/obj/item/reagent_containers/glass/bucket,
/obj/item/mop,
+/obj/item/assembly/prox_sensor,
/obj/item/storage/bag/trash{
pixel_x = 6
},
@@ -954,6 +957,7 @@
/area/shuttle/abandoned/engine)
"bm" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/decal/cleanable/dirt{
desc = "A thin layer of dust coating the floor.";
name = "dust"
@@ -1130,6 +1134,7 @@
/area/shuttle/abandoned/bridge)
"by" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper,
/turf/open/floor/plating,
/area/shuttle/abandoned/engine)
@@ -2600,6 +2605,7 @@
"do" = (
/obj/machinery/door/airlock/external,
/obj/effect/mapping_helpers/airlock/cyclelink_helper,
+/obj/structure/fans/tiny,
/obj/effect/decal/cleanable/dirt{
desc = "A thin layer of dust coating the floor.";
name = "dust"
@@ -2670,6 +2676,7 @@
/area/shuttle/abandoned/medbay)
"dt" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 1
},
diff --git a/_maps/shuttles/whiteship_delta.dmm b/_maps/shuttles/whiteship_delta.dmm
index 160f6adfd5..cc8eb19bab 100644
--- a/_maps/shuttles/whiteship_delta.dmm
+++ b/_maps/shuttles/whiteship_delta.dmm
@@ -19,6 +19,7 @@
/area/shuttle/abandoned/crew)
"ad" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper,
/obj/docking_port/mobile{
callTime = 250;
@@ -443,6 +444,7 @@
name = "E.V.A Access"
},
/turf/open/floor/plasteel,
+/obj/structure/fans/tiny,
/area/shuttle/abandoned/crew)
"aK" = (
/obj/effect/decal/cleanable/dirt{
@@ -628,6 +630,7 @@
name = "E.V.A Access"
},
/obj/effect/decal/cleanable/dirt,
+/obj/structure/fans/tiny,
/turf/open/floor/plasteel,
/area/shuttle/abandoned/bar)
"aX" = (
@@ -2531,6 +2534,7 @@
/obj/machinery/door/airlock/external/glass{
name = "E.V.A Access"
},
+/obj/structure/fans/tiny,
/obj/effect/decal/cleanable/dirt{
desc = "A thin layer of dust coating the floor.";
name = "dust"
@@ -2555,6 +2559,7 @@
/obj/machinery/door/airlock/external/glass{
name = "E.V.A Access"
},
+/obj/structure/fans/tiny,
/turf/open/floor/plasteel,
/area/shuttle/abandoned/cargo)
"ds" = (
@@ -2782,6 +2787,7 @@
/area/shuttle/abandoned/medbay)
"dI" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper,
/obj/effect/decal/cleanable/dirt{
desc = "A thin layer of dust coating the floor.";
@@ -3103,6 +3109,7 @@
/area/shuttle/abandoned/cargo)
"eb" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 1
},
diff --git a/_maps/shuttles/whiteship_meta.dmm b/_maps/shuttles/whiteship_meta.dmm
index a7f977c34e..84fff43a44 100644
--- a/_maps/shuttles/whiteship_meta.dmm
+++ b/_maps/shuttles/whiteship_meta.dmm
@@ -34,6 +34,7 @@
/area/shuttle/abandoned/crew)
"ah" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper,
/obj/docking_port/mobile{
callTime = 250;
@@ -458,6 +459,7 @@
/area/shuttle/abandoned/crew)
"aI" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 1
},
@@ -583,6 +585,7 @@
name = "dust"
},
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 4
},
@@ -704,6 +707,7 @@
name = "dust"
},
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 8
},
@@ -1893,6 +1897,7 @@
/area/shuttle/abandoned/bridge)
"ch" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 4
},
@@ -1915,6 +1920,7 @@
/area/shuttle/abandoned/engine)
"cj" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 8
},
@@ -3094,6 +3100,7 @@
name = "dust"
},
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 4
},
@@ -3164,6 +3171,7 @@
name = "dust"
},
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 8
},
@@ -3432,6 +3440,7 @@
/obj/item/shovel,
/obj/item/pickaxe,
/obj/item/storage/box/lights/mixed,
+/obj/item/storage/box/lights/mixed,
/obj/item/mining_scanner,
/obj/effect/turf_decal/bot,
/turf/open/floor/plating,
@@ -3514,6 +3523,7 @@
/area/shuttle/abandoned/bar)
"dP" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper,
/obj/effect/decal/cleanable/dirt{
desc = "A thin layer of dust coating the floor.";
@@ -3808,6 +3818,7 @@
/area/shuttle/abandoned/cargo)
"ej" = (
/obj/machinery/door/airlock/external,
+/obj/structure/fans/tiny,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 1
},
diff --git a/_maps/shuttles/whiteship_pubby.dmm b/_maps/shuttles/whiteship_pubby.dmm
index 11214ef260..fef3918e1d 100644
--- a/_maps/shuttles/whiteship_pubby.dmm
+++ b/_maps/shuttles/whiteship_pubby.dmm
@@ -15,6 +15,7 @@
/obj/machinery/door/airlock/public/glass{
name = "Shuttle Airlock"
},
+/obj/structure/fans/tiny,
/turf/open/floor/plasteel/dark,
/area/shuttle/abandoned)
"e" = (
@@ -96,6 +97,7 @@
preferred_direction = 1;
width = 9
},
+/obj/structure/fans/tiny,
/turf/open/floor/plasteel/dark,
/area/shuttle/abandoned)
"p" = (
diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm
index 20eb648fe6..10b815e547 100644
--- a/code/__DEFINES/_tick.dm
+++ b/code/__DEFINES/_tick.dm
@@ -7,4 +7,7 @@
#define TICK_USAGE_REAL world.tick_usage //to be used where the result isn't checked
#define TICK_CHECK ( TICK_USAGE > Master.current_ticklimit )
-#define CHECK_TICK if TICK_CHECK stoplag()
+#define CHECK_TICK ( TICK_CHECK ? stoplag() : 0 )
+
+#define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 )
+#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 )
diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index 1b64ce7e6c..ad9aef5a8b 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -4,6 +4,11 @@
#define ALL (~0) //For convenience.
#define NONE 0
+//for convenience
+#define ENABLE_BITFIELD(variable, flag) (variable |= (flag))
+#define DISABLE_BITFIELD(variable, flag) (variable &= ~(flag))
+#define CHECK_BITFIELD(variable, flag) (variable & flag)
+
GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768))
// for /datum/var/datum_flags
diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm
index 37203792f7..13dccc74fe 100644
--- a/code/__DEFINES/lighting.dm
+++ b/code/__DEFINES/lighting.dm
@@ -34,6 +34,7 @@
#define LIGHT_COLOR_BLUE "#6496FA" //Cold, diluted blue. rgb(100, 150, 250)
#define LIGHT_COLOR_BLUEGREEN "#7DE1AF" //Light blueish green. rgb(125, 225, 175)
+#define LIGHT_COLOR_PALEBLUE "#7DAFE1" //A pale blue-ish color. rgb(125, 175, 225)
#define LIGHT_COLOR_CYAN "#7DE1E1" //Diluted cyan. rgb(125, 225, 225)
#define LIGHT_COLOR_LIGHT_CYAN "#40CEFF" //More-saturated cyan. rgb(64, 206, 255)
#define LIGHT_COLOR_DARK_BLUE "#6496FA" //Saturated blue. rgb(51, 117, 248)
@@ -58,7 +59,7 @@
#define LIGHT_RANGE_FIRE 3 //How many tiles standard fires glow.
#define LIGHTING_PLANE_ALPHA_VISIBLE 255
-#define LIGHTING_PLANE_ALPHA_NV_TRAIT 245
+#define LIGHTING_PLANE_ALPHA_NV_TRAIT 250
#define LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE 192
#define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 128 //For lighting alpha, small amounts lead to big changes. even at 128 its hard to figure out what is dark and what is light, at 64 you almost can't even tell.
#define LIGHTING_PLANE_ALPHA_INVISIBLE 0
diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm
index 3ffb583332..5fe65af8f3 100644
--- a/code/__HELPERS/time.dm
+++ b/code/__HELPERS/time.dm
@@ -63,96 +63,29 @@ GLOBAL_VAR_INIT(rollovercheck_last_timeofday, 0)
//Takes a value of time in deciseconds.
//Returns a text value of that number in hours, minutes, or seconds.
-/proc/DisplayTimeText(time_value, truncate = FALSE)
- var/second = (time_value)*0.1
- var/second_adjusted = null
- var/second_rounded = FALSE
- var/minute = null
- var/hour = null
- var/day = null
-
+/proc/DisplayTimeText(time_value, round_seconds_to = 0.1)
+ var/second = FLOOR(time_value * 0.1, round_seconds_to)
if(!second)
- return "0 seconds"
- if(second >= 60)
- minute = FLOOR(second/60, 1)
- second = round(second - (minute*60), 0.1)
- second_rounded = TRUE
- if(second) //check if we still have seconds remaining to format, or if everything went into minute.
- second_adjusted = round(second) //used to prevent '1 seconds' being shown
- if(day || hour || minute)
- if(second_adjusted == 1 && second >= 1)
- second = " and 1 second"
- else if(second > 1)
- second = " and [second_adjusted] seconds"
- else //shows a fraction if seconds is < 1
- if(second_rounded) //no sense rounding again if it's already done
- second = " and [second] seconds"
- else
- second = " and [round(second, 0.1)] seconds"
- else
- if(second_adjusted == 1 && second >= 1)
- second = "[truncate ? "second" : "1 second"]"
- else if(second > 1)
- second = "[second_adjusted] seconds"
- else
- if(second_rounded)
- second = "[second] seconds"
- else
- second = "[round(second, 0.1)] seconds"
- else
- second = null
-
- if(!minute)
- return "[second]"
- if(minute >= 60)
- hour = FLOOR(minute/60, 1)
- minute = (minute - (hour*60))
- if(minute) //alot simpler from here since you don't have to worry about fractions
- if(minute != 1)
- if((day || hour) && second)
- minute = ", [minute] minutes"
- else if((day || hour) && !second)
- minute = " and [minute] minutes"
- else
- minute = "[minute] minutes"
- else
- if((day || hour) && second)
- minute = ", 1 minute"
- else if((day || hour) && !second)
- minute = " and 1 minute"
- else
- minute = "[truncate ? "minute" : "1 minute"]"
- else
- minute = null
-
- if(!hour)
- return "[minute][second]"
- if(hour >= 24)
- day = FLOOR(hour/24, 1)
- hour = (hour - (day*24))
+ return "right now"
+ if(second < 60)
+ return "[second] second[(second != 1)? "s":""]"
+ var/minute = FLOOR(second / 60, 1)
+ second = FLOOR(MODULUS(second, 60), round_seconds_to)
+ var/secondT
+ if(second)
+ secondT = " and [second] second[(second != 1)? "s":""]"
+ if(minute < 60)
+ return "[minute] minute[(minute != 1)? "s":""][secondT]"
+ var/hour = FLOOR(minute / 60, 1)
+ minute = MODULUS(minute, 60)
+ var/minuteT
+ if(minute)
+ minuteT = " and [minute] minute[(minute != 1)? "s":""]"
+ if(hour < 24)
+ return "[hour] hour[(hour != 1)? "s":""][minuteT][secondT]"
+ var/day = FLOOR(hour / 24, 1)
+ hour = MODULUS(hour, 24)
+ var/hourT
if(hour)
- if(hour != 1)
- if(day && (minute || second))
- hour = ", [hour] hours"
- else if(day && (!minute || !second))
- hour = " and [hour] hours"
- else
- hour = "[hour] hours"
- else
- if(day && (minute || second))
- hour = ", 1 hour"
- else if(day && (!minute || !second))
- hour = " and 1 hour"
- else
- hour = "[truncate ? "hour" : "1 hour"]"
- else
- hour = null
-
- if(!day)
- return "[hour][minute][second]"
- if(day > 1)
- day = "[day] days"
- else
- day = "[truncate ? "day" : "1 day"]"
-
- return "[day][hour][minute][second]"
+ hourT = " and [hour] hour[(hour != 1)? "s":""]"
+ return "[day] day[(day != 1)? "s":""][hourT][minuteT][secondT]"
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 620c592d93..2f69b63ae9 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1239,6 +1239,20 @@ GLOBAL_REAL_VAR(list/stack_trace_storage)
pixel_x = initialpixelx
pixel_y = initialpixely
+/atom/proc/do_jiggle(targetangle = 45)
+ var/matrix/OM = matrix(transform)
+ var/matrix/M = matrix(transform)
+ M.Turn(pick(-targetangle, targetangle))
+ animate(src, transform = M, time = 10, easing = ELASTIC_EASING)
+ animate(src, transform = OM, time = 10, easing = ELASTIC_EASING)
+
+/atom/proc/do_squish(squishx = 1.2, squishy = 0.6)
+ var/matrix/OM = matrix(transform)
+ var/matrix/M = matrix(transform)
+ M.Scale(squishx, squishy)
+ animate(src, transform = M, time = 10, easing = BOUNCE_EASING)
+ animate(src, transform = OM, time = 10, easing = BOUNCE_EASING)
+
/proc/weightclass2text(var/w_class)
switch(w_class)
if(WEIGHT_CLASS_TINY)
@@ -1530,4 +1544,4 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
. = list()
for(var/i in L)
if(condition.Invoke(i))
- . |= i
\ No newline at end of file
+ . |= i
diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm
index 2d3bc949c3..d0506c7dcb 100644
--- a/code/_globalvars/lists/maintenance_loot.dm
+++ b/code/_globalvars/lists/maintenance_loot.dm
@@ -38,6 +38,7 @@ GLOBAL_LIST_INIT(maintenance_loot, list(
/obj/item/clothing/suit/hazardvest = 1,
/obj/item/clothing/under/rank/vice = 1,
/obj/item/clothing/suit/hooded/flashsuit = 2,
+ /obj/item/clothing/accessory/medal/greytide = 1,
/obj/item/assembly/prox_sensor = 4,
/obj/item/assembly/timer = 3,
/obj/item/flashlight = 4,
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 53f03ebfff..595e1d9ca0 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -7,6 +7,9 @@
/datum/config_entry/string/servername // server name (the name of the game window)
+/datum/config_entry/string/servertagline
+ config_entry_value = "We forgot to set the server's tagline in config.txt"
+
/datum/config_entry/string/serversqlname // short form server name used for the DB
/datum/config_entry/string/stationname // station name (the name of the station in-game)
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 6f018424e8..d54f6fc3ef 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -454,7 +454,7 @@ SUBSYSTEM_DEF(ticker)
if(CONFIG_GET(flag/tgstyle_maprotation))
INVOKE_ASYNC(SSmapping, /datum/controller/subsystem/mapping/.proc/maprotate)
else
- SSvote.initiate_vote("map","server")
+ SSvote.initiate_vote("map","server",TRUE)
/datum/controller/subsystem/ticker/proc/HasRoundStarted()
return current_state >= GAME_STATE_PLAYING
diff --git a/code/datums/components/storage/concrete/rped.dm b/code/datums/components/storage/concrete/rped.dm
index 14f6fe28b3..e1cc4ff6ae 100644
--- a/code/datums/components/storage/concrete/rped.dm
+++ b/code/datums/components/storage/concrete/rped.dm
@@ -5,7 +5,7 @@
click_gather = TRUE
max_w_class = WEIGHT_CLASS_NORMAL
max_combined_w_class = 100
- max_items = 50
+ max_items = 100
display_numerical_stacking = TRUE
/datum/component/storage/concrete/rped/can_be_inserted(obj/item/I, stop_messages, mob/M)
@@ -22,7 +22,7 @@
click_gather = TRUE
max_w_class = WEIGHT_CLASS_BULKY // can fit vending refills
max_combined_w_class = 800
- max_items = 400
+ max_items = 350
display_numerical_stacking = TRUE
/datum/component/storage/concrete/bluespace/rped/can_be_inserted(obj/item/I, stop_messages, mob/M)
@@ -31,3 +31,6 @@
if (!stop_messages)
to_chat(M, "[parent] only accepts machine parts!")
return FALSE
+
+/datum/component/storage/concrete/cyborg/rped
+ max_items = 150
diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm
index 55af26b6b2..bb3c584cb9 100644
--- a/code/datums/components/storage/storage.dm
+++ b/code/datums/components/storage/storage.dm
@@ -169,10 +169,12 @@
if(locked)
to_chat(M, "[parent] seems to be locked!")
return FALSE
+ var/atom/A = parent
var/obj/item/I = O
if(collection_mode == COLLECT_ONE)
if(can_be_inserted(I, null, M))
handle_item_insertion(I, null, M)
+ A.do_squish()
return
if(!isturf(I.loc))
return
@@ -189,6 +191,7 @@
stoplag(1)
qdel(progress)
to_chat(M, "You put everything you could [insert_preposition] [parent].")
+ A.do_squish(1.4, 0.4)
/datum/component/storage/proc/handle_mass_item_insertion(list/things, datum/component/storage/src_object, mob/user, datum/progressbar/progress)
var/atom/source_real_location = src_object.real_location()
@@ -246,6 +249,7 @@
while (do_after(M, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress)))
stoplag(1)
qdel(progress)
+ A.do_squish(0.8, 1.2)
/datum/component/storage/proc/mass_remove_from_storage(atom/target, list/things, datum/progressbar/progress, trigger_on_found = TRUE)
var/atom/real_location = real_location()
@@ -455,6 +459,7 @@
return FALSE
if(dump_destination.storage_contents_dump_act(src, M))
playsound(A, "rustle", 50, 1, -5)
+ A.do_squish(0.8, 1.2)
return TRUE
return FALSE
@@ -473,6 +478,8 @@
return TRUE
return FALSE
handle_item_insertion(I, FALSE, M)
+ var/atom/A = parent
+ A.do_squish()
/datum/component/storage/proc/return_inv(recursive)
var/list/ret = list()
@@ -514,6 +521,7 @@
if(A.loc != M)
return
playsound(A, "rustle", 50, 1, -5)
+ A.do_jiggle()
if(istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
M.putItemFromInventoryInHandIfPossible(A, H.held_index)
@@ -539,6 +547,8 @@
if(!L.incapacitated() && I == L.get_active_held_item())
if(!SEND_SIGNAL(I, COMSIG_CONTAINS_STORAGE) && can_be_inserted(I, FALSE)) //If it has storage it should be trying to dump, not insert.
handle_item_insertion(I, FALSE, L)
+ var/atom/A = parent
+ A.do_squish()
//This proc return 1 if the item can be picked up and 0 if it can't.
//Set the stop_messages to stop it from printing messages
@@ -712,6 +722,7 @@
to_chat(user, "[parent] seems to be locked!")
else
show_to(user)
+ A.do_jiggle()
/datum/component/storage/proc/signal_on_pickup(datum/source, mob/user)
var/atom/A = parent
@@ -732,16 +743,30 @@
return hide_from(target)
/datum/component/storage/proc/on_alt_click(datum/source, mob/user)
- if(!isliving(user) || user.incapacitated() || !quickdraw || locked || !user.CanReach(parent))
+ if(!isliving(user) || !user.CanReach(parent))
return
- var/obj/item/I = locate() in real_location()
- if(!I)
+ if(locked)
+ to_chat(user, "[parent] seems to be locked!")
return
- remove_from_storage(I, get_turf(user))
- if(!user.put_in_hands(I))
- to_chat(user, "You fumble for [I] and it falls on the floor.")
+
+ var/atom/A = parent
+ if(!quickdraw)
+ A.add_fingerprint(user)
+ user_show_to_mob(user)
+ playsound(A, "rustle", 50, 1, -5)
+ return
+
+ if(!user.incapacitated())
+ var/obj/item/I = locate() in real_location()
+ if(!I)
+ return
+ A.add_fingerprint(user)
+ remove_from_storage(I, get_turf(user))
+ if(!user.put_in_hands(I))
+ to_chat(user, "You fumble for [I] and it falls on the floor.")
+ return
+ user.visible_message("[user] draws [I] from [parent]!", "You draw [I] from [parent].")
return
- user.visible_message("[user] draws [I] from [parent]!", "You draw [I] from [parent].")
/datum/component/storage/proc/action_trigger(datum/signal_source, datum/action/source)
gather_mode_switch(source.owner)
diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm
index 8e067fc47e..fad1c8e615 100644
--- a/code/datums/traits/negative.dm
+++ b/code/datums/traits/negative.dm
@@ -38,15 +38,25 @@
var/obj/item/heirloom_type
switch(quirk_holder.mind.assigned_role)
if("Clown")
+ heirloom_type = /obj/item/paint/anycolor
heirloom_type = /obj/item/bikehorn/golden
if("Mime")
- heirloom_type = /obj/item/reagent_containers/food/snacks/baguette
+ heirloom_type = /obj/item/toy/dummy
+ heirloom_type = /obj/item/paint/anycolor
+ if("Cook")
+ heirloom_type = /obj/item/kitchen/knife/scimitar
+ if("Medical Doctor")
+ heirloom_type = /obj/item/healthanalyzer/advanced
+ if("Station Engineer")
+ heirloom_type = /obj/item/wirecutters/brass
+ if("Atmospheric Technician")
+ heirloom_type = /obj/item/extinguisher/mini/family
if("Lawyer")
heirloom_type = /obj/item/gavelhammer
if("Janitor")
heirloom_type = /obj/item/mop
if("Security Officer")
- heirloom_type = /obj/item/book/manual/wiki/security_space_law
+ heirloom_type = /obj/item/clothing/accessory/medal/silver/valor
if("Scientist")
heirloom_type = /obj/item/toy/plush/slimeplushie
if("Assistant")
@@ -257,14 +267,32 @@
/datum/quirk/phobia
name = "Phobia"
- desc = "You've had a traumatic past, that has scared you for life while dealing with your greatest fear."
+ desc = "You've had a traumatic past, one that has scarred you for life, and cripples you when dealing with your greatest fears."
value = -2 // It can hardstun you. You can be a job that your phobia targets...
- gain_text = "You feel your fears manifest themselfs."
- lose_text = "You feel your fears fade away."
- medical_record_text = "Patient has an extreme or irrational fear of or aversion to something."
+ gain_text = "You begin to tremble as an immeasurable fear grips your mind."
+ lose_text = "Your confidence wipes away the fear that had been plaguing you."
+ medical_record_text = "Patient has an extreme or irrational fear and aversion to an undefined stimuli."
var/datum/brain_trauma/mild/phobia/phobia
-
+
/datum/quirk/phobia/add()
var/mob/living/carbon/human/H = quirk_holder
phobia = new
H.gain_trauma(phobia, TRAUMA_RESILIENCE_SURGERY)
+
+/datum/quirk/mute
+ name = "Mute"
+ desc = "Due to some accident, medical condition, or simply by choice, you are completely unable to speak."
+ value = -2 //HALP MAINTS
+ gain_text = "You find yourself unable to speak!"
+ lose_text = "You feel a growing strength in your vocal chords."
+ medical_record_text = "Functionally mute, patient is unable to use their voice in any capacity."
+ var/datum/brain_trauma/severe/mute
+
+/datum/quirk/mute/add()
+ var/mob/living/carbon/human/H = quirk_holder
+ H.gain_trauma(mute, TRAUMA_RESILIENCE_SURGERY)
+
+/datum/quirk/mute/on_process()
+ if(quirk_holder.mind && LAZYLEN(quirk_holder.mind.antag_datums))
+ to_chat(quirk_holder, "Your antagonistic nature has caused your voice to return.")
+ qdel(src)
diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm
index 173f7e6b33..345212cc1b 100644
--- a/code/datums/weather/weather_types/ash_storm.dm
+++ b/code/datums/weather/weather_types/ash_storm.dm
@@ -93,6 +93,10 @@
/datum/weather/ash_storm/weather_act(mob/living/L)
if(is_ash_immune(L))
return
+ if(is_species(L, /datum/species/lizard/ashwalker))
+ if(L.getStaminaLoss() <= STAMINA_SOFTCRIT)
+ L.adjustStaminaLossBuffered(4)
+ return
L.adjustFireLoss(4)
diff --git a/code/game/area/areas/centcom.dm b/code/game/area/areas/centcom.dm
index d5c8a8c841..1241b7a240 100644
--- a/code/game/area/areas/centcom.dm
+++ b/code/game/area/areas/centcom.dm
@@ -29,6 +29,13 @@
/area/centcom/holding
name = "Holding Facility"
+/area/centcom/vip
+ name = "VIP Zone"
+ dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
+ /area/centcom/winterball
+ name = "winterball Zone"
+ dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
+
/area/centcom/supplypod
name = "Supplypod Facility"
icon_state = "supplypod"
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index ba840d0771..e56f6d366c 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -622,8 +622,12 @@
else if(direction & WEST)
pixel_x_diff = -8
- animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2)
- animate(src, pixel_x = pixel_x - pixel_x_diff, pixel_y = pixel_y - pixel_y_diff, time = 2)
+ var/matrix/OM = matrix(transform)
+ var/matrix/M = matrix(transform)
+ M.Turn(pixel_x_diff ? pixel_x_diff*2 : pick(-16, 16))
+
+ animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, transform = M, time = 2)
+ animate(src, pixel_x = pixel_x - pixel_x_diff, pixel_y = pixel_y - pixel_y_diff, transform = OM, time = 2)
/atom/movable/proc/do_item_attack_animation(atom/A, visual_effect_icon, obj/item/used_item)
var/image/I
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index 5029eaa03a..09047b05a9 100644
--- a/code/game/gamemodes/revolution/revolution.dm
+++ b/code/game/gamemodes/revolution/revolution.dm
@@ -36,7 +36,7 @@
///////////////////////////
/datum/game_mode/revolution/announce()
to_chat(world, "The current game mode is - Revolution!")
- to_chat(world, "Some crewmembers are attempting to start a revolution!
\nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD and CMO. Convert other crewmembers (excluding the heads of staff, and security officers) to your cause by flashing them. Protect your leaders.
\nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by beating them in the head).")
+ to_chat(world, "Some crewmembers are attempting to start a revolution!
\nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD, QM and CMO. Convert other crewmembers (excluding the heads of staff, and security officers) to your cause by flashing them. Protect your leaders.
\nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by beating them in the head).")
///////////////////////////////////////////////////////////////////////////////
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 4d3fd006d8..7137f28185 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -212,6 +212,17 @@
icon_state = "sleeper_s"
controls_inside = TRUE
+/obj/machinery/sleeper/syndie/Initialize()
+ . = ..()
+ component_parts = list()
+ component_parts += new /obj/item/circuitboard/machine/sleeper(null)
+ component_parts += new /obj/item/stock_parts/matter_bin/super(null)
+ component_parts += new /obj/item/stock_parts/manipulator/pico(null)
+ component_parts += new /obj/item/stack/sheet/glass(null)
+ component_parts += new /obj/item/stack/sheet/glass(null)
+ component_parts += new /obj/item/stack/cable_coil(null)
+ RefreshParts()
+
/obj/machinery/sleeper/syndie/fullupgrade/Initialize()
. = ..()
component_parts = list()
diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm
index 3a6cf2b922..7beb32eebb 100644
--- a/code/game/machinery/dance_machine.dm
+++ b/code/game/machinery/dance_machine.dm
@@ -5,7 +5,7 @@
icon_state = "jukebox"
verb_say = "states"
density = TRUE
- req_access = list(ACCESS_BAR)
+ req_one_access = list(ACCESS_BAR, ACCESS_KITCHEN, ACCESS_HYDROPONICS, ACCESS_ENGINE, ACCESS_CARGO, ACCESS_THEATRE)
var/active = FALSE
var/list/rangers = list()
var/stop = 0
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index a157f9c485..43e8dff2b9 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -41,6 +41,7 @@
/obj/machinery/suit_storage_unit/engine
suit_type = /obj/item/clothing/suit/space/hardsuit/engine
mask_type = /obj/item/clothing/mask/breath
+ storage_type= /obj/item/clothing/shoes/magboots
/obj/machinery/suit_storage_unit/ce
suit_type = /obj/item/clothing/suit/space/hardsuit/engine/elite
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 6c0795028b..d8c0fbeeb3 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -275,6 +275,18 @@
variance = 25
harmful = TRUE
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/seedscatter
+ name = "\improper Melon Seed \"Scattershot\""
+ desc = "A weapon for combat exosuits. Shoots a spread of pellets, shaped as seed."
+ icon_state = "mecha_scatter"
+ equip_cooldown = 30
+ projectile = /obj/item/projectile/bullet/seed
+ projectiles = 4
+ projectile_energy_cost = 55
+ projectiles_per_shot = 10
+ variance = 20
+ harmful = TRUE
+
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg
name = "\improper Ultra AC 2"
desc = "A weapon for combat exosuits. Shoots a rapid, three shot burst."
diff --git a/code/game/objects/effects/glowshroom.dm b/code/game/objects/effects/glowshroom.dm
index 13497dfb8a..c009d3ec79 100644
--- a/code/game/objects/effects/glowshroom.dm
+++ b/code/game/objects/effects/glowshroom.dm
@@ -96,7 +96,7 @@
for(var/turf/open/floor/earth in view(3,src))
if(is_type_in_typecache(earth, blacklisted_glowshroom_turfs))
continue
- if(!ownturf.CanAtmosPass(earth))
+ if(!disease_air_spread_walk(ownturf, earth))
continue
if(spreadsIntoAdjacent || !locate(/obj/structure/glowshroom) in view(1,earth))
possibleLocs += earth
diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm
index a1f6639865..795871d3b5 100644
--- a/code/game/objects/items/RCD.dm
+++ b/code/game/objects/items/RCD.dm
@@ -36,6 +36,7 @@ RLD
var/no_ammo_message = "The \'Low Ammo\' light on the device blinks yellow."
var/has_ammobar = FALSE //controls whether or not does update_icon apply ammo indicator overlays
var/ammo_sections = 10 //amount of divisions in the ammo indicator overlay/number of ammo indicator states
+ var/custom_range = 7
/obj/item/construction/Initialize()
. = ..()
@@ -119,7 +120,7 @@ RLD
return .
/obj/item/construction/proc/range_check(atom/A, mob/user)
- if(!(A in view(7, get_turf(user))))
+ if(!(A in range(custom_range, get_turf(user))))
to_chat(user, "The \'Out of Range\' light on [src] blinks red.")
return FALSE
else
@@ -445,13 +446,23 @@ RLD
matter = 160
/obj/item/construction/rcd/combat
- name = "industrial RCD"
+ name = "Combat RCD"
+ desc = "A device used to rapidly build and deconstruct. Reload with metal, plasteel, glass or compressed matter cartridges. This RCD has been upgraded to be able to remove Rwalls!"
icon_state = "ircd"
item_state = "ircd"
max_matter = 500
matter = 500
canRturf = TRUE
+/obj/item/construction/rcd/industrial
+ name = "industrial RCD"
+ icon_state = "ircd"
+ item_state = "ircd"
+ max_matter = 500
+ matter = 500
+ delay_mod = 0.6
+ sheetmultiplier = 8
+
/obj/item/rcd_ammo
name = "compressed matter cartridge"
desc = "Highly compressed matter for the RCD."
@@ -509,8 +520,9 @@ RLD
icon_state = "rld-5"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
- matter = 200
- max_matter = 200
+ matter = 500
+ max_matter = 500
+ sheetmultiplier = 16
var/mode = LIGHT_MODE
actions_types = list(/datum/action/item_action/pick_color)
@@ -521,7 +533,7 @@ RLD
var/walldelay = 10
var/floordelay = 10
- var/decondelay = 15
+ var/decondelay = 10
var/color_choice = null
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index d22ef23291..cbbd7a387a 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -35,6 +35,7 @@
/obj/item/flashlight/attack_self(mob/user)
on = !on
update_brightness(user)
+ playsound(user, on ? 'sound/weapons/magin.ogg' : 'sound/weapons/magout.ogg', 40, 1)
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm
index 8e391d9559..a57bf47a2e 100644
--- a/code/game/objects/items/extinguisher.dm
+++ b/code/game/objects/items/extinguisher.dm
@@ -42,6 +42,18 @@
sprite_name = "miniFE"
dog_fashion = null
+/obj/item/extinguisher/mini/family
+ name = "pocket fire extinguisher"
+ desc = "A old fashen pocket fire extinguisher that has been modified with a larger water tank, and a small high-power sprayer. It feels cool to the touch and has a small humming to it..."
+ icon_state = "miniFE0"
+ item_state = "miniFE"
+ throwforce = 1
+ w_class = WEIGHT_CLASS_SMALL
+ force = 2
+ max_water = 40
+ power = 7
+ cooling_power = 3
+
/obj/item/extinguisher/Initialize()
. = ..()
create_reagents(max_water)
diff --git a/code/game/objects/items/implants/implant_mindshield.dm b/code/game/objects/items/implants/implant_mindshield.dm
index 2f76b0cdc3..2c197eb7ec 100644
--- a/code/game/objects/items/implants/implant_mindshield.dm
+++ b/code/game/objects/items/implants/implant_mindshield.dm
@@ -29,8 +29,12 @@
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.unconvertable)
if(!silent)
target.visible_message("[target] seems to resist the implant!", "You feel something interfering with your mental conditioning, but you resist it!")
+ var/obj/item/implanter/I = loc
removed(target, 1)
qdel(src)
+ if(istype(I))
+ I.imp = null
+ I.update_icon()
return FALSE
var/datum/antagonist/rev/rev = target.mind.has_antag_datum(/datum/antagonist/rev)
diff --git a/code/game/objects/items/kitchen.dm b/code/game/objects/items/kitchen.dm
index bf7b0b1b3b..11d6f264b8 100644
--- a/code/game/objects/items/kitchen.dm
+++ b/code/game/objects/items/kitchen.dm
@@ -176,3 +176,12 @@
user.visible_message("[user] begins flattening [user.p_their()] head with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!")
return BRUTELOSS
/* Trays moved to /obj/item/storage/bag */
+
+/obj/item/kitchen/knife/scimitar
+ name = "Scimitar knife"
+ desc = "A knife used to cleanly butcher. Its razor-sharp edge has been honed for butchering, but has been poorly maintained over the years."
+ attack_verb = list("cleaved", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+
+/obj/item/kitchen/knife/scimiar/Initialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 90 - force, 100, force - 60) //bonus chance increases depending on force
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index 26712c2431..3a35f4d8ef 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -18,8 +18,8 @@
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BELT
- force = 10
- throwforce = 7
+ force = 14
+ throwforce = 10
w_class = WEIGHT_CLASS_NORMAL
attack_verb = list("flogged", "whipped", "lashed", "disciplined")
hitsound = 'sound/weapons/chainhit.ogg'
@@ -57,8 +57,8 @@
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
flags_1 = CONDUCT_1
obj_flags = UNIQUE_RENAME
- force = 15
- throwforce = 10
+ force = 18
+ throwforce = 15
w_class = WEIGHT_CLASS_BULKY
block_chance = 50
armour_penetration = 75
diff --git a/code/game/objects/items/paint.dm b/code/game/objects/items/paint.dm
index c81fd08274..a6f5830dd4 100644
--- a/code/game/objects/items/paint.dm
+++ b/code/game/objects/items/paint.dm
@@ -56,22 +56,34 @@
icon_state = "paint_neutral"
/obj/item/paint/anycolor/attack_self(mob/user)
- var/t1 = input(user, "Please select a color:", "Locking Computer", null) in list( "red", "blue", "green", "yellow", "violet", "black", "white")
+ var/t1 = input(user, "Please select a color:", "Locking Computer", null) in list( "red", "pink", "blue", "cyan", "green", "lime", "yellow", "orange", "violet", "purple", "black", "gray", "white")
if ((user.get_active_held_item() != src || user.stat || user.restrained()))
return
switch(t1)
if("red")
item_color = "C73232"
+ if("pink")
+ item_color = "FFC0CD"
if("blue")
item_color = "5998FF"
+ if("cyan")
+ item_color = "00FFFF"
if("green")
item_color = "2A9C3B"
+ if("lime")
+ item_color = "00FF00"
if("yellow")
item_color = "CFB52B"
+ if("orange")
+ item_color = "fFA700"
if("violet")
item_color = "AE4CCD"
+ if("purple")
+ item_color = "800080"
if("white")
item_color = "FFFFFF"
+ if("gray")
+ item_color = "808080"
if("black")
item_color = "333333"
icon_state = "paint_[t1]"
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index bbace70bec..e63ddd1c02 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -190,8 +190,10 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
new/datum/stack_recipe("rolling pin", /obj/item/kitchen/rollingpin, 2, time = 30), \
new/datum/stack_recipe("wooden chair", /obj/structure/chair/wood/, 3, time = 10, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("winged wooden chair", /obj/structure/chair/wood/wings, 3, time = 10, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("plywood chair", /obj/structure/chair/comfy/plywood, 4, time = 10, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("wooden barricade", /obj/structure/barricade/wooden, 5, time = 50, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("wooden door", /obj/structure/mineral_door/wood, 10, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("rustic wooden door", /obj/structure/mineral_door/woodrustic, 10, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("coffin", /obj/structure/closet/crate/coffin, 5, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("book case", /obj/structure/bookcase, 4, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("drying rack", /obj/machinery/smartfridge/drying_rack, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm
index 907b26c75c..3b28c30402 100644
--- a/code/game/objects/items/stacks/tiles/tile_types.dm
+++ b/code/game/objects/items/stacks/tiles/tile_types.dm
@@ -111,6 +111,22 @@
/obj/item/stack/tile/carpet/black/fifty
amount = 50
+/obj/item/stack/tile/carpet/blackred
+ name = "red carpet"
+ icon_state = "tile-carpet-blackred"
+ turf_type = /turf/open/floor/carpet/blackred
+
+/obj/item/stack/tile/carpet/blackred/fifty
+ amount = 50
+
+/obj/item/stack/tile/carpet/monochrome
+ name = "monochrome carpet"
+ icon_state = "tile-carpet-monochrome"
+ turf_type = /turf/open/floor/carpet/monochrome
+
+/obj/item/stack/tile/carpet/monochrome/fifty
+ amount = 50
+
/obj/item/stack/tile/fakespace
name = "astral carpet"
singular_name = "astral carpet"
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 249eb67c05..7414e0d5c2 100755
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -369,7 +369,8 @@
/obj/item/reagent_containers/food/drinks/soda_cans/space_up,
/obj/item/reagent_containers/food/drinks/soda_cans/pwr_game,
/obj/item/reagent_containers/food/drinks/soda_cans/lemon_lime,
- /obj/item/reagent_containers/food/drinks/drinkingglass/filled/nuka_cola
+ /obj/item/reagent_containers/food/drinks/drinkingglass/filled/nuka_cola,
+ /obj/item/reagent_containers/food/drinks/drinkingglass/filled/syndicatebomb
))
new rig_snacks(src)
diff --git a/code/game/objects/items/storage/lockbox.dm b/code/game/objects/items/storage/lockbox.dm
index bce38548cc..4924051855 100644
--- a/code/game/objects/items/storage/lockbox.dm
+++ b/code/game/objects/items/storage/lockbox.dm
@@ -178,3 +178,21 @@
/obj/item/storage/lockbox/medal/sci/PopulateContents()
for(var/i in 1 to 3)
new /obj/item/clothing/accessory/medal/plasma/nobel_science(src)
+
+/obj/item/storage/lockbox/medal/engineering
+ name = "engineering medal box"
+ desc = "A locked box used to store medals to be given to the members of the engineering department."
+ req_access = list(ACCESS_CE)
+
+/obj/item/storage/lockbox/medal/engineering/PopulateContents()
+ for(var/i in 1 to 3)
+ new /obj/item/clothing/accessory/medal/engineer(src)
+
+/obj/item/storage/lockbox/medal/medical
+ name = "medical medal box"
+ desc = "A locked box used to store medals to be given to the members of the medical department."
+ req_access = list(ACCESS_CMO)
+
+/obj/item/storage/lockbox/medal/medical/PopulateContents()
+ for(var/i in 1 to 3)
+ new /obj/item/clothing/accessory/medal/ribbon/medical_doctor(src)
\ No newline at end of file
diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm
index 6667cee76a..0a02cc0d4c 100644
--- a/code/game/objects/items/storage/toolbox.dm
+++ b/code/game/objects/items/storage/toolbox.dm
@@ -88,8 +88,10 @@
. = ..()
if(proximity)
if(isobj(A))
- var/obj/O = A
- O.take_damage(20)
+ var/obj/machinery/door/O = A
+ if(istype(O) && (O.damage_deflection >= 10)) //Eh fine. //This is a meme cast do not do what I did!
+ return
+ O.take_damage(13)
/obj/item/storage/toolbox/mechanical/old/heirloom/PopulateContents()
return
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 7b52ca7af1..7aaee7c5d4 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -1039,6 +1039,7 @@
icon = 'icons/obj/toy.dmi'
icon_state = "snowball"
throwforce = 12 //pelt your enemies to death with lumps of snow
+ damtype = STAMINA
/obj/item/toy/snowball/afterattack(atom/target as mob|obj|turf|area, mob/user)
. = ..()
diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm
index bd4ee0d35b..f0a233fea6 100644
--- a/code/game/objects/items/twohanded.dm
+++ b/code/game/objects/items/twohanded.dm
@@ -803,6 +803,7 @@
slot_flags = ITEM_SLOT_BACK
force_unwielded = 11
force_wielded = 20 //I have no idea how to balance
+ reach = 2
throwforce = 22
throw_speed = 4
embedding = list("embedded_impact_pain_multiplier" = 3)
diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm
index 840ca74143..e944eb32da 100644
--- a/code/game/objects/structures/beds_chairs/chair.dm
+++ b/code/game/objects/structures/beds_chairs/chair.dm
@@ -192,6 +192,17 @@
/obj/structure/chair/comfy/lime
color = rgb(255,251,0)
+/obj/structure/chair/comfy/plywood
+ name = "plywood chair"
+ desc = "A relaxing plywood chair."
+ icon_state = "plywood_chair"
+ anchored = FALSE
+ buildstacktype = /obj/item/stack/sheet/mineral/wood
+ buildstackamount = 4
+
+/obj/structure/chair/comfy/plywood/GetArmrest()
+ return mutable_appearance('icons/obj/chairs.dmi', "plywood_chair_armrest")
+
/obj/structure/chair/comfy/shuttle
name = "shuttle seat"
desc = "A comfortable, secure seat. It has a more sturdy looking buckling system, for smoother flights."
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm
index 45216a6d95..dd2e8428b1 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm
@@ -30,6 +30,7 @@
new /obj/item/circuitboard/machine/techfab/department/engineering(src)
new /obj/item/extinguisher/advanced(src)
new /obj/item/storage/photo_album/CE(src)
+ new /obj/item/storage/lockbox/medal/engineering(src)
/obj/structure/closet/secure_closet/engineering_electrical
name = "electrical supplies locker"
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
index 93da17b9b1..d424bcf876 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
@@ -77,6 +77,7 @@
new /obj/item/wallframe/defib_mount(src)
new /obj/item/circuitboard/machine/techfab/department/medical(src)
new /obj/item/storage/photo_album/CMO(src)
+ new /obj/item/storage/lockbox/medal/medical(src)
/obj/structure/closet/secure_closet/animal
name = "animal control"
diff --git a/code/game/objects/structures/fluff.dm b/code/game/objects/structures/fluff.dm
index d9a4406acd..baf0cf312f 100644
--- a/code/game/objects/structures/fluff.dm
+++ b/code/game/objects/structures/fluff.dm
@@ -70,6 +70,13 @@
desc = "A towering basalt sculpture of a drake. Cracks run down its surface and parts of it have fallen off."
icon_state = "drake_statue_falling"
+/obj/structure/fluff/lightpost
+ name = "lightpost"
+ desc = "A homely lightpost adorned with festive decor."
+ icon = 'icons/obj/2x2.dmi'
+ icon_state = "lightpost"
+ deconstructible = FALSE
+ layer = EDGED_TURF_LAYER
/obj/structure/fluff/bus
name = "bus"
@@ -167,3 +174,11 @@
desc = "A crudely-made sign with the words 'fok of' written in some sort of red paint."
icon = 'icons/obj/fluff.dmi'
icon_state = "fokof"
+
+/obj/structure/fluff/snowlegion
+ name = "snowlegion"
+ desc = "Looks like that weird kid with the tiger plushie has been round here again."
+ icon = 'icons/obj/fluff.dmi'
+ icon_state = "snowlegion"
+ anchored = TRUE
+ deconstructible = FALSE
diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm
index 3c9b70e967..13ca421daa 100644
--- a/code/game/objects/structures/mineral_doors.dm
+++ b/code/game/objects/structures/mineral_doors.dm
@@ -223,6 +223,16 @@
max_integrity = 200
rad_insulation = RAD_VERY_LIGHT_INSULATION
+/obj/structure/mineral_door/woodrustic
+ name = "rustic wood door"
+ icon_state = "woodrustic"
+ openSound = 'sound/effects/doorcreaky.ogg'
+ closeSound = 'sound/effects/doorcreaky.ogg'
+ sheetType = /obj/item/stack/sheet/mineral/wood
+ sheetAmount = 10
+ max_integrity = 200
+ rad_insulation = RAD_VERY_LIGHT_INSULATION
+
/obj/structure/mineral_door/paperframe
name = "paper frame door"
icon_state = "paperframe"
diff --git a/code/game/objects/structures/signs/signs_maps.dm b/code/game/objects/structures/signs/signs_maps.dm
index 8468516502..f3188b90c2 100644
--- a/code/game/objects/structures/signs/signs_maps.dm
+++ b/code/game/objects/structures/signs/signs_maps.dm
@@ -45,3 +45,12 @@
name = "command department"
desc = "A direction sign, pointing out which way the Command department is."
icon_state = "direction_bridge"
+
+/obj/structure/sign/directions/bar
+ name = "bar"
+ desc = "A direction sign, pointing out which way the Bar is."
+ icon_state = "direction_bar"
+ /obj/structure/sign/directions/cafe
+ name = "cafe"
+ desc = "A direction sign, pointing out which way the Cafe is."
+ icon_state = "direction_cafe"
diff --git a/code/game/objects/structures/table_frames.dm b/code/game/objects/structures/table_frames.dm
index 1aaae691f8..e979d4f18e 100644
--- a/code/game/objects/structures/table_frames.dm
+++ b/code/game/objects/structures/table_frames.dm
@@ -68,6 +68,22 @@
to_chat(user, "You start adding [C] to [src]...")
if(do_after(user, 20, target = src) && C.use(1))
make_new_table(/obj/structure/table/wood/fancy/black)
+ else if(istype(I, /obj/item/stack/tile/carpet/blackred))
+ var/obj/item/stack/tile/carpet/blackred/C = I
+ if(C.get_amount() < 1)
+ to_chat(user, "You need one red carpet sheet to do this!")
+ return
+ to_chat(user, "You start adding [C] to [src]...")
+ if(do_after(user, 20, target = src) && C.use(1))
+ make_new_table(/obj/structure/table/wood/fancy/blackred)
+ else if(istype(I, /obj/item/stack/tile/carpet/monochrome))
+ var/obj/item/stack/tile/carpet/monochrome/C = I
+ if(C.get_amount() < 1)
+ to_chat(user, "You need one monochrome carpet sheet to do this!")
+ return
+ to_chat(user, "You start adding [C] to [src]...")
+ if(do_after(user, 20, target = src) && C.use(1))
+ make_new_table(/obj/structure/table/wood/fancy/monochrome)
else if(istype(I, /obj/item/stack/tile/carpet))
var/obj/item/stack/tile/carpet/C = I
if(C.get_amount() < 1)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 78c8971759..8c29c1b2c5 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -298,7 +298,7 @@
frame = /obj/structure/table_frame
framestack = /obj/item/stack/rods
buildstack = /obj/item/stack/tile/carpet
- canSmoothWith = list(/obj/structure/table/wood/fancy, /obj/structure/table/wood/fancy/black)
+ canSmoothWith = list(/obj/structure/table/wood/fancy, /obj/structure/table/wood/fancy/black, /obj/structure/table/wood/fancy/blackred, /obj/structure/table/wood/fancy/monochrome)
/obj/structure/table/wood/fancy/New()
// New() is used so that the /black subtype can override `icon` easily and
@@ -314,11 +314,28 @@
icon_state = "fancy_table_black"
buildstack = /obj/item/stack/tile/carpet/black
+/obj/structure/table/wood/fancy/blackred
+ icon = 'icons/obj/structures.dmi'
+ icon_state = "fancy_table_blackred"
+ buildstack = /obj/item/stack/tile/carpet/blackred
+
+/obj/structure/table/wood/fancy/blackred/New()
+ . = ..()
+ icon = 'icons/obj/smooth_structures/fancy_table_blackred.dmi'
+
+/obj/structure/table/wood/fancy/monochrome
+ icon = 'icons/obj/structures.dmi'
+ icon_state = "fancy_table_monochrome"
+ buildstack = /obj/item/stack/tile/carpet/monochrome
+
+/obj/structure/table/wood/fancy/monochrome/New()
+ . = ..()
+ icon = 'icons/obj/smooth_structures/fancy_table_monochrome.dmi'
+
/obj/structure/table/wood/fancy/black/New()
. = ..()
// Ditto above.
icon = 'icons/obj/smooth_structures/fancy_table_black.dmi'
-
/*
* Reinforced tables
*/
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 84351cdb95..b7e4e4131e 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -200,5 +200,9 @@
'sound/vore/prey/death_04.ogg','sound/vore/prey/death_05.ogg','sound/vore/prey/death_06.ogg',
'sound/vore/prey/death_07.ogg','sound/vore/prey/death_08.ogg','sound/vore/prey/death_09.ogg',
'sound/vore/prey/death_10.ogg')
+ if("clang")
+ soundin = pick('sound/effects/clang1.ogg', 'sound/effects/clang2.ogg')
+ if("clangsmall")
+ soundin = pick('sound/effects/clangsmall1.ogg', 'sound/effects/clangsmall2.ogg')
//END OF CIT CHANGES
return soundin
diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm
index 2165dbd8db..6297cadc63 100644
--- a/code/game/turfs/closed.dm
+++ b/code/game/turfs/closed.dm
@@ -49,6 +49,14 @@
baseturfs = /turf/closed/indestructible/sandstone
smooth = SMOOTH_TRUE
+/turf/closed/indestructible/wood
+ name = "wooden wall"
+ desc = "A wall with wooden plating. Stiff."
+ icon = 'icons/turf/walls/wood_wall.dmi'
+ icon_state = "wood"
+ baseturfs = /turf/closed/indestructible/wood
+ smooth = SMOOTH_TRUE
+
/turf/closed/indestructible/oldshuttle/corner
icon_state = "corner"
@@ -76,6 +84,7 @@
icon = 'icons/turf/walls/riveted.dmi'
icon_state = "riveted"
smooth = SMOOTH_TRUE
+ explosion_block = INFINITY
/turf/closed/indestructible/riveted/uranium
icon = 'icons/turf/walls/uranium_wall.dmi'
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index 2476419a1b..659a58b2dc 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -19,7 +19,7 @@
var/mob/living/L = user
if(L.resting && do_after(L, max(10, L.getStaminaLoss()*0.5), 0, src))
if(Adjacent(L, src))
- L.forceMove(src)
+ step(L, get_dir(L, src))
playsound(L, "rustle", 25, 1)
/turf/open/indestructible
@@ -49,6 +49,20 @@
if(istype(AM))
playsound(src,sound,50,1)
+/turf/open/indestructible/cobble/side
+ icon_state = "cobble_side"
+
+/turf/open/indestructible/cobble/corner
+ icon_state = "cobble_corner"
+
+/turf/open/indestructible/cobble
+ name = "cobblestone path"
+ desc = "A simple but beautiful path made of various sized stones."
+ icon = 'icons/turf/floors.dmi'
+ icon_state = "cobble"
+ baseturfs = /turf/open/indestructible/cobble
+ tiled_dirt = FALSE
+
/turf/open/indestructible/necropolis
name = "necropolis floor"
desc = "It's regarding you suspiciously."
diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm
index 195245d967..fc1dec2acb 100644
--- a/code/game/turfs/simulated/floor/fancy_floor.dm
+++ b/code/game/turfs/simulated/floor/fancy_floor.dm
@@ -180,8 +180,19 @@
/turf/open/floor/carpet/black
icon = 'icons/turf/floors/carpet_black.dmi'
floor_tile = /obj/item/stack/tile/carpet/black
- canSmoothWith = list(/turf/open/floor/carpet/black)
+ canSmoothWith = list(/turf/open/floor/carpet/black, /turf/open/floor/carpet/blackred, /turf/open/floor/carpet/monochrome)
+/turf/open/floor/carpet/blackred
+ icon = 'icons/turf/floors/carpet_blackred.dmi'
+ floor_tile = /obj/item/stack/tile/carpet/blackred
+ icon_state = "tile-carpet-blackred"
+ canSmoothWith = list(/turf/open/floor/carpet/black, /turf/open/floor/carpet/blackred, /turf/open/floor/carpet/monochrome)
+
+/turf/open/floor/carpet/monochrome
+ icon = 'icons/turf/floors/carpet_monochrome.dmi'
+ floor_tile = /obj/item/stack/tile/carpet/monochrome
+ icon_state = "tile-carpet-monochrome"
+ canSmoothWith = list(/turf/open/floor/carpet/black, /turf/open/floor/carpet/blackred, /turf/open/floor/carpet/monochrome)
/turf/open/floor/carpet/narsie_act(force, ignore_mobs, probability = 20)
. = (prob(probability) || force)
diff --git a/code/game/turfs/simulated/river.dm b/code/game/turfs/simulated/river.dm
index e451fe4892..83fade33b2 100644
--- a/code/game/turfs/simulated/river.dm
+++ b/code/game/turfs/simulated/river.dm
@@ -4,11 +4,14 @@
#define RANDOM_LOWER_X 50
#define RANDOM_LOWER_Y 50
+#define RIVERGEN_SAFETY_LOCK 1000000
+
/proc/spawn_rivers(target_z, nodes = 4, turf_type = /turf/open/lava/smooth/lava_land_surface, whitelist_area = /area/lavaland/surface/outdoors/unexplored, min_x = RANDOM_LOWER_X, min_y = RANDOM_LOWER_Y, max_x = RANDOM_UPPER_X, max_y = RANDOM_UPPER_Y)
var/list/river_nodes = list()
var/num_spawned = 0
var/list/possible_locs = block(locate(min_x, min_y, target_z), locate(max_x, max_y, target_z))
- while(num_spawned < nodes && possible_locs.len)
+ var/safety = 0
+ while(num_spawned < nodes && possible_locs.len && (safety++ < RIVERGEN_SAFETY_LOCK))
var/turf/T = pick(possible_locs)
var/area/A = get_area(T)
if(!istype(A, whitelist_area) || (T.flags_1 & NO_LAVA_GEN_1))
@@ -16,7 +19,8 @@
else
river_nodes += new /obj/effect/landmark/river_waypoint(T)
num_spawned++
-
+
+ safety = 0
//make some randomly pathing rivers
for(var/A in river_nodes)
var/obj/effect/landmark/river_waypoint/W = A
@@ -30,7 +34,7 @@
break
var/detouring = 0
var/cur_dir = get_dir(cur_turf, target_turf)
- while(cur_turf != target_turf)
+ while(cur_turf != target_turf && (safety++ < RIVERGEN_SAFETY_LOCK))
if(detouring) //randomly snake around a bit
if(prob(20))
diff --git a/code/game/world.dm b/code/game/world.dm
index e83a01e7f2..d8576206dd 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -263,7 +263,7 @@ GLOBAL_VAR(restart_counter)
s += "Citadel" //Replace this with something else. Or ever better, delete it and uncomment the game version. CIT CHANGE - modifies the hub entry link
s += ""
s += ")\]" //CIT CHANGE - encloses the server title in brackets to make the hub entry fancier
- s += "
That furry /TG/code server your mother warned you about.
" //CIT CHANGE - adds a tagline!
+ s += "
[CONFIG_GET(string/servertagline)]
" //CIT CHANGE - adds a tagline!
var/n = 0
for (var/mob/M in GLOB.player_list)
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index 603e4daf2d..85866d5c51 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -1,23 +1,195 @@
-// Code taken from /bay/station.
-// Modified to allow consequtive querys in one invocation, terminated with ";"
+#define SDQL_qdel_datum(d) qdel(d)
+
+//SDQL2 datumized, /tg/station special!
-// Examples
/*
- -- Will call the proc for all computers in the world, thats dir is 2.
- CALL ex_act(EXPLODE_DEVASTATE) ON /obj/machinery/computer IN world WHERE dir == 2
- -- Will open a window with a list of all the closets in the world, with a link to VV them.
- SELECT /obj/structure/closet/secure_closet/security/cargo IN world WHERE icon_off == "secoff"
- -- Will change all the tube lights to green
- UPDATE /obj/machinery/light IN world SET color = "#0F0" WHERE icon_state == "tube1"
- -- Will delete all pickaxes. "IN world" is not required.
- DELETE /obj/item/pickaxe
- -- Will flicker the lights once, then turn all mobs green. The semicolon is important to separate the consecutive querys, but is not required for standard one-query use
- CALL flicker(1) ON /obj/machinery/light; UPDATE /mob SET color = "#00cc00"
+ Welcome admins, badmins and coders alike, to Structured Datum Query Language.
+ SDQL allows you to powerfully run code on batches of objects (or single objects, it's still unmatched
+ even there.)
+ When I say "powerfully" I mean it you're in for a ride.
- --You can use operators other than ==, such as >, <=, != and etc..
+ Ok so say you want to get a list of every mob. How does one do this?
+ "SELECT /mob"
+ This will open a list of every object in world that is a /mob.
+ And you can VV them if you need.
+
+ What if you want to get every mob on a *specific z-level*?
+ "SELECT /mob WHERE z == 4"
+
+ What if you want to select every mob on even numbered z-levels?
+ "SELECT /mob WHERE z % 2 == 0"
+
+ Can you see where this is going? You can select objects with an arbitrary expression.
+ These expressions can also do variable access and proc calls (yes, both on-object and globals!)
+ Keep reading!
+
+ Ok. What if you want to get every machine in the SSmachine process list? Looping through world is kinda
+ slow.
+
+ "SELECT * IN SSmachines.machinery"
+
+ Here "*" as type functions as a wildcard.
+ We know everything in the global SSmachines.machinery list is a machine.
+
+ You can specify "IN " to return a list to operate on.
+ This can be any list that you can wizard together from global variables and global proc calls.
+ Every variable/proc name in the "IN" block is global.
+ It can also be a single object, in which case the object is wrapped in a list for you.
+ So yeah SDQL is unironically better than VV for complex single-object operations.
+
+ You can of course combine these.
+ "SELECT * IN SSmachines.machinery WHERE z == 4"
+ "SELECT * IN SSmachines.machinery WHERE stat & 2" // (2 is NOPOWER, can't use defines from SDQL. Sorry!)
+ "SELECT * IN SSmachines.machinery WHERE stat & 2 && z == 4"
+
+ The possibilities are endless (just don't crash the server, ok?).
+
+ Oh it gets better.
+
+ You can use "MAP " to run some code per object and use the result. For example:
+
+ "SELECT /obj/machinery/power/smes MAP [charge / capacity * 100, RCon_tag, src]"
+
+ This will give you a list of all the APCs, their charge AND RCon tag. Useful eh?
+
+ [] being a list here. Yeah you can write out lists directly without > lol lists in VV. Color matrix
+ shenanigans inbound.
+
+ After the "MAP" segment is executed, the rest of the query executes as if it's THAT object you just made
+ (here the list).
+ Yeah, by the way, you can chain these MAP / WHERE things FOREVER!
+
+ "SELECT /mob WHERE client MAP client WHERE holder MAP holder"
+
+ What if some dumbass admin spawned a bajillion spiders and you need to kill them all?
+ Oh yeah you'd rather not delete all the spiders in maintenace. Only that one room the spiders were
+ spawned in.
+
+ "DELETE /mob/living/carbon/superior_animal/giant_spider WHERE loc.loc == marked"
+
+ Here I used VV to mark the area they were in, and since loc.loc = area, voila.
+ Only the spiders in a specific area are gone.
+
+ Or you know if you want to catch spiders that crawled into lockers too (how even?)
+
+ "DELETE /mob/living/carbon/superior_animal/giant_spider WHERE global.get_area(src) == marked"
+
+ What else can you do?
+
+ Well suppose you'd rather gib those spiders instead of simply flat deleting them...
+
+ "CALL gib() ON /mob/living/carbon/superior_animal/giant_spider WHERE global.get_area(src) == marked"
+
+ Or you can have some fun..
+
+ "CALL forceMove(marked) ON /mob/living/carbon/superior_animal"
+
+ You can also run multiple queries sequentially:
+
+ "CALL forceMove(marked) ON /mob/living/carbon/superior_animal; CALL gib() ON
+ /mob/living/carbon/superior_animal"
+
+ And finally, you can directly modify variables on objects.
+
+ "UPDATE /mob WHERE client SET client.color = [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]"
+
+ Don't crash the server, OK?
+
+ A quick recommendation: before you run something like a DELETE or another query.. Run it through SELECT
+ first.
+ You'd rather not gib every player on accident.
+ Or crash the server.
+
+ By the way, queries are slow and take a while. Be patient.
+ They don't hang the entire server though.
+
+ With great power comes great responsability.
+
+ Here's a slightly more formal quick reference.
+
+ The 4 queries you can do are:
+
+ "SELECT "
+ "CALL ON "
+ "UPDATE SET var=,var2="
+ "DELETE "
+
+ "" in this context is " [IN ] [chain of MAP/WHERE modifiers]"
+
+ "IN" (or "FROM", that works too but it's kinda weird to read),
+ is the list of objects to work on. This defaults to world if not provided.
+ But doing something like "IN living_mob_list" is quite handy and can optimize your query.
+ All names inside the IN block are global scope, so you can do living_mob_list (a global var) easily.
+ You can also run it on a single object. Because SDQL is that convenient even for single operations.
+
+ filters out objects of, well, that type easily. "*" is a wildcard and just takes everything in
+ the source list.
+
+ And then there's the MAP/WHERE chain.
+ These operate on each individual object being ran through the query.
+ They're both expressions like IN, but unlike it the expression is scoped *on the object*.
+ So if you do "WHERE z == 4", this does "src.z", effectively.
+ If you want to access global variables, you can do `global.living_mob_list`.
+ Same goes for procs.
+
+ MAP "changes" the object into the result of the expression.
+ WHERE "drops" the object if the expression is falsey (0, null or "")
+
+ What can you do inside expressions?
+
+ * Proc calls
+ * Variable reads
+ * Literals (numbers, strings, type paths, etc...)
+ * \ref referencing: {0x30000cc} grabs the object with \ref [0x30000cc]
+ * Lists: [a, b, c] or [a: b, c: d]
+ * Math and stuff.
+ * A few special variables: src (the object currently scoped on), usr (your mob),
+ marked (your marked datum), global(global scope)
+
+ TG ADDITIONS START:
+ Add USING keyword to the front of the query to use options system
+ The defaults aren't necessarily implemented, as there is no need to.
+ Available options: (D) means default
+ PROCCALL = (D)ASYNC, BLOCKING
+ SELECT = FORCE_NULLS, (D)SKIP_NULLS
+ PRIORITY = HIGH, (D) NORMAL
+ AUTOGC = (D) AUTOGC, KEEP_ALIVE
+
+ Example: USING PROCCALL = BLOCKING, SELECT = FORCE_NULLS, PRIORITY = HIGH SELECT /mob FROM world WHERE z == 1
*/
+
+#define SDQL2_STATE_ERROR 0
+#define SDQL2_STATE_IDLE 1
+#define SDQL2_STATE_PRESEARCH 2
+#define SDQL2_STATE_SEARCHING 3
+#define SDQL2_STATE_EXECUTING 4
+#define SDQL2_STATE_SWITCHING 5
+#define SDQL2_STATE_HALTING 6
+
+#define SDQL2_VALID_OPTION_TYPES list("proccall", "select", "priority", "autogc")
+#define SDQL2_VALID_OPTION_VALUES list("async", "blocking", "force_nulls", "skip_nulls", "high", "normal", "keep_alive")
+
+#define SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS (1<<0)
+#define SDQL2_OPTION_BLOCKING_CALLS (1<<1)
+#define SDQL2_OPTION_HIGH_PRIORITY (1<<2) //High priority SDQL query, allow using almost all of the tick.
+#define SDQL2_OPTION_DO_NOT_AUTOGC (1<<3)
+
+#define SDQL2_OPTIONS_DEFAULT (SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS)
+
+#define SDQL2_IS_RUNNING (state == SDQL2_STATE_EXECUTING || state == SDQL2_STATE_SEARCHING || state == SDQL2_STATE_SWITCHING || state == SDQL2_STATE_PRESEARCH)
+#define SDQL2_HALT_CHECK if(!SDQL2_IS_RUNNING) {state = SDQL2_STATE_HALTING; return FALSE;};
+
+#define SDQL2_TICK_CHECK ((options & SDQL2_OPTION_HIGH_PRIORITY)? CHECK_TICK_HIGH_PRIORITY : CHECK_TICK)
+
+#define SDQL2_STAGE_SWITCH_CHECK if(state != SDQL2_STATE_SWITCHING){\
+ if(state == SDQL2_STATE_HALTING){\
+ state = SDQL2_STATE_IDLE;\
+ return FALSE}\
+ state = SDQL2_STATE_ERROR;\
+ CRASH("SDQL2 fatal error");};
+
/client/proc/SDQL2_query(query_text as message)
set category = "Debug"
if(!check_rights(R_DEBUG)) //Shouldn't happen... but just to be safe.
@@ -25,139 +197,694 @@
log_admin("Non-admin [key_name(usr)] attempted to execute a SDQL query!")
return FALSE
var/list/results = world.SDQL2_query(query_text, key_name_admin(usr), "[key_name(usr)]")
- for(var/I in 1 to 3)
- to_chat(usr, results[I])
+ if(length(results) == 3)
+ for(var/I in 1 to 3)
+ to_chat(usr, results[I])
SSblackbox.record_feedback("nested tally", "SDQL query", 1, list(ckey, query_text))
/world/proc/SDQL2_query(query_text, log_entry1, log_entry2)
- var/query_log = "executed SDQL query: \"[query_text]\"."
+ var/query_log = "executed SDQL query(s): \"[query_text]\"."
message_admins("[log_entry1] [query_log]")
query_log = "[log_entry2] [query_log]"
log_game(query_log)
NOTICE(query_log)
+
+ var/start_time_total = REALTIMEOFDAY
+
+ if(!length(query_text))
+ return
+ var/list/query_list = SDQL2_tokenize(query_text)
+ if(!length(query_list))
+ return
+ var/list/querys = SDQL_parse(query_list)
+ if(!length(querys))
+ return
+ var/list/datum/SDQL2_query/running = list()
+ for(var/list/query_tree in querys)
+ var/datum/SDQL2_query/query = new /datum/SDQL2_query(query_tree)
+ if(QDELETED(query))
+ continue
+ if(usr)
+ query.show_next_to_key = usr.ckey
+ running += query
+ var/msg = "Starting query #[query.id] - [query.get_query_text()]."
+ if(usr)
+ to_chat(usr, "[msg]")
+ log_admin(msg)
+ query.ARun()
+ var/finished = FALSE
var/objs_all = 0
var/objs_eligible = 0
- var/start_time = REALTIMEOFDAY
-
- if(!query_text || length(query_text) < 1)
- return
-
-
- var/list/query_list = SDQL2_tokenize(query_text)
-
- if(!query_list || query_list.len < 1)
- return
-
- var/list/querys = SDQL_parse(query_list)
-
-
- if(!querys || querys.len < 1)
- return
-
- var/list/refs = list()
- var/where_used = FALSE
- for(var/list/query_tree in querys)
- var/list/from_objs = list()
- var/list/select_types = list()
-
- switch(query_tree[1])
- if("explain")
- SDQL_testout(query_tree["explain"])
- return
-
- if("call")
- if("on" in query_tree)
- select_types = query_tree["on"]
+ var/selectors_used = FALSE
+ var/list/combined_refs = list()
+ do
+ CHECK_TICK
+ finished = TRUE
+ for(var/i in running)
+ var/datum/SDQL2_query/query = i
+ if(QDELETED(query))
+ running -= query
+ continue
+ else if(query.state != SDQL2_STATE_IDLE)
+ finished = FALSE
+ else if(query.state == SDQL2_STATE_ERROR)
+ if(usr)
+ to_chat(usr, "SDQL query [query.get_query_text()] errored. It will NOT be automatically garbage collected. Please remove manually.")
+ running -= query
+ else
+ if(query.finished)
+ objs_all += islist(query.obj_count_all)? length(query.obj_count_all) : query.obj_count_all
+ objs_eligible += islist(query.obj_count_eligible)? length(query.obj_count_eligible) : query.obj_count_eligible
+ selectors_used |= query.where_switched
+ combined_refs |= query.select_refs
+ running -= query
+ if(!CHECK_BITFIELD(query.options, SDQL2_OPTION_DO_NOT_AUTOGC))
+ QDEL_IN(query, 50)
else
- return
+ if(usr)
+ to_chat(usr, "SDQL query [query.get_query_text()] was halted. It will NOT be automatically garbage collected. Please remove manually.")
+ running -= query
+ while(!finished)
- if("select", "delete", "update")
- select_types = query_tree[query_tree[1]]
+ var/end_time_total = REALTIMEOFDAY - start_time_total
+ return list("SDQL query combined results: [query_text]",\
+ "SDQL query completed: [objs_all] objects selected by path, and [selectors_used ? objs_eligible : objs_all] objects executed on after WHERE filtering/MAPping if applicable.",\
+ "SDQL combined querys took [DisplayTimeText(end_time_total)] to complete.") + combined_refs
- from_objs = world.SDQL_from_objs(query_tree["from"])
+GLOBAL_LIST_INIT(sdql2_queries, GLOB.sdql2_queries || list())
+GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null, "VIEW VARIABLES (all)", null))
- var/list/objs = list()
+/datum/SDQL2_query
+ var/list/query_tree
+ var/state = SDQL2_STATE_IDLE
+ var/options = SDQL2_OPTIONS_DEFAULT
+ var/superuser = FALSE //Run things like proccalls without using admin protections
+ var/allow_admin_interact = TRUE //Allow admins to do things to this excluding varedit these two vars
+ var/static/id_assign = 1
+ var/id = 0
- for(var/type in select_types)
- objs += SDQL_get_all(type, from_objs)
- CHECK_TICK
- objs_all = objs.len
+ var/qdel_on_finish = FALSE
- if("where" in query_tree)
- where_used = TRUE
- var/objs_temp = objs
- objs = list()
- for(var/datum/d in objs_temp)
- if(SDQL_expression(d, query_tree["where"]))
- objs += d
- objs_eligible++
- CHECK_TICK
+ //Last run
+ //General
+ var/finished = FALSE
+ var/start_time
+ var/end_time
+ var/where_switched = FALSE
+ var/show_next_to_key
+ //Select query only
+ var/list/select_refs
+ var/list/select_text
+ //Runtime tracked
+ //These three are weird. For best performance, they are only a number when they're not being changed by the SDQL searching/execution code. They only become numbers when they finish changing.
+ var/list/obj_count_all
+ var/list/obj_count_eligible
+ var/list/obj_count_finished
- switch(query_tree[1])
- if("call")
- for(var/datum/d in objs)
- world.SDQL_var(d, query_tree["call"][1], source = d)
- CHECK_TICK
+ //Statclick
+ var/obj/effect/statclick/SDQL2_delete/delete_click
+ var/obj/effect/statclick/SDQL2_action/action_click
- if("delete")
- for(var/datum/d in objs)
- SDQL_qdel_datum(d)
- CHECK_TICK
+/datum/SDQL2_query/New(list/tree, SU = FALSE, admin_interact = TRUE, _options = SDQL2_OPTIONS_DEFAULT, finished_qdel = FALSE)
+ if(IsAdminAdvancedProcCall() || !LAZYLEN(tree))
+ qdel(src)
+ return
+ LAZYADD(GLOB.sdql2_queries, src)
+ superuser = SU
+ allow_admin_interact = admin_interact
+ query_tree = tree
+ options = _options
+ id = id_assign++
+ qdel_on_finish = finished_qdel
- if("select")
- var/text = ""
- for(var/datum/t in objs)
- text += SDQL_gen_vv_href(t)
- refs[REF(t)] = TRUE
- CHECK_TICK
- usr << browse(text, "window=SDQL-result")
+/datum/SDQL2_query/Destroy()
+ state = SDQL2_STATE_HALTING
+ query_tree = null
+ obj_count_all = null
+ obj_count_eligible = null
+ obj_count_finished = null
+ select_text = null
+ select_refs = null
+ GLOB.sdql2_queries -= src
+ return ..()
- if("update")
- if("set" in query_tree)
- var/list/set_list = query_tree["set"]
- for(var/datum/d in objs)
- SDQL_internal_vv(d, set_list)
- CHECK_TICK
+/datum/SDQL2_query/proc/get_query_text()
+ var/list/out = list()
+ recursive_list_print(out, query_tree)
+ return out.Join()
- var/end_time = REALTIMEOFDAY
- end_time -= start_time
- return list("SDQL query results: [query_text]",\
- "SDQL query completed: [objs_all] objects selected by path, and [where_used ? objs_eligible : objs_all] objects executed on after WHERE filtering if applicable.",\
- "SDQL query took [DisplayTimeText(end_time)] to complete.") + refs
+/proc/recursive_list_print(list/output = list(), list/input, datum/callback/datum_handler, datum/callback/atom_handler)
+ output += "\[ "
+ for(var/i in 1 to input.len)
+ var/final = i == input.len
+ var/key = input[i]
-/proc/SDQL_qdel_datum(datum/d)
- qdel(d)
-
-/proc/SDQL_gen_vv_href(t)
- var/text = ""
- text += "[REF(t)]"
- if(istype(t, /atom))
- var/atom/a = t
- var/turf/T = a.loc
- var/turf/actual = get_turf(a)
- if(istype(T))
- text += ": [t] at turf [T] [ADMIN_COORDJMP(T)]
"
- else if(a.loc && istype(actual))
- text += ": [t] in [a.loc] at turf [actual] [ADMIN_COORDJMP(actual)]
"
+ //print the key
+ if(islist(key))
+ recursive_list_print(output, key, datum_handler, atom_handler)
+ else if(is_proper_datum(key) && (datum_handler || (isatom(key) && atom_handler)))
+ if(isatom(key) && atom_handler)
+ output += atom_handler.Invoke(key)
+ else
+ output += datum_handler.Invoke(key)
else
- text += ": [t]
"
- else
- text += ": [t]
"
- return text
+ output += "[key]"
-/proc/SDQL_internal_vv(d, list/set_list)
+ //print the value
+ var/is_value = (!isnum(key) && !isnull(input[key]))
+ if(is_value)
+ var/value = input[key]
+ if(islist(value))
+ recursive_list_print(output, value, datum_handler, atom_handler)
+ else if(is_proper_datum(value) && (datum_handler || (isatom(value) && atom_handler)))
+ if(isatom(value) && atom_handler)
+ output += atom_handler.Invoke(value)
+ else
+ output += datum_handler.Invoke(value)
+ else
+ output += " = [value]"
+
+ if(!final)
+ output += " , "
+
+ output += " \]"
+
+/datum/SDQL2_query/proc/text_state()
+ switch(state)
+ if(SDQL2_STATE_ERROR)
+ return "###ERROR"
+ if(SDQL2_STATE_IDLE)
+ return "####IDLE"
+ if(SDQL2_STATE_PRESEARCH)
+ return "PRESEARCH"
+ if(SDQL2_STATE_SEARCHING)
+ return "SEARCHING"
+ if(SDQL2_STATE_EXECUTING)
+ return "EXECUTING"
+ if(SDQL2_STATE_SWITCHING)
+ return "SWITCHING"
+ if(SDQL2_STATE_HALTING)
+ return "##HALTING"
+
+/datum/SDQL2_query/proc/generate_stat()
+ if(!allow_admin_interact)
+ return
+ if(!delete_click)
+ delete_click = new(null, "INITIALIZING", src)
+ if(!action_click)
+ action_click = new(null, "INITIALIZNG", src)
+ stat("[id] ", delete_click.update("DELETE QUERY | STATE : [text_state()] | ALL/ELIG/FIN \
+ [islist(obj_count_all)? length(obj_count_all) : (isnull(obj_count_all)? "0" : obj_count_all)]/\
+ [islist(obj_count_eligible)? length(obj_count_eligible) : (isnull(obj_count_eligible)? "0" : obj_count_eligible)]/\
+ [islist(obj_count_finished)? length(obj_count_finished) : (isnull(obj_count_finished)? "0" : obj_count_finished)] - [get_query_text()]"))
+ stat(" ", action_click.update("[SDQL2_IS_RUNNING? "HALT" : "RUN"]"))
+
+/datum/SDQL2_query/proc/delete_click()
+ admin_del(usr)
+
+/datum/SDQL2_query/proc/action_click()
+ if(SDQL2_IS_RUNNING)
+ admin_halt(usr)
+ else
+ admin_run(usr)
+
+/datum/SDQL2_query/proc/admin_halt(user = usr)
+ if(!SDQL2_IS_RUNNING)
+ return
+ var/msg = "[key_name(user)] has halted query #[id]"
+ message_admins(msg)
+ log_admin(msg)
+ state = SDQL2_STATE_HALTING
+
+/datum/SDQL2_query/proc/admin_run(mob/user = usr)
+ if(SDQL2_IS_RUNNING)
+ return
+ var/msg = "[key_name(user)] has (re)started query #[id]"
+ message_admins(msg)
+ log_admin(msg)
+ show_next_to_key = user.ckey
+ ARun()
+
+/datum/SDQL2_query/proc/admin_del(user = usr)
+ var/msg = "[key_name(user)] has stopped + deleted query #[id]"
+ message_admins(msg)
+ log_admin(msg)
+ qdel(src)
+
+/datum/SDQL2_query/proc/set_option(name, value)
+ switch(name)
+ if("select")
+ switch(value)
+ if("force_nulls")
+ DISABLE_BITFIELD(options, SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS)
+ if("proccall")
+ switch(value)
+ if("blocking")
+ ENABLE_BITFIELD(options, SDQL2_OPTION_BLOCKING_CALLS)
+ if("priority")
+ switch(value)
+ if("high")
+ ENABLE_BITFIELD(options, SDQL2_OPTION_HIGH_PRIORITY)
+ if("autogc")
+ switch(value)
+ if("keep_alive")
+ ENABLE_BITFIELD(options, SDQL2_OPTION_DO_NOT_AUTOGC)
+
+/datum/SDQL2_query/proc/ARun()
+ INVOKE_ASYNC(src, .proc/Run)
+
+/datum/SDQL2_query/proc/Run()
+ if(SDQL2_IS_RUNNING)
+ return FALSE
+ if(query_tree["options"])
+ for(var/name in query_tree["options"])
+ var/value = query_tree["options"][name]
+ set_option(name, value)
+ select_refs = list()
+ select_text = null
+ obj_count_all = 0
+ obj_count_eligible = 0
+ obj_count_finished = 0
+ start_time = REALTIMEOFDAY
+
+ state = SDQL2_STATE_PRESEARCH
+ var/list/search_tree = PreSearch()
+ SDQL2_STAGE_SWITCH_CHECK
+
+ state = SDQL2_STATE_SEARCHING
+ var/list/found = Search(search_tree)
+ SDQL2_STAGE_SWITCH_CHECK
+
+ state = SDQL2_STATE_EXECUTING
+ Execute(found)
+ SDQL2_STAGE_SWITCH_CHECK
+
+ end_time = REALTIMEOFDAY
+ state = SDQL2_STATE_IDLE
+ finished = TRUE
+ . = TRUE
+ if(show_next_to_key)
+ var/client/C = GLOB.directory[show_next_to_key]
+ if(C)
+ var/mob/showmob = C.mob
+ to_chat(showmob, "SDQL query results: [get_query_text()]
\
+ SDQL query completed: [islist(obj_count_all)? length(obj_count_all) : obj_count_all] objects selected by path, and \
+ [where_switched? "[islist(obj_count_eligible)? length(obj_count_eligible) : obj_count_eligible] objects executed on after WHERE keyword selection." : ""]
\
+ SDQL query took [DisplayTimeText(end_time - start_time)] to complete.")
+ if(length(select_text))
+ var/text = islist(select_text)? select_text.Join() : select_text
+ var/static/result_offset = 0
+ showmob << browse(text, "window=SDQL-result-[result_offset++]")
+ show_next_to_key = null
+ if(qdel_on_finish)
+ qdel(src)
+
+/datum/SDQL2_query/proc/PreSearch()
+ SDQL2_HALT_CHECK
+ switch(query_tree[1])
+ if("explain")
+ SDQL_testout(query_tree["explain"])
+ state = SDQL2_STATE_HALTING
+ return
+ if("call")
+ . = query_tree["on"]
+ if("select", "delete", "update")
+ . = query_tree[query_tree[1]]
+ state = SDQL2_STATE_SWITCHING
+
+/datum/SDQL2_query/proc/Search(list/tree)
+ SDQL2_HALT_CHECK
+ var/type = tree[1]
+ var/list/from = tree[2]
+ var/list/objs = SDQL_from_objs(from)
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ objs = SDQL_get_all(type, objs)
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ // 1 and 2 are type and FROM.
+ var/i = 3
+ while (i <= tree.len)
+ var/key = tree[i++]
+ var/list/expression = tree[i++]
+ switch (key)
+ if ("map")
+ for(var/j = 1 to objs.len)
+ var/x = objs[j]
+ objs[j] = SDQL_expression(x, expression)
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ if ("where")
+ where_switched = TRUE
+ var/list/out = list()
+ obj_count_eligible = out
+ for(var/x in objs)
+ if(SDQL_expression(x, expression))
+ out += x
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ objs = out
+ if(islist(obj_count_eligible))
+ obj_count_eligible = objs.len
+ else
+ obj_count_eligible = obj_count_all
+ . = objs
+ state = SDQL2_STATE_SWITCHING
+
+/datum/SDQL2_query/proc/SDQL_from_objs(list/tree)
+ if(IsAdminAdvancedProcCall())
+ if("world" in tree)
+ var/text = "[key_name(usr)] attempted to grab world with a procedure call to a SDQL datum."
+ message_admins(text)
+ log_admin(text)
+ return
+ if("world" in tree)
+ return world
+ return SDQL_expression(world, tree)
+
+/datum/SDQL2_query/proc/SDQL_get_all(type, location)
+ var/list/out = list()
+ obj_count_all = out
+
+// If only a single object got returned, wrap it into a list so the for loops run on it.
+ if(!islist(location) && location != world)
+ location = list(location)
+
+ if(type == "*")
+ for(var/i in location)
+ var/datum/d = i
+ if(d.can_vv_get() || superuser)
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ return out
+ if(istext(type))
+ type = text2path(type)
+ var/typecache = typecacheof(type)
+
+ if(ispath(type, /mob))
+ for(var/mob/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ else if(ispath(type, /turf))
+ for(var/turf/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ else if(ispath(type, /obj))
+ for(var/obj/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ else if(ispath(type, /area))
+ for(var/area/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ else if(ispath(type, /atom))
+ for(var/atom/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ else if(ispath(type, /datum))
+ if(location == world) //snowflake for byond shortcut
+ for(var/datum/d) //stupid byond trick to have it not return atoms to make this less laggy
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ else
+ for(var/datum/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ obj_count_all = out.len
+ return out
+
+/datum/SDQL2_query/proc/Execute(list/found)
+ SDQL2_HALT_CHECK
+ select_refs = list()
+ select_text = list()
+ switch(query_tree[1])
+ if("call")
+ for(var/i in found)
+ if(!is_proper_datum(i))
+ continue
+ world.SDQL_var(i, query_tree["call"][1], null, i, superuser, src)
+ obj_count_finished++
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ if("delete")
+ for(var/datum/d in found)
+ SDQL_qdel_datum(d)
+ obj_count_finished++
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ if("select")
+ var/list/text_list = list()
+ var/print_nulls = !(options & SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS)
+ obj_count_finished = select_refs
+ for(var/i in found)
+ SDQL_print(i, text_list, print_nulls)
+ select_refs[REF(i)] = TRUE
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ select_text = text_list
+
+ if("update")
+ if("set" in query_tree)
+ var/list/set_list = query_tree["set"]
+ for(var/d in found)
+ if(!is_proper_datum(d))
+ continue
+ SDQL_internal_vv(d, set_list)
+ obj_count_finished++
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ if(islist(obj_count_finished))
+ obj_count_finished = obj_count_finished.len
+ state = SDQL2_STATE_SWITCHING
+
+/datum/SDQL2_query/proc/SDQL_print(object, list/text_list, print_nulls = TRUE)
+ if(is_proper_datum(object))
+ text_list += "[REF(object)] : [object]"
+ if(istype(object, /atom))
+ var/atom/A = object
+ var/turf/T = A.loc
+ var/area/a
+ if(istype(T))
+ text_list += " at [T] [ADMIN_COORDJMP(T)]"
+ a = T.loc
+ else
+ var/turf/final = get_turf(T) //Recursive, hopefully?
+ if(istype(final))
+ text_list += " at [final] [ADMIN_COORDJMP(final)]"
+ a = final.loc
+ else
+ text_list += " at nonexistant location"
+ if(a)
+ text_list += " in area [a]"
+ if(T.loc != a)
+ text_list += " inside [T]"
+ text_list += "
"
+ else if(islist(object))
+ var/list/L = object
+ var/first = TRUE
+ text_list += "\["
+ for (var/x in L)
+ if (!first)
+ text_list += ", "
+ first = FALSE
+ SDQL_print(x, text_list)
+ if (!isnull(x) && !isnum(x) && L[x] != null)
+ text_list += " -> "
+ SDQL_print(L[L[x]])
+ text_list += "]
"
+ else
+ if(isnull(object))
+ if(print_nulls)
+ text_list += "NULL
"
+ else
+ text_list += "[object]
"
+
+/datum/SDQL2_query/CanProcCall()
+ if(!allow_admin_interact)
+ return FALSE
+ return ..()
+
+/datum/SDQL2_query/vv_edit_var(var_name, var_value)
+ if(!allow_admin_interact)
+ return FALSE
+ if(var_name == NAMEOF(src, superuser) || var_name == NAMEOF(src, allow_admin_interact) || var_name == NAMEOF(src, query_tree))
+ return FALSE
+ return ..()
+
+/datum/SDQL2_query/proc/SDQL_internal_vv(d, list/set_list)
for(var/list/sets in set_list)
var/datum/temp = d
var/i = 0
for(var/v in sets)
if(++i == sets.len)
- temp.vv_edit_var(v, SDQL_expression(d, set_list[sets]))
+ if(superuser)
+ if(temp.vars.Find(v))
+ temp.vars[v] = SDQL_expression(d, set_list[sets])
+ else
+ temp.vv_edit_var(v, SDQL_expression(d, set_list[sets]))
break
- if(temp.vars.Find(v) && (istype(temp.vars[v], /datum)))
+ if(temp.vars.Find(v) && (istype(temp.vars[v], /datum) || istype(temp.vars[v], /client)))
temp = temp.vars[v]
else
break
+/datum/SDQL2_query/proc/SDQL_function_blocking(datum/object, procname, list/arguments, source)
+ var/list/new_args = list()
+ for(var/arg in arguments)
+ new_args[++new_args.len] = SDQL_expression(source, arg)
+ if(object == GLOB) // Global proc.
+ procname = "/proc/[procname]"
+ return superuser? (call(procname)(new_args)) : (WrapAdminProcCall(GLOBAL_PROC, procname, new_args))
+ return superuser? (call(object, procname)(new_args)) : (WrapAdminProcCall(object, procname, new_args))
+
+/datum/SDQL2_query/proc/SDQL_function_async(datum/object, procname, list/arguments, source)
+ set waitfor = FALSE
+ return SDQL_function_blocking(object, procname, arguments, source)
+
+/datum/SDQL2_query/proc/SDQL_expression(datum/object, list/expression, start = 1)
+ var/result = 0
+ var/val
+
+ for(var/i = start, i <= expression.len, i++)
+ var/op = ""
+
+ if(i > start)
+ op = expression[i]
+ i++
+
+ var/list/ret = SDQL_value(object, expression, i)
+ val = ret["val"]
+ i = ret["i"]
+
+ if(op != "")
+ switch(op)
+ if("+")
+ result = (result + val)
+ if("-")
+ result = (result - val)
+ if("*")
+ result = (result * val)
+ if("/")
+ result = (result / val)
+ if("&")
+ result = (result & val)
+ if("|")
+ result = (result | val)
+ if("^")
+ result = (result ^ val)
+ if("%")
+ result = (result % val)
+ if("=", "==")
+ result = (result == val)
+ if("!=", "<>")
+ result = (result != val)
+ if("<")
+ result = (result < val)
+ if("<=")
+ result = (result <= val)
+ if(">")
+ result = (result > val)
+ if(">=")
+ result = (result >= val)
+ if("and", "&&")
+ result = (result && val)
+ if("or", "||")
+ result = (result || val)
+ else
+ to_chat(usr, "SDQL2: Unknown op [op]")
+ result = null
+ else
+ result = val
+
+ return result
+
+/datum/SDQL2_query/proc/SDQL_value(datum/object, list/expression, start = 1)
+ var/i = start
+ var/val = null
+
+ if(i > expression.len)
+ return list("val" = null, "i" = i)
+
+ if(istype(expression[i], /list))
+ val = SDQL_expression(object, expression[i])
+
+ else if(expression[i] == "TRUE")
+ val = TRUE
+
+ else if(expression[i] == "FALSE")
+ val = FALSE
+
+ else if(expression[i] == "!")
+ var/list/ret = SDQL_value(object, expression, i + 1)
+ val = !ret["val"]
+ i = ret["i"]
+
+ else if(expression[i] == "~")
+ var/list/ret = SDQL_value(object, expression, i + 1)
+ val = ~ret["val"]
+ i = ret["i"]
+
+ else if(expression[i] == "-")
+ var/list/ret = SDQL_value(object, expression, i + 1)
+ val = -ret["val"]
+ i = ret["i"]
+
+ else if(expression[i] == "null")
+ val = null
+
+ else if(isnum(expression[i]))
+ val = expression[i]
+
+ else if(ispath(expression[i]))
+ val = expression[i]
+
+ else if(copytext(expression[i], 1, 2) in list("'", "\""))
+ val = copytext(expression[i], 2, length(expression[i]))
+
+ else if(expression[i] == "\[")
+ var/list/expressions_list = expression[++i]
+ val = list()
+ for(var/list/expression_list in expressions_list)
+ var/result = SDQL_expression(object, expression_list)
+ var/assoc
+ if(expressions_list[expression_list] != null)
+ assoc = SDQL_expression(object, expressions_list[expression_list])
+ if(assoc != null)
+ // Need to insert the key like this to prevent duplicate keys fucking up.
+ var/list/dummy = list()
+ dummy[result] = assoc
+ result = dummy
+ val += result
+ else
+ val = world.SDQL_var(object, expression, i, object, superuser, src)
+ i = expression.len
+
+ return list("val" = val, "i" = i)
+
/proc/SDQL_parse(list/query_list)
var/datum/SDQL_parser/parser = new()
var/list/querys = list()
@@ -193,8 +920,6 @@
qdel(parser)
return querys
-
-
/proc/SDQL_testout(list/query_tree, indent = 0)
var/static/whitespace = " "
var/spaces = ""
@@ -220,182 +945,19 @@
else
to_chat(usr, "[spaces][whitespace][query_tree[item]]")
-
-
-/world/proc/SDQL_from_objs(list/tree)
- if("world" in tree)
- return src
- return SDQL_expression(src, tree)
-
-/proc/SDQL_get_all(type, location)
- var/list/out = list()
-
-// If only a single object got returned, wrap it into a list so the for loops run on it.
- if(!islist(location) && location != world)
- location = list(location)
-
- type = text2path(type)
- var/typecache = typecacheof(type)
-
- if(ispath(type, /mob))
- for(var/mob/d in location)
- if(typecache[d.type] && d.can_vv_get())
- out += d
- CHECK_TICK
-
- else if(ispath(type, /turf))
- for(var/turf/d in location)
- if(typecache[d.type] && d.can_vv_get())
- out += d
- CHECK_TICK
-
- else if(ispath(type, /obj))
- for(var/obj/d in location)
- if(typecache[d.type] && d.can_vv_get())
- out += d
- CHECK_TICK
-
- else if(ispath(type, /area))
- for(var/area/d in location)
- if(typecache[d.type] && d.can_vv_get())
- out += d
- CHECK_TICK
-
- else if(ispath(type, /atom))
- for(var/atom/d in location)
- if(typecache[d.type] && d.can_vv_get())
- out += d
- CHECK_TICK
- else if(ispath(type, /datum))
- if(location == world) //snowflake for byond shortcut
- for(var/datum/d) //stupid byond trick to have it not return atoms to make this less laggy
- if(typecache[d.type] && d.can_vv_get())
- out += d
- CHECK_TICK
- else
- for(var/datum/d in location)
- if(typecache[d.type] && d.can_vv_get())
- out += d
- CHECK_TICK
-
- return out
-
-
-/proc/SDQL_expression(datum/object, list/expression, start = 1)
- var/result = 0
- var/val
-
- for(var/i = start, i <= expression.len, i++)
- var/op = ""
-
- if(i > start)
- op = expression[i]
- i++
-
- var/list/ret = SDQL_value(object, expression, i)
- val = ret["val"]
- i = ret["i"]
-
- if(op != "")
- switch(op)
- if("+")
- result = (result + val)
- if("-")
- result = (result - val)
- if("*")
- result = (result * val)
- if("/")
- result = (result / val)
- if("&")
- result = (result & val)
- if("|")
- result = (result | val)
- if("^")
- result = (result ^ val)
- if("=", "==")
- result = (result == val)
- if("!=", "<>")
- result = (result != val)
- if("<")
- result = (result < val)
- if("<=")
- result = (result <= val)
- if(">")
- result = (result > val)
- if(">=")
- result = (result >= val)
- if("and", "&&")
- result = (result && val)
- if("or", "||")
- result = (result || val)
- else
- to_chat(usr, "SDQL2: Unknown op [op]")
- result = null
- else
- result = val
-
- return result
-
-/proc/SDQL_value(datum/object, list/expression, start = 1)
- var/i = start
- var/val = null
-
- if(i > expression.len)
- return list("val" = null, "i" = i)
-
- if(istype(expression[i], /list))
- val = SDQL_expression(object, expression[i])
-
- else if(expression[i] == "!")
- var/list/ret = SDQL_value(object, expression, i + 1)
- val = !ret["val"]
- i = ret["i"]
-
- else if(expression[i] == "~")
- var/list/ret = SDQL_value(object, expression, i + 1)
- val = ~ret["val"]
- i = ret["i"]
-
- else if(expression[i] == "-")
- var/list/ret = SDQL_value(object, expression, i + 1)
- val = -ret["val"]
- i = ret["i"]
-
- else if(expression[i] == "null")
- val = null
-
- else if(isnum(expression[i]))
- val = expression[i]
-
- else if(copytext(expression[i], 1, 2) in list("'", "\""))
- val = copytext(expression[i], 2, length(expression[i]))
-
- else if(expression[i] == "\[")
- var/list/expressions_list = expression[++i]
- val = list()
- for(var/list/expression_list in expressions_list)
- var/result = SDQL_expression(object, expression_list)
- var/assoc
- if(expressions_list[expression_list] != null)
- assoc = SDQL_expression(object, expressions_list[expression_list])
- if(assoc != null)
- // Need to insert the key like this to prevent duplicate keys fucking up.
- var/list/dummy = list()
- dummy[result] = assoc
- result = dummy
- val += result
- else
- val = world.SDQL_var(object, expression, i, object)
- i = expression.len
-
- return list("val" = val, "i" = i)
-
-/world/proc/SDQL_var(datum/object, list/expression, start = 1, source)
+//Staying as a world proc as this is called too often for changes to offset the potential IsAdminAdvancedProcCall checking overhead.
+/world/proc/SDQL_var(object, list/expression, start = 1, source, superuser, datum/SDQL2_query/query)
var/v
+ var/static/list/exclude = list("usr", "src", "marked", "global")
var/long = start < expression.len
- if(object == world && long && expression[start + 1] == ".")
- to_chat(usr, "Sorry, but world variables are not supported at the moment.")
+ var/datum/D
+ if(is_proper_datum(object))
+ D = object
+
+ if (object == world && (!long || expression[start + 1] == ".") && !(expression[start] in exclude))
+ to_chat(usr, "World variables are not allowed to be accessed. Use global.")
return null
+
else if(expression [start] == "{" && long)
if(lowertext(copytext(expression[start + 1], 1, 3)) != "0x")
to_chat(usr, "Invalid pointer syntax: [expression[start + 1]]")
@@ -405,12 +967,13 @@
to_chat(usr, "Invalid pointer: [expression[start + 1]]")
return null
start++
- else if((!long || expression[start + 1] == ".") && (expression[start] in object.vars))
- if(object.can_vv_get(expression[start]))
- v = object.vars[expression[start]]
+ long = start < expression.len
+ else if(D != null && (!long || expression[start + 1] == ".") && (expression[start] in D.vars))
+ if(D.can_vv_get(expression[start]) || superuser)
+ v = D.vars[expression[start]]
else
v = "SECRET"
- else if(long && expression[start + 1] == ":" && hascall(object, expression[start]))
+ else if(D != null && long && expression[start + 1] == ":" && hascall(D, expression[start]))
v = expression[start]
else if(!long || expression[start + 1] == ".")
switch(expression[start])
@@ -427,38 +990,70 @@
v = world
if("global")
v = GLOB
+ if("MC")
+ v = Master
+ if("FS")
+ v = Failsafe
+ if("CFG")
+ v = config
+ //Subsystem switches
+ if("SSgarbage")
+ v = SSgarbage
+ if("SSmachines")
+ v = SSmachines
+ if("SSobj")
+ v = SSobj
+ if("SSresearch")
+ v = SSresearch
+ if("SSprojectiles")
+ v = SSprojectiles
+ if("SSfastprocess")
+ v = SSfastprocess
+ if("SSticker")
+ v = SSticker
+ if("SStimer")
+ v = SStimer
+ if("SSradiation")
+ v = SSradiation
+ if("SSnpcpool")
+ v = SSnpcpool
+ if("SSmobs")
+ v = SSmobs
+ if("SSmood")
+ v = SSmood
+ if("SSquirks")
+ v = SSquirks
+ if("SSwet_floors")
+ v = SSwet_floors
+ if("SSshuttle")
+ v = SSshuttle
+ if("SSmapping")
+ v = SSmapping
+ if("SSevents")
+ v = SSevents
+ //End
else
return null
else if(object == GLOB) // Shitty ass hack kill me.
v = expression[start]
if(long)
if(expression[start + 1] == ".")
- return SDQL_var(v, expression[start + 2], source = source)
+ return SDQL_var(v, expression[start + 2], null, source, superuser, query)
else if(expression[start + 1] == ":")
- return SDQL_function(object, v, expression[start + 2], source)
+ return (query.options & SDQL2_OPTION_BLOCKING_CALLS)? query.SDQL_function_async(object, v, expression[start + 2], source) : query.SDQL_function_blocking(object, v, expression[start + 2], source)
else if(expression[start + 1] == "\[" && islist(v))
var/list/L = v
- var/index = SDQL_expression(source, expression[start + 2])
+ var/index = query.SDQL_expression(source, expression[start + 2])
if(isnum(index) && (!ISINTEGER(index) || L.len < index))
to_chat(usr, "Invalid list index: [index]")
return null
return L[index]
return v
-/proc/SDQL_function(var/datum/object, var/procname, var/list/arguments, source)
- set waitfor = FALSE
- var/list/new_args = list()
- for(var/arg in arguments)
- new_args += SDQL_expression(source, arg)
- if(object == GLOB) // Global proc.
- procname = "/proc/[procname]"
- return WrapAdminProcCall(GLOBAL_PROC, procname, new_args)
- return WrapAdminProcCall(object, procname, new_args)
-
/proc/SDQL2_tokenize(query_text)
var/list/whitespace = list(" ", "\n", "\t")
- var/list/single = list("(", ")", ",", "+", "-", ".", ";", "{", "}", "\[", "]", ":")
+ var/list/single = list("(", ")", ",", "+", "-", ".", "\[", "]", "{", "}", ";", ":")
var/list/multi = list(
"=" = list("", "="),
"<" = list("", "=", ">"),
@@ -560,3 +1155,20 @@
if(word != "")
query_list += word
return query_list
+
+/proc/is_proper_datum(thing)
+ return istype(thing, /datum) || istype(thing, /client)
+
+/obj/effect/statclick/SDQL2_delete/Click()
+ var/datum/SDQL2_query/Q = target
+ Q.delete_click()
+
+/obj/effect/statclick/SDQL2_action/Click()
+ var/datum/SDQL2_query/Q = target
+ Q.action_click()
+
+/obj/effect/statclick/SDQL2_VV_all
+ name = "VIEW VARIABLES"
+
+/obj/effect/statclick/SDQL2_VV_all/Click()
+ usr.client.debug_variables(GLOB.sdql2_queries)
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm
index 1bd95bc239..272bf83ca4 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm
@@ -7,36 +7,34 @@
//
// query : select_query | delete_query | update_query | call_query | explain
// explain : 'EXPLAIN' query
+// select_query : 'SELECT' object_selectors
+// delete_query : 'DELETE' object_selectors
+// update_query : 'UPDATE' object_selectors 'SET' assignments
+// call_query : 'CALL' variable 'ON' object_selectors // Note here: 'variable' does function calls. This simplifies parsing.
//
-// select_query : 'SELECT' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
-// delete_query : 'DELETE' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
-// update_query : 'UPDATE' select_list [('FROM' | 'IN') from_list] 'SET' assignments ['WHERE' bool_expression]
-// call_query : 'CALL' call_function ['ON' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]]
+// select_item : '*' | object_type
//
-// select_list : select_item [',' select_list]
-// select_item : '*' | select_function | object_type
-// select_function : count_function
-// count_function : 'COUNT' '(' '*' ')' | 'COUNT' '(' object_types ')'
+// object_selectors : select_item [('FROM' | 'IN') from_item] [modifier_list]
+// modifier_list : ('WHERE' bool_expression | 'MAP' expression) [modifier_list]
//
-// from_list : from_item [',' from_list]
-// from_item : 'world' | object_type
+// from_item : 'world' | expression
//
-// call_function : ['(' [arguments] ')']
+// call_function : '(' [arguments] ')'
// arguments : expression [',' arguments]
//
-// object_type : | string
+// object_type :
//
-// assignments : assignment, [',' assignments]
+// assignments : assignment [',' assignments]
// assignment : '=' expression
-// variable : | '.' variable
+// variable : | '.' variable | '[' ']' | '[' ']' '.' variable
//
// bool_expression : expression comparitor expression [bool_operator bool_expression]
// expression : ( unary_expression | '(' expression ')' | value ) [binary_operator expression]
// unary_expression : unary_operator ( unary_expression | value | '(' expression ')' )
// comparitor : '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
-// value : variable | string | number | 'null'
+// value : variable | string | number | 'null' | object_type
// unary_operator : '!' | '-' | '~'
-// binary_operator : comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^'
+// binary_operator : comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' | '%'
// bool_operator : 'AND' | '&&' | 'OR' | '||'
//
// string : ''' ''' | '"' '"'
@@ -51,10 +49,9 @@
var/list/query
var/list/tree
- var/list/select_functions = list("count")
var/list/boolean_operators = list("and", "or", "&&", "||")
var/list/unary_operators = list("!", "-", "~")
- var/list/binary_operators = list("+", "-", "/", "*", "&", "|", "^")
+ var/list/binary_operators = list("+", "-", "/", "*", "&", "|", "^", "%")
var/list/comparitors = list("=", "==", "!=", "<>", "<", "<=", ">", ">=")
/datum/SDQL_parser/New(query_list)
@@ -62,12 +59,12 @@
/datum/SDQL_parser/proc/parse_error(error_message)
error = 1
- to_chat(usr, "SQDL2 Parsing Error: [error_message]")
+ to_chat(usr, "SQDL2 Parsing Error: [error_message]")
return query.len + 1
/datum/SDQL_parser/proc/parse()
tree = list()
- query(1, tree)
+ query_options(1, tree)
if(error)
return list()
@@ -91,354 +88,547 @@
/datum/SDQL_parser/proc/tokenl(i)
return lowertext(token(i))
+/datum/SDQL_parser/proc/query_options(i, list/node)
+ var/list/options = list()
+ if(tokenl(i) == "using")
+ i = option_assignments(i + 1, node, options)
+ query(i, node)
+ if(length(options))
+ node["options"] = options
+
+//option_assignment: query_option '=' define
+/datum/SDQL_parser/proc/option_assignment(i, list/node, list/assignment_list = list())
+ var/type = tokenl(i)
+ if(!(type in SDQL2_VALID_OPTION_TYPES))
+ parse_error("Invalid option type: [type]")
+ if(!(token(i + 1) == "="))
+ parse_error("Invalid option assignment symbol: [token(i + 1)]")
+ var/val = tokenl(i + 2)
+ if(!(val in SDQL2_VALID_OPTION_VALUES))
+ parse_error("Invalid optoin value: [val]")
+ assignment_list[type] = val
+ return (i + 3)
+
+//option_assignments: option_assignment, [',' option_assignments]
+/datum/SDQL_parser/proc/option_assignments(i, list/node, list/store)
+ i = option_assignment(i, node, store)
+
+ if(token(i) == ",")
+ i = option_assignments(i + 1, node, store)
+
+ return i
//query: select_query | delete_query | update_query
/datum/SDQL_parser/proc/query(i, list/node)
query_type = tokenl(i)
+
switch(query_type)
if("select")
select_query(i, node)
+
if("delete")
delete_query(i, node)
+
if("update")
update_query(i, node)
+
if("call")
call_query(i, node)
+
if("explain")
node += "explain"
node["explain"] = list()
query(i + 1, node["explain"])
-// select_query: 'SELECT' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
+// select_query: 'SELECT' object_selectors
/datum/SDQL_parser/proc/select_query(i, list/node)
var/list/select = list()
- i = select_list(i + 1, select)
- node += "select"
+ i = object_selectors(i + 1, select)
+
node["select"] = select
- selectors(i, node)
return i
-//delete_query: 'DELETE' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
+
+//delete_query: 'DELETE' object_selectors
/datum/SDQL_parser/proc/delete_query(i, list/node)
var/list/select = list()
- i = select_list(i + 1, select)
- node += "delete"
+ i = object_selectors(i + 1, select)
+
node["delete"] = select
- selectors(i, node)
+
return i
-//update_query: 'UPDATE' select_list [('FROM' | 'IN') from_list] 'SET' assignments ['WHERE' bool_expression]
+
+//update_query: 'UPDATE' object_selectors 'SET' assignments
/datum/SDQL_parser/proc/update_query(i, list/node)
var/list/select = list()
- i = select_list(i + 1, select)
- node += "update"
+ i = object_selectors(i + 1, select)
+
node["update"] = select
+
if(tokenl(i) != "set")
i = parse_error("UPDATE has misplaced SET")
+
var/list/set_assignments = list()
i = assignments(i + 1, set_assignments)
- node += "set"
+
node["set"] = set_assignments
- selectors(i, node)
+
return i
-//call_query: 'CALL' call_function ['ON' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]]
+
+//call_query: 'CALL' call_function ['ON' object_selectors]
/datum/SDQL_parser/proc/call_query(i, list/node)
var/list/func = list()
i = variable(i + 1, func) // Yes technically does anything variable() matches but I don't care, if admins fuck up this badly then they shouldn't be allowed near SDQL.
- node += "call"
+
node["call"] = func
+
if(tokenl(i) != "on")
- return i
+ return parse_error("You need to specify what to call ON.")
+
var/list/select = list()
- i = select_list(i + 1, select)
- node += "on"
+ i = object_selectors(i + 1, select)
+
node["on"] = select
- selectors(i, node)
+
return i
-//select_list: select_item [',' select_list]
+// object_selectors: select_item [('FROM' | 'IN') from_item] [modifier_list]
+/datum/SDQL_parser/proc/object_selectors(i, list/node)
+ i = select_item(i, node)
+
+ if (tokenl(i) == "from" || tokenl(i) == "in")
+ i++
+ var/list/from = list()
+ i = from_item(i, from)
+ node[++node.len] = from
+
+ else
+ node[++node.len] = list("world")
+
+ i = modifier_list(i, node)
+ return i
+
+// modifier_list: ('WHERE' bool_expression | 'MAP' expression) [modifier_list]
+/datum/SDQL_parser/proc/modifier_list(i, list/node)
+ while (TRUE)
+ if (tokenl(i) == "where")
+ i++
+ node += "where"
+ var/list/expr = list()
+ i = bool_expression(i, expr)
+ node[++node.len] = expr
+
+ else if (tokenl(i) == "map")
+ i++
+ node += "map"
+ var/list/expr = list()
+ i = expression(i, expr)
+ node[++node.len] = expr
+
+ else
+ return i
+
+//select_list:select_item [',' select_list]
/datum/SDQL_parser/proc/select_list(i, list/node)
i = select_item(i, node)
+
if(token(i) == ",")
i = select_list(i + 1, node)
+
return i
//assignments: assignment, [',' assignments]
/datum/SDQL_parser/proc/assignments(i, list/node)
i = assignment(i, node)
+
if(token(i) == ",")
i = assignments(i + 1, node)
+
return i
+
//select_item: '*' | select_function | object_type
/datum/SDQL_parser/proc/select_item(i, list/node)
- if(token(i) == "*")
+ if (token(i) == "*")
node += "*"
i++
- else if(tokenl(i) in select_functions)
- i = select_function(i, node)
- else
+
+ else if (copytext(token(i), 1, 2) == "/")
i = object_type(i, node)
+
+ else
+ i = parse_error("Expected '*' or type path for select item")
+
return i
// Standardized method for handling the IN/FROM and WHERE options.
/datum/SDQL_parser/proc/selectors(i, list/node)
while (token(i))
var/tok = tokenl(i)
- if(tok in list("from", "in"))
+ if (tok in list("from", "in"))
var/list/from = list()
i = from_item(i + 1, from)
+
node["from"] = from
continue
- if(tok == "where")
+
+ if (tok == "where")
var/list/where = list()
i = bool_expression(i + 1, where)
+
node["where"] = where
continue
+
parse_error("Expected either FROM, IN or WHERE token, found [token(i)] instead.")
return i + 1
- if(!node.Find("from"))
+
+ if (!node.Find("from"))
node["from"] = list("world")
+
return i
-//from_item: 'world' | object_type
+//from_item: 'world' | expression
/datum/SDQL_parser/proc/from_item(i, list/node)
if(token(i) == "world")
node += "world"
i++
+
else
i = expression(i, node)
+
return i
+
//bool_expression: expression [bool_operator bool_expression]
/datum/SDQL_parser/proc/bool_expression(i, list/node)
+
var/list/bool = list()
i = expression(i, bool)
+
node[++node.len] = bool
+
if(tokenl(i) in boolean_operators)
i = bool_operator(i, node)
i = bool_expression(i, node)
+
return i
+
//assignment: '=' expression
/datum/SDQL_parser/proc/assignment(var/i, var/list/node, var/list/assignment_list = list())
assignment_list += token(i)
+
if(token(i + 1) == ".")
i = assignment(i + 2, node, assignment_list)
+
else if(token(i + 1) == "=")
var/exp_list = list()
node[assignment_list] = exp_list
+
i = expression(i + 2, exp_list)
+
else
parse_error("Assignment expected, but no = found")
+
return i
-//variable: | '.' variable
+
+//variable: | '.' variable | '[' ']' | '[' ']' '.' variable
/datum/SDQL_parser/proc/variable(i, list/node)
var/list/L = list(token(i))
node[++node.len] = L
+
if(token(i) == "{")
- L += token(i+1)
+ L += token(i + 1)
i += 2
+
if(token(i) != "}")
parse_error("Missing } at end of pointer.")
+
if(token(i + 1) == ".")
L += "."
i = variable(i + 2, L)
- else if(token(i + 1) == "(") // OH BOY PROC
+
+ else if (token(i + 1) == "(") // OH BOY PROC
var/list/arguments = list()
i = call_function(i, null, arguments)
L += ":"
L[++L.len] = arguments
- else if(token(i + 1) == "\[")
+
+ else if (token(i + 1) == "\[")
var/list/expression = list()
i = expression(i + 2, expression)
if (token(i) != "]")
parse_error("Missing ] at the end of list access.")
+
L += "\["
L[++L.len] = expression
i++
+
else
i++
+
return i
+
+//object_type:
+/datum/SDQL_parser/proc/object_type(i, list/node)
+
+ if (copytext(token(i), 1, 2) != "/")
+ return parse_error("Expected type, but it didn't begin with /")
+
+ var/path = text2path(token(i))
+ if (path == null)
+ return parse_error("Nonexistant type path: [token(i)]")
+
+ node += path
+
+ return i + 1
+
+
+//comparitor: '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
+/datum/SDQL_parser/proc/comparitor(i, list/node)
+
+ if(token(i) in list("=", "==", "!=", "<>", "<", "<=", ">", ">="))
+ node += token(i)
+
+ else
+ parse_error("Unknown comparitor [token(i)]")
+
+ return i + 1
+
+
+//bool_operator: 'AND' | '&&' | 'OR' | '||'
+/datum/SDQL_parser/proc/bool_operator(i, list/node)
+
+ if(tokenl(i) in list("and", "or", "&&", "||"))
+ node += token(i)
+
+ else
+ parse_error("Unknown comparitor [token(i)]")
+
+ return i + 1
+
+
+//string: ''' ''' | '"' '"'
+/datum/SDQL_parser/proc/string(i, list/node)
+
+ if(copytext(token(i), 1, 2) in list("'", "\""))
+ node += token(i)
+
+ else
+ parse_error("Expected string but found '[token(i)]'")
+
+ return i + 1
+
//array: '[' expression, expression, ... ']'
-/datum/SDQL_parser/proc/array(i, list/node)
+/datum/SDQL_parser/proc/array(var/i, var/list/node)
+ // Arrays get turned into this: list("[", list(exp_1a = exp_1b, ...), ...), "[" is to mark the next node as an array.
if(copytext(token(i), 1, 2) != "\[")
parse_error("Expected an array but found '[token(i)]'")
return i + 1
- node += token(i)
+
+ node += token(i) // Add the "["
+
var/list/expression_list = list()
+
i++
if(token(i) != "]")
var/list/temp_expression_list = list()
var/tok
do
tok = token(i)
- if(tok == "," || tok == ":")
- if(temp_expression_list == null)
+ if (tok == "," || tok == ":")
+ if (temp_expression_list == null)
parse_error("Found ',' or ':' without expression in an array.")
return i + 1
+
expression_list[++expression_list.len] = temp_expression_list
temp_expression_list = null
- if(tok == ":")
+ if (tok == ":")
temp_expression_list = list()
i = expression(i + 1, temp_expression_list)
expression_list[expression_list[expression_list.len]] = temp_expression_list
temp_expression_list = null
tok = token(i)
- if(tok != ",")
- if(tok == "]")
+ if (tok != ",")
+ if (tok == "]")
break
+
parse_error("Expected ',' or ']' after array assoc value, but found '[token(i)]'")
return i
+
+
i++
continue
+
temp_expression_list = list()
i = expression(i, temp_expression_list)
+
+ // Ok, what the fuck BYOND?
+ // Not having these lines here causes the parser to die
+ // on an error saying that list/token() doesn't exist as a proc.
+ // These lines prevent that.
+ // I assume the compiler/VM is shitting itself and swapping out some variables internally?
+ // While throwing in debug logging it disappeared
+ // And these 3 lines prevent it from happening while being quiet.
+ // So.. it works.
+ // Don't touch it.
+ var/whatthefuck = i
+ whatthefuck = src.type
+ whatthefuck = whatthefuck
+
while(token(i) && token(i) != "]")
- if(temp_expression_list)
+
+ if (temp_expression_list)
expression_list[++expression_list.len] = temp_expression_list
+
node[++node.len] = expression_list
- return i + 1
-//object_type: | string
-/datum/SDQL_parser/proc/object_type(i, list/node)
- if(copytext(token(i), 1, 2) == "/")
- node += token(i)
- else
- i = string(i, node)
- return i + 1
-
-//comparitor: '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
-/datum/SDQL_parser/proc/comparitor(i, list/node)
- if(token(i) in list("=", "==", "!=", "<>", "<", "<=", ">", ">="))
- node += token(i)
- else
- parse_error("Unknown comparitor [token(i)]")
- return i + 1
-
-//bool_operator: 'AND' | '&&' | 'OR' | '||'
-/datum/SDQL_parser/proc/bool_operator(i, list/node)
- if(tokenl(i) in list("and", "or", "&&", "||"))
- node += token(i)
- else
- parse_error("Unknown comparitor [token(i)]")
- return i + 1
-
-//string: ''' ''' | '"' '"'
-/datum/SDQL_parser/proc/string(i, list/node)
- if(copytext(token(i), 1, 2) in list("'", "\""))
- node += token(i)
- else
- parse_error("Expected string but found '[token(i)]'")
return i + 1
//call_function: ['(' [arguments] ')']
/datum/SDQL_parser/proc/call_function(i, list/node, list/arguments)
if(length(tokenl(i)))
var/procname = ""
- if(token(i) == "global" && token(i+1) == ".")
+ if(tokenl(i) == "global" && token(i + 1) == ".") // Global proc.
i += 2
procname = "global."
node += procname + token(i++)
if(token(i) != "(")
parse_error("Expected ( but found '[token(i)]'")
+
else if(token(i + 1) != ")")
- var/list/expression_list_temp = list()
+ var/list/temp_expression_list = list()
do
- i = expression(i + 1, expression_list_temp)
+ i = expression(i + 1, temp_expression_list)
if(token(i) == ",")
- arguments[++arguments.len] = expression_list_temp
- expression_list_temp = list()
+ arguments[++arguments.len] = temp_expression_list
+ temp_expression_list = list()
continue
+
while(token(i) && token(i) != ")")
- arguments[++arguments.len] = expression_list_temp
+
+ arguments[++arguments.len] = temp_expression_list // The code this is copy pasted from won't be executed when it's the last param, this fixes that.
else
i++
else
parse_error("Expected a function but found nothing")
return i + 1
-//select_function: count_function
-/datum/SDQL_parser/proc/select_function(i, list/node)
- parse_error("Sorry, function calls aren't available yet")
- return i
//expression: ( unary_expression | '(' expression ')' | value ) [binary_operator expression]
/datum/SDQL_parser/proc/expression(i, list/node)
+
if(token(i) in unary_operators)
i = unary_expression(i, node)
+
else if(token(i) == "(")
var/list/expr = list()
+
i = expression(i + 1, expr)
+
if(token(i) != ")")
parse_error("Missing ) at end of expression.")
+
else
i++
+
node[++node.len] = expr
+
else
i = value(i, node)
+
if(token(i) in binary_operators)
i = binary_operator(i, node)
i = expression(i, node)
+
else if(token(i) in comparitors)
i = binary_operator(i, node)
+
var/list/rhs = list()
i = expression(i, rhs)
+
node[++node.len] = rhs
+
+
return i
+
//unary_expression: unary_operator ( unary_expression | value | '(' expression ')' )
/datum/SDQL_parser/proc/unary_expression(i, list/node)
+
if(token(i) in unary_operators)
var/list/unary_exp = list()
+
unary_exp += token(i)
i++
+
if(token(i) in unary_operators)
i = unary_expression(i, unary_exp)
+
else if(token(i) == "(")
var/list/expr = list()
+
i = expression(i + 1, expr)
+
if(token(i) != ")")
parse_error("Missing ) at end of expression.")
+
else
i++
+
unary_exp[++unary_exp.len] = expr
+
else
i = value(i, unary_exp)
+
node[++node.len] = unary_exp
+
+
else
parse_error("Expected unary operator but found '[token(i)]'")
+
return i
-//binary_operator: comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^'
+
+//binary_operator: comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' | '%'
/datum/SDQL_parser/proc/binary_operator(i, list/node)
+
if(token(i) in (binary_operators + comparitors))
node += token(i)
+
else
parse_error("Unknown binary operator [token(i)]")
+
return i + 1
-//value: variable | string | number | 'null'
+
+//value: variable | string | number | 'null' | object_type
/datum/SDQL_parser/proc/value(i, list/node)
if(token(i) == "null")
node += "null"
i++
- else if(lowertext(copytext(token(i),1,3)) == "0x" && isnum(hex2num(copytext(token(i),3))))
- node += hex2num(copytext(token(i),3))
+
+ else if(lowertext(copytext(token(i), 1, 3)) == "0x" && isnum(hex2num(copytext(token(i), 3))))
+ node += hex2num(copytext(token(i), 3))
i++
+
else if(isnum(text2num(token(i))))
node += text2num(token(i))
i++
+
else if(copytext(token(i), 1, 2) in list("'", "\""))
i = string(i, node)
+
else if(copytext(token(i), 1, 2) == "\[") // Start a list.
i = array(i, node)
+ else if(copytext(token(i), 1, 2) == "/")
+ i = object_type(i, node)
else
i = variable(i, node)
- return i
-/*EXPLAIN SELECT * WHERE 42 = 6 * 9 OR val = - 5 == 7*/
+ return i
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
index 56faa1f6c4..a984e40fee 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
@@ -48,6 +48,12 @@
/proc/_image(icon, loc, icon_state, layer, dir)
return image(icon, loc, icon_state, layer, dir)
+/proc/_istype(object, type)
+ return istype(object, type)
+
+/proc/_ispath(path, type)
+ return ispath(path, type)
+
/proc/_length(E)
return length(E)
@@ -208,4 +214,3 @@
/proc/_step_away(ref, trg, max)
step_away(ref, trg, max)
-
diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm
index 97cc991523..72da825e0c 100644
--- a/code/modules/admin/verbs/playsound.dm
+++ b/code/modules/admin/verbs/playsound.dm
@@ -4,10 +4,12 @@
if(!check_rights(R_SOUNDS))
return
- var/freq = 1
var/vol = input(usr, "What volume would you like the sound to play at?",, 100) as null|num
if(!vol)
return
+ var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
+ if(!freq)
+ freq = 1
vol = CLAMP(vol, 1, 100)
var/sound/admin_sound = new()
@@ -96,13 +98,17 @@
if (data["webpage_url"])
webpage_url = "[title]"
+ var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
+ if(!freq)
+ freq = 1
+ pitch = freq
+
var/res = alert(usr, "Show the title of and link to this song to the players?\n[title]",, "No", "Yes", "Cancel")
switch(res)
if("Yes")
to_chat(world, "An admin played: [webpage_url]")
if("Cancel")
return
-
SSblackbox.record_feedback("nested tally", "played_url", 1, list("[ckey]", "[web_sound_input]"))
log_admin("[key_name(src)] played web sound: [web_sound_input]")
message_admins("[key_name(src)] played web sound: [web_sound_input]")
diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm
index 250434762a..845c66fb33 100644
--- a/code/modules/antagonists/cult/blood_magic.dm
+++ b/code/modules/antagonists/cult/blood_magic.dm
@@ -277,7 +277,7 @@
/datum/action/innate/cult/blood_spell/veiling
name = "Conceal Presence"
desc = "A multi-function spell that alternates between hiding and revealing nearby cult runes, structures, turf, and airlocks."
- invocation = "Kla'atu barada nikt'o!"
+ invocation = "Kla'atu barada nikt'o."
button_icon_state = "gone"
charges = 10
var/revealing = FALSE //if it reveals or not
@@ -326,7 +326,7 @@
/datum/action/innate/cult/blood_spell/manipulation
name = "Blood Rites"
desc = "A complex spell that allows you to gather blood and use it for healing or other powerful spells."
- invocation = "Fel'th Dol Ab'orod!"
+ invocation = "Fel'th Dol Ab'orod."
button_icon_state = "manip"
charges = 5
magic_path = "/obj/item/melee/blood_magic/manipulator"
@@ -402,7 +402,7 @@
/obj/item/melee/blood_magic/stun
name = "Stunning Aura"
color = RUNE_COLOR_RED
- invocation = "Fuu ma'jin!"
+ invocation = "Fuu ma'jin."
/obj/item/melee/blood_magic/stun/afterattack(atom/target, mob/living/carbon/user, proximity)
if(!isliving(target) || !proximity)
@@ -434,6 +434,7 @@
else
to_chat(user, "In an brilliant flash of red, [L] falls to the ground!")
L.Knockdown(160)
+ L.adjustStaminaLoss(140) //Ensures hard stamcrit
L.flash_act(1,1)
if(issilicon(target))
var/mob/living/silicon/S = L
@@ -498,7 +499,7 @@
/obj/item/melee/blood_magic/shackles
name = "Binding Aura"
desc = "Allows you to bind a victim and temporarily silence them."
- invocation = "In'totum Lig'abis!"
+ invocation = "In'totum Lig'abis."
color = "#000000" // black
/obj/item/melee/blood_magic/shackles/afterattack(atom/target, mob/living/carbon/user, proximity)
@@ -548,7 +549,7 @@
/obj/item/melee/blood_magic/construction
name = "Corrupting Aura"
desc = "Corrupts metal and plasteel into more sinister forms."
- invocation = "Ethra p'ni dedol!"
+ invocation = "Ethra p'ni dedol."
color = "#000000" // black
/obj/item/melee/blood_magic/construction/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
diff --git a/code/modules/antagonists/cult/cult_comms.dm b/code/modules/antagonists/cult/cult_comms.dm
index 375d250f59..8452f78ed3 100644
--- a/code/modules/antagonists/cult/cult_comms.dm
+++ b/code/modules/antagonists/cult/cult_comms.dm
@@ -27,7 +27,7 @@
var/my_message
if(!message)
return
- user.whisper("O bidai nabora se[pick("'","`")]sma!", language = /datum/language/common)
+ user.whisper("O bidai nabora se[pick("'","`")]sma.", language = /datum/language/common)
user.whisper(html_decode(message))
var/title = "Acolyte"
var/span = "cult italic"
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index aff0292b87..51c8803cee 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -25,7 +25,7 @@ Runes can either be invoked by one's self or with many different cultists. Each
layer = LOW_OBJ_LAYER
color = RUNE_COLOR_RED
- var/invocation = "Aiy ele-mayo!" //This is said by cultists when the rune is invoked.
+ var/invocation = "Aiy ele-mayo." //This is said by cultists when the rune is invoked.
var/req_cultists = 1 //The amount of cultists required around the rune to invoke it. If only 1, any cultist can invoke it.
var/req_cultists_text //if we have a description override for required cultists to invoke
var/rune_in_use = FALSE // Used for some runes, this is for when you want a rune to not be usable when in use.
@@ -163,7 +163,7 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/malformed
cultist_name = "malformed rune"
cultist_desc = "a senseless rune written in gibberish. No good can come from invoking this."
- invocation = "Ra'sha yoka!"
+ invocation = "Ra'sha yoka."
invoke_damage = 30
/obj/effect/rune/malformed/Initialize(mapload, set_keyword)
@@ -180,7 +180,7 @@ structure_check() searches for nearby cultist structures required for the invoca
cultist_name = "Offer"
cultist_desc = "offers a noncultist above it to Nar'Sie, either converting them or sacrificing them."
req_cultists_text = "2 for conversion, 3 for living sacrifices and sacrifice targets."
- invocation = "Mah'weyh pleggh at e'ntrath!"
+ invocation = "Mah'weyh pleggh at e'ntrath."
icon_state = "3"
color = RUNE_COLOR_OFFER
req_cultists = 1
@@ -213,7 +213,7 @@ structure_check() searches for nearby cultist structures required for the invoca
var/datum/team/cult/Cult_team = C.cult_team
var/is_convertable = is_convertable_to_cult(L,C.cult_team)
if(L.stat != DEAD && (is_clock || is_convertable))
- invocation = "Mah'weyh pleggh at e'ntrath!"
+ invocation = "Mah'weyh pleggh at e'ntrath."
..()
if(is_clock)
L.visible_message("[L]'s eyes glow a defiant yellow!", \
@@ -223,7 +223,7 @@ structure_check() searches for nearby cultist structures required for the invoca
else if(is_convertable)
do_convert(L, invokers)
else
- invocation = "Barhah hra zar'garis!"
+ invocation = "Barhah hra zar'garis."
..()
do_sacrifice(L, invokers)
animate(src, color = oldcolor, time = 5)
@@ -318,7 +318,7 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/empower
cultist_name = "Empower"
cultist_desc = "allows cultists to prepare greater amounts of blood magic at far less of a cost."
- invocation = "H'drak v'loso, mir'kanas verbot!"
+ invocation = "H'drak v'loso, mir'kanas verbot."
icon_state = "3"
color = RUNE_COLOR_TALISMAN
construct_invoke = FALSE
@@ -332,7 +332,7 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/teleport
cultist_name = "Teleport"
cultist_desc = "warps everything above it to another chosen teleport rune."
- invocation = "Sas'so c'arta forbici!"
+ invocation = "Sas'so c'arta forbici."
icon_state = "2"
color = RUNE_COLOR_TELEPORT
req_keyword = TRUE
@@ -509,7 +509,7 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/raise_dead
cultist_name = "Revive"
cultist_desc = "requires a dead, mindless, or inactive cultist placed upon the rune. Provided there have been sufficient sacrifices, they will be given a new life."
- invocation = "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" //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 = RUNE_COLOR_MEDIUMRED
var/static/revives_used = -SOULS_TO_REVIVE // Cultists get one "free" revive
@@ -544,7 +544,7 @@ structure_check() searches for nearby cultist structures required for the invoca
fail_invoke()
return
if(user.name == "Herbert West")
- invocation = "To life, to life, I bring them!"
+ invocation = "To life, to life, I bring them."
else
invocation = initial(invocation)
..()
@@ -600,7 +600,7 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/wall
cultist_name = "Barrier"
cultist_desc = "when invoked, makes a temporary invisible wall to block passage. Can be invoked again to reverse this."
- invocation = "Khari'd! Eske'te tannin!"
+ invocation = "Khari'd. Eske'te tannin."
icon_state = "4"
color = RUNE_COLOR_DARKRED
CanAtmosPass = ATMOS_PASS_DENSITY
@@ -682,7 +682,7 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/summon
cultist_name = "Summon Cultist"
cultist_desc = "summons a single cultist to the rune. Requires 2 invokers."
- invocation = "N'ath reth sh'yro eth d'rekkathnor!"
+ invocation = "N'ath reth sh'yro eth d'rekkathnor."
req_cultists = 2
invoke_damage = 10
icon_state = "3"
@@ -733,7 +733,7 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/blood_boil
cultist_name = "Boil Blood"
cultist_desc = "boils the blood of non-believers who can see the rune, rapidly dealing extreme amounts of damage. Requires 3 invokers."
- invocation = "Dedo ol'btoh!"
+ invocation = "Dedo ol'btoh."
icon_state = "4"
color = RUNE_COLOR_BURNTORANGE
light_color = LIGHT_COLOR_LAVA
@@ -797,7 +797,7 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/manifest
cultist_name = "Spirit Realm"
cultist_desc = "manifests a spirit servant of the Geometer and allows you to ascend as a spirit yourself. The invoker must not move from atop the rune, and will take damage for each summoned spirit."
- invocation = "Gal'h'rfikk harfrandid mud'gib!" //how the fuck do you pronounce this
+ invocation = "Gal'h'rfikk harfrandid mud'gib." //how the fuck do you pronounce this
icon_state = "7"
invoke_damage = 10
construct_invoke = FALSE
@@ -924,7 +924,7 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/apocalypse
cultist_name = "Apocalypse"
cultist_desc = "a harbinger of the end times. Grows in strength with the cult's desperation - but at the risk of... side effects."
- invocation = "Ta'gh fara'qha fel d'amar det!"
+ invocation = "Ta'gh fara'qha fel d'amar det."
icon = 'icons/effects/96x96.dmi'
icon_state = "apoc"
pixel_x = -32
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index e365f09b55..ade5458765 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -588,12 +588,18 @@ This is here to make the tiles around the station mininuke change when it's arme
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
if(istype(loneop))
loneop.weight += 1
+ if(loneop.weight % 5 == 0)
+ message_admins("[src] is stationary in [ADMIN_VERBOSEJMP(newturf)]. The weight of Lone Operative is now [loneop.weight].")
+ log_game("[src] is stationary for too long in [loc_name(newturf)], and has increased the weight of the Lone Operative event to [loneop.weight].")
else
lastlocation = newturf
last_disk_move = world.time
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
if(istype(loneop) && prob(loneop.weight))
loneop.weight = max(loneop.weight - 1, 0)
+ if(loneop.weight % 5 == 0)
+ message_admins("[src] is on the move (currently in [ADMIN_VERBOSEJMP(newturf)]). The weight of Lone Operative is now [loneop.weight].")
+ log_game("[src] being on the move has reduced the weight of the Lone Operative event to [loneop.weight].")
/obj/item/disk/nuclear/examine(mob/user)
. = ..()
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index a397fbb0d5..90df57f48b 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -202,10 +202,10 @@
/datum/antagonist/rev/farewell()
if(ishuman(owner.current))
owner.current.visible_message("[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!", null, null, null, owner.current)
- to_chat(owner, "You are no longer a brainwashed revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...")
+ to_chat(owner, "You are no longer a brainwashed revolutionary! Your memory is hazy from the time you were a rebel... You don't seem to be able to recall the names of your comrades, not even your leaders...")
else if(issilicon(owner.current))
owner.current.visible_message("The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.", null, null, null, owner.current)
- to_chat(owner, "The frame's firmware detects and deletes your neural reprogramming! You remember nothing but the name of the one who flashed you.")
+ to_chat(owner, "The frame's firmware detects and deletes your neural reprogramming! You remember nothing of your time spent reprogrammed, you can't even remember the names or identities of anyone involved...")
//blunt trauma deconversions call this through species.dm spec_attacked_by()
/datum/antagonist/rev/proc/remove_revolutionary(borged, deconverter)
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 6fac9730f3..e6cae518b6 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -60,50 +60,13 @@
owner.current.forceMove(pick(GLOB.wizardstart))
/datum/antagonist/wizard/proc/create_objectives()
- switch(rand(1,100))
- if(1 to 30)
- var/datum/objective/assassinate/kill_objective = new
- kill_objective.owner = owner
- kill_objective.find_target()
- objectives += kill_objective
+ var/datum/objective/new_objective = new("Cause as much creative mayhem as you can aboard the station! The more outlandish your methods of achieving this, the better! Make sure there's a decent amount of crew alive to tell of your tale.")
+ new_objective.owner = owner
+ objectives += new_objective
- if (!(locate(/datum/objective/escape) in owner.objectives))
- var/datum/objective/escape/escape_objective = new
- escape_objective.owner = owner
- objectives += escape_objective
-
- if(31 to 60)
- var/datum/objective/steal/steal_objective = new
- steal_objective.owner = owner
- steal_objective.find_target()
- objectives += steal_objective
-
- if (!(locate(/datum/objective/escape) in owner.objectives))
- var/datum/objective/escape/escape_objective = new
- escape_objective.owner = owner
- objectives += escape_objective
-
- if(61 to 85)
- var/datum/objective/assassinate/kill_objective = new
- kill_objective.owner = owner
- kill_objective.find_target()
- objectives += kill_objective
-
- var/datum/objective/steal/steal_objective = new
- steal_objective.owner = owner
- steal_objective.find_target()
- objectives += steal_objective
-
- if (!(locate(/datum/objective/survive) in owner.objectives))
- var/datum/objective/survive/survive_objective = new
- survive_objective.owner = owner
- objectives += survive_objective
-
- else
- if (!(locate(/datum/objective/hijack) in owner.objectives))
- var/datum/objective/hijack/hijack_objective = new
- hijack_objective.owner = owner
- objectives += hijack_objective
+ var/datum/objective/escape/escape_objective = new
+ escape_objective.owner = owner
+ objectives += escape_objective
for(var/datum/objective/O in objectives)
owner.objectives += O
diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm
index ebca252167..bf5d8efb13 100644
--- a/code/modules/atmospherics/environmental/LINDA_fire.dm
+++ b/code/modules/atmospherics/environmental/LINDA_fire.dm
@@ -20,8 +20,8 @@
if(active_hotspot)
if(soh)
if((tox > 0.5 || trit > 0.5) && oxy > 0.5)
- if(active_hotspot.temperature < exposed_temperature)
- active_hotspot.temperature = exposed_temperature
+ if(active_hotspot.temperature < exposed_temperature*50)
+ active_hotspot.temperature = exposed_temperature*50
if(active_hotspot.volume < exposed_volume)
active_hotspot.volume = exposed_volume
return 1
@@ -36,7 +36,7 @@
return 0
active_hotspot = new /obj/effect/hotspot(src)
- active_hotspot.temperature = exposed_temperature
+ active_hotspot.temperature = exposed_temperature*50
active_hotspot.volume = exposed_volume*25
active_hotspot.just_spawned = (current_cycle < SSair.times_fired)
@@ -47,6 +47,7 @@
heating.temperature = exposed_temperature
heating.react()
assume_air(heating)
+ air_update_turf()
return igniting
//This is the icon for fire on turfs, also helps for nurturing small fires until they are full tile
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index bcb3752cbf..0a7b76cc79 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -650,7 +650,7 @@
if(0)
add_overlay(AALARM_OVERLAY_GREEN)
overlay_state = AALARM_OVERLAY_GREEN
- light_color = LIGHT_COLOR_BLUEGREEN
+ light_color = LIGHT_COLOR_PALEBLUE
set_light(brightness_on)
if(1)
add_overlay(AALARM_OVERLAY_WARN)
diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm
index afdbde128e..276c488bce 100644
--- a/code/modules/awaymissions/corpse.dm
+++ b/code/modules/awaymissions/corpse.dm
@@ -65,9 +65,9 @@
/obj/effect/mob_spawn/Destroy()
GLOB.poi_list -= src
- LAZYREMOVE(GLOB.mob_spawners[name], src)
- if(!LAZYLEN(GLOB.mob_spawners[name]))
- GLOB.mob_spawners -= name
+ LAZYREMOVE(GLOB.mob_spawners[job_description ? job_description : name], src)
+ if(!LAZYLEN(GLOB.mob_spawners[job_description ? job_description : name]))
+ GLOB.mob_spawners -= job_description ? job_description : name
return ..()
/obj/effect/mob_spawn/proc/special(mob/M)
diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm
index 16ecae7a5f..7cb771737e 100644
--- a/code/modules/awaymissions/mission_code/snowdin.dm
+++ b/code/modules/awaymissions/mission_code/snowdin.dm
@@ -10,6 +10,10 @@
name = "Snowdin Tundra Plains"
icon_state = "awaycontent25"
+/area/awaymission/snowdin/outside/vip
+ name = "Snowdin Tundra Plains"
+ icon_state = "awaycontent25"
+
/area/awaymission/snowdin/post
name = "Snowdin Outpost"
icon_state = "awaycontent2"
@@ -116,7 +120,7 @@
name = "Snowdin Main Base"
icon_state = "awaycontent16"
dynamic_lighting = DYNAMIC_LIGHTING_ENABLED
- requires_power = TRUE
+ requires_power = FALSE
/area/awaymission/snowdin/dungeon1
name = "Snowdin Depths"
diff --git a/code/modules/buildmode/submodes/advanced.dm b/code/modules/buildmode/submodes/advanced.dm
index 22eb998cdf..418f504777 100644
--- a/code/modules/buildmode/submodes/advanced.dm
+++ b/code/modules/buildmode/submodes/advanced.dm
@@ -8,6 +8,7 @@
/datum/buildmode_mode/advanced/show_help(client/c)
to_chat(c, "***********************************************************")
to_chat(c, "Right Mouse Button on buildmode button = Set object type")
+ to_chat(c, "Left Mouse Button + alt on turf/obj = Copy object type")
to_chat(c, "Left Mouse Button on turf/obj = Place objects")
to_chat(c, "Right Mouse Button = Delete objects")
to_chat(c, "")
@@ -32,7 +33,15 @@
var/list/pa = params2list(params)
var/left_click = pa.Find("left")
var/right_click = pa.Find("right")
- if(left_click)
+ var/alt_click = pa.Find("alt")
+
+ if(left_click && alt_click)
+ if (istype(object, /turf) || istype(object, /obj) || istype(object, /mob))
+ objholder = object.type
+ to_chat(c, "[initial(object.name)] ([object.type]) selected.")
+ else
+ to_chat(c, "[initial(object.name)] is not a turf, object, or mob! Please select again.")
+ else if(left_click)
if(ispath(objholder,/turf))
var/turf/T = get_turf(object)
log_admin("Build Mode: [key_name(c)] modified [T] in [AREACOORD(object)] to [objholder]")
@@ -47,4 +56,3 @@
if(isobj(object))
log_admin("Build Mode: [key_name(c)] deleted [object] at [AREACOORD(object)]")
qdel(object)
-
\ No newline at end of file
diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm
index 6ad00a47ea..a23c57eab7 100644
--- a/code/modules/cargo/packs.dm
+++ b/code/modules/cargo/packs.dm
@@ -81,6 +81,30 @@
crate_name = "emergency crate"
crate_type = /obj/structure/closet/crate/internals
+/datum/supply_pack/emergency/rcds
+ name = "Emergency RCDs"
+ desc = "Bombs going off on station? SME blown and now you need to fix the hole it left behind? Well this crate has a pare of Rcds to be able to easily fix up any problem you may have!"
+ cost = 1500
+ contains = list(/obj/item/construction/rcd,
+ /obj/item/construction/rcd)
+ crate_name = "emergency rcds"
+ crate_type = /obj/structure/closet/crate/internals
+
+/datum/supply_pack/emergency/soft_suit
+ name = "Emergency Space Suit "
+ desc = "Is there bombs going off left and right? Is there meteors shooting around the station? Well we have two fragile space suit for emergencys as well as air and masks."
+ cost = 1000
+ contains = list(/obj/item/tank/internals/air,
+ /obj/item/tank/internals/air,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/suit/space/fragile,
+ /obj/item/clothing/suit/space/fragile,
+ /obj/item/clothing/head/helmet/space/fragile,
+ /obj/item/clothing/head/helmet/space/fragile)
+ crate_name = "emergency crate"
+ crate_type = /obj/structure/closet/crate/internals
+
/datum/supply_pack/emergency/firefighting
name = "Firefighting Crate"
desc = "Only you can prevent station fires. Partner up with two firefighter suits, gas masks, flashlights, large oxygen tanks, extinguishers, and hardhats!"
@@ -241,6 +265,26 @@
crate_name = "weed control crate"
crate_type = /obj/structure/closet/crate/secure/hydroponics
+/datum/supply_pack/medical/anitvirus
+ name = "Virus Containment Crate"
+ desc = "Viro let out a death plague Mk II again? Someone didnt wash there hands? Old plagues born anew? Well this crate is for you! Hope you cure it before it brakes out of the station... This crate needs medical access to open and has two bio suits, a box of needles and beakers, five spaceacillin needles, and a medibot."
+ cost = 3000
+ access = ACCESS_MEDICAL
+ contains = list(/mob/living/simple_animal/bot/medbot,
+ /obj/item/clothing/head/bio_hood,
+ /obj/item/clothing/head/bio_hood,
+ /obj/item/clothing/suit/bio_suit,
+ /obj/item/clothing/suit/bio_suit,
+ /obj/item/reagent_containers/syringe/antiviral,
+ /obj/item/reagent_containers/syringe/antiviral,
+ /obj/item/reagent_containers/syringe/antiviral,
+ /obj/item/reagent_containers/syringe/antiviral,
+ /obj/item/reagent_containers/syringe/antiviral,
+ /obj/item/storage/box/syringes,
+ /obj/item/storage/box/beakers)
+ crate_name = "virus containment unit crate"
+ crate_type = /obj/structure/closet/crate/secure/plasma
+
//////////////////////////////////////////////////////////////////////////////
//////////////////////////// Security ////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@@ -280,6 +324,15 @@
/obj/item/clothing/head/fedora/det_hat)
crate_name = "forensics crate"
+/datum/supply_pack/security/sechardsuit
+ name = "Sec Hardsuit"
+ desc = "One Sec Hardsuit with a small air tank and mask."
+ cost = 3000 // half of SWAT gear for have the armor and half the gear
+ contains = list(/obj/item/clothing/suit/space/hardsuit/security,
+ /obj/item/tank/internals/air,
+ /obj/item/clothing/mask/gas)
+ crate_name = "sec hardsuit crate"
+
/datum/supply_pack/security/helmets
name = "Helmets Crate"
desc = "Contains three standard-issue brain buckets. Requires Security access to open."
@@ -362,6 +415,29 @@
/obj/item/melee/baton/loaded)
crate_name = "stun baton crate"
+/datum/supply_pack/security/russianclothing
+ name = "Russian Surplus Clothing"
+ desc = "An old russian crate full of surplus armor that they used to use! Has two sets of bulletproff armor, a few union suits and some warm hats!"
+ hidden = TRUE
+ contraband = TRUE
+ cost = 5000 // Its basicly sec suits, good boots/gloves
+ contains = list(/obj/item/clothing/suit/security/officer/russian,
+ /obj/item/clothing/suit/security/officer/russian,
+ /obj/item/clothing/shoes/combat,
+ /obj/item/clothing/shoes/combat,
+ /obj/item/clothing/head/ushanka,
+ /obj/item/clothing/head/ushanka,
+ /obj/item/clothing/suit/armor/bulletproof,
+ /obj/item/clothing/suit/armor/bulletproof,
+ /obj/item/clothing/head/helmet/alt,
+ /obj/item/clothing/head/helmet/alt,
+ /obj/item/clothing/gloves/combat,
+ /obj/item/clothing/gloves/combat,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/mask/gas)
+ crate_name = "surplus russian clothing"
+ crate_type = /obj/structure/closet/crate/internals
+
/datum/supply_pack/security/taser
name = "Taser Crate"
desc = "From the depths of stunbased combat, this order rises above, supreme. Contains three hybrid tasers, capable of firing both electrodes and disabling shots. Requires Security access to open."
@@ -633,6 +709,24 @@
/obj/item/clothing/glasses/meson/engine)
crate_name = "engineering gear crate"
+/datum/supply_pack/engineering/engihardsuit
+ name = "Engineering Hardsuit"
+ desc = "Poly 'Who stole all the hardsuits!' Well now you can get more hardsuits if needed! NOTE ONE HARDSUIT IS IN THIS CRATE, as well as one air tank and maks!"
+ cost = 2500
+ contains = list(/obj/item/tank/internals/air,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/suit/space/hardsuit/engine)
+ crate_name = "engineering hardsuit"
+
+/datum/supply_pack/engineering/industrialrcd
+ name = "Industrial RCD"
+ desc = "A industrial RCD in case the station has gone through more then one meteor storm and the CE needs to bring out the somthing a bit more reliable. Dose not contain spare ammo for the industrial RCD or any other RCD modles."
+ cost = 4500
+ access = ACCESS_CE
+ contains = list(/obj/item/construction/rcd/industrial)
+ crate_name = "industrial rcd"
+ crate_type = /obj/structure/closet/crate/secure/engineering
+
/datum/supply_pack/engineering/powergamermitts
name = "Insulated Gloves Crate"
desc = "The backbone of modern society. Barely ever ordered for actual engineering. Contains three insulated gloves."
@@ -978,6 +1072,28 @@
contains = list(/obj/item/stack/sheet/mineral/wood/fifty)
crate_name = "wood planks crate"
+/datum/supply_pack/materials/rcdammo
+ name = "Spare RDC ammo"
+ desc = "This crate contains sixteen RCD ammo packs, to help with any holes or projects people mite be working on."
+ cost = 3750
+ contains = list(/obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo,
+ /obj/item/rcd_ammo)
+ crate_name = "rcd ammo"
+
/datum/supply_pack/materials/bz
name = "BZ Canister Crate"
desc = "Contains a canister of BZ. Requires Toxins access to open."
@@ -1120,6 +1236,15 @@
/obj/item/storage/firstaid/regular)
crate_name = "first aid kit crate"
+/datum/supply_pack/medical/medicalhardsuit
+ name = "Medical Hardsuit"
+ desc = "Got people being spaced left and right? Hole in the same room as the dead body of Hos or cap? Fear not, now you can buy one medical hardsuit with a mask and air tank to save your fellow crewmembers."
+ cost = 2500
+ contains = list(/obj/item/tank/internals/air,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/suit/space/hardsuit/medical)
+ crate_name = "medical hardsuit"
+
/datum/supply_pack/medical/iv_drip
name = "IV Drip Crate"
desc = "Contains a single IV drip for administering blood to patients."
@@ -1238,6 +1363,7 @@
crate_type = /obj/structure/closet/crate/secure/plasma
dangerous = TRUE
+
//////////////////////////////////////////////////////////////////////////////
//////////////////////////// Science /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@@ -1411,6 +1537,18 @@
crate_name = "janitor backpack crate"
crate_type = /obj/structure/closet/crate/secure
+/datum/supply_pack/service/advlighting
+ name = "Advanced Lighting crate"
+ desc = "Thanks to advanced lighting tech we here at the Lamp Factory have be able to produce more lamps and lamp items! This crate has three lamps, a box of lights and a state of the art rapid-light-device!"
+ cost = 2500 //Fair
+ contains = list(/obj/item/construction/rld,
+ /obj/item/flashlight/lamp,
+ /obj/item/flashlight/lamp,
+ /obj/item/flashlight/lamp/green,
+ /obj/item/storage/box/lights/mixed)
+ crate_name = "advanced lighting crate"
+ crate_type = /obj/structure/closet/crate/secure
+
/datum/supply_pack/service/mule
name = "MULEbot Crate"
desc = "Pink-haired Quartermaster not doing her job? Replace her with this tireless worker, today!"
@@ -1444,7 +1582,7 @@
/datum/supply_pack/service/carpet
name = "Premium Carpet Crate"
- desc = "Plasteel floor tiles getting on your nerves? These stacks of extra soft carpet will tie any room together."
+ desc = "Plasteel floor tiles getting on your nerves? These stacks of extra soft carpet will tie any room together. Contains the classics."
cost = 1000
contains = list(/obj/item/stack/tile/carpet/fifty,
/obj/item/stack/tile/carpet/fifty,
@@ -1452,6 +1590,16 @@
/obj/item/stack/tile/carpet/black/fifty)
crate_name = "premium carpet crate"
+/datum/supply_pack/service/carpet2
+ name = "Premium Carpet Crate #2"
+ desc = "Plasteel floor tiles getting on your nerves? These stacks of extra soft carpet will tie any room together. Contains red, and monochrome"
+ cost = 1000
+ contains = list(/obj/item/stack/tile/carpet/blackred/fifty,
+ /obj/item/stack/tile/carpet/blackred/fifty,
+ /obj/item/stack/tile/carpet/monochrome/fifty,
+ /obj/item/stack/tile/carpet/monochrome/fifty)
+ crate_name = "premium carpet crate #2"
+
/datum/supply_pack/service/lightbulbs
name = "Replacement Lights"
desc = "May the light of Aether shine upon this station! Or at least, the light of forty two light tubes and twenty one light bulbs as well as a light replacer."
@@ -1611,6 +1759,22 @@
access = ACCESS_THEATRE
crate_type = /obj/structure/closet/crate/secure
+/datum/supply_pack/organic/hunting
+ name = "Huntting gear"
+ desc = "Even in space, we can fine prey to hunt, this crate contains everthing a fine hunter needs to have a sporting time. This crate needs armory access to open. A true huntter only needs a fine bottle of cognac, a nice coat, some good o' cigars, and of cource a huntting shotgun. "
+ cost = 3500
+ contraband = TRUE
+ contains = list(/obj/item/clothing/head/flatcap,
+ /obj/item/clothing/suit/hooded/wintercoat/captain,
+ /obj/item/reagent_containers/food/drinks/bottle/cognac,
+ /obj/item/storage/fancy/cigarettes/cigars/havana,
+ /obj/item/clothing/gloves/color/white,
+ /obj/item/clothing/under/rank/curator,
+ /obj/item/gun/ballistic/shotgun/lethal)
+ access = ACCESS_ARMORY
+ crate_name = "sporting crate"
+ crate_type = /obj/structure/closet/crate/secure // Would have liked a wooden crate but access >:(
+
/datum/supply_pack/organic/hydroponics
name = "Hydroponics Crate"
desc = "Supplies for growing a great garden! Contains two bottles of ammonia, two Plant-B-Gone spray bottles, a hatchet, cultivator, plant analyzer, as well as a pair of leather gloves and a botanist's apron."
@@ -2213,7 +2377,9 @@
/obj/item/canvas/twentythreeXtwentythree,
/obj/item/storage/crayons,
/obj/item/storage/crayons,
- /obj/item/toy/crayon/rainbow)
+ /obj/item/toy/crayon/rainbow,
+ /obj/item/toy/crayon/white,
+ /obj/item/toy/crayon/white)
crate_name = "art supply crate"
crate_type = /obj/structure/closet/crate/wooden
diff --git a/code/modules/client/verbs/aooc.dm b/code/modules/client/verbs/aooc.dm
index 90c0dc5a55..893501a852 100644
--- a/code/modules/client/verbs/aooc.dm
+++ b/code/modules/client/verbs/aooc.dm
@@ -61,7 +61,7 @@
antaglisting |= M.current.client
for(var/mob/M in GLOB.player_list)
- if(M.client && (M.stat == DEAD || M.client.holder))
+ if(M.client && (M.stat == DEAD || M.client.holder) && !istype(M, /mob/dead/new_player))
antaglisting |= M.client
for(var/client/C in antaglisting)
diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm
index 6c22334f78..b691074c27 100644
--- a/code/modules/clothing/gloves/miscellaneous.dm
+++ b/code/modules/clothing/gloves/miscellaneous.dm
@@ -69,6 +69,7 @@
if(M.a_intent == INTENT_HARM)
M.changeNext_move(CLICK_CD_RAPID)
+ M.adjustStaminaLoss(-2) //Restore 2/3 of the stamina used assuming empty stam buffer. With proper stamina buffer management, this results in a net gain of +.5 stamina per click.
if(warcry)
M.say("[warcry]", ignore_spam = TRUE, forced = "north star warcry")
.= FALSE
diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm
index 89f114f7e5..cd8efd74bf 100644
--- a/code/modules/clothing/spacesuits/miscellaneous.dm
+++ b/code/modules/clothing/spacesuits/miscellaneous.dm
@@ -168,6 +168,7 @@ Contains:
strip_delay = 130
item_flags = NODROP
brightness_on = 7
+ resistance_flags = ACID_PROOF
/obj/item/clothing/suit/space/hardsuit/ert
name = "emergency response team suit"
@@ -179,6 +180,7 @@ Contains:
armor = list("melee" = 65, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 80)
slowdown = 0
strip_delay = 130
+ resistance_flags = ACID_PROOF
//ERT Security
/obj/item/clothing/head/helmet/space/hardsuit/ert/sec
diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm
index cbc8f33e3a..6d9a02e05a 100644
--- a/code/modules/clothing/suits/bio.dm
+++ b/code/modules/clothing/suits/bio.dm
@@ -5,7 +5,7 @@
desc = "A hood that protects the head and face from biological contaminants."
permeability_coefficient = 0.01
clothing_flags = THICKMATERIAL | BLOCK_GAS_SMOKE_EFFECT
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100)
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 60, "fire" = 30, "acid" = 100)
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEHAIR|HIDEFACIALHAIR|HIDEFACE
resistance_flags = ACID_PROOF
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH
@@ -22,7 +22,7 @@
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
slowdown = 1
allowed = list(/obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/pen, /obj/item/flashlight/pen, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray)
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100)
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 60, "fire" = 30, "acid" = 100)
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAUR
strip_delay = 70
equip_delay_other = 70
diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm
index 7c79aff143..2516d446d6 100644
--- a/code/modules/clothing/suits/reactive_armour.dm
+++ b/code/modules/clothing/suits/reactive_armour.dm
@@ -126,7 +126,7 @@
/obj/item/clothing/suit/armor/reactive/stealth
name = "reactive stealth armor"
- reactivearmor_cooldown_duration = 300
+ reactivearmor_cooldown_duration = 65
desc = "An experimental suit of armor that renders the wearer invisible on detection of imminent harm, and creates a decoy that runs away from the owner. You can't fight what you can't see."
/obj/item/clothing/suit/armor/reactive/stealth/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
@@ -153,7 +153,7 @@
name = "reactive tesla armor"
desc = "An experimental suit of armor with sensitive detectors hooked up to a huge capacitor grid, with emitters strutting out of it. Zap."
siemens_coefficient = -1
- reactivearmor_cooldown_duration = 150
+ reactivearmor_cooldown_duration = 20
var/tesla_power = 25000
var/tesla_range = 20
var/tesla_flags = TESLA_MOB_DAMAGE | TESLA_OBJ_DAMAGE
@@ -187,7 +187,7 @@
/obj/item/clothing/suit/armor/reactive/repulse
name = "reactive repulse armor"
- reactivearmor_cooldown_duration = 200
+ reactivearmor_cooldown_duration = 20
desc = "An experimental suit of armor that violently throws back attackers."
/obj/item/clothing/suit/armor/reactive/repulse/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
@@ -213,7 +213,7 @@
/obj/item/clothing/suit/armor/reactive/table
name = "reactive table armor"
- reactivearmor_cooldown_duration = 500
+ reactivearmor_cooldown_duration = 0
desc = "If you can't beat the memes, embrace them."
var/tele_range = 10
diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm
index dd0083a1b7..6bf96429b9 100644
--- a/code/modules/clothing/under/accessories.dm
+++ b/code/modules/clothing/under/accessories.dm
@@ -139,7 +139,7 @@
SSblackbox.record_feedback("associative", "commendation", 1, list("commender" = "[user.real_name]", "commendee" = "[M.real_name]", "medal" = "[src]", "reason" = input))
GLOB.commendations += "[user.real_name] awarded [M.real_name] the [name]! \n- [input]"
commended = TRUE
- desc += "
The inscription reads: [input] - [user.real_name]"
+ desc += "
The inscription reads: [input] - [user.real_name]"
log_game("[key_name(M)] was given the following commendation by [key_name(user)]: [input]")
message_admins("[key_name(M)] was given the following commendation by [key_name(user)]: [input]")
@@ -157,6 +157,16 @@
desc = "A bronze heart-shaped medal awarded for sacrifice. It is often awarded posthumously or for severe injury in the line of duty."
icon_state = "bronze_heart"
+/obj/item/clothing/accessory/medal/engineer
+ name = "\"Shift's Best Electrician\" award"
+ desc = "An award bestowed upon engineers who have excelled at keeping the station running in the best possible condition against all odds."
+ icon_state = "engineer"
+
+/obj/item/clothing/accessory/medal/greytide
+ name = "\"Greytider of the shift\" award"
+ desc = "An award for only the most annoying of assistants. Locked doors mean nothing to you and behaving is not in your vocabulary"
+ icon_state = "greytide"
+
/obj/item/clothing/accessory/medal/ribbon
name = "ribbon"
desc = "A ribbon"
@@ -167,6 +177,11 @@
name = "\"cargo tech of the shift\" award"
desc = "An award bestowed only upon those cargotechs who have exhibited devotion to their duty in keeping with the highest traditions of Cargonia."
+/obj/item/clothing/accessory/medal/ribbon/medical_doctor
+ name = "\"doctor of the shift\" award"
+ desc = "An award bestowed only upon the most capable doctors who have upheld the Hippocratic Oath to the best of their ability"
+ icon_state = "medical_doctor"
+
/obj/item/clothing/accessory/medal/silver
name = "silver medal"
desc = "A silver medal."
@@ -280,7 +295,7 @@
desc = "Fills you with the conviction of JUSTICE. Lawyers tend to want to show it to everyone they meet."
icon_state = "lawyerbadge"
item_color = "lawyerbadge"
-
+
/obj/item/clothing/accessory/lawyers_badge/attack_self(mob/user)
if(prob(1))
user.say("The testimony contradicts the evidence!", forced = "attorney's badge")
diff --git a/code/modules/clothing/under/vg_under.dm b/code/modules/clothing/under/vg_under.dm
index 64bf75d5ac..f416f2ecc5 100644
--- a/code/modules/clothing/under/vg_under.dm
+++ b/code/modules/clothing/under/vg_under.dm
@@ -1,3 +1,6 @@
+// Fixed to work with Citadel code. Apparently none of them had NO_MUTANTRACE flags.
+// I was pissy when I realised how to fix this because it's so fucking easy and nobody apparently had done it.
+
/obj/item/clothing/under/stripper_pink
name = "pink stripper outfit"
icon_state = "stripper_p"
@@ -6,7 +9,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/stripper_green
name = "green stripper outfit"
@@ -16,8 +19,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/wedding/bride_orange
name = "orange wedding dress"
@@ -28,7 +30,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/wedding/bride_purple
name = "purple wedding dress"
@@ -39,7 +41,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/wedding/bride_blue
name = "blue wedding dress"
@@ -50,7 +52,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/wedding/bride_red
name = "red wedding dress"
@@ -61,7 +63,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/wedding/bride_white
name = "white wedding dress"
@@ -72,7 +74,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/mankini
name = "pink mankini"
@@ -82,6 +84,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/*
/obj/item/clothing/under/psysuit
@@ -126,7 +129,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/russobluecamooutfit
name = "russian blue camo"
@@ -137,7 +140,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/stilsuit
name = "stillsuit"
@@ -148,7 +151,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/aviatoruniform
name = "aviator uniform"
@@ -159,7 +162,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/bikersuit
name = "biker's outfit"
@@ -169,7 +172,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/jacketsuit
name = "richard's outfit"
@@ -180,7 +183,7 @@
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
obj/item/clothing/under/mega
name = "\improper DRN-001 suit"
@@ -191,7 +194,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/proto
name = "The Prototype Suit"
@@ -202,7 +205,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/megax
name = "\improper Maverick Hunter regalia"
@@ -213,7 +216,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/joe
name = "The Sniper Suit"
@@ -224,7 +227,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/roll
name = "\improper DRN-002 Dress"
@@ -235,7 +238,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/gokugidown
name = "turtle hermit undershirt"
@@ -246,7 +249,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/gokugi
name = "turtle hermit outfit"
@@ -257,7 +260,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/doomguy
name = "\improper Doomguy's pants"
@@ -268,7 +271,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/vault13
name = "vault 13 Jumpsuit"
@@ -279,7 +282,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/vault
name = "vault jumpsuit"
@@ -290,7 +293,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/clownpiece
name = "Clownpiece's Pierrot suit"
@@ -301,7 +304,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/cia
name = "casual IAA outfit"
@@ -312,7 +315,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/greaser
name = "greaser outfit"
@@ -322,7 +325,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/greaser/New()
var/greaser_colour = "default"
@@ -341,6 +344,8 @@ obj/item/clothing/under/mega
item_color = "greaser_[greaser_colour]"
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
+
/obj/item/clothing/under/wintercasualwear
name = "winter casualwear"
@@ -351,8 +356,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/casualwear
name = "spring casualwear"
@@ -363,7 +367,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/keyholesweater
name = "keyhole sweater"
@@ -374,7 +378,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/casualhoodie
name = "casual hoodie"
@@ -385,8 +389,7 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
-
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
/obj/item/clothing/under/casualhoodie/skirt
icon_state = "hoodieskirt"
@@ -394,6 +397,8 @@ obj/item/clothing/under/mega
item_color = "hoodieskirt"
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
+
/*
/obj/item/clothing/under/mummy_rags
name = "mummy rags"
@@ -427,3 +432,5 @@ obj/item/clothing/under/mega
can_adjust = 0
icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
+
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index e33cfbd25d..a838b62e0d 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -18,7 +18,14 @@
"Your money can buy happiness!", \
"Engage direct marketing!", \
"Advertising is legalized lying! But don't let that put you off our great deals!", \
- "You don't want to buy anything? Yeah, well, I didn't want to buy your mom either.")
+ "You don't want to buy anything? Yeah, well, I didn't want to buy your mom either.",
+ "Gamers, rise up!",
+ "Ok, now, this is epic.",
+ "HUMAN FUNNY.",
+ "But I'm already tracer!",
+ "How do I vore people?",
+ "ERP?",
+ "Not epic bros...")
/datum/round_event/brand_intelligence/announce(fake)
diff --git a/code/modules/events/devil.dm b/code/modules/events/devil.dm
index 3760cbe05d..7d6e0bd441 100644
--- a/code/modules/events/devil.dm
+++ b/code/modules/events/devil.dm
@@ -39,6 +39,7 @@
var/datum/job/jobdatum = SSjob.GetJob("Assistant")
devil.job = jobdatum.title
jobdatum.equip(devil)
+ success_spawn = TRUE
return SUCCESSFUL_SPAWN
diff --git a/code/modules/events/vent_clog.dm b/code/modules/events/vent_clog.dm
index 160304b1c3..3945a12a40 100644
--- a/code/modules/events/vent_clog.dm
+++ b/code/modules/events/vent_clog.dm
@@ -79,12 +79,28 @@
typepath = /datum/round_event/vent_clog/beer
max_occurrences = 0
+/datum/round_event/vent_clog/beer
+ reagentsAmount = 100
+
/datum/round_event_control/vent_clog/plasma_decon
name = "Plasma decontamination"
typepath = /datum/round_event/vent_clog/plasma_decon
max_occurrences = 0
-/datum/round_event/vent_clog/beer
+/datum/round_event_control/vent_clog/female
+ name = "FemCum stationwide"
+ typepath = /datum/round_event/vent_clog/female
+ max_occurrences = 0
+
+/datum/round_event/vent_clog/female
+ reagentsAmount = 100
+
+/datum/round_event_control/vent_clog/male
+ name = "Semen stationwide"
+ typepath = /datum/round_event/vent_clog/male
+ max_occurrences = 0
+
+/datum/round_event/vent_clog/male
reagentsAmount = 100
/datum/round_event/vent_clog/beer/announce()
@@ -102,6 +118,30 @@
foam.start()
CHECK_TICK
+/datum/round_event/vent_clog/male/start()
+ for(var/obj/machinery/atmospherics/components/unary/vent in vents)
+ if(vent && vent.loc)
+ var/datum/reagents/R = new/datum/reagents(1000)
+ R.my_atom = vent
+ R.add_reagent("semen", reagentsAmount)
+
+ var/datum/effect_system/foam_spread/foam = new
+ foam.set_up(200, get_turf(vent), R)
+ foam.start()
+ CHECK_TICK
+
+/datum/round_event/vent_clog/female/start()
+ for(var/obj/machinery/atmospherics/components/unary/vent in vents)
+ if(vent && vent.loc)
+ var/datum/reagents/R = new/datum/reagents(1000)
+ R.my_atom = vent
+ R.add_reagent("femcum", reagentsAmount)
+
+ var/datum/effect_system/foam_spread/foam = new
+ foam.set_up(200, get_turf(vent), R)
+ foam.start()
+ CHECK_TICK
+
/datum/round_event/vent_clog/plasma_decon/announce()
priority_announce("We are deploying an experimental plasma decontamination system. Please stand away from the vents and do not breathe the smoke that comes out.", "Central Command Update")
diff --git a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
index c24b22ce71..9d173a34a5 100644
--- a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
@@ -90,6 +90,10 @@
name = "Nuka Cola"
list_reagents = list("nuka_cola" = 50)
+/obj/item/reagent_containers/food/drinks/drinkingglass/filled/syndicatebomb
+ name = "Syndicat Bomb"
+ list_reagents = list("syndicatebomb" = 50)
+
/obj/item/reagent_containers/food/drinks/drinkingglass/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/reagent_containers/food/snacks/egg)) //breaking eggs
var/obj/item/reagent_containers/food/snacks/egg/E = I
diff --git a/code/modules/food_and_drinks/food/snacks_bread.dm b/code/modules/food_and_drinks/food/snacks_bread.dm
index 0638bfad25..0f11fc8ed2 100644
--- a/code/modules/food_and_drinks/food/snacks_bread.dm
+++ b/code/modules/food_and_drinks/food/snacks_bread.dm
@@ -185,6 +185,17 @@
icon_state = ""
bitesize = 2
+GLOBAL_VAR_INIT(frying_hardmode, TRUE)
+GLOBAL_VAR_INIT(frying_bad_chem_add_volume, TRUE)
+GLOBAL_LIST_INIT(frying_bad_chems, list(
+"bad_food" = 10,
+"clf3" = 2,
+"aranesp" = 2,
+"blackpowder" = 10,
+"phlogiston" = 3,
+"cyanide" = 3,
+))
+
/obj/item/reagent_containers/food/snacks/deepfryholder/Initialize(mapload, obj/item/fried)
. = ..()
name = fried.name //We'll determine the other stuff when it's actually removed
@@ -210,6 +221,13 @@
else
fried.forceMove(src)
trash = fried
+ if(!istype(fried, /obj/item/reagent_containers/food) && GLOB.frying_hardmode && GLOB.frying_bad_chems.len)
+ var/R = rand(1, GLOB.frying_bad_chems.len)
+ var/bad_chem = GLOB.frying_bad_chems[R]
+ var/bad_chem_amount = GLOB.frying_bad_chems[bad_chem]
+ if(GLOB.frying_bad_chem_add_volume)
+ reagents.maximum_volume += bad_chem_amount
+ reagents.add_reagent(bad_chem, bad_chem_amount)
/obj/item/reagent_containers/food/snacks/deepfryholder/Destroy()
if(trash)
diff --git a/code/modules/food_and_drinks/food/snacks_pizza.dm b/code/modules/food_and_drinks/food/snacks_pizza.dm
index a6b026ac30..5bf5abacb0 100644
--- a/code/modules/food_and_drinks/food/snacks_pizza.dm
+++ b/code/modules/food_and_drinks/food/snacks_pizza.dm
@@ -149,16 +149,16 @@
icon_state = "pineapplepizza"
slice_path = /obj/item/reagent_containers/food/snacks/pizzaslice/pineapple
bonus_reagents = list("nutriment" = 6, "vitamin" = 6)
- tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 2, "ham" = 2)
- foodtype = GRAIN | VEGETABLES | DAIRY | MEAT | FRUIT | PINEAPPLE
+ tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 6, "ham" = 2)
+ foodtype = PINEAPPLE //Over powering tast of gods fruit
/obj/item/reagent_containers/food/snacks/pizzaslice/pineapple
name = "\improper Hawaiian pizza slice"
desc = "A slice of delicious controversy."
icon_state = "pineapplepizzaslice"
filling_color = "#FF4500"
- tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 2, "ham" = 2)
- foodtype = GRAIN | VEGETABLES | DAIRY | MEAT | FRUIT | PINEAPPLE
+ tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 6, "ham" = 2)
+ foodtype = PINEAPPLE //Over powering tast of gods fruit
/obj/item/reagent_containers/food/snacks/pizza/arnold
name = "\improper Arnold pizza"
diff --git a/code/modules/holodeck/turfs.dm b/code/modules/holodeck/turfs.dm
index 2a36983561..a4c89b8874 100644
--- a/code/modules/holodeck/turfs.dm
+++ b/code/modules/holodeck/turfs.dm
@@ -122,7 +122,7 @@
/turf/open/floor/holofloor/wood
icon_state = "wood"
tiled_dirt = FALSE
-
+
/turf/open/floor/holofloor/snow
gender = PLURAL
name = "snow"
@@ -133,6 +133,15 @@
bullet_sizzle = TRUE
bullet_bounce_sound = null
tiled_dirt = FALSE
+ baseturfs = /turf/open/floor/holofloor/snow
+
+/turf/open/floor/holofloor/snow/attack_hand(mob/living/user)
+ . = ..()
+ if(.)
+ return
+ user.visible_message("[user] scroops up some snow from [src].", "You scoop up some snow from [src].")
+ var/obj/item/toy/snowball/S = new(get_turf(src))
+ user.put_in_hands(S)
/turf/open/floor/holofloor/snow/cold
initial_gas_mix = "nob=7500;TEMP=2.7"
@@ -143,3 +152,22 @@
icon = 'icons/turf/floors.dmi'
icon_state = "asteroid"
tiled_dirt = FALSE
+
+/turf/open/floor/holofloor/ice
+ name = "ice sheet"
+ desc = "A sheet of solid ice. Looks slippery."
+ icon = 'icons/turf/floors/ice_turf.dmi'
+ icon_state = "unsmooth"
+ baseturfs = /turf/open/floor/holofloor/ice
+ slowdown = 1
+ footstep = FOOTSTEP_FLOOR
+
+/turf/open/floor/holofloor/ice/smooth
+ icon_state = "smooth"
+ smooth = SMOOTH_MORE | SMOOTH_BORDER
+ canSmoothWith = list(/turf/open/floor/plating/ice/smooth, /turf/open/floor/plating/ice, /turf/open/floor/holofloor/ice)
+ baseturfs = /turf/open/floor/holofloor/ice/smooth
+
+/turf/open/floor/holofloor/ice/Initialize()
+ . = ..()
+ MakeSlippery(TURF_WET_PERMAFROST, INFINITY, 0, INFINITY, TRUE)
diff --git a/code/modules/hydroponics/grown/tomato.dm b/code/modules/hydroponics/grown/tomato.dm
index 767de8011a..d07f5d4d08 100644
--- a/code/modules/hydroponics/grown/tomato.dm
+++ b/code/modules/hydroponics/grown/tomato.dm
@@ -136,6 +136,7 @@
spawn(30)
if(!QDELETED(src))
+ investigate_log("[key_name(user)] released a killer tomato at [COORD(src)]", INVESTIGATE_BOTANY)
var/mob/living/simple_animal/hostile/killertomato/K = new /mob/living/simple_animal/hostile/killertomato(get_turf(src.loc))
K.maxHealth += round(seed.endurance / 3)
K.melee_damage_lower += round(seed.potency / 10)
diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm
index 591fdfd535..1ea0068a1a 100644
--- a/code/modules/hydroponics/hydroitemdefines.dm
+++ b/code/modules/hydroponics/hydroitemdefines.dm
@@ -24,10 +24,7 @@
lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi'
volume = 100
-
-/obj/item/reagent_containers/spray/weedspray/Initialize()
- . = ..()
- reagents.add_reagent("weedkiller", 100)
+ list_reagents = list("weedkiller" = 100)
/obj/item/reagent_containers/spray/weedspray/suicide_act(mob/user)
user.visible_message("[user] is huffing [src]! It looks like [user.p_theyre()] trying to commit suicide!")
@@ -42,10 +39,7 @@
lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi'
volume = 100
-
-/obj/item/reagent_containers/spray/pestspray/Initialize()
- . = ..()
- reagents.add_reagent("pestkiller", 100)
+ list_reagents = list("pestkiller" = 100)
/obj/item/reagent_containers/spray/pestspray/suicide_act(mob/user)
user.visible_message("[user] is huffing [src]! It looks like [user.p_theyre()] trying to commit suicide!")
@@ -137,6 +131,7 @@
else
var/turf/user_turf = get_turf(user)
var/dir_to_target = get_dir(user_turf, get_turf(A))
+ var/stam_gain = 0
swiping = TRUE
var/static/list/scythe_slash_angles = list(0, 45, 90, -45, -90)
for(var/i in scythe_slash_angles)
@@ -144,7 +139,10 @@
for(var/obj/structure/spacevine/V in T)
if(user.Adjacent(V))
melee_attack_chain(user, V)
+ stam_gain += 5 //should be hitcost
swiping = FALSE
+ stam_gain += 2 //Initial hitcost
+ user.adjustStaminaLoss(-stam_gain)
// *************************************
// Nutrient defines for hydroponics
@@ -153,71 +151,45 @@
/obj/item/reagent_containers/glass/bottle/nutrient
name = "bottle of nutrient"
- icon = 'icons/obj/chemical.dmi'
volume = 50
- w_class = WEIGHT_CLASS_TINY
amount_per_transfer_from_this = 10
possible_transfer_amounts = list(1,2,5,10,15,25,50)
/obj/item/reagent_containers/glass/bottle/nutrient/Initialize()
. = ..()
- src.pixel_x = rand(-5, 5)
- src.pixel_y = rand(-5, 5)
+ pixel_x = rand(-5, 5)
+ pixel_y = rand(-5, 5)
/obj/item/reagent_containers/glass/bottle/nutrient/ez
name = "bottle of E-Z-Nutrient"
desc = "Contains a fertilizer that causes mild mutations with each harvest."
- icon = 'icons/obj/chemical.dmi'
-
-/obj/item/reagent_containers/glass/bottle/nutrient/ez/Initialize()
- . = ..()
- reagents.add_reagent("eznutriment", 50)
+ list_reagents = list("eznutriment" = 50)
/obj/item/reagent_containers/glass/bottle/nutrient/l4z
name = "bottle of Left 4 Zed"
desc = "Contains a fertilizer that limits plant yields to no more than one and causes significant mutations in plants."
- icon = 'icons/obj/chemical.dmi'
-
-/obj/item/reagent_containers/glass/bottle/nutrient/l4z/Initialize()
- . = ..()
- reagents.add_reagent("left4zednutriment", 50)
+ list_reagents = list("left4zednutriment" = 50)
/obj/item/reagent_containers/glass/bottle/nutrient/rh
name = "bottle of Robust Harvest"
desc = "Contains a fertilizer that increases the yield of a plant by 30% while causing no mutations."
- icon = 'icons/obj/chemical.dmi'
-
-/obj/item/reagent_containers/glass/bottle/nutrient/rh/Initialize()
- . = ..()
- reagents.add_reagent("robustharvestnutriment", 50)
+ list_reagents = list("robustharvestnutriment" = 50)
/obj/item/reagent_containers/glass/bottle/nutrient/empty
name = "bottle"
- icon = 'icons/obj/chemical.dmi'
/obj/item/reagent_containers/glass/bottle/killer
- name = "bottle"
- icon = 'icons/obj/chemical.dmi'
volume = 50
- w_class = WEIGHT_CLASS_TINY
amount_per_transfer_from_this = 10
possible_transfer_amounts = list(1,2,5,10,15,25,50)
/obj/item/reagent_containers/glass/bottle/killer/weedkiller
name = "bottle of weed killer"
desc = "Contains a herbicide."
- icon = 'icons/obj/chemical.dmi'
-
-/obj/item/reagent_containers/glass/bottle/killer/weedkiller/Initialize()
- . = ..()
- reagents.add_reagent("weedkiller", 50)
+ list_reagents = list("weedkiller" = 50)
/obj/item/reagent_containers/glass/bottle/killer/pestkiller
name = "bottle of pest spray"
desc = "Contains a pesticide."
- icon = 'icons/obj/chemical.dmi'
-
-/obj/item/reagent_containers/glass/bottle/killer/pestkiller/Initialize()
- . = ..()
- reagents.add_reagent("pestkiller", 50)
+ list_reagents = list("pestkiller" = 50)
\ No newline at end of file
diff --git a/code/modules/jobs/job_types/job.dm b/code/modules/jobs/job_types/job.dm
index 16e1ccdfb7..0e1daff5e0 100644
--- a/code/modules/jobs/job_types/job.dm
+++ b/code/modules/jobs/job_types/job.dm
@@ -127,6 +127,8 @@
return 0
if(!CONFIG_GET(flag/use_age_restriction_for_jobs))
return 0
+ if(C.prefs.db_flags & DB_FLAG_EXEMPT)
+ return 0
if(!isnum(C.player_age))
return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced
if(!isnum(minimal_player_age))
diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm
index 4852d99acb..1e77a907c6 100644
--- a/code/modules/mining/machine_vending.dm
+++ b/code/modules/mining/machine_vending.dm
@@ -19,22 +19,24 @@
new /datum/data/mining_equipment("Soap", /obj/item/soap/nanotrasen, 200),
new /datum/data/mining_equipment("Laser Pointer", /obj/item/laser_pointer, 300),
new /datum/data/mining_equipment("Alien Toy", /obj/item/clothing/mask/facehugger/toy, 300),
- new /datum/data/mining_equipment("Stabilizing Serum", /obj/item/hivelordstabilizer, 400),
new /datum/data/mining_equipment("Fulton Beacon", /obj/item/fulton_core, 400),
new /datum/data/mining_equipment("Shelter Capsule", /obj/item/survivalcapsule, 400),
new /datum/data/mining_equipment("GAR Meson Scanners", /obj/item/clothing/glasses/meson/gar, 500),
new /datum/data/mining_equipment("Explorer's Webbing", /obj/item/storage/belt/mining, 500),
new /datum/data/mining_equipment("Point Transfer Card", /obj/item/card/mining_point_card, 500),
- new /datum/data/mining_equipment("Survival Medipen", /obj/item/reagent_containers/hypospray/medipen/survival, 500),
+ new /datum/data/mining_equipment("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 600),
new /datum/data/mining_equipment("Tracking Implant Kit", /obj/item/storage/box/minertracker, 600),
+ new /datum/data/mining_equipment("Survival Medipen", /obj/item/reagent_containers/hypospray/medipen/survival, 750),
+ new /datum/data/mining_equipment("Stabilizing Serum", /obj/item/hivelordstabilizer, 750),
new /datum/data/mining_equipment("Jaunter", /obj/item/wormhole_jaunter, 750),
new /datum/data/mining_equipment("Kinetic Crusher", /obj/item/twohanded/required/kinetic_crusher, 750),
new /datum/data/mining_equipment("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750),
new /datum/data/mining_equipment("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 800),
new /datum/data/mining_equipment("Burn First-Aid Kit", /obj/item/storage/firstaid/fire, 800),
- new /datum/data/mining_equipment("First-Aid Kit", /obj/item/storage/firstaid/regular, 800),
+ new /datum/data/mining_equipment("First-Aid Kit", /obj/item/storage/firstaid/regular, 800),
new /datum/data/mining_equipment("Advanced Scanner", /obj/item/t_scanner/adv_mining_scanner, 800),
new /datum/data/mining_equipment("Resonator", /obj/item/resonator, 800),
+ new /datum/data/mining_equipment("Mini Extinguisher", /obj/item/extinguisher/mini, 1000),
new /datum/data/mining_equipment("Fulton Pack", /obj/item/extraction_pack, 1000),
new /datum/data/mining_equipment("Lazarus Injector", /obj/item/lazarus_injector, 1000),
new /datum/data/mining_equipment("Silver Pickaxe", /obj/item/pickaxe/silver, 1000),
@@ -59,7 +61,8 @@
new /datum/data/mining_equipment("KA Range Increase", /obj/item/borg/upgrade/modkit/range, 1000),
new /datum/data/mining_equipment("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000),
new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000),
- new /datum/data/mining_equipment("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000)
+ new /datum/data/mining_equipment("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000),
+ new /datum/data/mining_equipment("Premium Accelerator", /obj/item/gun/energy/kinetic_accelerator/premiumka, 8000)
)
/datum/data/mining_equipment
@@ -314,4 +317,4 @@
new /obj/item/clothing/mask/gas/seva(drop_location)
SSblackbox.record_feedback("tally", "suit_voucher_redeemed", 1, selection)
- qdel(voucher)
\ No newline at end of file
+ qdel(voucher)
diff --git a/code/modules/mob/living/brain/brain.dm b/code/modules/mob/living/brain/brain.dm
index e8a15d70ae..76b416772e 100644
--- a/code/modules/mob/living/brain/brain.dm
+++ b/code/modules/mob/living/brain/brain.dm
@@ -15,7 +15,6 @@
OB.brainmob = src
forceMove(OB)
-
/mob/living/brain/proc/create_dna()
stored_dna = new /datum/dna/stored(src)
if(!stored_dna.species)
@@ -28,6 +27,7 @@
death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA
if(mind) //You aren't allowed to return to brains that don't exist
mind.current = null
+ mind.active = FALSE //No one's using it anymore.
ghostize() //Ghostize checks for key so nothing else is necessary.
container = null
return ..()
diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm
index 90d20bc236..9e0bb0428b 100644
--- a/code/modules/mob/living/brain/posibrain.dm
+++ b/code/modules/mob/living/brain/posibrain.dm
@@ -36,7 +36,7 @@ GLOBAL_VAR(posibrain_notify_cooldown)
/obj/item/mmi/posibrain/proc/ping_ghosts(msg, newlymade)
if(newlymade || GLOB.posibrain_notify_cooldown <= world.time)
- notify_ghosts("[name] [msg] in [get_area(src)]!", ghost_sound = !newlymade ? 'sound/effects/ghost2.ogg':null, enter_link = "(Click to enter)", source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_POSIBRAIN)
+ notify_ghosts("[name] [msg] in [get_area(src)]!", ghost_sound = !newlymade ? 'sound/misc/server-ready.ogg':null, enter_link = "(Click to enter)", source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_POSIBRAIN)
if(!newlymade)
GLOB.posibrain_notify_cooldown = world.time + askDelay
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 7bc0e281c5..ea13255dfe 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -188,6 +188,8 @@
if(thrown_thing)
visible_message("[src] has thrown [thrown_thing].")
src.log_message("has thrown [thrown_thing]", LOG_ATTACK)
+ do_attack_animation(target, no_effect = 1)
+ playsound(loc, 'sound/weapons/punchmiss.ogg', 50, 1, -1)
newtonian_move(get_dir(target, src))
thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src)
diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm
index b22498670a..c21f9ce213 100644
--- a/code/modules/mob/living/carbon/damage_procs.dm
+++ b/code/modules/mob/living/carbon/damage_procs.dm
@@ -178,7 +178,6 @@
parts -= picked
if(updating_health)
updatehealth()
- update_stamina()
if(update)
update_damage_overlays()
update_stamina() //CIT CHANGE - makes sure update_stamina() always gets called after a health update
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 548381ba8a..7582d7fb0e 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -599,11 +599,7 @@
//Check for dresscode violations
if(istype(head, /obj/item/clothing/head/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/syndi) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi))
- threatcount += 6 //fuk u antags <3
-
- //Check for nonhuman scum
- if(dna && dna.species.id && dna.species.id != "human")
- threatcount += 1
+ threatcount += 4 //fuk u antags <3 //no you
//mindshield implants imply trustworthyness
if(has_trait(TRAIT_MINDSHIELD))
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index aba8e98583..ce906565d2 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -272,7 +272,10 @@
"[M] disarmed [src]!")
else
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
- Knockdown(100)
+ if(!lying) //CITADEL EDIT
+ Knockdown(100, TRUE, FALSE, 30, 25)
+ else
+ Knockdown(100)
log_combat(M, src, "tackled")
visible_message("[M] has tackled down [src]!", \
"[M] has tackled down [src]!")
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index 617e62773d..e3164e0dcb 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -92,3 +92,5 @@
limbs_id = "lizard"
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,DIGITIGRADE)
inherent_traits = list(TRAIT_NOGUNS,TRAIT_NOBREATH)
+ burnmod = 0.9
+ brutemod = 0.9
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index ef9c89cf1d..fa435d11c0 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -23,7 +23,7 @@
var/bprv = handle_bodyparts()
if(bprv & BODYPART_LIFE_UPDATE_HEALTH)
updatehealth()
- update_stamina()
+ update_stamina()
if(stat != DEAD)
handle_brain_damage()
diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm
index dfc81396e7..4e5c7decc5 100644
--- a/code/modules/mob/living/damage_procs.dm
+++ b/code/modules/mob/living/damage_procs.dm
@@ -243,7 +243,7 @@
adjustStaminaLoss(-stamina, FALSE)
if(updating_health)
updatehealth()
- update_stamina()
+ update_stamina()
// damage ONE external organ, organ gets randomly selected from damaged ones.
/mob/living/proc/take_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE)
@@ -252,7 +252,7 @@
adjustStaminaLoss(stamina, FALSE)
if(updating_health)
updatehealth()
- update_stamina()
+ update_stamina()
// heal MANY bodyparts, in random order
/mob/living/proc/heal_overall_damage(brute = 0, burn = 0, stamina = 0, only_robotic = FALSE, only_organic = TRUE, updating_health = TRUE)
@@ -261,7 +261,7 @@
adjustStaminaLoss(-stamina, FALSE)
if(updating_health)
updatehealth()
- update_stamina()
+ update_stamina()
// damage MANY bodyparts, in random order
/mob/living/proc/take_overall_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE)
@@ -270,7 +270,7 @@
adjustStaminaLoss(stamina, FALSE)
if(updating_health)
updatehealth()
- update_stamina()
+ update_stamina()
//heal up to amount damage, in a given order
/mob/living/proc/heal_ordered_damage(amount, list/damage_types)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
index c60dae6a35..45745bd84e 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
@@ -44,6 +44,7 @@ Difficulty: Hard
ranged_cooldown_time = 10
ranged = 1
pixel_x = -32
+ gender = MALE
del_on_death = 1
crusher_loot = list(/obj/structure/closet/crate/necropolis/bubblegum/crusher)
loot = list(/obj/structure/closet/crate/necropolis/bubblegum)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index a563aef86a..79215ff5f5 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -564,7 +564,12 @@
GLOB.cameranet.stat_entry()
if(statpanel("Tickets"))
GLOB.ahelp_tickets.stat_entry()
-
+ if(length(GLOB.sdql2_queries))
+ if(statpanel("SDQL2"))
+ stat("Access Global SDQL2 List", GLOB.sdql2_vv_statobj)
+ for(var/i in GLOB.sdql2_queries)
+ var/datum/SDQL2_query/Q = i
+ Q.generate_stat()
if(listed_turf && client)
if(!TurfAdjacent(listed_turf))
listed_turf = null
diff --git a/code/modules/ninja/ninja_event.dm b/code/modules/ninja/ninja_event.dm
index 9fd8e4baf2..ccd9b765c1 100644
--- a/code/modules/ninja/ninja_event.dm
+++ b/code/modules/ninja/ninja_event.dm
@@ -33,7 +33,6 @@ Contents:
control.occurrences--
return ..()
-
/datum/round_event/ghost_role/ninja/spawn_role()
//selecting a spawn_loc
if(!spawn_loc)
@@ -74,6 +73,7 @@ Contents:
spawned_mobs += Ninja
message_admins("[ADMIN_LOOKUPFLW(Ninja)] has been made into a ninja by an event.")
log_game("[key_name(Ninja)] was spawned as a ninja by an event.")
+ success_spawn = TRUE
return SUCCESSFUL_SPAWN
@@ -86,4 +86,4 @@ Contents:
A.real_name = "[pick(GLOB.ninja_titles)] [pick(GLOB.ninja_names)]"
A.copy_to(new_ninja)
new_ninja.dna.update_dna_identity()
- return new_ninja
\ No newline at end of file
+ return new_ninja
diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm
index f3f960c0ee..8e121dc809 100644
--- a/code/modules/power/generator.dm
+++ b/code/modules/power/generator.dm
@@ -65,7 +65,7 @@
if(delta_temperature > 0 && cold_air_heat_capacity > 0 && hot_air_heat_capacity > 0)
- var/efficiency = 0.65
+ var/efficiency = 0.45
var/energy_transfer = delta_temperature*hot_air_heat_capacity*cold_air_heat_capacity/(hot_air_heat_capacity+cold_air_heat_capacity)
diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm
index 33ebefbf4c..2265f806a5 100644
--- a/code/modules/power/tesla/coil.dm
+++ b/code/modules/power/tesla/coil.dm
@@ -94,7 +94,7 @@
var/power = (powernet.avail/2)
add_load(power)
playsound(src.loc, 'sound/magic/lightningshock.ogg', 100, 1, extrarange = 5)
- tesla_zap(src, 10, power/(coeff/2))
+ tesla_zap(src, 10, power/(coeff/2), TESLA_FUSION_FLAGS)
tesla_buckle_check(power/(coeff/2))
// Tesla R&D researcher
@@ -174,4 +174,4 @@
flick("grounding_rodhit", src)
tesla_buckle_check(power)
else
- ..()
\ No newline at end of file
+ ..()
diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
index 39cf924169..d57edf154d 100644
--- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm
+++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
@@ -115,7 +115,7 @@
/obj/item/ammo_casing/shotgun/dart/noreact
name = "cryostasis shotgun dart"
- desc = "A dart for use in shotguns, using similar technology as cryostatis beakers to keep internal reagents from reacting. Can be injected with up to 10 units of any chemical."
+ desc = "A dart for use in shotguns. Uses technology similar to cryostasis beakers to keep internal reagents from reacting. Can be injected with up to 10 units of any chemical."
icon_state = "cnrshell"
reagent_amount = 10
reagent_react = FALSE
diff --git a/code/modules/projectiles/boxes_magazines/external/shotgun.dm b/code/modules/projectiles/boxes_magazines/external/shotgun.dm
index bb04446129..1fa3db28b5 100644
--- a/code/modules/projectiles/boxes_magazines/external/shotgun.dm
+++ b/code/modules/projectiles/boxes_magazines/external/shotgun.dm
@@ -34,3 +34,8 @@
name = "shotgun magazine (12g meteor slugs)"
icon_state = "m12gbc"
ammo_type = /obj/item/ammo_casing/shotgun/meteorslug
+
+/obj/item/ammo_box/magazine/m12g/scatter
+ name = "shotgun magazine (12g scatter laser shot slugs)"
+ icon_state = "m12gb"
+ ammo_type = /obj/item/ammo_casing/shotgun/laserslug
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index 10ce341fec..fba355d738 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -22,9 +22,44 @@
var/max_mod_capacity = 100
var/list/modkits = list()
-
+
var/recharge_timerid
+/obj/item/gun/energy/kinetic_accelerator/premiumka
+ name = "premium accelerator"
+ desc = "A premium kinetic accelerator fitted with an extended barrel and increased pressure tank."
+ icon_state = "premiumgun"
+ item_state = "premiumgun"
+ lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
+ ammo_type = list(/obj/item/ammo_casing/energy/kinetic/premium)
+
+/obj/item/gun/energy/kinetic_accelerator/premiumka/dropped()
+ . = ..()
+ if(!QDELING(src) && !holds_charge)
+ // Put it on a delay because moving item from slot to hand
+ // calls dropped().
+ addtimer(CALLBACK(src, .proc/empty_if_not_held), 1.60)
+
+/obj/item/ammo_casing/energy/kinetic/premium
+ projectile_type = /obj/item/projectile/kinetic/premium
+
+/obj/item/projectile/kinetic/premium
+ name = "kinetic force"
+ icon_state = null
+ damage = 50
+ damage_type = BRUTE
+ flag = "bomb"
+ range = 4
+ log_override = TRUE
+
+/obj/item/gun/energy/kinetic_accelerator/premiumka/update_icon()
+ ..()
+ if(!can_shoot())
+ add_overlay("[icon_state]_empty")
+ else
+ cut_overlays()
+
/obj/item/gun/energy/kinetic_accelerator/examine(mob/user)
..()
if(max_mod_capacity)
diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm
index 457ec78e9b..c8c4a73b3b 100644
--- a/code/modules/projectiles/projectile/bullets/shotgun.dm
+++ b/code/modules/projectiles/projectile/bullets/shotgun.dm
@@ -88,8 +88,12 @@
do_sparks(1, TRUE, src)
..()
-// Mech Scattershot
+// Mech Scattershots
/obj/item/projectile/bullet/scattershot
damage = 20
stamina = 65
+
+/obj/item/projectile/bullet/seed
+ damage = 4
+ stamina = 1
diff --git a/code/modules/projectiles/projectile/special/neurotoxin.dm b/code/modules/projectiles/projectile/special/neurotoxin.dm
index 46027e7bdf..baf1495abb 100644
--- a/code/modules/projectiles/projectile/special/neurotoxin.dm
+++ b/code/modules/projectiles/projectile/special/neurotoxin.dm
@@ -3,10 +3,12 @@
icon_state = "neurotoxin"
damage = 5
damage_type = TOX
- knockdown = 100
/obj/item/projectile/bullet/neurotoxin/on_hit(atom/target, blocked = FALSE)
if(isalien(target))
knockdown = 0
nodamage = TRUE
+ else if(isliving(target))
+ var/mob/living/L = target
+ L.Knockdown(100, TRUE, FALSE, 30, 25)
return ..()
diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
index f9dbd1928b..6e4d5e5a7d 100644
--- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
@@ -1398,15 +1398,15 @@ All effects don't start immediately, but rather get worse over time; the rate is
/datum/reagent/consumable/ethanol/eggnog
name = "Eggnog"
id = "eggnog"
- description = "For enjoying the most wonderful time of the year."
+ description = "The traditional way to get absolutely hammered at a Christmas party."
color = "#fcfdc6" // rgb: 252, 253, 198
nutriment_factor = 2 * REAGENTS_METABOLISM
boozepwr = 1
quality = DRINK_VERYGOOD
taste_description = "custard and alcohol"
- glass_icon_state = "glass_yellow"
+ glass_icon_state = "nog3"
glass_name = "eggnog"
- glass_desc = "For enjoying the most wonderful time of the year."
+ glass_desc = "The traditional way to get absolutely hammered at a Christmas party."
/datum/reagent/consumable/ethanol/narsour
diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm
index 685b37564a..52b212f8c5 100644
--- a/code/modules/reagents/chemistry/reagents/food_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm
@@ -112,7 +112,7 @@
/datum/reagent/consumable/cooking_oil/reaction_obj(obj/O, reac_volume)
if(holder && holder.chem_temp >= fry_temperature)
- if(isitem(O) && !istype(O, /obj/item/reagent_containers/food/snacks/deepfryholder))
+ if(isitem(O) && !istype(O, /obj/item/reagent_containers/food/snacks/deepfryholder) && !(O.resistance_flags & (FIRE_PROOF|INDESTRUCTIBLE)))
O.loc.visible_message("[O] rapidly fries as it's splashed with hot oil! Somehow.")
var/obj/item/reagent_containers/food/snacks/deepfryholder/F = new(O.drop_location(), O)
F.fry(volume)
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index 671e62a9e6..4e278d2ede 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -546,19 +546,14 @@
overdose_threshold = 45
addiction_threshold = 30
-/datum/reagent/medicine/ephedrine/on_mob_add(mob/living/L)
- ..()
- L.add_trait(TRAIT_GOTTAGOFAST, id)
-
-/datum/reagent/medicine/ephedrine/on_mob_delete(mob/living/L)
- L.remove_trait(TRAIT_GOTTAGOFAST, id)
- ..()
-
/datum/reagent/medicine/ephedrine/on_mob_life(mob/living/carbon/M)
M.AdjustStun(-20, 0)
M.AdjustKnockdown(-20, 0)
M.AdjustUnconscious(-20, 0)
- M.adjustStaminaLoss(-1*REM, 0)
+ M.adjustStaminaLoss(-4.5*REM, 0)
+ M.Jitter(10)
+ if(prob(50))
+ M.confused = max(M.confused, 1)
..()
return TRUE
diff --git a/code/modules/recycling/disposal/pipe.dm b/code/modules/recycling/disposal/pipe.dm
index 6c269e44be..aed2310a7f 100644
--- a/code/modules/recycling/disposal/pipe.dm
+++ b/code/modules/recycling/disposal/pipe.dm
@@ -1,5 +1,7 @@
// Disposal pipes
+#define IFFY 2
+
/obj/structure/disposalpipe
name = "disposal pipe"
desc = "An underfloor disposal pipe."
@@ -16,6 +18,7 @@
var/dpdir = NONE // bitmask of pipe directions
var/initialize_dirs = NONE // bitflags of pipe directions added on init, see \code\_DEFINES\pipe_construction.dm
var/flip_type // If set, the pipe is flippable and becomes this type when flipped
+ var/canclank = FALSE // Determines if the pipe will cause a clank sound when holders pass by it. use the IFFY define for weird-ass edge cases like the segment subtype
var/obj/structure/disposalconstruct/stored
@@ -75,6 +78,8 @@
H.merge(H2)
H.forceMove(P)
+ if(P.canclank == TRUE || (P.canclank == IFFY && P.dpdir != 3 && P.dpdir != 12))
+ playsound(P, H.hasmob ? "clang" : "clangsmall", H.hasmob ? 50 : 25, 1)
return P
else // if wasn't a pipe, then they're now in our turf
H.forceMove(get_turf(src))
@@ -184,6 +189,7 @@
/obj/structure/disposalpipe/segment
icon_state = "pipe"
initialize_dirs = DISP_DIR_FLIP
+ canclank = IFFY
// A three-way junction with dir being the dominant direction
@@ -191,6 +197,7 @@
icon_state = "pipe-j1"
initialize_dirs = DISP_DIR_RIGHT | DISP_DIR_FLIP
flip_type = /obj/structure/disposalpipe/junction/flip
+ canclank = TRUE
// next direction to move
// if coming in from secondary dirs, then next is primary dir
@@ -229,6 +236,7 @@
//a trunk joining to a disposal bin or outlet on the same turf
/obj/structure/disposalpipe/trunk
icon_state = "pipe-t"
+ canclank = TRUE
var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet
/obj/structure/disposalpipe/trunk/Initialize()
@@ -299,3 +307,5 @@
/obj/structure/disposalpipe/broken/deconstruct()
qdel(src)
+
+#undef IFFY
diff --git a/code/modules/recycling/disposal/pipe_sorting.dm b/code/modules/recycling/disposal/pipe_sorting.dm
index 5e26e9b767..d85c4bca78 100644
--- a/code/modules/recycling/disposal/pipe_sorting.dm
+++ b/code/modules/recycling/disposal/pipe_sorting.dm
@@ -5,6 +5,7 @@
desc = "An underfloor disposal pipe with a sorting mechanism."
icon_state = "pipe-j1s"
initialize_dirs = DISP_DIR_RIGHT | DISP_DIR_FLIP
+ canclank = TRUE
/obj/structure/disposalpipe/sorting/nextdir(obj/structure/disposalholder/H)
var/sortdir = dpdir & ~(dir | turn(dir, 180))
diff --git a/code/modules/research/designs/electronics_designs.dm b/code/modules/research/designs/electronics_designs.dm
index e9db641811..552976824d 100644
--- a/code/modules/research/designs/electronics_designs.dm
+++ b/code/modules/research/designs/electronics_designs.dm
@@ -118,3 +118,16 @@
build_path = /obj/item/disk/integrated_circuit/upgrade/clone
category = list("Electronics")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
+
+//CIT ADDITIONS
+/datum/design/drone_shell
+ name = "Drone Shell"
+ desc = "A shell of a maintenance drone, an expendable robot built to perform station repairs."
+ id = "drone_shell"
+ build_type = MECHFAB | PROTOLATHE
+ materials = list(MAT_METAL = 800, MAT_GLASS = 350)
+ construction_time = 150
+ build_path = /obj/item/drone_shell
+ category = list("Misc")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
+
diff --git a/code/modules/research/designs/mecha_designs.dm b/code/modules/research/designs/mecha_designs.dm
index cee0dc7413..3ee7d344dc 100644
--- a/code/modules/research/designs/mecha_designs.dm
+++ b/code/modules/research/designs/mecha_designs.dm
@@ -147,6 +147,16 @@
construction_time = 100
category = list("Exosuit Equipment")
+/datum/design/mech_seedscatter
+ name = "Exosuit Weapon (Melon Seed \"Scattershot\")"
+ desc = "Allows for the construction of Melon Seed Scattershot."
+ id = "mech_seedscatter"
+ build_type = MECHFAB
+ build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/seedscatter
+ materials = list(MAT_METAL=10000, MAT_GLASS = 10000)
+ construction_time = 70
+ category = list("Exosuit Equipment")
+
/datum/design/mech_carbine
name = "Exosuit Weapon (FNX-99 \"Hades\" Carbine)"
desc = "Allows for the construction of FNX-99 \"Hades\" Carbine."
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index b7da52468b..ca3f2b420b 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -44,6 +44,16 @@
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+/datum/design/medicalkit
+ name = "Empty Medkit"
+ desc = "A plastic medical kit for storging medical items."
+ id = "medicalkit"
+ build_type = PROTOLATHE
+ materials = list(MAT_PLASTIC = 5000)
+ build_path = /obj/item/storage/firstaid //So we dont spawn medical items in it
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
/datum/design/xlarge_beaker
name = "X-large Beaker"
id = "xlarge_beaker"
@@ -526,6 +536,80 @@
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+/////////////////////
+//Adv Surgery Tools//
+/////////////////////
+
+/datum/design/drapes
+ name = "Plastic Drapes"
+ desc = "A large surgery drape made of plastic."
+ id = "drapes"
+ build_type = PROTOLATHE
+ materials = list(MAT_PLASTIC = 2500)
+ build_path = /obj/item/surgical_drapes
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/retractor_adv
+ name = "Advanced Retractor"
+ desc = "A high-class, premium retractor, featuring precision crafted, silver-plated hook-ends and an electrum handle."
+ id = "retractor_adv"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 1500, MAT_GOLD = 1000)
+ build_path = /obj/item/retractor/adv
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/hemostat_adv
+ name = "Advanced Hemostat"
+ desc = "An exceptionally fine pair of arterial forceps. These appear to be plated in electrum and feel soft to the touch."
+ id = "hemostat_adv"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 1000, MAT_GOLD = 1500)
+ build_path = /obj/item/hemostat/adv
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/cautery_adv
+ name = "Electrocautery" //This is based on real-life science.
+ desc = "A high-tech unipolar Electrocauter. This tiny device contains an extremely powerful microbattery that uses arcs of electricity to painlessly sear wounds shut. It seems to recharge with the user's body-heat. Wow!"
+ id = "cautery_adv"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 1000, MAT_GOLD = 1500)
+ build_path = /obj/item/cautery/adv
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/surgicaldrill_adv
+ name = "Surgical Autodrill"
+ desc = "With a diamond tip and built-in depth and safety sensors, this drill alerts the user before overpenetrating a patient's skull or tooth. There also appears to be a disable switch."
+ id = "surgicaldrill_adv"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 2500, MAT_SILVER = 6000, MAT_GOLD = 5500, MAT_DIAMOND = 3500)
+ build_path = /obj/item/surgicaldrill/adv
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/scalpel_adv
+ name = "Precision Scalpel"
+ desc = "A perfectly balanced electrum scalpel with a silicon-coated edge to eliminate wear and tear."
+ id = "scalpel_adv"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 1500, MAT_GLASS = 1500, MAT_SILVER = 4000, MAT_GOLD = 2500)
+ build_path = /obj/item/scalpel/adv
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/circular_saw_adv
+ name = "Diamond-Grit Circular Saw"
+ desc = "For those Assistants with REALLY thick skulls."
+ id = "circular_saw_adv"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 7500, MAT_GLASS = 6000, MAT_SILVER = 6500, MAT_GOLD = 7500, MAT_DIAMOND = 4500)
+ build_path = /obj/item/circular_saw/adv
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
/////////////////////
///Surgery Designs///
/////////////////////
diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm
index bf503c2653..2402967ff3 100644
--- a/code/modules/research/designs/weapon_designs.dm
+++ b/code/modules/research/designs/weapon_designs.dm
@@ -294,7 +294,7 @@
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
/datum/design/cryostatis_shotgun_dart
- name = "Cryostatis Shotgun Dart"
+ name = "Cryostasis Shotgun Dart"
desc = "A shotgun dart designed with similar internals to that of a cryostatis beaker, allowing reagents to not react when inside."
id = "shotgundartcryostatis"
build_type = PROTOLATHE
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 826f8d05b8..43b21771c5 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -43,13 +43,24 @@
description = "Various tools fit for basic mech units"
design_ids = list("mech_drill", "mech_mscanner", "mech_extinguisher", "mech_cable_layer")
+
+/datum/techweb_node/surplus_lims
+ id = "surplus_lims"
+ display_name = "Basic Prosthetics"
+ description = "Basic fragile lims for the impaired."
+ starting_node = TRUE
+ prereq_ids = list("biotech")
+ design_ids = list("basic_l_arm", "basic_r_arm", "basic_r_leg", "basic_l_leg")
+ export_price = 5000
+
+
/////////////////////////Biotech/////////////////////////
/datum/techweb_node/biotech
id = "biotech"
display_name = "Biological Technology"
description = "What makes us tick." //the MC, silly!
prereq_ids = list("base")
- design_ids = list("chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "blood_bag")
+ design_ids = list("medicalkit", "chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "blood_bag")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -71,21 +82,21 @@
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
-/datum/techweb_node/surplus_lims
- id = "surplus_lims"
- display_name = "Basic Prosthetics"
- description = "Basic fragile lims for the impaired."
- prereq_ids = list("biotech")
- design_ids = list("basic_l_arm", "basic_r_arm", "basic_r_leg", "basic_l_leg")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000) // You can knock them off with a glass shard...
- export_price = 5000
-
/datum/techweb_node/advance_lims
id = "advance_lims"
display_name = "Upgraded Prosthetics"
description = "Reinforced prosthetics for the impaired."
prereq_ids = list("adv_biotech", "surplus_lims")
design_ids = list("adv_l_arm", "adv_r_arm", "adv_r_leg", "adv_l_leg")
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1250)
+ export_price = 5000
+
+/datum/techweb_node/advance_surgerytools
+ id = "advance_surgerytools"
+ display_name = "Advanced Surgery Tools"
+ description = "Refined and improved redesigns for the run-of-the-mill medical utensils."
+ prereq_ids = list("adv_biotech", "adv_surgery")
+ design_ids = list("drapes", "retractor_adv", "hemostat_adv", "cautery_adv", "surgicaldrill_adv", "scalpel_adv", "circular_saw_adv")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -254,7 +265,7 @@
display_name = "Basic Robotics Research"
description = "Programmable machines that make our lives lazier."
prereq_ids = list("base")
- design_ids = list("paicard")
+ design_ids = list("paicard", "drone_shell")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
export_price = 5000
@@ -724,6 +735,15 @@
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
+/datum/techweb_node/mech_seedscatter
+ id = "mech_seedscatter"
+ display_name = "Exosuit Weapon (Melon Seed \"Scattershot\")"
+ description = "An advanced piece of mech weaponry"
+ prereq_ids = list("ballistic_weapons")
+ design_ids = list("mech_seedscatter")
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
+ export_price = 5000
+
/datum/techweb_node/mech_carbine
id = "mech_carbine"
display_name = "Exosuit Weapon (FNX-99 \"Hades\" Carbine)"
diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm
index bfe1b2dc3b..9f42cf0baa 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -28,6 +28,7 @@
eye_color = HMN.eye_color
if(HMN.has_trait(TRAIT_NIGHT_VISION) && !lighting_alpha)
lighting_alpha = LIGHTING_PLANE_ALPHA_NV_TRAIT
+ see_in_dark = 8
M.update_tint()
owner.update_sight()
diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm
index aaf859ee52..517dfb412a 100644
--- a/code/modules/surgery/tools.dm
+++ b/code/modules/surgery/tools.dm
@@ -7,6 +7,15 @@
flags_1 = CONDUCT_1
w_class = WEIGHT_CLASS_TINY
+/obj/item/retractor/adv
+ name = "Advanced Retractor"
+ desc = "A high-class, premium retractor, featuring precision crafted, silver-plated hook-ends and an electrum handle."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "retractor"
+ materials = list(MAT_METAL=6000, MAT_GLASS=3000)
+ flags_1 = CONDUCT_1
+ w_class = WEIGHT_CLASS_TINY
+ toolspeed = 0.65
/obj/item/retractor/augment
name = "retractor"
@@ -18,7 +27,6 @@
w_class = WEIGHT_CLASS_TINY
toolspeed = 0.5
-
/obj/item/hemostat
name = "hemostat"
desc = "You think you have seen this before."
@@ -29,6 +37,16 @@
w_class = WEIGHT_CLASS_TINY
attack_verb = list("attacked", "pinched")
+/obj/item/hemostat/adv
+ name = "Advanced Hemostat"
+ desc = "An exceptionally fine pair of arterial forceps. These appear to be plated in electrum and feel soft to the touch."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "hemostat"
+ materials = list(MAT_METAL=5000, MAT_GLASS=2500)
+ flags_1 = CONDUCT_1
+ w_class = WEIGHT_CLASS_TINY
+ toolspeed = 0.65
+ attack_verb = list("attacked", "pinched")
/obj/item/hemostat/augment
name = "hemostat"
@@ -52,6 +70,16 @@
w_class = WEIGHT_CLASS_TINY
attack_verb = list("burnt")
+/obj/item/cautery/adv
+ name = "Electrocautery"
+ desc = "A high-tech unipolar Electrocauter. This tiny device contains an extremely powerful microbattery that uses arcs of electricity to painlessly sear wounds shut. It seems to recharge with the user's body-heat. Wow!"
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "cautery"
+ materials = list(MAT_METAL=2500, MAT_GLASS=750)
+ flags_1 = CONDUCT_1
+ w_class = WEIGHT_CLASS_TINY
+ toolspeed = 0.5
+ attack_verb = list("burnt")
/obj/item/cautery/augment
name = "cautery"
@@ -79,6 +107,21 @@
w_class = WEIGHT_CLASS_NORMAL
attack_verb = list("drilled")
+/obj/item/surgicaldrill/adv
+ name = "Surgical Autodrill"
+ desc = "With a diamond tip and built-in depth and safety sensors, this drill alerts the user before overpenetrating a patient's skull or tooth. There also appears to be a disable switch."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "drill"
+ lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
+ hitsound = 'sound/weapons/circsawhit.ogg'
+ materials = list(MAT_METAL=10000, MAT_GLASS=6000)
+ flags_1 = CONDUCT_1
+ force = 13 //Damions are not ment for flesh cutting!
+ w_class = WEIGHT_CLASS_NORMAL
+ toolspeed = 0.65
+ attack_verb = list("drilled")
+ sharpness = IS_SHARP_ACCURATE // Were making them use a damion for this...
/obj/item/surgicaldrill/augment
name = "surgical drill"
@@ -116,6 +159,25 @@
. = ..()
AddComponent(/datum/component/butchering, 80 * toolspeed, 100, 0)
+/obj/item/scalpel/adv
+ name = "Precision Scalpel"
+ desc = "A perfectly balanced electrum scalpel with a silicon-coated edge to eliminate wear and tear."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "scalpel"
+ lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
+ flags_1 = CONDUCT_1
+ force = 8
+ w_class = WEIGHT_CLASS_TINY
+ throwforce = 7
+ throw_speed = 3
+ throw_range = 6
+ materials = list(MAT_METAL=4000, MAT_GLASS=1000)
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ toolspeed = 0.65
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ sharpness = IS_SHARP_ACCURATE
+
/obj/item/scalpel/augment
name = "scalpel"
desc = "Ultra-sharp blade attached directly to your bone for extra-accuracy."
@@ -161,6 +223,26 @@
. = ..()
AddComponent(/datum/component/butchering, 40 * toolspeed, 100, 5, 'sound/weapons/circsawhit.ogg') //saws are very accurate and fast at butchering
+/obj/item/circular_saw/adv
+ name = "Diamond-Grit Circular Saw"
+ desc = "For those Assistants with REALLY thick skulls."
+ icon = 'icons/obj/surgery.dmi'
+ icon_state = "saw"
+ lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
+ hitsound = 'sound/weapons/circsawhit.ogg'
+ throwhitsound = 'sound/weapons/pierce.ogg'
+ flags_1 = CONDUCT_1
+ force = 13
+ w_class = WEIGHT_CLASS_NORMAL
+ throwforce = 6
+ throw_speed = 1
+ throw_range = 3
+ materials = list(MAT_METAL=10000, MAT_GLASS=6000)
+ attack_verb = list("attacked", "slashed", "sawed", "cut")
+ toolspeed = 0.65
+ sharpness = IS_SHARP
+
/obj/item/circular_saw/augment
name = "circular saw"
desc = "A small but very fast spinning saw. Edges dulled to prevent accidental cutting inside of the surgeon."
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index 32eaf0cde9..1e3060137a 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -506,6 +506,12 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/ammo_box/magazine/m12g/meteor
include_modes = list(/datum/game_mode/nuclear)
+/datum/uplink_item/ammo/shotgun/scatter
+ name = "12g Scatter Laser shot Slugs"
+ desc = "An alternative 8-round Scatter Laser Shot magazine for use in the Bulldog shotgun."
+ item = /obj/item/ammo_box/magazine/m12g/scatter
+ cost = 5 // most armor has less laser protection then bullet
+
/datum/uplink_item/ammo/shotgun/bag
name = "12g Ammo Duffel Bag"
desc = "A duffel bag filled with enough 12g ammo to supply an entire team, at a discounted price."
diff --git a/code/modules/vending/assist.dm b/code/modules/vending/assist.dm
index de30109824..89c330789e 100644
--- a/code/modules/vending/assist.dm
+++ b/code/modules/vending/assist.dm
@@ -1,12 +1,19 @@
/obj/machinery/vending/assist
- products = list(/obj/item/assembly/prox_sensor = 5,
- /obj/item/assembly/igniter = 3,
- /obj/item/assembly/signaler = 4,
- /obj/item/wirecutters = 1,
- /obj/item/cartridge/signal = 4)
- contraband = list(/obj/item/assembly/timer = 2,
- /obj/item/assembly/voice = 2,
- /obj/item/assembly/health = 2)
+ products = list(/obj/item/assembly/prox_sensor = 7,
+ /obj/item/assembly/igniter = 6,
+ /obj/item/assembly/signaler = 6,
+ /obj/item/wirecutters = 3,
+ /obj/item/stock_parts/cell/crap = 6,
+ /obj/item/cartridge/signal = 6)
+ contraband = list(/obj/item/assembly/timer = 4,
+ /obj/item/assembly/voice = 4,
+ /obj/item/assembly/health = 4,
+ /obj/item/pressure_plate = 2,
+ /obj/item/multitool = 2,
+ /obj/item/stock_parts/cell/upgraded = 2)
+ premium = list(/obj/item/stock_parts/cell/upgraded/plus = 2,
+ /obj/item/flashlight/lantern = 2,
+ /obj/item/beacon = 2)
product_ads = "Only the finest!;Have some tools.;The most robust equipment.;The finest gear in space!"
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
resistance_flags = FIRE_PROOF
diff --git a/code/modules/vending/coffee.dm b/code/modules/vending/coffee.dm
index 6ac9dd648d..c30c087d7b 100644
--- a/code/modules/vending/coffee.dm
+++ b/code/modules/vending/coffee.dm
@@ -8,6 +8,11 @@
/obj/item/reagent_containers/food/drinks/mug/tea = 25,
/obj/item/reagent_containers/food/drinks/mug/coco = 25)
contraband = list(/obj/item/reagent_containers/food/drinks/ice = 12)
+ premium = list(/obj/item/reagent_containers/food/snacks/chocolatebar = 3,
+ /obj/item/reagent_containers/food/condiment/milk = 2,
+ /obj/item/reagent_containers/food/drinks/bottle/cream = 2,
+ /obj/item/reagent_containers/food/condiment/sugar = 1)
+
refill_canister = /obj/item/vending_refill/coffee
/obj/item/vending_refill/coffee
diff --git a/code/modules/vending/engivend.dm b/code/modules/vending/engivend.dm
index 0e64e0a367..9358d1652f 100644
--- a/code/modules/vending/engivend.dm
+++ b/code/modules/vending/engivend.dm
@@ -4,21 +4,28 @@
icon_state = "engivend"
icon_deny = "engivend-deny"
req_access = list(ACCESS_ENGINE_EQUIP)
- products = list(/obj/item/clothing/glasses/meson/engine = 2,
- /obj/item/clothing/glasses/welding = 3,
- /obj/item/multitool = 4,
+ products = list(/obj/item/clothing/glasses/meson/engine = 5,
+ /obj/item/clothing/glasses/welding = 5,
+ /obj/item/multitool = 5,
/obj/item/construction/rcd/loaded = 3,
/obj/item/grenade/chem_grenade/smart_metal_foam = 10,
- /obj/item/geiger_counter = 5,
+ /obj/item/geiger_counter = 6,
/obj/item/stock_parts/cell/high = 10,
- /obj/item/electronics/airlock = 10,
+ /obj/item/electronics/airlock = 10,
/obj/item/electronics/apc = 10,
/obj/item/electronics/airalarm = 10,
/obj/item/electronics/firealarm = 10,
- /obj/item/electronics/firelock = 10
+ /obj/item/electronics/firelock = 10,
+ /obj/item/rcd_ammo = 3
)
- contraband = list(/obj/item/stock_parts/cell/potato = 3)
+ contraband = list(/obj/item/stock_parts/cell/potato = 3,
+ /obj/item/rcd_ammo = 2,
+ /obj/item/circuitboard/computer/slot_machine = 1,
+ /obj/item/tank/internals/emergency_oxygen/double = 3
+ )
premium = list(/obj/item/storage/belt/utility = 3,
- /obj/item/storage/box/smart_metal_foam = 1)
+ /obj/item/storage/box/smart_metal_foam = 3,
+ /obj/item/rcd_ammo/large = 5
+ )
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
resistance_flags = FIRE_PROOF
diff --git a/code/modules/vending/medical_wall.dm b/code/modules/vending/medical_wall.dm
index 84c4891589..018eb09d86 100644
--- a/code/modules/vending/medical_wall.dm
+++ b/code/modules/vending/medical_wall.dm
@@ -13,6 +13,7 @@
/obj/item/reagent_containers/medspray/sterilizine = 1)
contraband = list(/obj/item/reagent_containers/pill/tox = 2,
/obj/item/reagent_containers/pill/morphine = 2)
+ premium = list(/obj/item/reagent_containers/medspray/synthflesh = 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/vending_refill/wallmed
@@ -26,3 +27,4 @@
/obj/item/reagent_containers/pill/patch/styptic = 1,
/obj/item/reagent_containers/pill/patch/silver_sulf = 1,
/obj/item/reagent_containers/medspray/sterilizine = 1)
+ premium = list(/obj/item/reagent_containers/medspray/synthflesh = 2)
diff --git a/code/modules/vending/snack.dm b/code/modules/vending/snack.dm
index 69e510ad5a..19138bad32 100644
--- a/code/modules/vending/snack.dm
+++ b/code/modules/vending/snack.dm
@@ -12,6 +12,9 @@
/obj/item/reagent_containers/food/snacks/spacetwinkie = 6,
/obj/item/reagent_containers/food/snacks/cheesiehonkers = 6)
contraband = list(/obj/item/reagent_containers/food/snacks/syndicake = 6)
+ premium = list(/obj/item/storage/box/donkpockets = 1,
+ /obj/item/reagent_containers/food/snacks/poppypretzel = 3)
+
refill_canister = /obj/item/vending_refill/snack
var/chef_compartment_access = "28" //ACCESS_KITCHEN
diff --git a/code/modules/vending/youtool.dm b/code/modules/vending/youtool.dm
index 495d60a461..ebaad4d715 100644
--- a/code/modules/vending/youtool.dm
+++ b/code/modules/vending/youtool.dm
@@ -3,19 +3,20 @@
desc = "Tools for tools."
icon_state = "tool"
icon_deny = "tool-deny"
- products = list(/obj/item/stack/cable_coil/random = 10,
- /obj/item/crowbar = 5,
- /obj/item/weldingtool = 3,
- /obj/item/wirecutters = 5,
- /obj/item/wrench = 5,
- /obj/item/analyzer = 5,
- /obj/item/t_scanner = 5,
- /obj/item/screwdriver = 5,
- /obj/item/flashlight/glowstick = 3,
- /obj/item/flashlight/glowstick/red = 3,
- /obj/item/flashlight = 5)
- contraband = list(/obj/item/weldingtool/hugetank = 2,
- /obj/item/clothing/gloves/color/fyellow = 2)
- premium = list(/obj/item/clothing/gloves/color/yellow = 1)
+ products = list(/obj/item/stack/cable_coil/random = 15,
+ /obj/item/crowbar = 10,
+ /obj/item/weldingtool = 6,
+ /obj/item/wirecutters = 10,
+ /obj/item/wrench = 10,
+ /obj/item/analyzer = 10,
+ /obj/item/t_scanner = 10,
+ /obj/item/screwdriver = 10,
+ /obj/item/flashlight/glowstick = 6,
+ /obj/item/flashlight/glowstick/red = 6,
+ /obj/item/flashlight = 7)
+ contraband = list(/obj/item/weldingtool/largetank = 4,
+ /obj/item/clothing/gloves/color/fyellow = 4)
+ premium = list(/obj/item/clothing/gloves/color/yellow = 2,
+ /obj/item/weldingtool/hugetank = 2)
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70)
resistance_flags = FIRE_PROOF
diff --git a/config/config.txt b/config/config.txt
index 33bff862d9..3bc9f873a9 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -16,6 +16,9 @@ $include antag_rep.txt
## Server name: This appears at the top of the screen in-game and in the BYOND hub. Uncomment and replace 'tgstation' with the name of your choice.
# SERVERNAME tgstation
+## Server tagline: This will appear right below the server's title.
+# SERVERTAGLINE A generic TG-based server
+
## Server SQL name: This is the name used to identify the server to the SQL DB, distinct from SERVERNAME as it must be at most 32 characters.
# SERVERSQLNAME tgstation
diff --git a/html/changelogs/AutoChangeLog-pr-7754.yml b/html/changelogs/AutoChangeLog-pr-7754.yml
new file mode 100644
index 0000000000..2599671fb5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7754.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - bugfix: "The bug where ghost role spawners leave null entries in glob.mob_spawners has actually been fixed"
diff --git a/html/changelogs/AutoChangeLog-pr-7756.yml b/html/changelogs/AutoChangeLog-pr-7756.yml
new file mode 100644
index 0000000000..6355b1a6ef
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7756.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - bugfix: "The Kitchen Gun (TM) is no longer invisible."
diff --git a/html/changelogs/AutoChangeLog-pr-7765.yml b/html/changelogs/AutoChangeLog-pr-7765.yml
new file mode 100644
index 0000000000..6d7c5c4075
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7765.yml
@@ -0,0 +1,5 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - balance: "The majority of cult spells no longer have exclamation marks at the end of their invocations. This means they will no longer be bound to the rules of shouting. Which means sec officers will no longer be able to hear culties sacrificing the captain from the complete opposite side of the maintenance tunnel the sacrifice is happening in. However, things like EMP pulses and runeless teleportation still have the effect due to their inherent loud nature."
+ - balance: "The stun blood spell now instantly causes hard stamcrit. However, to offset this, it now also only has one charge and has a higher cost."
diff --git a/html/changelogs/AutoChangeLog-pr-7770.yml b/html/changelogs/AutoChangeLog-pr-7770.yml
new file mode 100644
index 0000000000..d949f17161
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7770.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - balance: "The Gloves of the North Star now restore 2/3 of the stamina used up by punches when harm intent punching. With aggressive play and good stamina buffer management, this makes it entirely plausible to roughly double your stamina regeneration per mob life process tick."
diff --git a/html/changelogs/AutoChangeLog-pr-7771.yml b/html/changelogs/AutoChangeLog-pr-7771.yml
new file mode 100644
index 0000000000..f537395993
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7771.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - balance: "Revs can no longer remember the name of the person who flashed them when deconverted. This brings it in-line with the other conversion antag deconversions."
diff --git a/html/changelogs/AutoChangeLog-pr-7772.yml b/html/changelogs/AutoChangeLog-pr-7772.yml
new file mode 100644
index 0000000000..5aa989cc93
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7772.yml
@@ -0,0 +1,5 @@
+author: "Tupinambis"
+delete-after: True
+changes:
+ - tweak: "Adds a mixed clothing closet to Deltastation Dorms."
+ - tweak: "Adds a bottle of unstable mutagen to Botany, on account of its distance from Chemistry."
diff --git a/html/changelogs/AutoChangeLog-pr-7776.yml b/html/changelogs/AutoChangeLog-pr-7776.yml
new file mode 100644
index 0000000000..43660c000a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7776.yml
@@ -0,0 +1,4 @@
+author: "Tupinambis"
+delete-after: True
+changes:
+ - bugfix: "Omegastation should no longer have any turf with missing textures, as those spots have been replaced with playing covered in a sandy turf decal."
diff --git a/html/changelogs/AutoChangeLog-pr-7778.yml b/html/changelogs/AutoChangeLog-pr-7778.yml
new file mode 100644
index 0000000000..75362eca05
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7778.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - bugfix: "The bug where you can sometimes get permanently stuck in stamcrit should HOPEFULLY be fixed. I'm unable to reproduce the bug myself, but let's hope."
diff --git a/html/changelogs/AutoChangeLog-pr-7779.yml b/html/changelogs/AutoChangeLog-pr-7779.yml
new file mode 100644
index 0000000000..4202149f90
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7779.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - balance: "The night vision trait now grants you darksight for an entire 1:1 screen, but the alpha of the lighting plane with the trait has been increased from 245 to 250 to balance it out"
diff --git a/html/changelogs/AutoChangeLog-pr-7781.yml b/html/changelogs/AutoChangeLog-pr-7781.yml
new file mode 100644
index 0000000000..d6f2ca5907
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7781.yml
@@ -0,0 +1,5 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - bugfix: "It's now actually possible to click the stamina hud button"
+ - bugfix: "The stamina hud button now displays your stamina buffer correctly"
diff --git a/html/changelogs/AutoChangeLog-pr-7782.yml b/html/changelogs/AutoChangeLog-pr-7782.yml
new file mode 100644
index 0000000000..17986ac78f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7782.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - balance: "Bone spears now have a reach of 2 tiles."
diff --git a/html/changelogs/AutoChangeLog-pr-7783.yml b/html/changelogs/AutoChangeLog-pr-7783.yml
new file mode 100644
index 0000000000..489f7ba055
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7783.yml
@@ -0,0 +1,4 @@
+author: "R3dtail"
+delete-after: True
+changes:
+ - rscadd: "Added a new carpet. Red! Also added said carpet to the Premium Carpet crate from the cargo supply console. Trilby said she'd take care of the crafting recipe."
diff --git a/html/changelogs/AutoChangeLog-pr-7791.yml b/html/changelogs/AutoChangeLog-pr-7791.yml
new file mode 100644
index 0000000000..d2cde53ae0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7791.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - bugfix: "You can no longer phase through solid objects via scrambling"
diff --git a/html/changelogs/AutoChangeLog-pr-7860.yml b/html/changelogs/AutoChangeLog-pr-7860.yml
new file mode 100644
index 0000000000..140f95aa40
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7860.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - bugfix: "The syndicate mask now works properly as intended."
diff --git a/html/changelogs/AutoChangeLog-pr-7865.yml b/html/changelogs/AutoChangeLog-pr-7865.yml
new file mode 100644
index 0000000000..a75282b293
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7865.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - balance: "Stamina is no longer affected by health at all."
diff --git a/html/changelogs/AutoChangeLog-pr-7866.yml b/html/changelogs/AutoChangeLog-pr-7866.yml
new file mode 100644
index 0000000000..17435075e0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7866.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - rscadd: "You can now right-click yourself in help intent in combat mode to instantly get up from resting. This will cost stamina equal to your entire stamina buffer. Manage your stamina well, and you'll be able to shrug off a single stray golden bolt"
diff --git a/html/changelogs/AutoChangeLog-pr-7876.yml b/html/changelogs/AutoChangeLog-pr-7876.yml
new file mode 100644
index 0000000000..649c0cc916
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7876.yml
@@ -0,0 +1,4 @@
+author: "coiax"
+delete-after: True
+changes:
+ - admin: "When the nuclear disk stays stationary long enough to trigger an increase for the lone op event chance, admins will be notified every five increments."
diff --git a/html/changelogs/AutoChangeLog-pr-7878.yml b/html/changelogs/AutoChangeLog-pr-7878.yml
new file mode 100644
index 0000000000..6d10310d94
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7878.yml
@@ -0,0 +1,4 @@
+author: "Tupinambis"
+delete-after: True
+changes:
+ - tweak: "Removed all living eggs from the xeno ruin because people self antag. Honk."
diff --git a/html/changelogs/AutoChangeLog-pr-7912.yml b/html/changelogs/AutoChangeLog-pr-7912.yml
new file mode 100644
index 0000000000..834ad80091
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7912.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - server: "The server's tagline is now a config option. People can now stop confusing us with BR cit"
diff --git a/html/changelogs/AutoChangeLog-pr-7914.yml b/html/changelogs/AutoChangeLog-pr-7914.yml
new file mode 100644
index 0000000000..aac983d7ea
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7914.yml
@@ -0,0 +1,4 @@
+author: "Ghom"
+delete-after: True
+changes:
+ - code_imp: "minor clean up on hydroponics reagent containers."
diff --git a/html/changelogs/AutoChangeLog-pr-7916.yml b/html/changelogs/AutoChangeLog-pr-7916.yml
new file mode 100644
index 0000000000..888f327f72
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7916.yml
@@ -0,0 +1,4 @@
+author: "Skoglol"
+delete-after: True
+changes:
+ - rscadd: "You can now alt click storage (bags, boxes, etc) to open it."
diff --git a/html/changelogs/AutoChangeLog-pr-7918.yml b/html/changelogs/AutoChangeLog-pr-7918.yml
new file mode 100644
index 0000000000..b2340c2f67
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7918.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - rscadd: "Attack animations will now rotate your character slightly, similar to Goon."
diff --git a/html/changelogs/AutoChangeLog-pr-7919.yml b/html/changelogs/AutoChangeLog-pr-7919.yml
new file mode 100644
index 0000000000..a62cc838c3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7919.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - rscadd: "Throwing items will now perform the attack animation and play a sound"
diff --git a/html/changelogs/AutoChangeLog-pr-7920.yml b/html/changelogs/AutoChangeLog-pr-7920.yml
new file mode 100644
index 0000000000..08b0ec1f13
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7920.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - rscadd: "flashlights will now make sounds when toggled on/off"
diff --git a/html/changelogs/AutoChangeLog-pr-7921.yml b/html/changelogs/AutoChangeLog-pr-7921.yml
new file mode 100644
index 0000000000..66a245257f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7921.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - rscadd: "Things in disposals will now emit sounds every single time they hit corners. This increases immersion."
diff --git a/html/changelogs/AutoChangeLog-pr-7922.yml b/html/changelogs/AutoChangeLog-pr-7922.yml
new file mode 100644
index 0000000000..08f06d7053
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7922.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - bugfix: "Air alarms now actually emit the proper light color when their status is okay."
diff --git a/html/changelogs/AutoChangeLog-pr-7923.yml b/html/changelogs/AutoChangeLog-pr-7923.yml
new file mode 100644
index 0000000000..5cf6527dad
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7923.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - rscadd: "Backpacks and other storage items will now jiggle and squish when you interact with them, similar to the animations seen on Goon."
diff --git a/html/changelogs/AutoChangeLog-pr-7930.yml b/html/changelogs/AutoChangeLog-pr-7930.yml
new file mode 100644
index 0000000000..221a2af942
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-7930.yml
@@ -0,0 +1,4 @@
+author: "nicc"
+delete-after: True
+changes:
+ - balance: "teg less gay maybe"
diff --git a/icons/mob/custom_w.dmi b/icons/mob/custom_w.dmi
index dc79247d48..1101f22d2e 100644
Binary files a/icons/mob/custom_w.dmi and b/icons/mob/custom_w.dmi differ
diff --git a/icons/mob/inhands/weapons/guns_lefthand.dmi b/icons/mob/inhands/weapons/guns_lefthand.dmi
index 5198f3bda9..dae3861233 100644
Binary files a/icons/mob/inhands/weapons/guns_lefthand.dmi and b/icons/mob/inhands/weapons/guns_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/guns_righthand.dmi b/icons/mob/inhands/weapons/guns_righthand.dmi
index afbd091d13..f3d5c42570 100644
Binary files a/icons/mob/inhands/weapons/guns_righthand.dmi and b/icons/mob/inhands/weapons/guns_righthand.dmi differ
diff --git a/icons/obj/2x2.dmi b/icons/obj/2x2.dmi
index a72a86f3d8..c5b1df680a 100644
Binary files a/icons/obj/2x2.dmi and b/icons/obj/2x2.dmi differ
diff --git a/icons/obj/chairs.dmi b/icons/obj/chairs.dmi
index 96e4a4228e..3754ff052c 100644
Binary files a/icons/obj/chairs.dmi and b/icons/obj/chairs.dmi differ
diff --git a/icons/obj/clothing/accessories.dmi b/icons/obj/clothing/accessories.dmi
index 5da31a3a4f..c35956687d 100644
Binary files a/icons/obj/clothing/accessories.dmi and b/icons/obj/clothing/accessories.dmi differ
diff --git a/icons/obj/custom.dmi b/icons/obj/custom.dmi
index 197b870284..a05fbb72ab 100644
Binary files a/icons/obj/custom.dmi and b/icons/obj/custom.dmi differ
diff --git a/icons/obj/doors/mineral_doors.dmi b/icons/obj/doors/mineral_doors.dmi
index 7682252a6d..03fab197fe 100644
Binary files a/icons/obj/doors/mineral_doors.dmi and b/icons/obj/doors/mineral_doors.dmi differ
diff --git a/icons/obj/drinks.dmi b/icons/obj/drinks.dmi
index e6e8ff9eb7..a32d0234d6 100644
Binary files a/icons/obj/drinks.dmi and b/icons/obj/drinks.dmi differ
diff --git a/icons/obj/fluff.dmi b/icons/obj/fluff.dmi
index b476d25228..72edaa0528 100644
Binary files a/icons/obj/fluff.dmi and b/icons/obj/fluff.dmi differ
diff --git a/icons/obj/guns/energy.dmi b/icons/obj/guns/energy.dmi
index d8092a6095..e3ec4171ad 100644
Binary files a/icons/obj/guns/energy.dmi and b/icons/obj/guns/energy.dmi differ
diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi
index 6309e5c084..0ee0e3d2d1 100644
Binary files a/icons/obj/guns/projectile.dmi and b/icons/obj/guns/projectile.dmi differ
diff --git a/icons/obj/smooth_structures/fancy_table_blackred.dmi b/icons/obj/smooth_structures/fancy_table_blackred.dmi
new file mode 100644
index 0000000000..0766c2f2f9
Binary files /dev/null and b/icons/obj/smooth_structures/fancy_table_blackred.dmi differ
diff --git a/icons/obj/smooth_structures/fancy_table_monochrome.dmi b/icons/obj/smooth_structures/fancy_table_monochrome.dmi
new file mode 100644
index 0000000000..54b1b17577
Binary files /dev/null and b/icons/obj/smooth_structures/fancy_table_monochrome.dmi differ
diff --git a/icons/obj/structures.dmi b/icons/obj/structures.dmi
index 897508fa8c..2e1fdfa7a8 100644
Binary files a/icons/obj/structures.dmi and b/icons/obj/structures.dmi differ
diff --git a/icons/obj/tiles.dmi b/icons/obj/tiles.dmi
index 8b439d2bad..9305e4b7bc 100644
Binary files a/icons/obj/tiles.dmi and b/icons/obj/tiles.dmi differ
diff --git a/icons/turf/floors.dmi b/icons/turf/floors.dmi
index 518adfc766..2a56a03e66 100644
Binary files a/icons/turf/floors.dmi and b/icons/turf/floors.dmi differ
diff --git a/icons/turf/floors/carpet_blackred.dmi b/icons/turf/floors/carpet_blackred.dmi
new file mode 100644
index 0000000000..e75ce04381
Binary files /dev/null and b/icons/turf/floors/carpet_blackred.dmi differ
diff --git a/icons/turf/floors/carpet_monochrome.dmi b/icons/turf/floors/carpet_monochrome.dmi
new file mode 100644
index 0000000000..9ab9bc499e
Binary files /dev/null and b/icons/turf/floors/carpet_monochrome.dmi differ
diff --git a/modular_citadel/code/_onclick/hud/stamina.dm b/modular_citadel/code/_onclick/hud/stamina.dm
index 1266e88158..781b7cb25f 100644
--- a/modular_citadel/code/_onclick/hud/stamina.dm
+++ b/modular_citadel/code/_onclick/hud/stamina.dm
@@ -6,12 +6,12 @@
name = "stamina"
icon_state = "stamina0"
screen_loc = ui_stamina
- mouse_opacity = 0
+ mouse_opacity = 1
/obj/screen/staminas/Click(location,control,params)
if(isliving(usr))
var/mob/living/L = usr
- to_chat(L, "You have [L.getStaminaLoss()] stamina loss.
Your stamina buffer can take [L.stambuffer] stamina loss, and will use 50% of that stamina loss when recharging.
Your stamina buffer is [(100/L.stambuffer)-(L.bufferedstam*(100/L.stambuffer))]% full.")
+ to_chat(L, "You have [L.getStaminaLoss()] stamina loss.
Your stamina buffer can take [L.stambuffer] stamina loss, and will use 50% of that stamina loss when recharging.
Your stamina buffer is [(L.stambuffer*(100/L.stambuffer))-(L.bufferedstam*(100/L.stambuffer))]% full.")
/mob/living/carbon/human/proc/staminahudamount()
if(stat == DEAD || recoveringstam)
diff --git a/modular_citadel/code/datums/components/phantomthief.dm b/modular_citadel/code/datums/components/phantomthief.dm
index c09d17d930..d34e16f6e9 100644
--- a/modular_citadel/code/datums/components/phantomthief.dm
+++ b/modular_citadel/code/datums/components/phantomthief.dm
@@ -39,7 +39,7 @@
if(!istype(user))
return
if(!combattoggle_redir)
- combattoggle_redir = user.AddComponent(/datum/component/redirect,list(COMSIG_COMBAT_TOGGLED),CALLBACK(src,.proc/handlefilterstuff))
+ combattoggle_redir = user.AddComponent(/datum/component/redirect, list(COMSIG_COMBAT_TOGGLED = CALLBACK(src, .proc/handlefilterstuff)))
/datum/component/phantomthief/proc/OnDropped(mob/user)
if(!istype(user))
diff --git a/modular_citadel/code/datums/components/souldeath.dm b/modular_citadel/code/datums/components/souldeath.dm
new file mode 100644
index 0000000000..5beddf3529
--- /dev/null
+++ b/modular_citadel/code/datums/components/souldeath.dm
@@ -0,0 +1,35 @@
+/datum/component/souldeath
+ var/mob/living/wearer
+ var/equip_slot
+ var/signal = FALSE
+
+/datum/component/souldeath/Initialize()
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+ RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/equip)
+ RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/unequip)
+
+/datum/component/souldeath/proc/equip(datum/source, mob/living/equipper, slot)
+ if(!slot || equip_slot == slot)
+ wearer = equipper
+ RegisterSignal(wearer, COMSIG_MOB_DEATH, .proc/die, TRUE)
+ signal = TRUE
+ else
+ if(signal)
+ UnregisterSignal(wearer, COMSIG_MOB_DEATH)
+ signal = FALSE
+ return
+
+/datum/component/souldeath/proc/unequip()
+ UnregisterSignal(wearer, COMSIG_MOB_DEATH)
+ wearer = null
+ signal = FALSE
+
+/datum/component/souldeath/proc/die()
+ if(!wearer)
+ return //idfk
+ new/obj/effect/temp_visual/souldeath(wearer.loc, wearer)
+ playsound(wearer, 'modular_citadel/sound/misc/souldeath.ogg', 100, FALSE)
+
+/datum/component/souldeath/neck
+ equip_slot = SLOT_NECK
diff --git a/modular_citadel/code/game/objects/effects/temporary_visuals/souldeath.dm b/modular_citadel/code/game/objects/effects/temporary_visuals/souldeath.dm
new file mode 100644
index 0000000000..1ebf6fb2a6
--- /dev/null
+++ b/modular_citadel/code/game/objects/effects/temporary_visuals/souldeath.dm
@@ -0,0 +1,5 @@
+/obj/effect/temp_visual/souldeath
+ name = "soul death"
+ icon = 'modular_citadel/icons/effects/souldeath.dmi'
+ icon_state = "souldeath"
+ duration = 30
\ No newline at end of file
diff --git a/modular_citadel/code/modules/admin/chat_commands.dm b/modular_citadel/code/modules/admin/chat_commands.dm
index 7820ea8f4d..d5d66a1888 100644
--- a/modular_citadel/code/modules/admin/chat_commands.dm
+++ b/modular_citadel/code/modules/admin/chat_commands.dm
@@ -3,9 +3,9 @@
help_text = "What are Citadel Station 13 players salting about today? Spin the wheel and find out!"
/datum/tgs_chat_command/wheelofsalt/Run(datum/tgs_chat_user/sender, params)
- var/saltresult = "The wheel of salt [pick("clatters","vibrates","clanks","groans","moans","squeaks","emits a[pick(" god-forsaken"," lewd"," creepy"," generic","n orgasmic")] [pick("airhorn","bike horn","trumpet","clown","latex","vore","dog")] noise")] as it spins violently... And it seems the salt of the day is the "
- var/saltprimarysubject = "[pick("combat","medical","grab","furry","wall","orgasm","cat","ERP","lizard","dog","latex","vision cone","atmospherics","table","chem","vore","dogborg","Skylar Lineman","Mekhi Anderson","Peppermint","rework","cum","dick","cockvore","Medihound","sleeper","belly sleeper","door wires","flightsuit","coder privilege","Developer abuse","ban reason","github self merge","red panda","beret","male catgirl","powergame","Discord server","Clitadel","Cargonia","Solarian Republic","Main and RP merger","Salt","chem dispenser theft","Botany","moth","BWOINK","anal vore","stamina","Mason Jakops","mining","noodle","Lavaland","Necropolis")]"
- var/saltsecondarysubject = "[pick("rework","changes","r34","ban","removal","addition","leak","proposal","fanart","introduction","tabling","ERP","bikeshedding","sprites","semen keg","argument","theft","nerf","screeching","salt","creampie","lewding","murder","kissing")]"
+ var/saltresult = "The wheel of salt [pick("clatters","screams","vibrates","clanks","resonates","groans","moans","squeaks","emits a[pick(" god-forsaken"," lewd"," creepy"," generic","n orgasmic"," demonic")] [pick("airhorn","bike horn","trumpet","clown","latex","vore","dog","laughing")] noise")] as it spins violently... And it seems the salt of the day is the "
+ var/saltprimarysubject = "[pick("combat","medical","grab","furry","wall","orgasm","cat","ERP","lizard","dog","latex","vision cone","atmospherics","table","chem","vore","dogborg","Skylar Lineman","Mekhi Anderson","Peppermint","rework","cum","dick","cockvore","Medihound","sleeper","belly sleeper","door wires","flightsuit","coder privilege","Developer abuse","ban reason","github self merge","red panda","beret","male catgirl","powergame","hexacrocin","Discord server","Clitadel","Cargonia","Solarian Republic","Main and RP merger","bluespace","salt","chem dispenser theft","Botany","moth","BWOINK","anal vore","stamina","Mason Jakops","mining","noodle","milf","Lavaland","Necropolis","Ashwalker","Chase Redtail","Drew Mint","Pavel Marsk","Jecca Qua","Joker Amari","Durgit","chaplain","Antag","nanite","Syndicate","Nar-Sie","Ratvar","Cult","maint","Foam-Force","AI","cyborg","ghost","clockwork","cyberpunk","vaporwave","Clown","Leon Beech","Mime","security","research","Megafauna","Bubblegum","Ash Drake","Legion","Colossus","White Shuttle","Changeling","Cowboy","Space Ninja","Poly","Revolutionary","Skyrim","forbidden fruits","xenomorph","blob","Nuclear Operative","crossdressing")]"
+ var/saltsecondarysubject = "[pick("rework","changes","r34","ban","removal","addition","leak","proposal","fanart","introduction","tabling","ERP","bikeshedding","crossdressing","sprites","semen keg","argument","theft","nerf","screeching","salt","creampie","lewding","murder","kissing","marriage","replacement","fucking","ship","netflix adaptation","dance","remaster","system","voyeur","decoration","pre-order","bukkake","seduction","worship","gangbang","handholding")]"
saltresult += "[saltprimarysubject] [saltsecondarysubject]"
return "[saltresult]!"
diff --git a/modular_citadel/code/modules/antagonists/cult/blood_magic.dm b/modular_citadel/code/modules/antagonists/cult/blood_magic.dm
deleted file mode 100644
index 065216a2c7..0000000000
--- a/modular_citadel/code/modules/antagonists/cult/blood_magic.dm
+++ /dev/null
@@ -1,3 +0,0 @@
-/datum/action/innate/cult/blood_spell/stun
- charges = 4
- health_cost = 3
diff --git a/modular_citadel/code/modules/arousal/toys/dildos.dm b/modular_citadel/code/modules/arousal/toys/dildos.dm
index 587702762c..4b0a7ad919 100644
--- a/modular_citadel/code/modules/arousal/toys/dildos.dm
+++ b/modular_citadel/code/modules/arousal/toys/dildos.dm
@@ -1,7 +1,7 @@
//////////
//DILDOS//
//////////
-obj/item/dildo
+/obj/item/dildo
name = "dildo"
desc = "Floppy!"
icon = 'modular_citadel/icons/obj/genitals/dildo.dmi'
@@ -18,9 +18,10 @@ obj/item/dildo
var/random_color = TRUE
var/random_size = FALSE
var/random_shape = FALSE
+ var/is_knotted = FALSE
//Lists moved to _cit_helpers.dm as globals so they're not instanced individually
-obj/item/dildo/proc/update_appearance()
+/obj/item/dildo/proc/update_appearance()
icon_state = "[dildo_type]_[dildo_shape]_[dildo_size]"
var/sizeword = ""
switch(dildo_size)
@@ -35,7 +36,7 @@ obj/item/dildo/proc/update_appearance()
name = "[sizeword][dildo_shape] [can_customize ? "custom " : ""][dildo_type]"
-obj/item/dildo/AltClick(mob/living/user)
+/obj/item/dildo/AltClick(mob/living/user)
if(QDELETED(src))
return
if(!isliving(user))
@@ -46,7 +47,7 @@ obj/item/dildo/AltClick(mob/living/user)
return
customize(user)
-obj/item/dildo/proc/customize(mob/living/user)
+/obj/item/dildo/proc/customize(mob/living/user)
if(!can_customize)
return FALSE
if(src && !user.incapacitated() && in_range(user,src))
@@ -75,7 +76,7 @@ obj/item/dildo/proc/customize(mob/living/user)
update_appearance()
return TRUE
-obj/item/dildo/Initialize()
+/obj/item/dildo/Initialize()
. = ..()
if(random_color == TRUE)
var/randcolor = pick(GLOB.dildo_colors)
@@ -91,38 +92,41 @@ obj/item/dildo/Initialize()
pixel_y = rand(-7,7)
pixel_x = rand(-7,7)
-obj/item/dildo/examine(mob/user)
+/obj/item/dildo/examine(mob/user)
..()
if(can_customize)
user << "Alt-Click \the [src.name] to customize it."
-obj/item/dildo/random//totally random
+/obj/item/dildo/random//totally random
name = "random dildo"//this name will show up in vendors and shit so you know what you're vending(or don't, i guess :^))
random_color = TRUE
random_shape = TRUE
random_size = TRUE
-
-obj/item/dildo/knotted
+/obj/item/dildo/knotted
dildo_shape = "knotted"
name = "knotted dildo"
+ attack_verb = list("penetrated", "knotted", "slapped", "inseminated")
obj/item/dildo/human
dildo_shape = "human"
name = "human dildo"
+ attack_verb = list("penetrated", "slapped", "inseminated")
obj/item/dildo/plain
dildo_shape = "plain"
name = "plain dildo"
+ attack_verb = list("penetrated", "slapped", "inseminated")
obj/item/dildo/flared
dildo_shape = "flared"
name = "flared dildo"
+ attack_verb = list("penetrated", "slapped", "neighed", "gaped", "prolapsed", "inseminated")
obj/item/dildo/flared/huge
- name = "literal horse cock"
- desc = "THIS THING IS HUGE!"
- dildo_size = 4
+ name = "literal horse cock"
+ desc = "THIS THING IS HUGE!"
+ dildo_size = 4
obj/item/dildo/custom
name = "customizable dildo"
@@ -131,3 +135,30 @@ obj/item/dildo/custom
random_color = TRUE
random_shape = TRUE
random_size = TRUE
+
+// Suicide acts, by request
+
+/obj/item/dildo/proc/manual_suicide(mob/living/user)
+ user.visible_message("[user] finally finishes deepthroating the [src], and their life.")
+ user.adjustOxyLoss(200)
+ user.death(0)
+
+/obj/item/dildo/suicide_act(mob/living/user)
+// is_knotted = ((src.dildo_shape == "knotted")?"They swallowed the knot":"Their face is turning blue")
+ if(do_after(user,17,target=src))
+ user.visible_message("[user] tears-up and gags as they shove [src] down their throat! It looks like [user.p_theyre()] trying to commit suicide!")
+ playsound(loc, 'sound/weapons/gagging.ogg', 50, 1, -1)
+ user.Stun(150)
+ user.adjust_blurriness(8)
+ user.adjust_eye_damage(10)
+ return MANUAL_SUICIDE
+
+/obj/item/dildo/flared/huge/suicide_act(mob/living/user)
+ if(do_after(user,35,target=src))
+ user.visible_message("[user] tears-up and gags as they try to deepthroat the [src]! WHY WOULD THEY DO THAT? It looks like [user.p_theyre()] trying to commit suicide!!")
+ playsound(loc, 'sound/weapons/gagging.ogg', 50, 2, -1)
+ user.Stun(300)
+ user.adjust_blurriness(8)
+ user.adjust_eye_damage(15)
+ return MANUAL_SUICIDE
+
diff --git a/modular_citadel/code/modules/cargo/packs.dm b/modular_citadel/code/modules/cargo/packs.dm
index c03a049b60..6d39a51a76 100644
--- a/modular_citadel/code/modules/cargo/packs.dm
+++ b/modular_citadel/code/modules/cargo/packs.dm
@@ -23,6 +23,6 @@
/datum/supply_pack/misc/jukebox
name = "Jukebox"
- cost = 1000000
+ cost = 35000
contains = list(/obj/machinery/jukebox)
crate_name = "Jukebox"
diff --git a/modular_citadel/code/modules/client/loadout/__donator.dm b/modular_citadel/code/modules/client/loadout/__donator.dm
index eda53c0afe..a3df7d9f8e 100644
--- a/modular_citadel/code/modules/client/loadout/__donator.dm
+++ b/modular_citadel/code/modules/client/loadout/__donator.dm
@@ -344,3 +344,27 @@ datum/gear/darksabresheath
category = SLOT_WEAR_SUIT
path = /obj/item/clothing/suit/hooded/ian_costume
ckeywhitelist = list("cathodetherobot")
+
+/datum/gear/sharkcloth
+ name = "Leon's Skimpy Outfit"
+ category = SLOT_WEAR_SUIT
+ path = /obj/item/clothing/under/leoskimpy
+ ckeywhitelist = list("spectrosis")
+
+/datum/gear/mimemask
+ name = "Mime Mask"
+ category = SLOT_WEAR_MASK
+ path = /obj/item/clothing/mask/gas/mime
+ ckeywhitelist = list("pireamaineach")
+
+/datum/gear/mimeoveralls
+ name = "Mime's Overalls"
+ category = SLOT_WEAR_SUIT
+ path = /obj/item/clothing/under/mimeoveralls
+ ckeywhitelist = list("pireamaineach")
+
+/datum/gear/soulneck
+ name = "Soul Necklace"
+ category = SLOT_IN_BACKPACK
+ path = /obj/item/clothing/neck/undertale
+ ckeywhitelist = list("twilightic")
\ No newline at end of file
diff --git a/modular_citadel/code/modules/client/loadout/backpack.dm b/modular_citadel/code/modules/client/loadout/backpack.dm
index b3a59425a9..886a2d4df9 100644
--- a/modular_citadel/code/modules/client/loadout/backpack.dm
+++ b/modular_citadel/code/modules/client/loadout/backpack.dm
@@ -56,4 +56,14 @@
/datum/gear/dildo
name = "Customizable dildo"
category = SLOT_IN_BACKPACK
- path = /obj/item/dildo/custom
\ No newline at end of file
+ path = /obj/item/dildo/custom
+
+/datum/gear/paperbin
+ name = "Paper Bin"
+ category = SLOT_IN_BACKPACK
+ path = /obj/item/paper_bin
+
+/datum/gear/crayons
+ name = "Box of crayons"
+ category = SLOT_IN_BACKPACK
+ path = /obj/item/storage/crayons
diff --git a/modular_citadel/code/modules/client/loadout/uniform.dm b/modular_citadel/code/modules/client/loadout/uniform.dm
index 7c592c60e1..e4e2f063d3 100644
--- a/modular_citadel/code/modules/client/loadout/uniform.dm
+++ b/modular_citadel/code/modules/client/loadout/uniform.dm
@@ -118,11 +118,43 @@
category = SLOT_W_UNIFORM
path = /obj/item/clothing/under/pants/track
+// Pantsless Sweaters
+
/datum/gear/turtleneck
name = "Tactitool Turtleneck"
category = SLOT_W_UNIFORM
path = /obj/item/clothing/under/syndicate/cosmetic
+/datum/gear/creamsweater
+ name = "Cream Commando Sweater"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/bb_sweater
+
+/datum/gear/blacksweater
+ name = "Black Commando Sweater"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/bb_sweater/black
+
+/datum/gear/purpsweater
+ name = "Purple Commando Sweater"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/bb_sweater/purple
+
+/datum/gear/greensweater
+ name = "Green Commando Sweater"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/bb_sweater/green
+
+/datum/gear/redsweater
+ name = "Red Commando Sweater"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/bb_sweater/red
+
+/datum/gear/bluesweater
+ name = "Navy Commando Sweater"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/bb_sweater/blue
+
/datum/gear/polykilt
name = "Polychromic Kilt"
category = SLOT_W_UNIFORM
diff --git a/modular_citadel/code/modules/client/verbs/who.dm b/modular_citadel/code/modules/client/verbs/who.dm
index 091dd7bf79..7640595e18 100644
--- a/modular_citadel/code/modules/client/verbs/who.dm
+++ b/modular_citadel/code/modules/client/verbs/who.dm
@@ -28,28 +28,37 @@
var/msg = ""
var/list/Lines = list()
+ var/list/assembled = list()
+ var/admin_mode = check_rights_for(src, R_ADMIN) && isobserver(mob)
+ if(admin_mode)
+ log_admin("[key_name(usr)] checked advanced who in-round")
if(length(GLOB.admins))
Lines += "Admins:"
for(var/X in GLOB.admins)
var/client/C = X
if(C && C.holder && !C.holder.fakekey)
- Lines += "\t [C.key][show_admin_info(C)] ([round(C.avgping, 1)]ms)"
+ assembled += "\t [C.key][admin_mode? "[show_admin_info(C)]":""] ([round(C.avgping, 1)]ms)"
+ Lines += sortList(assembled)
+ assembled.len = 0
if(length(GLOB.mentors))
Lines += "Mentors:"
for(var/X in GLOB.mentors)
var/client/C = X
- if(C)
- Lines += "\t [C.key][show_admin_info(C)] ([round(C.avgping, 1)]ms)"
-
+ if(C && (!C.holder || (C.holder && !C.holder.fakekey))) //>using stuff this complex instead of just using if/else lmao
+ assembled += "\t [C.key][admin_mode? "[show_admin_info(C)]":""] ([round(C.avgping, 1)]ms)"
+ Lines += sortList(assembled)
+ assembled.len = 0
Lines += "Players:"
for(var/X in sortList(GLOB.clients))
var/client/C = X
- if(!C) continue
+ if(!C)
+ continue
var/key = C.key
if(C.holder && C.holder.fakekey)
key = C.holder.fakekey
- Lines += "\t [key][show_admin_info(C)] ([round(C.avgping, 1)]ms)"
-
+ assembled += "\t [key][admin_mode? "[show_admin_info(C)]":""] ([round(C.avgping, 1)]ms)"
+ Lines += sortList(assembled)
+
for(var/line in Lines)
msg += "[line]\n"
@@ -60,9 +69,6 @@
if(!C)
return ""
- if(!check_rights_for(src, R_ADMIN))
- return ""
-
var/entry = ""
if(C.holder && C.holder.fakekey)
entry += " (as [C.holder.fakekey])"
diff --git a/modular_citadel/code/modules/clothing/glasses/phantomthief.dm b/modular_citadel/code/modules/clothing/glasses/phantomthief.dm
index e9d31f73d3..49eb089afa 100644
--- a/modular_citadel/code/modules/clothing/glasses/phantomthief.dm
+++ b/modular_citadel/code/modules/clothing/glasses/phantomthief.dm
@@ -36,7 +36,7 @@
if(!istype(user))
return
if(!combattoggle_redir)
- combattoggle_redir = user.AddComponent(/datum/component/redirect,list(COMSIG_COMBAT_TOGGLED),CALLBACK(src,.proc/injectadrenaline))
+ combattoggle_redir = user.AddComponent(/datum/component/redirect, list(COMSIG_COMBAT_TOGGLED = CALLBACK(src, .proc/injectadrenaline)))
/obj/item/clothing/glasses/phantomthief/syndicate/dropped(mob/user)
. = ..()
diff --git a/modular_citadel/code/modules/clothing/neck.dm b/modular_citadel/code/modules/clothing/neck.dm
new file mode 100644
index 0000000000..8d7d4747b7
--- /dev/null
+++ b/modular_citadel/code/modules/clothing/neck.dm
@@ -0,0 +1,23 @@
+/datum/action/item_action/zanderlocket
+ name = "Activate the locket"
+
+/obj/item/clothing/neck/undertale
+ name = "Sylphaen Heart Locket"
+ desc = "A heart shaped locket...The name: 'Zander Sylphaen is inscribed on the front. Something about this necklace fills you with determination."
+ icon = 'modular_citadel/icons/obj/clothing/cit_neck.dmi'
+ item_state = "undertale"
+ icon_state = "undertale"
+ alternate_worn_icon = 'modular_citadel/icons/mob/clothing/necks.dmi'
+ resistance_flags = FIRE_PROOF
+ actions_types = list(/datum/action/item_action/zanderlocket)
+ var/toggled = FALSE
+ var/obj/effect/heart/heart
+
+/datum/action/item_action/zanderlocket/Trigger()
+ new/obj/effect/temp_visual/souldeath(owner.loc, owner)
+ playsound(owner, 'modular_citadel/sound/misc/souldeath.ogg', 100, FALSE)
+
+
+/obj/item/clothing/neck/undertale/Initialize()
+ ..()
+ AddComponent(/datum/component/souldeath/neck)
diff --git a/modular_citadel/code/modules/clothing/under/trek_under.dm b/modular_citadel/code/modules/clothing/under/trek_under.dm
index 00f84b7583..ddcf0e6344 100644
--- a/modular_citadel/code/modules/clothing/under/trek_under.dm
+++ b/modular_citadel/code/modules/clothing/under/trek_under.dm
@@ -11,7 +11,7 @@
/obj/item/clothing/under/rank/trek
name = "Section 31 Uniform"
desc = "Oooh... right."
- icon = 'modular_citadel/icons/mob/clothing/trek_item_icon.dmi'
+ icon = 'modular_citadel/icons/obj/clothing/trek_item_icon.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/clothing/trek_mob_icon.dmi'
item_state = ""
can_adjust = FALSE //to prevent you from "wearing it casually"
@@ -91,11 +91,12 @@
/obj/item/clothing/suit/storage/trek/ds9
name = "Padded Overcoat"
desc = "The overcoat worn by all officers of the 2380s."
- icon = 'modular_citadel/icons/mob/clothing/trek_item_icon.dmi'
+ icon = 'modular_citadel/icons/obj/clothing/trek_item_icon.dmi'
icon_state = "trek_ds9_coat"
alternate_worn_icon = 'modular_citadel/icons/mob/clothing/trek_mob_icon.dmi'
item_state = "trek_ds9_coat"
body_parts_covered = CHEST|GROIN|ARMS
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
permeability_coefficient = 0.50
allowed = list(
/obj/item/flashlight, /obj/item/analyzer,
@@ -136,10 +137,11 @@
name = "Federation Uniform Jacket (Red)"
desc = "A uniform jacket from the United Federation. Starfleet still uses this uniform and there are variations of it. Set phasers to awesome."
- icon = 'modular_citadel/icons/mob/clothing/trek_item_icon.dmi'
+ icon = 'modular_citadel/icons/obj/clothing/trek_item_icon.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/clothing/trek_mob_icon.dmi'
icon_state = "fedcoat"
item_state = "fedcoat"
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
blood_overlay_type = "coat"
body_parts_covered = CHEST|GROIN|ARMS
@@ -204,7 +206,7 @@
/obj/item/clothing/suit/storage/fluff/modernfedcoat
name = "Modern Federation Uniform Jacket"
desc = "A modern uniform jacket from the United Federation. Their Starfleet had recently started using these uniforms. Wearing this makes you feel like a competant commander."
- icon = 'modular_citadel/icons/mob/clothing/trek_item_icon.dmi'
+ icon = 'modular_citadel/icons/obj/clothing/trek_item_icon.dmi'
alternate_worn_icon = 'modular_citadel/icons/mob/clothing/trek_mob_icon.dmi'
icon_state = "fedmodern"
item_state = "fedmodern"
@@ -237,20 +239,20 @@
/obj/item/clothing/head/caphat/formal/fedcover
name = "Federation Officer's Cap"
desc = "An officer's cap that demands discipline from the one who wears it."
- icon = 'modular_citadel/icons/mob/clothing/trek_item_icon.dmi'
+ icon = 'modular_citadel/icons/obj/clothing/trek_item_icon.dmi'
icon_state = "fedcapofficer"
alternate_worn_icon = 'modular_citadel/icons/mob/clothing/trek_mob_icon.dmi'
- item_state = "fedcapofficer_mob"
+ item_state = "fedcapofficer"
//Variants
/obj/item/clothing/head/caphat/formal/fedcover/medsci
icon_state = "fedcapsci"
- item_state = "fedcapsci_mob"
+ item_state = "fedcapsci"
/obj/item/clothing/head/caphat/formal/fedcover/eng
icon_state = "fedcapeng"
- item_state = "fedcapeng_mob"
+ item_state = "fedcapeng"
/obj/item/clothing/head/caphat/formal/fedcover/sec
icon_state = "fedcapsec"
- item_state = "fedcapsec_mob"
+ item_state = "fedcapsec"
diff --git a/modular_citadel/code/modules/custom_loadout/custom_items.dm b/modular_citadel/code/modules/custom_loadout/custom_items.dm
index 8e26f8014f..5f0d8cfc00 100644
--- a/modular_citadel/code/modules/custom_loadout/custom_items.dm
+++ b/modular_citadel/code/modules/custom_loadout/custom_items.dm
@@ -420,3 +420,22 @@
item_state = "Divine_robes"
icon_state = "Divine_robes"
mutantrace_variation = NO_MUTANTRACE_VARIATION
+
+/obj/item/clothing/under/leoskimpy
+ name = "Leon's Skimpy Outfit"
+ icon = 'icons/obj/custom.dmi'
+ alternate_worn_icon = 'icons/mob/custom_w.dmi'
+ desc = "A rather skimpy outfit."
+ item_state = "shark_cloth"
+ icon_state = "shark_cloth"
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
+
+/obj/item/clothing/under/mimeoveralls
+ name = "Mime's Overalls"
+ icon = 'icons/obj/custom.dmi'
+ alternate_worn_icon = 'icons/mob/custom_w.dmi'
+ desc = "A less-than-traditional mime's attire, completed by a set of dorky-looking overalls."
+ item_state = "moveralls"
+ icon_state = "moveralls"
+ mutantrace_variation = NO_MUTANTRACE_VARIATION
+
diff --git a/modular_citadel/code/modules/mob/cit_emotes.dm b/modular_citadel/code/modules/mob/cit_emotes.dm
index b80425e59a..34c44be23f 100644
--- a/modular_citadel/code/modules/mob/cit_emotes.dm
+++ b/modular_citadel/code/modules/mob/cit_emotes.dm
@@ -161,6 +161,20 @@
playsound(user, 'modular_citadel/sound/voice/weh.ogg', 50, 1, -1)
. = ..()
+/datum/emote/living/peep
+ key = "peep"
+ key_third_person = "peeps like a bird"
+ message = "peeps like a bird!"
+ emote_type = EMOTE_AUDIBLE
+
+/datum/emote/living/peep/run_emote(mob/living/user, params)
+ if(ishuman(user))
+ if(user.nextsoundemote >= world.time)
+ return
+ user.nextsoundemote = world.time + 7
+ playsound(user, 'modular_citadel/sound/voice/peep.ogg', 50, 1, -1)
+ . = ..()
+
/datum/emote/living/dab
key = "dab"
key_third_person = "suddenly hits a dab"
@@ -180,4 +194,19 @@
user.apply_damage(20, BRUTE, def_zone)
if(luck >= 95)
user.adjustBrainLoss(100)
- . = ..()
\ No newline at end of file
+ . = ..()
+
+
+/datum/emote/living/mothsqueak
+ key = "msqueak"
+ key_third_person = "lets out a tiny squeak"
+ message = "lets out a tiny squeak!"
+ emote_type = EMOTE_AUDIBLE
+ mob_type_allowed_typecache = list(/mob/living/carbon)
+
+/datum/emote/living/mothsqueak/run_emote(mob/living/user, params)
+ if(user.nextsoundemote >= world.time)
+ return
+ user.nextsoundemote = world.time + 7
+ playsound(user, 'modular_citadel/sound/voice/mothsqueak.ogg', 50, 1, -1)
+ . = ..()
diff --git a/modular_citadel/code/modules/mob/living/carbon/human/species.dm b/modular_citadel/code/modules/mob/living/carbon/human/species.dm
index 8d50bf16b0..232725e320 100644
--- a/modular_citadel/code/modules/mob/living/carbon/human/species.dm
+++ b/modular_citadel/code/modules/mob/living/carbon/human/species.dm
@@ -13,11 +13,30 @@
H.visible_message("[M] attempted to touch [H]!")
return TRUE
switch(M.a_intent)
- if("disarm")
+ if(INTENT_HELP)
+ if(M == H)
+ althelp(M, H, attacker_style)
+ return TRUE
+ return FALSE
+ if(INTENT_DISARM)
altdisarm(M, H, attacker_style)
return TRUE
return FALSE
+/datum/species/proc/althelp(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
+ if(user == target && istype(user))
+ if(user.getStaminaLoss() >= STAMINA_SOFTCRIT)
+ to_chat(user, "You're too exhausted for that.")
+ return
+ if(!user.resting)
+ to_chat(user, "You can only force yourself up if you're on the ground.")
+ return
+ user.visible_message("[user] forces [p_them()]self up to [p_their()] feet!", "You force yourself up to your feet!")
+ user.resting = 0
+ user.update_canmove()
+ user.adjustStaminaLossBuffered(user.stambuffer) //Rewards good stamina management by making it easier to instantly get up from resting
+ playsound(user, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
+
/datum/species/proc/altdisarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
if(user.getStaminaLoss() >= STAMINA_SOFTCRIT)
to_chat(user, "You're too exhausted.")
diff --git a/modular_citadel/code/modules/mob/living/living.dm b/modular_citadel/code/modules/mob/living/living.dm
index 955263b7af..b07b8ed64b 100644
--- a/modular_citadel/code/modules/mob/living/living.dm
+++ b/modular_citadel/code/modules/mob/living/living.dm
@@ -102,9 +102,9 @@
return FALSE
/mob/living/carbon/update_stamina()
- var/total_health = (min(health*2,100) - getStaminaLoss())
- if(getStaminaLoss())
- if(!recoveringstam && total_health <= STAMINA_CRIT_TRADITIONAL && !stat)
+ var/total_health = getStaminaLoss()
+ if(total_health)
+ if(!recoveringstam && total_health >= STAMINA_CRIT && !stat)
to_chat(src, "You're too exhausted to keep going...")
resting = TRUE
if(combatmode)
@@ -112,7 +112,7 @@
recoveringstam = TRUE
filters += CIT_FILTER_STAMINACRIT
update_canmove()
- if(recoveringstam && total_health >= STAMINA_SOFTCRIT_TRADITIONAL)
+ if(recoveringstam && total_health <= STAMINA_SOFTCRIT)
to_chat(src, "You don't feel nearly as exhausted anymore.")
recoveringstam = FALSE
filters -= CIT_FILTER_STAMINACRIT
diff --git a/modular_citadel/code/modules/mob/living/simple_animal/banana_spider.dm b/modular_citadel/code/modules/mob/living/simple_animal/banana_spider.dm
index 80e8234914..d2006cd12c 100644
--- a/modular_citadel/code/modules/mob/living/simple_animal/banana_spider.dm
+++ b/modular_citadel/code/modules/mob/living/simple_animal/banana_spider.dm
@@ -38,7 +38,7 @@
return
to_chat(user, "You decide to wake up the banana spider...")
awakening = 1
-
+
spawn(30)
if(!QDELETED(src))
var/mob/living/simple_animal/banana_spider/S = new /mob/living/simple_animal/banana_spider(get_turf(src.loc))
@@ -55,7 +55,8 @@
icon_dead = "banana_peel"
health = 1
maxHealth = 1
- turns_per_move = 5
+ turns_per_move = 5 //this isn't player speed =|
+ speed = 2 //this is player speed
loot = list(/obj/item/reagent_containers/food/snacks/deadbanana_spider)
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 270
@@ -67,7 +68,7 @@
response_harm = "splats"
speak_emote = list("chitters")
mouse_opacity = 2
- density = FALSE
+ density = TRUE
ventcrawler = VENTCRAWLER_ALWAYS
gold_core_spawnable = FRIENDLY_SPAWN
verb_say = "chitters"
@@ -75,19 +76,17 @@
verb_exclaim = "chitters loudly"
verb_yell = "chitters loudly"
var/squish_chance = 50
- del_on_death = 1
-
+ var/projectile_density = TRUE //griffons get shot
+ del_on_death = TRUE
/mob/living/simple_animal/banana_spider/Initialize()
. = ..()
var/area/A = get_area(src)
if(A)
notify_ghosts("A banana spider has been created in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE)
- GLOB.poi_list |= src
-
/mob/living/simple_animal/banana_spider/attack_ghost(mob/user)
- if(src.key)
+ if(key) //please stop using src. without a good reason.
return
if(CONFIG_GET(flag/use_age_restriction_for_jobs))
if(!isnum(user.client.player_age))
@@ -98,17 +97,19 @@
var/be_spider = alert("Become a banana spider? (Warning, You can no longer be cloned!)",,"Yes","No")
if(be_spider == "No" || QDELETED(src) || !isobserver(user))
return
- src.sentience_act()
- src.key = user.key
+ sentience_act()
+ key = user.key
density = TRUE
/mob/living/simple_animal/banana_spider/ComponentInitialize()
. = ..()
- AddComponent(/datum/component/slippery, 80)
+ AddComponent(/datum/component/slippery, 40)
-
-/mob/living/simple_animal/banana_spider/Crossed(var/atom/movable/AM)
+/mob/living/simple_animal/banana_spider/Crossed(atom/movable/AM) //no /var in proc headers
. = ..()
+ if(istype(AM, /obj/item/projectile) && projectile_density) //forced projectile density
+ var/obj/item/projectile/P = AM
+ P.Bump(src)
if(ismob(AM))
if(isliving(AM))
var/mob/living/A = AM
@@ -147,4 +148,4 @@
/obj/item/reagent_containers/food/snacks/deadbanana_spider/Initialize()
. = ..()
- AddComponent(/datum/component/slippery, 80)
+ AddComponent(/datum/component/slippery, 20)
diff --git a/modular_citadel/icons/effects/souldeath.dmi b/modular_citadel/icons/effects/souldeath.dmi
new file mode 100644
index 0000000000..f225bd5321
Binary files /dev/null and b/modular_citadel/icons/effects/souldeath.dmi differ
diff --git a/modular_citadel/icons/mob/clothing/necks.dmi b/modular_citadel/icons/mob/clothing/necks.dmi
new file mode 100644
index 0000000000..65198bc64c
Binary files /dev/null and b/modular_citadel/icons/mob/clothing/necks.dmi differ
diff --git a/modular_citadel/icons/mob/clothing/trek_mob_icon.dmi b/modular_citadel/icons/mob/clothing/trek_mob_icon.dmi
index 9323ea9f3c..45045b0580 100644
Binary files a/modular_citadel/icons/mob/clothing/trek_mob_icon.dmi and b/modular_citadel/icons/mob/clothing/trek_mob_icon.dmi differ
diff --git a/modular_citadel/icons/obj/clothing/cit_neck.dmi b/modular_citadel/icons/obj/clothing/cit_neck.dmi
index ce115c2aa7..abccd4a115 100644
Binary files a/modular_citadel/icons/obj/clothing/cit_neck.dmi and b/modular_citadel/icons/obj/clothing/cit_neck.dmi differ
diff --git a/modular_citadel/icons/obj/clothing/trek_item_icon.dmi b/modular_citadel/icons/obj/clothing/trek_item_icon.dmi
new file mode 100644
index 0000000000..4ac77773a0
Binary files /dev/null and b/modular_citadel/icons/obj/clothing/trek_item_icon.dmi differ
diff --git a/modular_citadel/sound/misc/souldeath.ogg b/modular_citadel/sound/misc/souldeath.ogg
new file mode 100644
index 0000000000..98a19c0765
Binary files /dev/null and b/modular_citadel/sound/misc/souldeath.ogg differ
diff --git a/modular_citadel/sound/voice/mothsqueak.ogg b/modular_citadel/sound/voice/mothsqueak.ogg
new file mode 100644
index 0000000000..c5ac979aed
Binary files /dev/null and b/modular_citadel/sound/voice/mothsqueak.ogg differ
diff --git a/modular_citadel/sound/voice/peep.ogg b/modular_citadel/sound/voice/peep.ogg
new file mode 100644
index 0000000000..b687300677
Binary files /dev/null and b/modular_citadel/sound/voice/peep.ogg differ
diff --git a/sound/effects/clang1.ogg b/sound/effects/clang1.ogg
new file mode 100644
index 0000000000..eef118e4d4
Binary files /dev/null and b/sound/effects/clang1.ogg differ
diff --git a/sound/effects/clang2.ogg b/sound/effects/clang2.ogg
new file mode 100644
index 0000000000..175fe98fd1
Binary files /dev/null and b/sound/effects/clang2.ogg differ
diff --git a/sound/effects/clangsmall1.ogg b/sound/effects/clangsmall1.ogg
new file mode 100644
index 0000000000..7824db66cf
Binary files /dev/null and b/sound/effects/clangsmall1.ogg differ
diff --git a/sound/effects/clangsmall2.ogg b/sound/effects/clangsmall2.ogg
new file mode 100644
index 0000000000..3211e66b75
Binary files /dev/null and b/sound/effects/clangsmall2.ogg differ
diff --git a/sound/weapons/gagging.ogg b/sound/weapons/gagging.ogg
new file mode 100644
index 0000000000..0b0a3783f4
Binary files /dev/null and b/sound/weapons/gagging.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 81a20ecee9..268d4f1ff9 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2772,6 +2772,7 @@
#include "modular_citadel\code\datums\uplink_items_cit.dm"
#include "modular_citadel\code\datums\components\material_container.dm"
#include "modular_citadel\code\datums\components\phantomthief.dm"
+#include "modular_citadel\code\datums\components\souldeath.dm"
#include "modular_citadel\code\datums\mood_events\generic_negative_events.dm"
#include "modular_citadel\code\datums\mood_events\generic_positive_events.dm"
#include "modular_citadel\code\datums\mood_events\moodular.dm"
@@ -2798,6 +2799,7 @@
#include "modular_citadel\code\game\objects\items.dm"
#include "modular_citadel\code\game\objects\tools.dm"
#include "modular_citadel\code\game\objects\effects\spawner\spawners.dm"
+#include "modular_citadel\code\game\objects\effects\temporary_visuals\souldeath.dm"
#include "modular_citadel\code\game\objects\effects\temporary_visuals\projectiles\impact.dm"
#include "modular_citadel\code\game\objects\effects\temporary_visuals\projectiles\muzzle.dm"
#include "modular_citadel\code\game\objects\effects\temporary_visuals\projectiles\tracer.dm"
@@ -2834,7 +2836,6 @@
#include "modular_citadel\code\modules\admin\holder2.dm"
#include "modular_citadel\code\modules\admin\secrets.dm"
#include "modular_citadel\code\modules\admin\topic.dm"
-#include "modular_citadel\code\modules\antagonists\cult\blood_magic.dm"
#include "modular_citadel\code\modules\arousal\arousal.dm"
#include "modular_citadel\code\modules\arousal\organs\breasts.dm"
#include "modular_citadel\code\modules\arousal\organs\eggsack.dm"
@@ -2871,6 +2872,7 @@
#include "modular_citadel\code\modules\client\loadout\uniform.dm"
#include "modular_citadel\code\modules\client\verbs\who.dm"
#include "modular_citadel\code\modules\clothing\clothing.dm"
+#include "modular_citadel\code\modules\clothing\neck.dm"
#include "modular_citadel\code\modules\clothing\glasses\phantomthief.dm"
#include "modular_citadel\code\modules\clothing\head\head.dm"
#include "modular_citadel\code\modules\clothing\spacesuits\cydonian_armor.dm"