diff --git a/_maps/deathmatch/arena_station.dmm b/_maps/deathmatch/arena_station.dmm
index c7654810360e1..50089df45e8a0 100644
--- a/_maps/deathmatch/arena_station.dmm
+++ b/_maps/deathmatch/arena_station.dmm
@@ -930,7 +930,7 @@
/area/deathmatch)
"LY" = (
/obj/structure/closet/secure_closet,
-/obj/item/gun/energy/beam_rifle,
+/obj/item/gun/energy/xray,
/turf/open/indestructible/vault,
/area/deathmatch)
"Mc" = (
diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm
index e26e63400333f..ef02944be5751 100644
--- a/_maps/map_files/Birdshot/birdshot.dmm
+++ b/_maps/map_files/Birdshot/birdshot.dmm
@@ -12780,6 +12780,9 @@
/area/station/security/checkpoint/science)
"fnm" = (
/obj/structure/filingcabinet/chestdrawer,
+/obj/item/book/manual/wiki/engineering_guide{
+ pixel_y = 6
+ },
/turf/open/floor/iron/grimy,
/area/station/engineering/main)
"fnw" = (
@@ -17534,10 +17537,6 @@
/obj/structure/table/greyscale,
/obj/item/clothing/gloves/color/yellow,
/obj/item/wrench,
-/obj/item/multitool{
- pixel_x = 4;
- pixel_y = 5
- },
/turf/open/floor/iron/grimy,
/area/station/engineering/main)
"gUC" = (
@@ -29651,8 +29650,14 @@
pixel_y = 4
},
/obj/machinery/light/small/directional/south,
-/obj/item/book/manual/wiki/engineering_guide{
- pixel_y = 4
+/obj/item/flatpack{
+ board = /obj/item/circuitboard/machine/flatpacker;
+ pixel_x = -6;
+ pixel_y = 5
+ },
+/obj/item/multitool{
+ pixel_x = 7;
+ pixel_y = -2
},
/turf/open/floor/iron/grimy,
/area/station/engineering/main)
@@ -45117,7 +45122,7 @@
/obj/structure/rack,
/obj/effect/spawner/random/armory/riot_helmet,
/obj/effect/spawner/random/armory/bulletproof_helmet,
-/obj/item/gun/energy/e_gun/dragnet,
+/obj/effect/spawner/random/armory/dragnet,
/turf/open/floor/iron/dark/small,
/area/station/ai_monitored/security/armory)
"qaV" = (
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index 57ff65acf22af..9f1a266b18575 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -882,11 +882,8 @@
pixel_x = 3
},
/obj/structure/window/reinforced/spawner/directional/north,
-/obj/item/gun/energy/e_gun/dragnet{
- pixel_y = 4
- },
-/obj/item/gun/energy/e_gun/dragnet,
/obj/effect/turf_decal/tile/neutral/fourcorners,
+/obj/effect/spawner/random/armory/dragnet,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/security/armory)
"alG" = (
@@ -78577,6 +78574,15 @@
/obj/structure/cable,
/obj/machinery/firealarm/directional/east,
/obj/effect/decal/cleanable/dirt,
+/obj/item/flatpack{
+ board = /obj/item/circuitboard/machine/flatpacker;
+ pixel_x = -6;
+ pixel_y = 5
+ },
+/obj/item/multitool{
+ pixel_x = 8
+ },
+/obj/structure/table,
/turf/open/floor/iron,
/area/station/engineering/storage_shared)
"tFG" = (
diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
index 984cf0ca1298e..9e5ecd8c0486f 100644
--- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm
+++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
@@ -3845,7 +3845,7 @@
"bil" = (
/obj/structure/railing/wooden_fence,
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"bin" = (
/obj/effect/turf_decal/stripes/asteroid/line{
dir = 4
@@ -6355,7 +6355,7 @@
dir = 1
},
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"bPV" = (
/obj/item/kirbyplants/random/dead,
/turf/open/floor/plating/snowed/icemoon,
@@ -11342,7 +11342,7 @@
/area/icemoon/surface/outdoors/nospawn)
"dlu" = (
/turf/closed/wall/mineral/wood/nonmetal,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"dlB" = (
/obj/structure/table/wood,
/obj/item/storage/photo_album/chapel,
@@ -20638,7 +20638,7 @@
dir = 5
},
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"ghE" = (
/obj/structure/disposalpipe/segment,
/obj/machinery/camera/directional/west{
@@ -23048,7 +23048,7 @@
/obj/effect/turf_decal/stripes/line{
dir = 1
},
-/obj/effect/mapping_helpers/airlock/access/all/medical/chemistry,
+/obj/effect/mapping_helpers/airlock/access/all/medical/general,
/obj/effect/turf_decal/tile/yellow/full,
/turf/open/floor/iron/large,
/area/station/medical/treatment_center)
@@ -26929,7 +26929,7 @@
dir = 6
},
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"idN" = (
/obj/structure/window/reinforced/spawner/directional/north,
/obj/machinery/door/window/brigdoor/left/directional/south{
@@ -30518,15 +30518,12 @@
"jko" = (
/obj/structure/railing,
/obj/structure/rack,
-/obj/item/gun/energy/e_gun/dragnet{
- pixel_y = 4
- },
-/obj/item/gun/energy/e_gun/dragnet,
/obj/structure/cable,
/obj/machinery/door/firedoor/border_only,
/obj/effect/turf_decal/tile/red/half/contrasted{
dir = 1
},
+/obj/effect/spawner/random/armory/dragnet,
/turf/open/floor/iron/dark/textured,
/area/station/ai_monitored/security/armory/upper)
"jkx" = (
@@ -30551,7 +30548,7 @@
dir = 9
},
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"jkN" = (
/obj/effect/spawner/random/entertainment/arcade,
/obj/machinery/status_display/ai/directional/north,
@@ -36851,7 +36848,7 @@
/obj/structure/table/wood,
/obj/item/flashlight/lantern/on,
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"kZa" = (
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -40539,7 +40536,7 @@
dir = 10
},
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"mhq" = (
/obj/structure/closet,
/obj/effect/spawner/random/maintenance,
@@ -43322,7 +43319,7 @@
},
/obj/item/soap/deluxe,
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"ncB" = (
/obj/machinery/door/airlock/security/glass{
name = "Brig Walkway"
@@ -45443,7 +45440,7 @@
"nEI" = (
/obj/item/flashlight/lantern/on,
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"nEV" = (
/obj/machinery/vending/wardrobe/sec_wardrobe,
/obj/structure/cable,
@@ -47295,7 +47292,7 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/obj/effect/mapping_helpers/airlock/access/all/medical/chemistry,
+/obj/effect/mapping_helpers/airlock/access/all/medical/general,
/turf/open/floor/iron/smooth,
/area/station/maintenance/department/medical/central)
"oiD" = (
@@ -53710,7 +53707,7 @@
"qbM" = (
/obj/structure/ore_container/food_trough/raptor_trough,
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"qbO" = (
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -58945,7 +58942,7 @@
},
/obj/item/raptor_dex,
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"rAr" = (
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -59334,7 +59331,7 @@
dir = 4
},
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"rEp" = (
/obj/structure/table,
/obj/item/hand_labeler,
@@ -74888,8 +74885,13 @@
/obj/effect/turf_decal/siding/yellow/corner,
/obj/machinery/status_display/evac/directional/south,
/obj/structure/table,
-/obj/effect/spawner/random/trash/food_packaging,
-/obj/effect/spawner/random/trash/cigbutt,
+/obj/item/flatpack{
+ board = /obj/item/circuitboard/machine/flatpacker;
+ pixel_x = -5
+ },
+/obj/item/multitool{
+ pixel_x = 8
+ },
/turf/open/floor/iron,
/area/station/engineering/lobby)
"wve" = (
@@ -74902,7 +74904,7 @@
/area/station/service/chapel)
"wvu" = (
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"wvv" = (
/obj/structure/disposalpipe/segment,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
@@ -78456,7 +78458,7 @@
dir = 8
},
/turf/open/misc/hay/icemoon,
-/area/icemoon/surface)
+/area/icemoon/underground/explored)
"xxI" = (
/obj/machinery/airalarm/directional/north,
/obj/item/kirbyplants/random,
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index dc42fb3fe9ce6..6cd01a35c9daf 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -9215,6 +9215,9 @@
dir = 4
},
/obj/machinery/light/small/directional/north,
+/obj/item/lightreplacer{
+ pixel_y = 7
+ },
/turf/open/floor/iron/dark/corner{
dir = 1
},
@@ -32127,12 +32130,11 @@
/area/station/maintenance/fore)
"loY" = (
/obj/structure/rack,
-/obj/item/gun/energy/e_gun/dragnet,
-/obj/item/gun/energy/e_gun/dragnet,
/obj/effect/turf_decal/tile/blue/half/contrasted{
dir = 4
},
/obj/machinery/light/small/directional/west,
+/obj/effect/spawner/random/armory/dragnet,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/security/armory)
"lpo" = (
@@ -58915,13 +58917,17 @@
"uJz" = (
/obj/effect/turf_decal/bot,
/obj/structure/rack,
-/obj/item/lightreplacer{
- pixel_y = 7
- },
/obj/machinery/status_display/evac/directional/east,
/obj/effect/turf_decal/tile/yellow{
dir = 4
},
+/obj/item/flatpack{
+ board = /obj/item/circuitboard/machine/flatpacker;
+ pixel_x = -5
+ },
+/obj/item/multitool{
+ pixel_x = 8
+ },
/turf/open/floor/iron/checker,
/area/station/engineering/storage_shared)
"uJB" = (
diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm
index 3c8cf0e7cb56c..7ce411f102fc1 100644
--- a/_maps/map_files/NorthStar/north_star.dmm
+++ b/_maps/map_files/NorthStar/north_star.dmm
@@ -53022,7 +53022,19 @@
/obj/effect/turf_decal/trimline/yellow/corner{
dir = 4
},
-/obj/item/storage/toolbox/mechanical,
+/obj/item/storage/toolbox/mechanical{
+ pixel_x = 8;
+ pixel_y = -4
+ },
+/obj/item/flatpack{
+ board = /obj/item/circuitboard/machine/flatpacker;
+ pixel_x = -6;
+ pixel_y = 5
+ },
+/obj/item/multitool{
+ pixel_x = 7;
+ pixel_y = 10
+ },
/turf/open/floor/iron/corner{
dir = 4
},
@@ -57656,7 +57668,7 @@
/area/station/science/lower)
"oOW" = (
/obj/structure/rack,
-/obj/item/gun/energy/e_gun/dragnet,
+/obj/effect/spawner/random/armory/dragnet,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/security/armory)
"oOY" = (
diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm
index b0c8d17c83315..a8b81413f9cb6 100644
--- a/_maps/map_files/tramstation/tramstation.dmm
+++ b/_maps/map_files/tramstation/tramstation.dmm
@@ -28696,10 +28696,9 @@
/area/station/cargo/storage)
"jlQ" = (
/obj/structure/rack,
-/obj/item/gun/energy/e_gun/dragnet,
-/obj/item/gun/energy/e_gun/dragnet,
/obj/item/radio/intercom/directional/north,
/obj/effect/turf_decal/tile/neutral/fourcorners,
+/obj/effect/spawner/random/armory/dragnet,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/security/armory)
"jlX" = (
@@ -49386,6 +49385,13 @@
},
/obj/structure/cable,
/obj/structure/table,
+/obj/item/multitool{
+ pixel_x = 8
+ },
+/obj/item/flatpack{
+ board = /obj/item/circuitboard/machine/flatpacker;
+ pixel_x = -5
+ },
/turf/open/floor/iron,
/area/station/engineering/break_room)
"qwq" = (
diff --git a/_maps/map_files/wawastation/wawastation.dmm b/_maps/map_files/wawastation/wawastation.dmm
index c02a4b1de4849..f06276d3e9fa2 100644
--- a/_maps/map_files/wawastation/wawastation.dmm
+++ b/_maps/map_files/wawastation/wawastation.dmm
@@ -6901,7 +6901,13 @@
/area/station/cargo/storage)
"cAy" = (
/obj/structure/table/glass,
-/obj/effect/spawner/random/food_or_drink/refreshing_beverage,
+/obj/item/flatpack{
+ board = /obj/item/circuitboard/machine/flatpacker;
+ pixel_x = -5
+ },
+/obj/item/multitool{
+ pixel_x = 8
+ },
/turf/open/floor/iron,
/area/station/engineering/lobby)
"cAC" = (
@@ -9208,7 +9214,7 @@
/area/station/medical/virology)
"dpf" = (
/obj/structure/transport/linear/public,
-/obj/machinery/light/floor,
+/obj/machinery/light/floor/transport,
/turf/open/floor/plating/elevatorshaft,
/area/station/medical/treatment_center)
"dpj" = (
@@ -9539,6 +9545,7 @@
"duB" = (
/obj/structure/cable,
/obj/structure/window/reinforced/spawner/directional/west,
+/obj/machinery/firealarm/directional/east,
/turf/open/floor/iron/dark,
/area/station/security/prison)
"duS" = (
@@ -13537,12 +13544,11 @@
/area/station/maintenance/department/medical)
"eQJ" = (
/obj/structure/rack,
-/obj/item/gun/energy/e_gun/dragnet,
-/obj/item/gun/energy/e_gun/dragnet,
/obj/effect/turf_decal/tile/red/half/contrasted{
dir = 1
},
/obj/item/radio/intercom/directional/south,
+/obj/effect/spawner/random/armory/dragnet,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/security/armory)
"eQQ" = (
@@ -16050,9 +16056,7 @@
/area/station/security/breakroom)
"fLM" = (
/obj/structure/transport/linear/public,
-/obj/machinery/light/floor{
- _status_traits = list("underfloor" = list("innate"))
- },
+/obj/machinery/light/floor/transport,
/turf/open/floor/plating/elevatorshaft,
/area/station/cargo/storage)
"fLU" = (
@@ -22386,10 +22390,6 @@
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/turf/open/floor/wood/tile,
/area/station/service/bar)
-"hWN" = (
-/obj/machinery/firealarm/directional/south,
-/turf/open/openspace,
-/area/station/security/prison)
"hWW" = (
/obj/structure/cable,
/obj/effect/landmark/start/atmospheric_technician,
@@ -35470,6 +35470,7 @@
/obj/structure/disposalpipe/segment{
dir = 4
},
+/obj/effect/mapping_helpers/airlock/access/any/engineering/construction,
/turf/open/floor/iron,
/area/station/engineering/lobby)
"mBY" = (
@@ -39893,6 +39894,7 @@
"omE" = (
/obj/structure/table/glass,
/obj/effect/spawner/random/food_or_drink/snack,
+/obj/effect/spawner/random/food_or_drink/refreshing_beverage,
/turf/open/floor/iron,
/area/station/engineering/lobby)
"omL" = (
@@ -45703,7 +45705,7 @@
/obj/structure/cable,
/obj/structure/disposalpipe/segment,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
-/turf/open/floor/plating,
+/turf/open/floor/iron,
/area/station/commons/storage/primary)
"qmI" = (
/obj/effect/turf_decal/tile/purple/fourcorners,
@@ -46634,6 +46636,10 @@
},
/turf/open/floor/iron/dark,
/area/station/ai_monitored/turret_protected/aisat/teleporter)
+"qFS" = (
+/obj/structure/window/reinforced/spawner/directional/south,
+/turf/open/floor/glass/reinforced,
+/area/station/security/prison)
"qGk" = (
/turf/closed/wall/r_wall,
/area/station/hallway/secondary/command)
@@ -49256,7 +49262,6 @@
/turf/open/floor/iron/white,
/area/station/medical/treatment_center)
"ryG" = (
-/obj/effect/mapping_helpers/airlock/access/any/engineering/maintenance/departmental,
/obj/structure/cable,
/obj/machinery/door/airlock/engineering/glass{
name = "Break Room"
@@ -49265,6 +49270,8 @@
/obj/machinery/door/firedoor,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
+/obj/effect/mapping_helpers/airlock/access/any/engineering/general,
+/obj/effect/mapping_helpers/airlock/access/any/engineering/construction,
/turf/open/floor/iron,
/area/station/engineering/break_room)
"ryP" = (
@@ -59706,7 +59713,7 @@
/area/station/medical/virology)
"vay" = (
/obj/structure/table/reinforced,
-/obj/item/storage/medkit,
+/obj/item/storage/medkit/regular,
/obj/effect/turf_decal/tile/dark_blue/half/contrasted{
dir = 1
},
@@ -60985,13 +60992,6 @@
/obj/effect/turf_decal/box,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/turret_protected/aisat/foyer)
-"vBA" = (
-/obj/machinery/light/floor{
- _status_traits = list("underfloor" = list("innate"))
- },
-/obj/structure/transport/linear/public,
-/turf/open/floor/plating/elevatorshaft,
-/area/station/cargo/storage)
"vBD" = (
/obj/structure/chair/office/light{
dir = 1
@@ -61598,6 +61598,7 @@
/obj/machinery/door/firedoor,
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
+/obj/effect/mapping_helpers/airlock/access/any/engineering/construction,
/turf/open/floor/iron,
/area/station/engineering/break_room)
"vMN" = (
@@ -61856,7 +61857,7 @@
"vQf" = (
/obj/machinery/suit_storage_unit/standard_unit,
/obj/machinery/door/window/brigdoor/right/directional/east{
- req_access = list("bridge")
+ req_access = list("command")
},
/obj/effect/turf_decal/bot,
/obj/machinery/light/small/dim/directional/west,
@@ -65281,6 +65282,10 @@
/obj/structure/rack,
/turf/open/floor/plating,
/area/station/asteroid)
+"xbr" = (
+/obj/structure/window/reinforced/spawner/directional/west,
+/turf/open/floor/glass/reinforced,
+/area/station/security/prison)
"xbs" = (
/obj/machinery/airalarm/directional/south,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
@@ -68155,7 +68160,7 @@
name = "Primary Tool Storage"
},
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
-/turf/open/floor/plating,
+/turf/open/floor/iron,
/area/station/commons/storage/primary)
"ydC" = (
/obj/effect/decal/cleanable/dirt/dust,
@@ -82681,8 +82686,8 @@ dgR
uPg
bui
yit
-yit
dpf
+yit
dKw
miD
dFY
@@ -83666,7 +83671,7 @@ uDB
uDB
uDB
sdc
-vBA
+fLM
soZ
jCX
jCX
@@ -151220,9 +151225,9 @@ hHw
uRG
oyP
dAq
+xbr
+qFS
wSc
-rwW
-hWN
jOV
vxX
vxX
diff --git a/_maps/virtual_domains/xeno_nest.dmm b/_maps/virtual_domains/xeno_nest.dmm
index 907436758c781..65f183d69c401 100644
--- a/_maps/virtual_domains/xeno_nest.dmm
+++ b/_maps/virtual_domains/xeno_nest.dmm
@@ -140,7 +140,7 @@
/area/ruin/space/has_grav/powered/virtual_domain)
"F" = (
/obj/structure/table/greyscale,
-/obj/item/gun/energy/beam_rifle,
+/obj/item/gun/energy/xray,
/obj/item/gun/energy/laser{
pixel_x = 4;
pixel_y = -6
diff --git a/code/__DEFINES/ai/pet_commands.dm b/code/__DEFINES/ai/pet_commands.dm
index 1e692b9f805aa..7404cb9acda84 100644
--- a/code/__DEFINES/ai/pet_commands.dm
+++ b/code/__DEFINES/ai/pet_commands.dm
@@ -7,3 +7,6 @@
#define BB_PET_TARGETING_STRATEGY "BB_pet_targeting"
/// Typecache of weakrefs to mobs this mob is friends with, will follow their instructions and won't attack them
#define BB_FRIENDS_LIST "BB_friends_list"
+
+///mothroach next meal key!
+#define BB_MOTHROACH_NEXT_EAT "mothroach_next_eat"
diff --git a/code/__DEFINES/fish.dm b/code/__DEFINES/fish.dm
index 24242699e3d01..8e03c920d248d 100644
--- a/code/__DEFINES/fish.dm
+++ b/code/__DEFINES/fish.dm
@@ -9,8 +9,8 @@
#define FAV_BAIT_DIFFICULTY_MOD -5
/// Difficulty modifier when bait is fish's disliked
#define DISLIKED_BAIT_DIFFICULTY_MOD 15
-/// Difficulty modifier when our fisherman has the trait TRAIT_SETTLER
-#define SETTLER_DIFFICULTY_MOD -5
+/// Difficulty modifier when our fisherman has the trait TRAIT_EXPERT_FISHER
+#define EXPERT_FISHER_DIFFICULTY_MOD -5
#define FISH_TRAIT_MINOR_DIFFICULTY_BOOST 5
diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm
index 2a2a092c6d05b..5f025ad99cffb 100644
--- a/code/__DEFINES/keybinding.dm
+++ b/code/__DEFINES/keybinding.dm
@@ -26,6 +26,7 @@
//Client
#define COMSIG_KB_CLIENT_GETHELP_DOWN "keybinding_client_gethelp_down"
#define COMSIG_KB_CLIENT_SCREENSHOT_DOWN "keybinding_client_screenshot_down"
+#define COMSIG_KB_CLIENT_FULLSCREEN_DOWN "keybinding_client_fullscreen_down"
#define COMSIG_KB_CLIENT_MINIMALHUD_DOWN "keybinding_client_minimalhud_down"
//Communication
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 6f6a9d209a10e..a0c40856dace1 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -448,6 +448,8 @@
#define FLASH_PROTECTION_NONE 0
#define FLASH_PROTECTION_FLASH 1
#define FLASH_PROTECTION_WELDER 2
+#define FLASH_PROTECTION_WELDER_PLUS 3
+#define FLASH_PROTECTION_MAXIMUM 4
// AI Toggles
#define AI_CAMERA_LUMINOSITY 5
diff --git a/code/__DEFINES/research/anomalies.dm b/code/__DEFINES/research/anomalies.dm
index db605431c610c..a1e30cd142f00 100644
--- a/code/__DEFINES/research/anomalies.dm
+++ b/code/__DEFINES/research/anomalies.dm
@@ -2,7 +2,7 @@
#define MAX_CORES_BLUESPACE 3
#define MAX_CORES_GRAVITATIONAL 8
#define MAX_CORES_FLUX 8
-#define MAX_CORES_VORTEX 8
+#define MAX_CORES_VORTEX 1
#define MAX_CORES_PYRO 8
#define MAX_CORES_HALLUCINATION 8
#define MAX_CORES_BIOSCRAMBLER 8
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 8f1c58bc96684..c34e0f2925e9f 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -1151,6 +1151,14 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Does this item bypass ranged armor checks?
#define TRAIT_BYPASS_RANGED_ARMOR "bypass_ranged_armor"
+/// Traits given by settler, each with their own specific effects for cases where someone would have that trait, but not the other settler effects
+
+#define TRAIT_EXPERT_FISHER "expert_fisher" // fishing is easier
+#define TRAIT_ROUGHRIDER "roughrider" // you can improve speed on mounted animals with a good mood
+#define TRAIT_STUBBY_BODY "stubby_body" // you have a stubby body that lessens your agility
+#define TRAIT_BEAST_EMPATHY "beast_empathy" // you're good with animals, such as with taming them
+#define TRAIT_STURDY_FRAME "sturdy_frame" // you suffer much lesser effects from equipment that slows you down
+
/// This item cannot be selected for or used by a theft objective (Spies, Traitors, etc.)
#define TRAIT_ITEM_OBJECTIVE_BLOCKED "item_objective_blocked"
/// This trait lets you attach limbs to any player without surgery.
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 9175b86dc3589..66f32028c6b9c 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -138,6 +138,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_BALD" = TRAIT_BALD,
"TRAIT_BALLOON_SUTRA" = TRAIT_BALLOON_SUTRA,
"TRAIT_BATON_RESISTANCE" = TRAIT_BATON_RESISTANCE,
+ "TRAIT_BEAST_EMPATHY" = TRAIT_BEAST_EMPATHY,
"TRAIT_BEING_BLADE_SHIELDED" = TRAIT_BEING_BLADE_SHIELDED,
"TRAIT_BLOB_ALLY" = TRAIT_BLOB_ALLY,
"TRAIT_BLOCK_SHUTTLE_MOVEMENT" = TRAIT_BLOCK_SHUTTLE_MOVEMENT,
@@ -208,6 +209,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_EXAMINE_FISHING_SPOT" = TRAIT_EXAMINE_FISHING_SPOT,
"TRAIT_EXAMINE_FITNESS" = TRAIT_EXAMINE_FITNESS,
"TRAIT_EXPANDED_FOV" = TRAIT_EXPANDED_FOV,
+ "TRAIT_EXPERT_FISHER" = TRAIT_EXPERT_FISHER,
"TRAIT_EXTROVERT" = TRAIT_EXTROVERT,
"TRAIT_FAKEDEATH" = TRAIT_FAKEDEATH,
"TRAIT_FASTMED" = TRAIT_FASTMED,
@@ -414,6 +416,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_ROCK_METAMORPHIC" = TRAIT_ROCK_METAMORPHIC,
"TRAIT_ROCK_STONER" = TRAIT_ROCK_STONER,
"TRAIT_ROD_SUPLEX" = TRAIT_ROD_SUPLEX,
+ "TRAIT_ROUGHRIDER" = TRAIT_ROUGHRIDER,
"TRAIT_SABRAGE_PRO" = TRAIT_SABRAGE_PRO,
"TRAIT_SECURITY_HUD" = TRAIT_SECURITY_HUD,
"TRAIT_SEE_GLASS_COLORS" = TRAIT_SEE_GLASS_COLORS,
@@ -445,7 +448,9 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_STIMULATED" = TRAIT_STIMULATED,
"TRAIT_STRONG_GRABBER" = TRAIT_STRONG_GRABBER,
"TRAIT_STRONG_STOMACH" = TRAIT_STRONG_STOMACH,
+ "TRAIT_STUBBY_BODY" = TRAIT_STUBBY_BODY,
"TRAIT_STUNIMMUNE" = TRAIT_STUNIMMUNE,
+ "TRAIT_STURDY_FRAME" = TRAIT_STURDY_FRAME,
"TRAIT_SUCCUMB_OVERRIDE" = TRAIT_SUCCUMB_OVERRIDE,
"TRAIT_SUICIDED" = TRAIT_SUICIDED,
"TRAIT_SUPERMATTER_SOOTHER" = TRAIT_SUPERMATTER_SOOTHER,
diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm
index 150a10c108494..d709113d2b175 100644
--- a/code/_globalvars/traits/admin_tooling.dm
+++ b/code/_globalvars/traits/admin_tooling.dm
@@ -36,6 +36,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_BADTOUCH" = TRAIT_BADTOUCH,
"TRAIT_BALD" = TRAIT_BALD,
"TRAIT_BATON_RESISTANCE" = TRAIT_BATON_RESISTANCE,
+ "TRAIT_BEAST_EMPATHY" = TRAIT_BEAST_EMPATHY,
"TRAIT_BLOCK_SHUTTLE_MOVEMENT" = TRAIT_BLOCK_SHUTTLE_MOVEMENT,
"TRAIT_BLOOD_CLANS" = TRAIT_BLOOD_CLANS,
"TRAIT_BLOODSHOT_EYES" = TRAIT_BLOODSHOT_EYES,
@@ -72,6 +73,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_EMPATH" = TRAIT_EMPATH,
"TRAIT_EXAMINE_FITNESS" = TRAIT_EXAMINE_FITNESS,
"TRAIT_EXPANDED_FOV" = TRAIT_EXPANDED_FOV,
+ "TRAIT_EXPERT_FISHER" = TRAIT_EXPERT_FISHER,
"TRAIT_FAKEDEATH" = TRAIT_FAKEDEATH,
"TRAIT_FAST_CUFFING" = TRAIT_FAST_CUFFING,
"TRAIT_FAT" = TRAIT_FAT,
@@ -188,6 +190,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_RESISTHIGHPRESSURE" = TRAIT_RESISTHIGHPRESSURE,
"TRAIT_RESISTLOWPRESSURE" = TRAIT_RESISTLOWPRESSURE,
"TRAIT_RESTRAINED" = TRAIT_RESTRAINED,
+ "TRAIT_ROUGHRIDER" = TRAIT_ROUGHRIDER,
"TRAIT_SECURITY_HUD" = TRAIT_SECURITY_HUD,
"TRAIT_SELF_AWARE" = TRAIT_SELF_AWARE,
"TRAIT_SETTLER" = TRAIT_SETTLER,
@@ -208,7 +211,9 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_STABLELIVER" = TRAIT_STABLELIVER,
"TRAIT_STRONG_GRABBER" = TRAIT_STRONG_GRABBER,
"TRAIT_STRONG_STOMACH" = TRAIT_STRONG_STOMACH,
+ "TRAIT_STUBBY_BODY" = TRAIT_STUBBY_BODY,
"TRAIT_STUNIMMUNE" = TRAIT_STUNIMMUNE,
+ "TRAIT_STURDY_FRAME" = TRAIT_STURDY_FRAME,
"TRAIT_SURGEON" = TRAIT_SURGEON,
"TRAIT_SURGICALLY_ANALYZED" = TRAIT_SURGICALLY_ANALYZED,
"TRAIT_TAGGER" = TRAIT_TAGGER,
diff --git a/code/datums/components/crafting/guncrafting.dm b/code/datums/components/crafting/guncrafting.dm
index 6d4f4713f8bb1..dcf42ee47b1a5 100644
--- a/code/datums/components/crafting/guncrafting.dm
+++ b/code/datums/components/crafting/guncrafting.dm
@@ -84,8 +84,8 @@
desc = "A suitcase containing the necessary gun parts to tranform a standard energy gun into a temperature gun. Fantastic at birthday parties and killing indigenious populations of lizardpeople."
/obj/item/weaponcrafting/gunkit/beam_rifle
- name = "particle acceleration rifle part kit (lethal)"
- desc = "The coup de grace of guncrafting. This suitcase contains the highly experimental rig for a particle acceleration rifle. Requires an energy gun, a stabilized flux anomaly and a stabilized gravity anomaly."
+ name = "\improper Event Horizon anti-existential beam rifle part kit (DOOMSDAY DEVICE, DO NOT CONSTRUCT)"
+ desc = "What fevered minds wrought this terrible construction kit? To create a frame to harness the strange energies that flow through the Spinward Sector towards such horrible acts of violence?"
/obj/item/weaponcrafting/gunkit/ebow
name = "energy crossbow part kit (less lethal)"
diff --git a/code/datums/components/crafting/ranged_weapon.dm b/code/datums/components/crafting/ranged_weapon.dm
index b646c4472ed98..666e84964c553 100644
--- a/code/datums/components/crafting/ranged_weapon.dm
+++ b/code/datums/components/crafting/ranged_weapon.dm
@@ -72,21 +72,18 @@
blacklist += subtypesof(/obj/item/gun/energy/e_gun)
/datum/crafting_recipe/beam_rifle
- name = "Particle Acceleration Rifle"
- result = /obj/item/gun/energy/beam_rifle
+ name = "Event Horizon Anti-Existential Beam Rifle"
+ result = /obj/item/gun/energy/event_horizon
reqs = list(
- /obj/item/gun/energy/e_gun = 1,
- /obj/item/assembly/signaler/anomaly/flux = 1,
+ /obj/item/assembly/signaler/anomaly/flux = 2,
/obj/item/assembly/signaler/anomaly/grav = 1,
+ /obj/item/assembly/signaler/anomaly/vortex = MAX_CORES_VORTEX,
+ /obj/item/assembly/signaler/anomaly/bluespace = 1,
/obj/item/weaponcrafting/gunkit/beam_rifle = 1,
)
- time = 10 SECONDS
+ time = 30 SECONDS //Maybe the delay will make you reconsider your choices
category = CAT_WEAPON_RANGED
-/datum/crafting_recipe/beam_rifle/New()
- ..()
- blacklist += subtypesof(/obj/item/gun/energy/e_gun)
-
/datum/crafting_recipe/ebow
name = "Energy Crossbow"
result = /obj/item/gun/energy/recharge/ebow/large
diff --git a/code/datums/components/riding/riding_mob.dm b/code/datums/components/riding/riding_mob.dm
index a808784b815ec..78158c55e8eb7 100644
--- a/code/datums/components/riding/riding_mob.dm
+++ b/code/datums/components/riding/riding_mob.dm
@@ -110,9 +110,14 @@
last_move_diagonal = ((direction & (direction - 1)) && (living_parent.loc == next))
var/modified_move_cooldown = vehicle_move_cooldown
var/modified_move_delay = vehicle_move_delay
- if(ishuman(user) && HAS_TRAIT(user, TRAIT_SETTLER))
- var/mob/living/carbon/human/settler_rider = user
- switch(settler_rider.mob_mood.sanity_level)
+ if(ishuman(user) && HAS_TRAIT(user, TRAIT_ROUGHRIDER)) // YEEHAW!
+ var/mob/living/carbon/human/rough_rider = user
+ var/ride_benefit = null
+ if(HAS_TRAIT(rough_rider, TRAIT_PRIMITIVE)) // closer to a beast than a man; you don't need to think to ride!
+ ride_benefit = SANITY_LEVEL_GREAT
+ else
+ ride_benefit = rough_rider.mob_mood.sanity_level
+ switch(ride_benefit)
if(SANITY_LEVEL_GREAT)
modified_move_cooldown *= 0.5
modified_move_delay *= 0.5
diff --git a/code/datums/components/tameable.dm b/code/datums/components/tameable.dm
index 43f48005bf89e..0d77688a22e7a 100644
--- a/code/datums/components/tameable.dm
+++ b/code/datums/components/tameable.dm
@@ -42,7 +42,7 @@
var/inform_tamer = FALSE
atom_parent.balloon_alert(attacker, "fed")
var/modified_tame_chance = current_tame_chance
- if(HAS_TRAIT(attacker, TRAIT_SETTLER))
+ if(HAS_TRAIT(attacker, TRAIT_BEAST_EMPATHY))
modified_tame_chance += 50
inform_tamer = TRUE
if(unique || !already_friends(attacker))
@@ -71,7 +71,7 @@
if(inform_tamer)
source.balloon_alert(tamer, "tamed")
- if(HAS_TRAIT(tamer, TRAIT_SETTLER))
+ if(HAS_TRAIT(tamer, TRAIT_BEAST_EMPATHY))
INVOKE_ASYNC(src, PROC_REF(rename_pet), source, tamer)
if(unique)
qdel(src)
diff --git a/code/datums/elements/basic_eating.dm b/code/datums/elements/basic_eating.dm
index 92b303c9be2a0..4f4f493e0ef33 100644
--- a/code/datums/elements/basic_eating.dm
+++ b/code/datums/elements/basic_eating.dm
@@ -93,4 +93,5 @@
if(isstack(target)) //if stack, only consume 1
var/obj/item/stack/food_stack = target
final_target = food_stack.split_stack(eater, 1)
+ eater.log_message("has eaten [target]!", LOG_ATTACK)
qdel(final_target)
diff --git a/code/datums/elements/climbable.dm b/code/datums/elements/climbable.dm
index 533facf709a92..a2c67742a357e 100644
--- a/code/datums/elements/climbable.dm
+++ b/code/datums/elements/climbable.dm
@@ -69,7 +69,7 @@
if(HAS_TRAIT(user, TRAIT_FREERUNNING)) //do you have any idea how fast I am???
adjusted_climb_time *= 0.8
adjusted_climb_stun *= 0.8
- if(HAS_TRAIT(user, TRAIT_SETTLER)) //hold on, gimme a moment, my tiny legs can't get over the goshdamn table
+ if(HAS_TRAIT(user, TRAIT_STUBBY_BODY)) //hold on, gimme a moment, my tiny legs can't get over the goshdamn table
adjusted_climb_time *= 1.5
adjusted_climb_stun *= 1.5
LAZYADDASSOCLIST(current_climbers, climbed_thing, user)
diff --git a/code/datums/id_trim/jobs.dm b/code/datums/id_trim/jobs.dm
index cfa608011bdf9..4d4c44138e0c2 100644
--- a/code/datums/id_trim/jobs.dm
+++ b/code/datums/id_trim/jobs.dm
@@ -545,11 +545,13 @@
ACCESS_MORGUE,
ACCESS_RESEARCH,
ACCESS_SCIENCE,
+ ACCESS_XENOBIOLOGY,
)
extra_access = list(
ACCESS_ROBOTICS,
ACCESS_TECH_STORAGE,
- ACCESS_XENOBIOLOGY,
+ ACCESS_ORDNANCE,
+ ACCESS_ORDNANCE_STORAGE,
)
template_access = list(
ACCESS_CAPTAIN,
@@ -960,6 +962,8 @@
ACCESS_GENETICS,
ACCESS_XENOBIOLOGY,
ACCESS_MORGUE_SECURE,
+ ACCESS_ORDNANCE,
+ ACCESS_ORDNANCE_STORAGE,
)
template_access = list(
ACCESS_CAPTAIN,
diff --git a/code/datums/keybinding/client.dm b/code/datums/keybinding/client.dm
index 81b9bb6c287a3..f36645692c992 100644
--- a/code/datums/keybinding/client.dm
+++ b/code/datums/keybinding/client.dm
@@ -32,6 +32,20 @@
winset(user, null, "command=.auto")
return TRUE
+/datum/keybinding/client/toggle_fullscreen
+ hotkey_keys = list("F11")
+ name = "toggle_fullscreen"
+ full_name = "Toggle Fullscreen"
+ description = "Makes the game window fullscreen."
+ keybind_signal = COMSIG_KB_CLIENT_FULLSCREEN_DOWN
+
+/datum/keybinding/client/toggle_fullscreen/down(client/user)
+ . = ..()
+ if(.)
+ return
+ user.toggle_fullscreen()
+ return TRUE
+
/datum/keybinding/client/minimal_hud
hotkey_keys = list("F12")
name = "minimal_hud"
diff --git a/code/datums/quirks/positive_quirks/settler.dm b/code/datums/quirks/positive_quirks/settler.dm
index 9b52403404b12..3b4084242b811 100644
--- a/code/datums/quirks/positive_quirks/settler.dm
+++ b/code/datums/quirks/positive_quirks/settler.dm
@@ -9,17 +9,26 @@
value = 4
mob_trait = TRAIT_SETTLER
quirk_flags = QUIRK_HUMAN_ONLY|QUIRK_CHANGES_APPEARANCE
- medical_record_text = "Patient appears to be abnormally stout."
+ medical_record_text = "Patient has been exposed to planetary conditions for extended periods, resulting in an excessively stout build."
mail_goodies = list(
/obj/item/clothing/shoes/workboots/mining,
/obj/item/gps,
)
+ /// Most of the behavior of settler is from these traits, rather than exclusively the quirk
+ var/list/settler_traits = list(
+ TRAIT_EXPERT_FISHER,
+ TRAIT_ROUGHRIDER,
+ TRAIT_STUBBY_BODY,
+ TRAIT_BEAST_EMPATHY,
+ TRAIT_STURDY_FRAME,
+ )
/datum/quirk/item_quirk/settler/add(client/client_source)
var/mob/living/carbon/human/human_quirkholder = quirk_holder
human_quirkholder.set_mob_height(HUMAN_HEIGHT_SHORTEST)
human_quirkholder.add_movespeed_modifier(/datum/movespeed_modifier/settler)
human_quirkholder.physiology.hunger_mod *= 0.5 //good for you, shortass, you don't get hungry nearly as often
+ human_quirkholder.add_traits(settler_traits, QUIRK_TRAIT)
/datum/quirk/item_quirk/settler/add_unique(client/client_source)
give_item_to_holder(/obj/item/storage/box/papersack/wheat, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS))
@@ -32,3 +41,4 @@
human_quirkholder.set_mob_height(HUMAN_HEIGHT_MEDIUM)
human_quirkholder.remove_movespeed_modifier(/datum/movespeed_modifier/settler)
human_quirkholder.physiology.hunger_mod *= 2
+ human_quirkholder.remove_traits(settler_traits, QUIRK_TRAIT)
diff --git a/code/datums/storage/subtypes/bag_of_holding.dm b/code/datums/storage/subtypes/bag_of_holding.dm
index a4ea699e42e21..aa812f5d1e007 100644
--- a/code/datums/storage/subtypes/bag_of_holding.dm
+++ b/code/datums/storage/subtypes/bag_of_holding.dm
@@ -38,7 +38,7 @@
user.investigate_log("has been gibbed by a bag of holding recursive insertion.", INVESTIGATE_DEATHS)
user.gib()
- var/obj/boh_tear/tear = new(rift_loc)
+ var/obj/reality_tear/tear = new(rift_loc)
tear.start_disaster()
qdel(to_insert)
qdel(parent)
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index d8ae957994794..8135a3af59346 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -595,7 +595,7 @@
stop_pulling()
else if(pulling.anchored || pulling.move_resist > move_force)
stop_pulling()
- if(!only_pulling && pulledby && moving_diagonally != FIRST_DIAG_STEP && (get_dist(src, pulledby) > 1 || z != pulledby.z)) //separated from our puller and not in the middle of a diagonal move.
+ if(!only_pulling && pulledby && moving_diagonally != FIRST_DIAG_STEP && (get_dist(src, pulledby) > 1 || (z != pulledby.z && !z_allowed))) //separated from our puller and not in the middle of a diagonal move.
pulledby.stop_pulling()
/atom/movable/proc/set_glide_size(target = 8)
@@ -792,7 +792,14 @@
if(target_turf != current_turf || (moving_diagonally != SECOND_DIAG_STEP && ISDIAGONALDIR(pull_dir)) || get_dist(src, pulling) > 1)
pulling.move_from_pull(src, target_turf, glide_size)
- check_pulling()
+ if (pulledby)
+ if (pulledby.currently_z_moving)
+ check_pulling(z_allowed = TRUE)
+ //dont call check_pulling() here at all if there is a pulledby that is not currently z moving
+ //because it breaks stair conga lines, for some fucking reason.
+ //it's fine because the pull will be checked when this whole proc is called by the mob doing the pulling anyways
+ else
+ check_pulling()
//glide_size strangely enough can change mid movement animation and update correctly while the animation is playing
//This means that if you don't override it late like this, it will just be set back by the movement update that's called when you move turfs.
diff --git a/code/game/machinery/computer/records/security.dm b/code/game/machinery/computer/records/security.dm
index c41779e7384ec..dac62612a4c74 100644
--- a/code/game/machinery/computer/records/security.dm
+++ b/code/game/machinery/computer/records/security.dm
@@ -176,7 +176,7 @@
return TRUE
if("set_note")
- var/note = trim(params["note"], MAX_MESSAGE_LEN)
+ var/note = strip_html_full(params["note"], MAX_MESSAGE_LEN)
investigate_log("[user] has changed the security note of record: \"[target]\" from \"[target.security_note]\" to \"[note]\".")
target.security_note = note
return TRUE
@@ -199,7 +199,7 @@
/// Handles adding a crime to a particular record.
/obj/machinery/computer/records/security/proc/add_crime(mob/user, datum/record/crew/target, list/params)
- var/input_name = trim(params["name"], MAX_CRIME_NAME_LEN)
+ var/input_name = strip_html_full(params["name"], MAX_CRIME_NAME_LEN)
if(!input_name)
to_chat(usr, span_warning("You must enter a name for the crime."))
playsound(src, 'sound/machines/terminal_error.ogg', 75, TRUE)
@@ -213,7 +213,7 @@
var/input_details
if(params["details"])
- input_details = trim(params["details"], MAX_MESSAGE_LEN)
+ input_details = strip_html_full(params["details"], MAX_MESSAGE_LEN)
if(params["fine"] == 0)
var/datum/crime/new_crime = new(name = input_name, details = input_details, author = usr)
@@ -245,13 +245,13 @@
return FALSE
if(params["name"] && length(params["name"]) > 2 && params["name"] != editing_crime.name)
- var/new_name = trim(params["name"], MAX_CRIME_NAME_LEN)
+ var/new_name = strip_html_full(params["name"], MAX_CRIME_NAME_LEN)
investigate_log("[user] edited crime: \"[editing_crime.name]\" for target: \"[target.name]\", changing the name to: \"[new_name]\".", INVESTIGATE_RECORDS)
editing_crime.name = new_name
return TRUE
if(params["details"] && length(params["description"]) > 2 && params["name"] != editing_crime.name)
- var/new_details = trim(params["details"], MAX_MESSAGE_LEN)
+ var/new_details = strip_html_full(params["details"], MAX_MESSAGE_LEN)
investigate_log("[user] edited crime \"[editing_crime.name]\" for target: \"[target.name]\", changing the details to: \"[new_details]\" from: \"[editing_crime.details]\".", INVESTIGATE_RECORDS)
editing_crime.details = new_details
return TRUE
@@ -327,9 +327,9 @@
playsound(src, 'sound/machines/printer.ogg', 100, TRUE)
var/obj/item/printable
- var/input_alias = trim(params["alias"], MAX_NAME_LEN) || target.name
- var/input_description = trim(params["desc"], MAX_BROADCAST_LEN) || "No further details."
- var/input_header = trim(params["head"], 8) || capitalize(params["type"])
+ var/input_alias = strip_html_full(params["alias"], MAX_NAME_LEN) || target.name
+ var/input_description = strip_html_full(params["desc"], MAX_BROADCAST_LEN) || "No further details."
+ var/input_header = strip_html_full(params["head"], 8) || capitalize(params["type"])
switch(params["type"])
if("missing")
diff --git a/code/game/machinery/flatpacker.dm b/code/game/machinery/flatpacker.dm
index 9468dacc24c56..03382dce8d81e 100644
--- a/code/game/machinery/flatpacker.dm
+++ b/code/game/machinery/flatpacker.dm
@@ -242,7 +242,7 @@
/obj/item/flatpack
name = "flatpack"
- desc = "A box containing a compacted packed machine. Use multitool to deploy."
+ desc = "A box containing a compactly packed machine. Use multitool to deploy."
icon = 'icons/obj/devices/circuitry_n_data.dmi'
icon_state = "flatpack"
w_class = WEIGHT_CLASS_HUGE //cart time
@@ -253,13 +253,25 @@
/// The board we deploy
var/obj/item/circuitboard/machine/board
-/obj/item/flatpack/Initialize(mapload, obj/item/circuitboard/machine/board)
+/obj/item/flatpack/Initialize(mapload, obj/item/circuitboard/machine/new_board)
. = ..()
- if(!isnull(board))
- src.board = board // i got board
+ var/static/list/tool_behaviors
+ if(!tool_behaviors)
+ tool_behaviors = string_assoc_nested_list(list(
+ TOOL_MULTITOOL = list(
+ SCREENTIP_CONTEXT_LMB = "Deploy",
+ ),
+ ))
+ AddElement(/datum/element/contextual_screentip_tools, tool_behaviors)
+ if(isnull(board) && isnull(new_board))
+ return INITIALIZE_HINT_QDEL //how
+
+ board = !isnull(new_board) ? new_board : new board(src) // i got board
+ if(board.loc != src)
board.forceMove(src)
- var/obj/machinery/build = initial(board.build_path)
- name += " ([initial(build.name)])"
+ var/obj/machinery/build = initial(board.build_path)
+ name += " ([initial(build.name)])"
+
/obj/item/flatpack/Destroy()
QDEL_NULL(board)
diff --git a/code/game/objects/effects/effect_system/effects_sparks.dm b/code/game/objects/effects/effect_system/effects_sparks.dm
index f9bb819fd42a1..c715cc1d7457d 100644
--- a/code/game/objects/effects/effect_system/effects_sparks.dm
+++ b/code/game/objects/effects/effect_system/effects_sparks.dm
@@ -74,12 +74,27 @@
if(isobj(singed))
var/obj/singed_obj = singed
- if(singed_obj.resistance_flags & FLAMMABLE && !(singed_obj.resistance_flags & ON_FIRE)) //only fire_act flammable objects instead of burning EVERYTHING
- singed_obj.fire_act(1,100)
if(singed_obj.reagents)
- var/datum/reagents/reagents = singed_obj.reagents
- reagents?.expose_temperature(1000)
- return
+ var/datum/reagents/reagents = singed_obj.reagents // heat up things that contain reagents before we check to see if they burn
+ reagents?.expose_temperature(1000) // we set this at 1000 because that's the max reagent temp for a chem heater, higher temps require more than sparks
+ if(singed_obj.custom_materials && (GET_MATERIAL_REF(/datum/material/plasma) in singed_obj.custom_materials))
+ singed_obj.fire_act(FIRE_MINIMUM_TEMPERATURE_TO_SPREAD,100)
+ return // if it's made of plasma we just start burning no matter what, even furniture (see right below)
+ if(isstructure(singed_obj) || ismachinery(singed_obj)) // don't ignite furniture even if it's flammable, leave that to actual fires
+ return
+ if(singed_obj.resistance_flags & FLAMMABLE && !(singed_obj.resistance_flags & ON_FIRE)) //only fire_act flammable objects instead of burning EVERYTHING
+ if(isitem(singed_obj))
+ var/obj/item/singed_item = singed_obj
+ var/ignite_chance = 120 // base chance applies to anything under WEIGHT_CLASS_NORMAL, so burn everything flammable that's small/tiny
+ if(singed_item.w_class > WEIGHT_CLASS_SMALL)
+ var/ignite_chance_penalty = (singed_item.w_class * 2 + round(singed_item.w_class * 0.5)) * 10 // size penalties to ignite chance: normal = 70, bulky = 100,
+ ignite_chance -= ignite_chance_penalty // the bigger the item, the less likely it is to ignite
+ if(prob(ignite_chance))
+ singed_item.fire_act(FIRE_MINIMUM_TEMPERATURE_TO_SPREAD,100)
+ return
+ else
+ singed_obj.fire_act(FIRE_MINIMUM_TEMPERATURE_TO_SPREAD,100)
+ return
if(isliving(singed))
var/mob/living/singed_living = singed
if(singed_living.fire_stacks)
diff --git a/code/game/objects/effects/spawners/random/armory.dm b/code/game/objects/effects/spawners/random/armory.dm
index 5292ca6ad0986..dfb71ff10d54b 100644
--- a/code/game/objects/effects/spawners/random/armory.dm
+++ b/code/game/objects/effects/spawners/random/armory.dm
@@ -45,6 +45,16 @@
icon_state = "shotgun"
loot = list(/obj/item/gun/ballistic/shotgun/riot)
+/obj/effect/spawner/random/armory/dragnet
+ name = "DRAGnet spawner"
+ icon_state = "dragnet"
+ loot = list(/obj/item/gun/energy/e_gun/dragnet)
+ spawn_loot_count = 2
+
+/obj/effect/spawner/random/armory/dragnet/spawn_loot(lootcount_override)
+ . = ..()
+ new /obj/item/dragnet_beacon(get_turf(src)) //And give them a beacon too!
+
// Armor
/obj/effect/spawner/random/armory/bulletproof_helmet
name = "bulletproof helmet spawner"
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index c43f32daacbdf..5dc826e8fd51e 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -552,26 +552,26 @@
if(ispath(trim))
SSid_access.apply_trim_to_card(src, trim)
-/obj/item/card/id/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/rupee))
+/obj/item/card/id/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, /obj/item/rupee))
to_chat(user, span_warning("Your ID smartly rejects the strange shard of glass. Who knew, apparently it's not ACTUALLY valuable!"))
- return
- else if(iscash(W))
- insert_money(W, user)
- return
- else if(istype(W, /obj/item/storage/bag/money))
- var/obj/item/storage/bag/money/money_bag = W
+ return ITEM_INTERACT_BLOCKING
+ else if(iscash(tool))
+ return insert_money(tool, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
+ else if(istype(tool, /obj/item/storage/bag/money))
+ var/obj/item/storage/bag/money/money_bag = tool
var/list/money_contained = money_bag.contents
var/money_added = mass_insert_money(money_contained, user)
- if (money_added)
- to_chat(user, span_notice("You stuff the contents into the card! They disappear in a puff of bluespace smoke, adding [money_added] worth of credits to the linked account."))
- return
- else
- return ..()
+ if(!money_added)
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You stuff the contents into the card! They disappear in a puff of bluespace smoke, adding [money_added] worth of credits to the linked account."))
+ return ITEM_INTERACT_SUCCESS
+ return NONE
/**
* Insert credits or coins into the ID card and add their value to the associated bank account.
*
+ * Returns TRUE if the money was successfully inserted, FALSE otherwise.
* Arguments:
* money - The item to attempt to convert to credits and insert into the card.
* user - The user inserting the item.
@@ -584,11 +584,11 @@
if(!registered_account)
to_chat(user, span_warning("[src] doesn't have a linked account to deposit [money] into!"))
- return
+ return FALSE
var/cash_money = money.get_item_credit_value()
if(!cash_money)
to_chat(user, span_warning("[money] doesn't seem to be worth anything!"))
- return
+ return FALSE
registered_account.adjust_money(cash_money, "System: Deposit")
SSblackbox.record_feedback("amount", "credits_inserted", cash_money)
log_econ("[cash_money] credits were inserted into [src] owned by [src.registered_name]")
@@ -599,6 +599,7 @@
to_chat(user, span_notice("The linked account now reports a balance of [registered_account.account_balance] cr."))
qdel(money)
+ return TRUE
/**
* Insert multiple money or money-equivalent items at once.
@@ -953,20 +954,41 @@
return ..()
-
-/obj/item/card/id/advanced/attackby(obj/item/W, mob/user, params)
+/obj/item/card/id/advanced/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
. = ..()
- if(istype(W, /obj/item/toy/crayon))
- var/obj/item/toy/crayon/our_crayon = W
- if(tgui_alert(usr, "Recolor Department or Subdepartment?", "Recoloring ID...", list("Department", "Subdepartment")) == "Department")
- if(!do_after(user, 2 SECONDS)) // Doesn't technically require a spraycan's cap to be off but shhh
- return
+ if(.)
+ return .
+
+ if(istype(tool, /obj/item/toy/crayon))
+ return recolor_id(user, tool)
+
+/obj/item/card/id/advanced/proc/recolor_id(mob/living/user, obj/item/toy/crayon/our_crayon)
+ if(our_crayon.is_capped)
+ balloon_alert(user, "take the cap off first!")
+ return ITEM_INTERACT_BLOCKING
+ var/choice = tgui_alert(usr, "Recolor Department or Subdepartment?", "Recoloring ID...", list("Department", "Subdepartment"))
+ if(isnull(choice) \
+ || QDELETED(user) \
+ || QDELETED(src) \
+ || QDELETED(our_crayon) \
+ || !usr.can_perform_action(src, ALLOW_RESTING) \
+ || !usr.can_perform_action(our_crayon, ALLOW_RESTING) \
+ )
+ return ITEM_INTERACT_BLOCKING
+
+ switch(choice)
+ if("Department")
+ if(!do_after(user, 2 SECONDS))
+ return ITEM_INTERACT_BLOCKING
department_color_override = our_crayon.paint_color
balloon_alert(user, "recolored")
- else if(do_after(user, 1 SECONDS))
+ if("Subdepartment")
+ if(!do_after(user, 1 SECONDS))
+ return ITEM_INTERACT_BLOCKING
subdepartment_color_override = our_crayon.paint_color
balloon_alert(user, "recolored")
- update_icon()
+ update_icon()
+ return ITEM_INTERACT_SUCCESS
/obj/item/card/id/advanced/proc/update_intern_status(datum/source, mob/user, slot)
SIGNAL_HANDLER
@@ -1275,27 +1297,38 @@
/// Time left on a card till they can leave.
var/time_left = 0
-/obj/item/card/id/advanced/prisoner/attackby(obj/item/card/id/C, mob/user)
- ..()
- var/list/id_access = C.GetAccess()
+/obj/item/card/id/advanced/prisoner/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ . = ..()
+ if(.)
+ return .
+
+ if(isidcard(tool))
+ return set_sentence_time(user, tool)
+
+/obj/item/card/id/advanced/prisoner/proc/set_sentence_time(mob/living/user, obj/item/card/id/our_card)
+ var/list/id_access = our_card.GetAccess()
if(!(ACCESS_BRIG in id_access))
- return FALSE
- if(loc != user)
+ balloon_alert(user, "access denied!")
+ return ITEM_INTERACT_BLOCKING
+ if(!user.is_holding(src))
to_chat(user, span_warning("You must be holding the ID to continue!"))
- return FALSE
- if(timed)
+ return ITEM_INTERACT_BLOCKING
+
+ if(timed) // If we already have a time set, reset the card
timed = FALSE
time_to_assign = initial(time_to_assign)
registered_name = initial(registered_name)
STOP_PROCESSING(SSobj, src)
- to_chat(user, "Restating prisoner ID to default parameters.")
- return
+ to_chat(user, "Resetting prisoner ID to default parameters.")
+ return ITEM_INTERACT_SUCCESS
+
var/choice = tgui_input_number(user, "Sentence time in seconds", "Sentencing")
- if(!choice || QDELETED(user) || QDELETED(src) || !usr.can_perform_action(src, FORBID_TELEKINESIS_REACH) || loc != user)
- return FALSE
+ if(isnull(choice) || QDELETED(user) || QDELETED(src) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH) || !user.is_holding(src))
+ return ITEM_INTERACT_BLOCKING
time_to_assign = choice
- to_chat(user, "You set the sentence time to [time_to_assign] seconds.")
+ to_chat(user, "You set the sentence time to [DisplayTimeText(time_to_assign * 10)].")
timed = TRUE
+ return ITEM_INTERACT_SUCCESS
/obj/item/card/id/advanced/prisoner/proc/start_timer()
say("Sentence started, welcome to the corporate rehabilitation center!")
@@ -1307,10 +1340,15 @@
return
if(timed)
- if(time_left <= 0)
+ if(time_to_assign > 0)
+ . += span_notice("The digital timer on the card is set to [DisplayTimeText(time_to_assign * 10)]. The timer will start once the prisoner passes through the prison gate scanners.")
+ else if(time_left <= 0)
. += span_notice("The digital timer on the card has zero seconds remaining. You leave a changed man, but a free man nonetheless.")
else
- . += span_notice("The digital timer on the card has [time_left] seconds remaining. Don't do the crime if you can't do the time.")
+ . += span_notice("The digital timer on the card has [DisplayTimeText(time_left * 10)] remaining. Don't do the crime if you can't do the time.")
+
+ . += span_notice("[EXAMINE_HINT("Swipe")] a security ID on the card to [timed ? "re" : ""]set the genpop sentence time.")
+ . += span_notice("Remember to [EXAMINE_HINT("swipe")] the card on a genpop locker to link it.")
/obj/item/card/id/advanced/prisoner/process(seconds_per_tick)
if(!timed)
@@ -1761,11 +1799,10 @@
voice_name += " (as [scribbled_name])"
stored_name[NAME_PART_INDEX] = voice_name
-/obj/item/card/cardboard/attackby(obj/item/item, mob/living/user, params)
- if(user.can_write(item, TRUE))
- INVOKE_ASYNC(src, PROC_REF(modify_card), user, item)
- return TRUE
- return ..()
+/obj/item/card/cardboard/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(user.can_write(tool, TRUE))
+ INVOKE_ASYNC(src, PROC_REF(modify_card), user, tool)
+ return ITEM_INTERACT_SUCCESS
///Lets the user write a name, assignment or trim on the card, or reset it. Only the name is important for the component.
/obj/item/card/cardboard/proc/modify_card(mob/living/user, obj/item/item)
diff --git a/code/game/objects/items/food/misc.dm b/code/game/objects/items/food/misc.dm
index 0e598c6820296..31ac87c0ff690 100644
--- a/code/game/objects/items/food/misc.dm
+++ b/code/game/objects/items/food/misc.dm
@@ -184,7 +184,7 @@
/obj/item/food/melonfruitbowl
name = "melon fruit bowl"
- desc = "For people who wants edible fruit bowls."
+ desc = "For people who want to experience an explosion of flavour."
icon_state = "melonfruitbowl"
food_reagents = list(
/datum/reagent/consumable/nutriment = 6,
diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm
index ffcb5d1857cda..d3e8bdd0ad88d 100644
--- a/code/game/objects/items/handcuffs.dm
+++ b/code/game/objects/items/handcuffs.dm
@@ -523,6 +523,7 @@
/obj/item/restraints/legcuffs/beartrap/energy/cyborg
breakouttime = 2 SECONDS // Cyborgs shouldn't have a strong restraint
+ slowdown = 3
/obj/item/restraints/legcuffs/bola
name = "bola"
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index 9ebcbb253d20b..d07f7ad21c5f5 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -132,6 +132,7 @@
take_damage(clamp(0.02 * exposed_temperature, 0, 20), BURN, FIRE, 0)
if(!(resistance_flags & ON_FIRE) && (resistance_flags & FLAMMABLE) && !(resistance_flags & FIRE_PROOF))
AddComponent(/datum/component/burning, custom_fire_overlay || GLOB.fire_overlay, burning_particles)
+ SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume)
return TRUE
return ..()
diff --git a/code/game/objects/structures/mystery_box.dm b/code/game/objects/structures/mystery_box.dm
index ab8a25f04c675..9dc8152f5b7bf 100644
--- a/code/game/objects/structures/mystery_box.dm
+++ b/code/game/objects/structures/mystery_box.dm
@@ -85,7 +85,6 @@ GLOBAL_LIST_INIT(mystery_magic, list(
/obj/item/gun/magic/staff/door,
/obj/item/gun/magic/staff/honk,
/obj/item/gun/magic/staff/spellblade,
- /obj/item/gun/magic/staff/locker,
/obj/item/gun/magic/staff/flying,
/obj/item/gun/magic/staff/babel,
/obj/item/singularityhammer,
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 515f1513ac754..565adb22dbc00 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -568,7 +568,6 @@ GLOBAL_LIST_EMPTY(station_turfs)
if(EXPLODE_LIGHT)
SSexplosions.low_mov_atom += movable_thing
-
/turf/narsie_act(force, ignore_mobs, probability = 20)
. = (prob(probability) || force)
for(var/I in src)
diff --git a/code/modules/antagonists/changeling/powers/adrenaline.dm b/code/modules/antagonists/changeling/powers/adrenaline.dm
index 10c06656b2978..3b6a550b18b0f 100644
--- a/code/modules/antagonists/changeling/powers/adrenaline.dm
+++ b/code/modules/antagonists/changeling/powers/adrenaline.dm
@@ -1,6 +1,6 @@
/datum/action/changeling/adrenaline
name = "Repurposed Glands"
- desc = "We shift almost all available muscle mass from the arms to the legs, disabling the former but making us unable to be downed for 15 seconds. Costs 10 chemicals."
+ desc = "We shift almost all available muscle mass from the arms to the legs, disabling the former but making us unable to be downed for 20 seconds. Costs 25 chemicals."
helptext = "Disables your arms and retracts bioweaponry, but regenerates your legs, grants you speed, and wakes you up from any stun."
button_icon_state = "adrenaline"
chemical_cost = 25 // similar cost to biodegrade, as they serve similar purposes
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index 9c41c54e84e09..416dbe390e16f 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -56,6 +56,8 @@
var/static/list/blacklisted_rune_turfs = typecacheof(list(/turf/open/space, /turf/open/openspace, /turf/open/lava, /turf/open/chasm))
/// Controls what types of turf we can spread rust to, increases as we unlock more powerful rust abilites
var/rust_strength = 0
+ /// Wether we are allowed to ascend
+ var/feast_of_owls = FALSE
/// Static list of what each path converts to in the UI (colors are TGUI colors)
var/static/list/path_to_ui_color = list(
PATH_START = "grey",
@@ -478,7 +480,8 @@
succeeded = FALSE
parts += "Objective #[count]: [objective.explanation_text] [objective.get_roundend_success_suffix()]"
count++
-
+ if(feast_of_owls)
+ parts += span_greentext("Ascension Forsaken")
if(ascended)
parts += span_greentext(span_big("THE HERETIC ASCENDED!"))
@@ -695,6 +698,8 @@
/datum/antagonist/heretic/proc/can_ascend()
if(!can_assign_self_objectives)
return FALSE // We spurned the offer of the Mansus :(
+ if(feast_of_owls)
+ return FALSE // We sold our ambition for immediate power :/
for(var/datum/objective/must_be_done as anything in objectives)
if(!must_be_done.check_completion())
return FALSE
diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
index e2af5390ba810..4b61648329214 100644
--- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
@@ -188,7 +188,6 @@
/datum/heretic_knowledge/ultimate/moon_final/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
. = ..()
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
priority_announce(
text = "[generate_heretic_text()] Laugh, for the ringleader [user.real_name] has ascended! \
The truth shall finally devour the lie! [generate_heretic_text()]",
@@ -199,8 +198,7 @@
user.client?.give_award(/datum/award/achievement/misc/moon_ascension, user)
ADD_TRAIT(user, TRAIT_MADNESS_IMMUNE, REF(src))
- heretic_datum.add_team_hud(user, /datum/antagonist/lunatic)
-
+ user.mind.add_antag_datum(/datum/antagonist/lunatic/master)
RegisterSignal(user, COMSIG_LIVING_LIFE, PROC_REF(on_life))
// Roughly 1/5th of the station will rise up as lunatics to the heretic
diff --git a/code/modules/antagonists/heretic/knowledge/starting_lore.dm b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
index f1b5f7f55ea19..50b57fd9e96b4 100644
--- a/code/modules/antagonists/heretic/knowledge/starting_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
@@ -293,3 +293,28 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
body.do_jitter_animation()
body.visible_message(span_danger("An awful ripping sound is heard as [ripped_thing]'s [exterior_text] is ripped straight out, wrapping around [le_book || "the book"], turning into an eldritch shade of blue!"))
return ..()
+
+/datum/heretic_knowledge/feast_of_owls
+ name = "Feast of Owls"
+ desc = "Allows you to undergo a ritual that gives you 5 knowledge points but locks you out of ascension. This can only be done once and cannot be reverted."
+ gain_text = "Under the soft glow of unreason there is a beast that stalks the night. I shall bring it forth and let it enter my presence. It will feast upon my amibitions and leave knowledge in its wake."
+ route = PATH_START
+ required_atoms = list()
+
+/datum/heretic_knowledge/feast_of_owls/can_be_invoked(datum/antagonist/heretic/invoker)
+ return !invoker.feast_of_owls
+
+/datum/heretic_knowledge/feast_of_owls/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
+ var/alert = tgui_alert(user,"Do you really want to forsake your ascension? This action cannot be reverted.", "Feast of Owls", list("Yes I'm sure", "No"), 30 SECONDS)
+ if( alert != "Yes I'm sure")
+ return FALSE
+ user.set_temp_blindness(5 SECONDS)
+ user.AdjustParalyzed(5 SECONDS)
+ var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ for(var/i in 0 to 4)
+ user.emote("scream")
+ playsound(loc, 'sound/items/eatfood.ogg', 100, TRUE)
+ heretic_datum.knowledge_points++
+ sleep(1 SECONDS)
+ to_chat(user,span_danger("You feel different..."))
+ heretic_datum.feast_of_owls = TRUE
diff --git a/code/modules/antagonists/heretic/moon_lunatic.dm b/code/modules/antagonists/heretic/moon_lunatic.dm
index dbc07a6b5054b..3d877ee962c11 100644
--- a/code/modules/antagonists/heretic/moon_lunatic.dm
+++ b/code/modules/antagonists/heretic/moon_lunatic.dm
@@ -13,16 +13,27 @@
var/datum/mind/ascended_heretic
// The body of the ascended heretic who created us
var/mob/living/carbon/human/ascended_body
+ // Our objective
+ var/datum/objective/lunatic/lunatic_obj
+
+/datum/antagonist/lunatic/on_gain()
+ // Masters gain an objective before so we dont want duplicates
+ for(var/objective in objectives)
+ if(!istype(objective, /datum/objective/lunatic))
+ continue
+ return ..()
+ var/datum/objective/lunatic/loony = new()
+ objectives += loony
+ lunatic_obj = loony
+ return ..()
/// Runs when the moon heretic creates us, used to give the lunatic a master
/datum/antagonist/lunatic/proc/set_master(datum/mind/heretic_master, mob/living/carbon/human/heretic_body)
src.ascended_heretic = heretic_master
src.ascended_body = heretic_body
- var/datum/objective/lunatic/lunatic_obj = new()
lunatic_obj.master = heretic_master
lunatic_obj.update_explanation_text()
- objectives += lunatic_obj
to_chat(owner, span_boldnotice("Ruin the lie, save the truth through obeying [heretic_master] the ringleader!"))
@@ -30,8 +41,7 @@
var/mob/living/our_mob = mob_override || owner.current
handle_clown_mutation(our_mob, "Ancient knowledge from the moon has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
our_mob.faction |= FACTION_HERETIC
- add_team_hud(our_mob)
- add_team_hud(our_mob, /datum/antagonist/heretic)
+ add_team_hud(our_mob, /datum/antagonist/lunatic)
ADD_TRAIT(our_mob, TRAIT_MADNESS_IMMUNE, REF(src))
var/datum/action/cooldown/lunatic_track/moon_track = new /datum/action/cooldown/lunatic_track()
@@ -52,7 +62,28 @@
/datum/objective/lunatic
explanation_text = "Assist your ringleader. If you are seeing this, scroll up in chat for who that is and report this"
var/datum/mind/master
+ // If the person with this objective is a lunatic master
+ var/is_master = FALSE
/datum/objective/lunatic/update_explanation_text()
. = ..()
- explanation_text = "Assist your ringleader [master]"
+ if(is_master)
+ explanation_text = "Lead your lunatics to further your own goals!"
+ return
+ explanation_text = "Assist your ringleader [master], do not harm fellow lunatics"
+
+// Lunatic master
+/datum/antagonist/lunatic/master
+ name = "\improper Ringleader"
+ antag_hud_name = "lunatic_master"
+
+/datum/antagonist/lunatic/master/on_gain()
+ var/datum/objective/lunatic/loony = new()
+ objectives += loony
+ loony.is_master = TRUE
+ loony.update_explanation_text()
+ return ..()
+
+/datum/antagonist/lunatic/master/apply_innate_effects(mob/living/mob_override)
+ var/mob/living/our_mob = mob_override || owner.current
+ add_team_hud(our_mob, /datum/antagonist/lunatic)
diff --git a/code/modules/bitrunning/objects/disks.dm b/code/modules/bitrunning/objects/disks.dm
index 6e166d5eb7fdb..17b768c54d08f 100644
--- a/code/modules/bitrunning/objects/disks.dm
+++ b/code/modules/bitrunning/objects/disks.dm
@@ -142,3 +142,46 @@
/obj/item/dualsaber/green,
/obj/item/grenade/syndieminibomb,
)
+
+///proto-kinetic accelerator mods, to be applied to pka's given inside domains
+/obj/item/bitrunning_disk/item/pka_mods
+ name = "bitrunning gear: proto-kinetic accelerator mods"
+ selectable_items = list(
+ /obj/item/borg/upgrade/modkit/range,
+ /obj/item/borg/upgrade/modkit/damage,
+ /obj/item/borg/upgrade/modkit/cooldown,
+ /obj/item/borg/upgrade/modkit/aoe/mobs,
+ /obj/item/borg/upgrade/modkit/human_passthrough,
+ )
+
+/obj/item/bitrunning_disk/item/pka_mods/premium
+ name = "bitrunning gear: premium proto-kinetic accelerator mods"
+ selectable_items = list(
+ /obj/item/borg/upgrade/modkit/cooldown/repeater,
+ /obj/item/borg/upgrade/modkit/lifesteal,
+ /obj/item/borg/upgrade/modkit/resonator_blasts,
+ /obj/item/borg/upgrade/modkit/bounty,
+ /obj/item/borg/upgrade/modkit/indoors,
+ )
+
+///proto-kinetic crusher trophies, to be applied to pkc's given inside domains
+/obj/item/bitrunning_disk/item/pkc_mods
+ name = "bitrunning gear: proto-kinetic crusher mods"
+ selectable_items = list(
+ /obj/item/crusher_trophy/watcher_wing,
+ /obj/item/crusher_trophy/blaster_tubes/magma_wing,
+ /obj/item/crusher_trophy/legion_skull,
+ /obj/item/crusher_trophy/wolf_ear,
+ )
+
+/obj/item/bitrunning_disk/item/pkc_mods/premium
+ name = "bitrunning gear: premium proto-kinetic crusher mods"
+ selectable_items = list(
+ /obj/item/crusher_trophy/watcher_wing/ice_wing,
+ /obj/item/crusher_trophy/blaster_tubes,
+ /obj/item/crusher_trophy/miner_eye,
+ /obj/item/crusher_trophy/tail_spike,
+ /obj/item/crusher_trophy/demon_claws,
+ /obj/item/crusher_trophy/vortex_talisman,
+ /obj/item/crusher_trophy/ice_demon_cube,
+ )
diff --git a/code/modules/bitrunning/orders/tech.dm b/code/modules/bitrunning/orders/tech.dm
index a4bf59ce18dec..7e987e4818104 100644
--- a/code/modules/bitrunning/orders/tech.dm
+++ b/code/modules/bitrunning/orders/tech.dm
@@ -34,3 +34,23 @@
/datum/orderable_item/bitrunning_tech/flip_skillchip
item_path = /obj/item/skillchip/matrix_flip
cost_per_order = 2000
+
+/datum/orderable_item/bitrunning_tech/pka_mod
+ item_path = /obj/item/bitrunning_disk/item/pka_mods
+ cost_per_order = 750
+ desc = "This disk contains a program that lets you equip modkits for the proto-kinetic accelerator. Proto-kinetic accelerator not included."
+
+/datum/orderable_item/bitrunning_tech/pka_mod/premium
+ item_path = /obj/item/bitrunning_disk/item/pka_mods/premium
+ cost_per_order = 1800
+ desc = "This disk contains a program that lets you equip stronger modkits for the proto-kinetic accelerator. Proto-kinetic accelerator not included."
+
+/datum/orderable_item/bitrunning_tech/pkc_mod
+ item_path = /obj/item/bitrunning_disk/item/pkc_mods
+ cost_per_order = 750
+ desc = "This disk contains a program that lets you equip trophies for the proto-kinetic crusher. Proto-kinetic crusher no included."
+
+/datum/orderable_item/bitrunning_tech/pkc_mod/premium
+ item_path = /obj/item/bitrunning_disk/item/pkc_mods/premium
+ cost_per_order = 1800
+ desc = "This disk contains a program that lets you equip stronger trophies for the proto-kinetic crusher. Proto-kinetic crusher not included."
diff --git a/code/modules/cargo/packs/security.dm b/code/modules/cargo/packs/security.dm
index e36f9f84cacf1..05360fe913f0a 100644
--- a/code/modules/cargo/packs/security.dm
+++ b/code/modules/cargo/packs/security.dm
@@ -214,9 +214,12 @@
/datum/supply_pack/security/armory/dragnet
name = "DRAGnet Crate"
desc = "Contains three \"Dynamic Rapid-Apprehension of the Guilty\" netting devices, \
- a recent breakthrough in law enforcement prisoner management technology."
+ a recent breakthrough in law enforcement prisoner management technology. Includes a DRAGnet beacon."
cost = CARGO_CRATE_VALUE * 5
- contains = list(/obj/item/gun/energy/e_gun/dragnet = 3)
+ contains = list(
+ /obj/item/gun/energy/e_gun/dragnet = 3,
+ /obj/item/dragnet_beacon = 1
+ )
crate_name = "\improper DRAGnet crate"
/datum/supply_pack/security/armory/energy
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 8eb0def90bfa3..7cd3965718e60 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -211,18 +211,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if ("change_slot")
// Save existing character
save_character()
-
- // SAFETY: `load_character` performs sanitization the slot number
- if (!load_character(params["slot"]))
- tainted_character_profiles = TRUE
- randomise_appearance_prefs()
- save_character()
-
- for (var/datum/preference_middleware/preference_middleware as anything in middleware)
- preference_middleware.on_new_character(usr)
-
- character_preview_view.update_body()
-
+ // SAFETY: `switch_to_slot` performs sanitization on the slot number
+ switch_to_slot(params["slot"])
+ return TRUE
+ if ("remove_current_slot")
+ remove_current_slot()
return TRUE
if ("rotate")
character_preview_view.setDir(turn(character_preview_view.dir, -90))
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index aa6b1840c50b2..9a6448e2e6d54 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -368,6 +368,43 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
return TRUE
+/datum/preferences/proc/switch_to_slot(new_slot)
+ // SAFETY: `load_character` performs sanitization on the slot number
+ if (!load_character(new_slot))
+ tainted_character_profiles = TRUE
+ randomise_appearance_prefs()
+ save_character()
+
+ for (var/datum/preference_middleware/preference_middleware as anything in middleware)
+ preference_middleware.on_new_character(usr)
+
+ character_preview_view.update_body()
+
+/datum/preferences/proc/remove_current_slot()
+ PRIVATE_PROC(TRUE)
+
+ var/closest_slot
+ for (var/other_slot in default_slot - 1 to 1 step -1)
+ var/save_data = savefile.get_entry("character[other_slot]")
+ if (!isnull(save_data))
+ closest_slot = other_slot
+ break
+
+ if (isnull(closest_slot))
+ for (var/other_slot in default_slot + 1 to max_save_slots)
+ var/save_data = savefile.get_entry("character[other_slot]")
+ if (!isnull(save_data))
+ closest_slot = other_slot
+ break
+
+ if (isnull(closest_slot))
+ stack_trace("remove_current_slot() being called when there are no slots to go to, the client should prevent this")
+ return
+
+ savefile.remove_entry("character[default_slot]")
+ tainted_character_profiles = TRUE
+ switch_to_slot(closest_slot)
+
/datum/preferences/proc/sanitize_be_special(list/input_be_special)
var/list/output = list()
diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm
index f0915bdbf77cd..7a7c073b75a6a 100644
--- a/code/modules/experisci/experiment/experiments.dm
+++ b/code/modules/experisci/experiment/experiments.dm
@@ -335,7 +335,7 @@
name = "Exosuit Materials: Load Strain Test"
description = "Exosuit equipment places unique strain upon the structure of the vehicle. Scan exosuits you have assembled from your exosuit fabricator and fully equipped to accelerate our structural stress simulations."
possible_types = list(/obj/vehicle/sealed/mecha)
- total_requirement = 2
+ total_requirement = 1
/// Scan for organs you didn't start the round with
/datum/experiment/scanning/people/novel_organs
@@ -383,6 +383,7 @@
description = "We need to gather data on how cybernetic vital organs integrate with human biology. Conduct a scan on a human with these implants to help us understand their compatibility"
performance_hint = "Perform an organ manipulation surgery to replace one of the vital organs with a cybernetic variant."
required_traits_desc = "augmented vital organs"
+ required_count = 1
/datum/experiment/scanning/people/augmented_organs/is_valid_scan_target(mob/living/carbon/human/check)
. = ..()
@@ -431,15 +432,19 @@
required_reagent = /datum/reagent/cryostylane
min_purity = 0.99
-/datum/experiment/scanning/bluespace_crystal
+/datum/experiment/scanning/points/bluespace_crystal
name = "Bluespace Crystal Sampling"
description = "Investigate the properties of bluespace crystals by scanning either an artificial or naturally occurring variant. This will help us deepen our understanding of bluespace phenomena."
- required_atoms = list(/obj/item/stack/ore/bluespace_crystal = 1)
+ required_points = 1
+ required_atoms = list(
+ /obj/item/stack/ore/bluespace_crystal = 1,
+ /obj/item/stack/sheet/bluespace_crystal = 1
+ )
/datum/experiment/scanning/points/machinery_tiered_scan/tier2_any
name = "Upgraded Stock Parts Benchmark"
description = "Our newly-designed machinery components require practical application tests for hints at possible further advancements, as well as a general confirmation that we didn't actually design worse parts somehow. Scan any machinery with Upgraded Parts and report the results."
- required_points = 8
+ required_points = 4
required_atoms = list(
/obj/machinery = 1
)
@@ -448,7 +453,7 @@
/datum/experiment/scanning/points/machinery_tiered_scan/tier3_any
name = "Advanced Stock Parts Benchmark"
description = "Our newly-designed machinery components require practical application tests for hints at possible further advancements, as well as a general confirmation that we didn't actually design worse parts somehow. Scan any machinery with Advanced Parts and report the results."
- required_points = 8
+ required_points = 4
required_atoms = list(
/obj/machinery = 1
)
diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm
index eb681dc16cbfd..66d2bf0a880e3 100644
--- a/code/modules/fishing/sources/_fish_source.dm
+++ b/code/modules/fishing/sources/_fish_source.dm
@@ -85,8 +85,8 @@ GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
. = fishing_difficulty
// Difficulty modifier added by having the Settler quirk
- if(HAS_TRAIT(fisherman, TRAIT_SETTLER))
- . += SETTLER_DIFFICULTY_MOD
+ if(HAS_TRAIT(fisherman, TRAIT_EXPERT_FISHER))
+ . += EXPERT_FISHER_DIFFICULTY_MOD
// Difficulty modifier added by the fisher's skill level
if(!challenge || !(challenge.special_effects & FISHING_MINIGAME_RULE_NO_EXP))
diff --git a/code/modules/food_and_drinks/machinery/gibber.dm b/code/modules/food_and_drinks/machinery/gibber.dm
index e0002817f782e..cd50f29ffe478 100644
--- a/code/modules/food_and_drinks/machinery/gibber.dm
+++ b/code/modules/food_and_drinks/machinery/gibber.dm
@@ -5,6 +5,7 @@
icon_state = "grinder"
density = TRUE
circuit = /obj/item/circuitboard/machine/gibber
+ anchored_tabletop_offset = 8
var/operating = FALSE //Is it on?
var/dirty = FALSE // Does it need cleaning?
@@ -15,13 +16,15 @@
/obj/machinery/gibber/Initialize(mapload)
. = ..()
+ RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_cleaned))
if(prob(5))
name = "meat grinder"
desc = "Okay, if I... if I chop you up in a meat grinder, and the only thing that comes out, that's left of you, is your eyeball, \
you'r- you're PROBABLY DEAD! You're probably going to - not you, I'm just sayin', like, if you- if somebody were to, like, \
push you into a meat grinder, and, like, your- one of your finger bones is still intact, they're not gonna pick it up and go, \
Well see, yeah it wasn't deadly, it wasn't an instant kill move! You still got, like, this part of your finger left!"
- add_overlay("grjam")
+ dirty = TRUE
+ update_appearance(UPDATE_OVERLAYS)
/obj/machinery/gibber/RefreshParts()
. = ..()
@@ -45,16 +48,23 @@
/obj/machinery/gibber/update_overlays()
. = ..()
if(dirty)
- . +="grbloody"
- if(machine_stat & (NOPOWER|BROKEN))
+ . += "grinder_bloody"
+ if(machine_stat & (NOPOWER|BROKEN) || panel_open)
return
if(!occupant)
- . += "grjam"
+ . += "grinder_empty"
+ . += emissive_appearance(icon, "grinder_empty", src, alpha = src.alpha)
return
if(operating)
- . += "gruse"
+ . += "grinder_active"
+ . += emissive_appearance(icon, "grinder_active", src, alpha = src.alpha)
+ . += "grinder_jaws_active"
return
- . += "gridle"
+ . += "grinder_loaded"
+ . += emissive_appearance(icon, "grinder_loaded", src, alpha = src.alpha)
+
+/obj/machinery/gibber/on_set_panel_open(old_value)
+ update_appearance(UPDATE_OVERLAYS)
/obj/machinery/gibber/attack_paw(mob/user, list/modifiers)
return attack_hand(user, modifiers)
@@ -160,8 +170,7 @@
operating = TRUE
update_appearance()
- var/offset = prob(50) ? -2 : 2
- animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 200) //start shaking
+ Shake(pixelshiftx = 1, pixelshifty = 0, duration = gibtime)
var/mob/living/mob_occupant = occupant
var/sourcename = mob_occupant.real_name
var/sourcejob
@@ -223,6 +232,8 @@
/obj/machinery/gibber/proc/make_meat(obj/item/stack/sheet/animalhide/skin, list/obj/item/food/meat/slab/allmeat, meat_produced, gibtype, list/datum/disease/diseases)
playsound(src.loc, 'sound/effects/splat.ogg', 50, TRUE)
operating = FALSE
+ if (!dirty && prob(50))
+ dirty = TRUE
var/turf/T = get_turf(src)
var/list/turf/nearby_turfs = RANGE_TURFS(3,T) - T
if(skin)
@@ -265,3 +276,7 @@
if(victim.loc == input)
victim.forceMove(src)
victim.gib(DROP_ALL_REMAINS)
+
+/obj/machinery/gibber/proc/on_cleaned(obj/source_component, obj/source)
+ dirty = FALSE
+ update_appearance(UPDATE_OVERLAYS)
diff --git a/code/modules/food_and_drinks/machinery/processor.dm b/code/modules/food_and_drinks/machinery/processor.dm
index 4a60cdbc26592..78dd71df128d9 100644
--- a/code/modules/food_and_drinks/machinery/processor.dm
+++ b/code/modules/food_and_drinks/machinery/processor.dm
@@ -4,7 +4,8 @@
name = "food processor"
desc = "An industrial grinder used to process meat and other foods. Keep hands clear of intake area while operating."
icon = 'icons/obj/machines/kitchen.dmi'
- icon_state = "processor1"
+ base_icon_state = "processor"
+ icon_state = "processor"
layer = BELOW_OBJ_LAYER
density = TRUE
pass_flags = PASSTABLE
@@ -91,7 +92,7 @@
if(processing)
to_chat(user, span_warning("[src] is in the process of processing!"))
return TRUE
- if(default_deconstruction_screwdriver(user, "processor", "processor1", attacking_item) || default_pry_open(attacking_item, close_after_pry = TRUE) || default_deconstruction_crowbar(attacking_item))
+ if(default_deconstruction_screwdriver(user, base_icon_state + "_open", base_icon_state, attacking_item) || default_pry_open(attacking_item, close_after_pry = TRUE) || default_deconstruction_crowbar(attacking_item))
return
if(istype(attacking_item, /obj/item/storage/bag/tray))
@@ -157,7 +158,7 @@
total_time += recipe.time
var/duration = (total_time / rating_speed)
- INVOKE_ASYNC(src, TYPE_PROC_REF(/atom, Shake), 2, 2, duration, max(duration*0.02, 0.01)) //initial values work out to duration 4 seconds, interval 0.8
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/atom, Shake), 1, 0, duration)
sleep(duration)
for(var/atom/movable/content_item in processor_contents)
var/datum/food_processor_process/recipe = PROCESSOR_SELECT_RECIPE(content_item)
@@ -189,6 +190,8 @@
/obj/machinery/processor/slime
name = "slime processor"
+ base_icon_state = "processor_slime"
+ icon_state = "processor_slime"
desc = "An industrial grinder with a sticker saying appropriated for science department. Keep hands clear of intake area while operating."
circuit = /obj/item/circuitboard/machine/processor/slime
diff --git a/code/modules/mob/living/basic/alien/_alien.dm b/code/modules/mob/living/basic/alien/_alien.dm
index 823ab896c0a22..907d28aaa4187 100644
--- a/code/modules/mob/living/basic/alien/_alien.dm
+++ b/code/modules/mob/living/basic/alien/_alien.dm
@@ -63,7 +63,7 @@
AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_CLAW)
/mob/living/basic/alien/get_butt_sprite()
- return BUTT_SPRITE_XENOMORPH
+ return icon('icons/mob/butts.dmi', BUTT_SPRITE_XENOMORPH)
///Places alien weeds on the turf the mob is currently standing on.
/mob/living/basic/alien/proc/place_weeds()
diff --git a/code/modules/mob/living/basic/drone/_drone.dm b/code/modules/mob/living/basic/drone/_drone.dm
index ae72054899b11..6501e35d51dc2 100644
--- a/code/modules/mob/living/basic/drone/_drone.dm
+++ b/code/modules/mob/living/basic/drone/_drone.dm
@@ -265,7 +265,7 @@
dust()
/mob/living/basic/drone/get_butt_sprite()
- return BUTT_SPRITE_DRONE
+ return icon('icons/mob/butts.dmi', BUTT_SPRITE_DRONE)
/mob/living/basic/drone/examine(mob/user)
. = list("This is [icon2html(src, user)] \a [src]!")
diff --git a/code/modules/mob/living/basic/vermin/mothroach.dm b/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm
similarity index 88%
rename from code/modules/mob/living/basic/vermin/mothroach.dm
rename to code/modules/mob/living/basic/vermin/mothroach/mothroach.dm
index 4c06665a14afc..a0079065de437 100644
--- a/code/modules/mob/living/basic/vermin/mothroach.dm
+++ b/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm
@@ -36,6 +36,10 @@
/mob/living/basic/mothroach/Initialize(mapload)
. = ..()
+ var/static/list/food_types = list(/obj/item/clothing)
+ AddElement(/datum/element/basic_eating, food_types = food_types)
+ ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(food_types))
+ AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/pet_bonus, "squeaks happily!")
add_verb(src, /mob/living/proc/toggle_resting)
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
@@ -64,16 +68,6 @@
else
playsound(loc, 'sound/voice/moth/scream_moth.ogg', 50, TRUE)
-/datum/ai_controller/basic_controller/mothroach
- blackboard = list()
-
- ai_traits = STOP_MOVING_WHEN_PULLED
- ai_movement = /datum/ai_movement/basic_avoidance
- idle_behavior = /datum/idle_behavior/idle_random_walk
- planning_subtrees = list(
- /datum/ai_planning_subtree/random_speech/mothroach,
- )
-
/mob/living/basic/mothroach/bar
name = "mothroach bartender"
desc = "A mothroach serving drinks. Look at him go."
diff --git a/code/modules/mob/living/basic/vermin/mothroach/mothroach_ai.dm b/code/modules/mob/living/basic/vermin/mothroach/mothroach_ai.dm
new file mode 100644
index 0000000000000..5ef330cdc64f9
--- /dev/null
+++ b/code/modules/mob/living/basic/vermin/mothroach/mothroach_ai.dm
@@ -0,0 +1,35 @@
+#define MOTHROACH_EAT_TIMER 1 MINUTES
+
+/datum/ai_controller/basic_controller/mothroach
+ blackboard = list(
+ BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items,
+ )
+
+ ai_traits = STOP_MOVING_WHEN_PULLED
+ ai_movement = /datum/ai_movement/basic_avoidance
+ idle_behavior = /datum/idle_behavior/idle_random_walk
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/find_food/mothroach,
+ /datum/ai_planning_subtree/target_retaliate/to_flee,
+ /datum/ai_planning_subtree/flee_target/from_flee_key,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ /datum/ai_planning_subtree/random_speech/mothroach,
+ )
+
+/datum/ai_controller/basic_controller/mothroach/TryPossessPawn(atom/new_pawn)
+ . = ..()
+ if(. & AI_CONTROLLER_INCOMPATIBLE)
+ return
+ RegisterSignal(new_pawn, COMSIG_MOB_ATE, PROC_REF(on_eaten))
+
+/datum/ai_controller/basic_controller/mothroach/proc/on_eaten(datum/source)
+ SIGNAL_HANDLER
+ set_blackboard_key(BB_MOTHROACH_NEXT_EAT, world.time + MOTHROACH_EAT_TIMER)
+
+/datum/ai_planning_subtree/find_food/mothroach/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ if(world.time < controller.blackboard[BB_MOTHROACH_NEXT_EAT])
+ return
+ return ..()
+
+#undef MOTHROACH_EAT_TIMER
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 1ef72fd18e600..b0a747ff51d88 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -394,7 +394,15 @@
/obj/item/organ/internal/brain/primitive //No like books and stompy metal men
name = "primitive brain"
desc = "This juicy piece of meat has a clearly underdeveloped frontal lobe."
- organ_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_CAN_STRIP, TRAIT_PRIMITIVE) // No literacy
+ organ_traits = list(
+ TRAIT_ADVANCEDTOOLUSER,
+ TRAIT_CAN_STRIP,
+ TRAIT_PRIMITIVE, // No literacy
+ TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION,
+ TRAIT_EXPERT_FISHER, // live off land, fish from river
+ TRAIT_ROUGHRIDER, // ride beast, chase down prey, flee from danger
+ TRAIT_BEAST_EMPATHY, // know the way of beast, calm with food
+ )
/obj/item/organ/internal/brain/golem
name = "crystalline matrix"
diff --git a/code/modules/mob/living/carbon/alien/adult/adult.dm b/code/modules/mob/living/carbon/alien/adult/adult.dm
index c15efb77a4a19..ad005888178ac 100644
--- a/code/modules/mob/living/carbon/alien/adult/adult.dm
+++ b/code/modules/mob/living/carbon/alien/adult/adult.dm
@@ -140,7 +140,7 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list(
return TRUE
/mob/living/carbon/alien/adult/get_butt_sprite()
- return BUTT_SPRITE_XENOMORPH
+ return icon('icons/mob/butts.dmi', BUTT_SPRITE_XENOMORPH)
// Aliens can touch acid
/mob/living/carbon/alien/can_touch_acid(atom/acided_atom, acid_power, acid_volume)
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 52ffe51bfe68b..1fe6555cddf9c 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -368,7 +368,7 @@
/mob/living/carbon/human/proc/item_heal(mob/user, brute_heal, burn_heal, heal_message_brute, heal_message_burn, required_bodytype)
var/obj/item/bodypart/affecting = src.get_bodypart(check_zone(user.zone_selected))
- if (!affecting || !(affecting.bodytype == required_bodytype))
+ if (!affecting || !(affecting.bodytype & required_bodytype))
to_chat(user, span_warning("[affecting] is already in good condition!"))
return FALSE
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 ba813901b3e87..71a5c4c026193 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -157,7 +157,6 @@ Lizard subspecies: ASHWALKERS
inherent_traits = list(
TRAIT_MUTANT_COLORS,
TRAIT_VIRUSIMMUNE,
- TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION,
)
species_language_holder = /datum/language_holder/lizard/ash
digitigrade_customization = DIGITIGRADE_FORCED
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 95e2e6ed13379..6d94c2c5be978 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -442,7 +442,7 @@
return // Silicons are always standing by default.
/mob/living/silicon/get_butt_sprite()
- return BUTT_SPRITE_QR_CODE
+ return icon('icons/mob/butts.dmi', BUTT_SPRITE_QR_CODE)
/**
* Records an IC event log entry in the cyborg's internal tablet.
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 80f1b2dfb484d..7dd1cf0d2880f 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1537,8 +1537,8 @@
else
speedies += thing.slowdown
- //if our movespeed mod is in the negatives, we don't modify it since that's a benefit
- if(speedies > 0 && HAS_TRAIT(src, TRAIT_SETTLER))
+ //if we have TRAIT_STURDY_FRAME, we reduce our overall speed penalty UNLESS that penalty would be a negative value, and therefore a speed boost.
+ if(speedies > 0 && HAS_TRAIT(src, TRAIT_STURDY_FRAME))
speedies *= 0.2
if(immutable_speedies)
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 123fb0de82cd7..d186661d87fae 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -140,8 +140,8 @@
new_borg.gender = gender
new_borg.SetInvisibility(INVISIBILITY_NONE)
- if(client)
- new_borg.updatename(client)
+ if(client?.prefs.read_preference(/datum/preference/name/cyborg) != DEFAULT_CYBORG_NAME)
+ new_borg.apply_pref_name(/datum/preference/name/cyborg, client)
if(mind) //TODO //TODO WHAT
if(!transfer_after)
diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm
index cb830b2128e7e..e1081d0365a9b 100644
--- a/code/modules/mod/modules/modules_engineering.dm
+++ b/code/modules/mod/modules/modules_engineering.dm
@@ -15,7 +15,8 @@
/obj/item/mod/module/welding/on_suit_activation()
var/obj/item/clothing/head_cover = mod.get_part_from_slot(ITEM_SLOT_HEAD) || mod.get_part_from_slot(ITEM_SLOT_MASK) || mod.get_part_from_slot(ITEM_SLOT_EYES)
if(istype(head_cover))
- head_cover.flash_protect = FLASH_PROTECTION_WELDER
+ //this is a screen that displays an image, so flash sensitives can use this to protect against flashes.
+ head_cover.flash_protect = FLASH_PROTECTION_MAXIMUM
/obj/item/mod/module/welding/on_suit_deactivation(deleting = FALSE)
if(deleting)
diff --git a/code/modules/modular_computers/computers/item/role_tablet_presets.dm b/code/modules/modular_computers/computers/item/role_tablet_presets.dm
index ee6c9ee68f0af..44392c8c62de9 100644
--- a/code/modules/modular_computers/computers/item/role_tablet_presets.dm
+++ b/code/modules/modular_computers/computers/item/role_tablet_presets.dm
@@ -97,6 +97,7 @@
/datum/computer_file/program/crew_manifest,
/datum/computer_file/program/robocontrol,
/datum/computer_file/program/science,
+ /datum/computer_file/program/scipaper_program,
/datum/computer_file/program/status,
/datum/computer_file/program/signal_commander,
)
@@ -187,6 +188,7 @@
starting_programs = list(
/datum/computer_file/program/atmosscan,
/datum/computer_file/program/science,
+ /datum/computer_file/program/scipaper_program,
/datum/computer_file/program/signal_commander,
)
diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm
index 0ec80da4bbb29..ad87960603c35 100644
--- a/code/modules/modular_computers/computers/machinery/console_presets.dm
+++ b/code/modules/modular_computers/computers/machinery/console_presets.dm
@@ -137,6 +137,7 @@
starting_programs += /datum/computer_file/program/bounty_board
starting_programs += /datum/computer_file/program/budgetorders
starting_programs += /datum/computer_file/program/shipping
+ starting_programs += /datum/computer_file/program/restock_tracker
/obj/machinery/modular_computer/preset/cargochat/cargo/setup_starting_software()
var/datum/computer_file/program/chatclient/chatprogram = cpu.find_file_by_name("ntnrc_client")
diff --git a/code/modules/modular_computers/file_system/programs/frontier.dm b/code/modules/modular_computers/file_system/programs/frontier.dm
index c41cf642ffb55..7a65513508d3e 100644
--- a/code/modules/modular_computers/file_system/programs/frontier.dm
+++ b/code/modules/modular_computers/file_system/programs/frontier.dm
@@ -8,7 +8,7 @@
program_open_overlay = "research"
tgui_id = "NtosScipaper"
program_icon = "paper-plane"
- download_access = list(ACCESS_ORDNANCE)
+ download_access = list(ACCESS_ORDNANCE, ACCESS_SCIENCE, ACCESS_AWAY_SCIENCE)
var/datum/techweb/linked_techweb
/// Unpublished, temporary paper datum.
diff --git a/code/modules/modular_computers/file_system/programs/restock_tracker.dm b/code/modules/modular_computers/file_system/programs/restock_tracker.dm
index 46462c0c6b531..8f2174ce97bbd 100644
--- a/code/modules/modular_computers/file_system/programs/restock_tracker.dm
+++ b/code/modules/modular_computers/file_system/programs/restock_tracker.dm
@@ -5,7 +5,7 @@
program_open_overlay = "restock"
extended_desc = "Nanotrasen IoT network listing all the vending machines found on station, and how well stocked they are each. Profitable!"
program_flags = PROGRAM_ON_NTNET_STORE | PROGRAM_REQUIRES_NTNET
- can_run_on_flags = PROGRAM_LAPTOP | PROGRAM_PDA
+ can_run_on_flags = PROGRAM_ALL
size = 4
program_icon = "cash-register"
tgui_id = "NtosRestock"
diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm
index b7796ad070a87..72d3ecd85ba03 100644
--- a/code/modules/paperwork/photocopier.dm
+++ b/code/modules/paperwork/photocopier.dm
@@ -495,10 +495,9 @@ GLOBAL_LIST_INIT(paper_blanks, init_paper_blanks())
/obj/machinery/photocopier/proc/make_ass_copy()
if(!check_ass())
return null
- var/butt_icon_state = ass.get_butt_sprite()
- if(isnull(butt_icon_state))
+ var/icon/temp_img = ass.get_butt_sprite()
+ if(isnull(temp_img))
return null
- var/icon/temp_img = icon('icons/mob/butts.dmi', butt_icon_state)
var/obj/item/photo/copied_ass = new /obj/item/photo(src)
var/datum/picture/toEmbed = new(name = "[ass]'s Ass", desc = "You see [ass]'s ass on the photo.", image = temp_img)
toEmbed.psize_x = 128
diff --git a/code/modules/power/apc/apc_attack.dm b/code/modules/power/apc/apc_attack.dm
index 8c9715f1dcb1c..50f10c47e33c1 100644
--- a/code/modules/power/apc/apc_attack.dm
+++ b/code/modules/power/apc/apc_attack.dm
@@ -85,7 +85,7 @@
return
/obj/machinery/power/apc/blob_act(obj/structure/blob/B)
- set_broken()
+ atom_break()
/obj/machinery/power/apc/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armor_penetration = 0)
// APC being at 0 integrity doesnt delete it outright. Combined with take_damage this might cause runtimes.
@@ -100,11 +100,6 @@
return damage_amount
. = ..()
-/obj/machinery/power/apc/atom_break(damage_flag)
- . = ..()
- if(.)
- set_broken()
-
/obj/machinery/power/apc/proc/can_use(mob/user, loud = 0) //used by attack_hand() and Topic()
if(isAdminGhostAI(user))
return TRUE
@@ -120,17 +115,6 @@
balloon_alert(user, "it's disabled!")
return .
-/obj/machinery/power/apc/proc/set_broken()
- if(machine_stat & BROKEN)
- return
- if(malfai && operating)
- malfai.malf_picker.processing_time = clamp(malfai.malf_picker.processing_time - 10,0,1000)
- operating = FALSE
- atom_break()
- if(occupier)
- malfvacate(TRUE)
- update()
-
/obj/machinery/power/apc/proc/shock(mob/user, prb)
if(!prob(prb))
return FALSE
diff --git a/code/modules/power/apc/apc_main.dm b/code/modules/power/apc/apc_main.dm
index 7e63aff7cbe1b..69732c69b97ac 100644
--- a/code/modules/power/apc/apc_main.dm
+++ b/code/modules/power/apc/apc_main.dm
@@ -250,38 +250,51 @@
/obj/machinery/power/apc/proc/on_saboteur(datum/source, disrupt_duration)
SIGNAL_HANDLER
+
disrupt_duration *= 0.1 // so, turns out, failure timer is in seconds, not deciseconds; without this, disruptions last 10 times as long as they probably should
energy_fail(disrupt_duration)
return COMSIG_SABOTEUR_SUCCESS
+/obj/machinery/power/apc/on_set_is_operational(old_value)
+ update_area_power_usage(!old_value)
+
+/obj/machinery/power/apc/update_name(updates)
+ . = ..()
+ if(auto_name)
+ name = "\improper [get_area_name(area, TRUE)] APC"
+
/obj/machinery/power/apc/proc/assign_to_area(area/target_area = get_area(src))
if(area == target_area)
return
disconnect_from_area()
area = target_area
- area.power_light = TRUE
- area.power_equip = TRUE
- area.power_environ = TRUE
- area.power_change()
+ update_area_power_usage(TRUE)
area.apc = src
auto_name = TRUE
update_appearance(UPDATE_NAME)
-/obj/machinery/power/apc/update_name(updates)
- . = ..()
- if(auto_name)
- name = "\improper [get_area_name(area, TRUE)] APC"
+/obj/machinery/power/apc/proc/update_area_power_usage(state)
+ //apc is non functional so force disable
+ if(state && (has_electronics != APC_ELECTRONICS_SECURED || (machine_stat & (BROKEN | MAINT)) || QDELETED(cell)))
+ state = FALSE
+
+ //no change in value
+ if(state == area.power_light && state == area.power_equip && state == area.power_environ)
+ return
+
+ area.power_light = state
+ area.power_equip = state
+ area.power_environ = state
+
+ area.power_change()
/obj/machinery/power/apc/proc/disconnect_from_area()
if(isnull(area))
return
- area.power_light = FALSE
- area.power_equip = FALSE
- area.power_environ = FALSE
- area.power_change()
+ update_area_power_usage(FALSE)
area.apc = null
area = null
@@ -318,9 +331,16 @@
else
. += "The cover is closed."
-/obj/machinery/power/apc/on_deconstruction(disassembled = TRUE)
- if(!(machine_stat & BROKEN))
- set_broken()
+/obj/machinery/power/apc/atom_break(damage_flag)
+ . = ..()
+ if(.)
+ if(malfai && operating)
+ malfai.malf_picker.processing_time = clamp(malfai.malf_picker.processing_time - 10, 0, 1000)
+ operating = FALSE
+ if(occupier)
+ malfvacate(TRUE)
+ update()
+
if(opened != APC_COVER_REMOVED)
opened = APC_COVER_REMOVED
coverlocked = FALSE
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index b55593ec913e7..74352134f59f4 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -480,21 +480,6 @@
charge_light_type = null
connector_type = "slimecore"
-/obj/item/stock_parts/cell/beam_rifle
- name = "beam rifle capacitor"
- desc = "A high powered capacitor that can provide huge amounts of energy in an instant."
- maxcharge = STANDARD_CELL_CHARGE * 50
- chargerate = STANDARD_CELL_RATE * 2.5 //Extremely energy intensive
-
-/obj/item/stock_parts/cell/beam_rifle/corrupt()
- return
-
-/obj/item/stock_parts/cell/beam_rifle/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- charge = clamp((charge-(10000/severity)),0,maxcharge)
-
/obj/item/stock_parts/cell/emergency_light
name = "miniature power cell"
desc = "A tiny power cell with a very low power capacity. Used in light fixtures to power them in the event of an outage."
diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm
index 43713cc49ae0c..9d3eb222fb843 100644
--- a/code/modules/power/lighting/light.dm
+++ b/code/modules/power/lighting/light.dm
@@ -77,12 +77,13 @@
var/fire_power = 0.5
///The Light colour to use when working in fire alarm status
var/fire_colour = COLOR_FIRE_LIGHT_RED
-
///Power usage - W per unit of luminosity
var/power_consumption_rate = 20
+ ///break if moved, if false also makes it ignore if the wall its on breaks
+ var/break_if_moved = TRUE
/obj/machinery/light/Move()
- if(status != LIGHT_BROKEN)
+ if(status != LIGHT_BROKEN && break_if_moved)
break_light_tube(TRUE)
return ..()
@@ -117,7 +118,8 @@
RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater))
RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
AddElement(/datum/element/atmos_sensitive, mapload)
- find_and_hang_on_wall(custom_drop_callback = CALLBACK(src, PROC_REF(knock_down)))
+ if(break_if_moved)
+ find_and_hang_on_wall(custom_drop_callback = CALLBACK(src, PROC_REF(knock_down)))
/obj/machinery/light/post_machine_initialize()
. = ..()
@@ -733,3 +735,8 @@
/obj/machinery/light/floor/broken
status = LIGHT_BROKEN
icon_state = "floor-broken"
+
+/obj/machinery/light/floor/transport
+ name = "transport light"
+ break_if_moved = FALSE
+ layer = BELOW_OPEN_DOOR_LAYER
diff --git a/code/modules/power/singularity/boh_tear.dm b/code/modules/power/singularity/reality_tear.dm
similarity index 59%
rename from code/modules/power/singularity/boh_tear.dm
rename to code/modules/power/singularity/reality_tear.dm
index a0089a7c94b35..e43301541a08f 100644
--- a/code/modules/power/singularity/boh_tear.dm
+++ b/code/modules/power/singularity/reality_tear.dm
@@ -1,4 +1,7 @@
-/obj/boh_tear
+/// Tear in the Fabric of Reality ///
+// Typically spawned by placing two bags of holding into one another, collapsing into a wandering singularity after a brief period as a stationary singularity.
+
+/obj/reality_tear
name = "tear in the fabric of reality"
desc = "As you gaze into the abyss, the only thing you can think is... \"Should I really be this close to it?\""
anchored = TRUE
@@ -15,30 +18,36 @@
pixel_y = -32
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
flags_1 = SUPERMATTER_IGNORES_1
+ /// Range that our singularity component consumes objects
+ var/singularity_consume_range = 1
+ /// Ranges that the singularity pulls objects
+ var/singularity_grav_pull = 21
+ /// Time before we begin our bagulo spawn
+ var/collapse_spawn_time = 9 SECONDS
-/obj/boh_tear/proc/start_disaster()
+/obj/reality_tear/proc/start_disaster()
apply_wibbly_filters(src)
playsound(loc, 'sound/effects/clockcult_gateway_disrupted.ogg', vary = 200, extrarange = 3, falloff_exponent = 1, frequency = 0.33, pressure_affected = FALSE, ignore_walls = TRUE, falloff_distance = 7)
AddComponent(
/datum/component/singularity, \
- consume_range = 1, \
- grav_pull = 21, \
+ consume_range = singularity_consume_range, \
+ grav_pull = singularity_grav_pull, \
roaming = FALSE, \
singularity_size = STAGE_SIX, \
)
- addtimer(CALLBACK(src, PROC_REF(bagulo_time)), 9 SECONDS, TIMER_DELETE_ME)
+ addtimer(CALLBACK(src, PROC_REF(reality_collapse)), collapse_spawn_time, TIMER_DELETE_ME)
animate(src, time = 7.5 SECONDS, transform = transform.Scale(2), flags = ANIMATION_PARALLEL)
animate(time = 2 SECONDS, transform = transform.Scale(0.25), easing = ELASTIC_EASING)
animate(time = 0.5 SECONDS, alpha = 0)
-/obj/boh_tear/proc/bagulo_time()
+/obj/reality_tear/proc/reality_collapse()
playsound(loc, 'sound/effects/supermatter.ogg', 200, vary = TRUE, extrarange = 3, falloff_exponent = 1, frequency = 0.5, pressure_affected = FALSE, ignore_walls = TRUE, falloff_distance = 7)
var/obj/singularity/bagulo = new(loc)
bagulo.expand(STAGE_TWO)
bagulo.energy = 400
qdel(src)
-/obj/boh_tear/attack_tk(mob/user)
+/obj/reality_tear/attack_tk(mob/user)
if(!isliving(user))
return
var/mob/living/jedi = user
@@ -47,3 +56,15 @@
jedi.spawn_dust()
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, attack_hand), jedi), 0.5 SECONDS)
return COMPONENT_CANCEL_ATTACK_CHAIN
+
+//The temporary tears in reality. Collapses into nothing, and has a significantly lower gravity pull range, but consumes more widely.
+
+/obj/reality_tear/temporary
+ name = "puncture in the fabric of reality"
+ desc = "Count your lucky stars that this wasn't anywhere near you."
+ singularity_consume_range = 2
+ singularity_grav_pull = 3
+ collapse_spawn_time = 2 SECONDS
+
+/obj/reality_tear/temporary/reality_collapse()
+ qdel(src)
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index 4c00e83b0bbc6..b977441e9ae11 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -26,14 +26,23 @@
var/single_shot_type_overlay = TRUE
///Should we give an overlay to empty guns?
var/display_empty = TRUE
- var/selfcharge = 0
- var/charge_timer = 0
- var/charge_delay = 8
+
///whether the gun's cell drains the cyborg user's cell to recharge
var/use_cyborg_cell = FALSE
///set to true so the gun is given an empty cell
var/dead_cell = FALSE
+ // Self charging vars
+
+ /// Whether or not our gun charges its own cell on a timer.
+ var/selfcharge = 0
+ /// The amount of time between instances of cell self recharge
+ var/charge_timer = 0
+ /// The amount of seconds_per_tick during process() before the gun charges itself
+ var/charge_delay = 8
+ /// The amount restored by the gun to the cell per self charge tick
+ var/self_charge_amount = STANDARD_ENERGY_GUN_SELF_CHARGE_RATE
+
/obj/item/gun/energy/fire_sounds()
// What frequency the energy gun's sound will make
var/pitch_to_use = 1
@@ -153,7 +162,7 @@
if(charge_timer < charge_delay)
return
charge_timer = 0
- cell.give(STANDARD_ENERGY_GUN_SELF_CHARGE_RATE * seconds_per_tick)
+ cell.give(self_charge_amount * seconds_per_tick)
if(!chambered) //if empty chamber we try to charge a new shot
recharge_newshot(TRUE)
update_appearance()
diff --git a/code/modules/projectiles/guns/energy/beam_rifle.dm b/code/modules/projectiles/guns/energy/beam_rifle.dm
index e29e1ef4878be..0bda1930c6260 100644
--- a/code/modules/projectiles/guns/energy/beam_rifle.dm
+++ b/code/modules/projectiles/guns/energy/beam_rifle.dm
@@ -1,588 +1,64 @@
-
-#define ZOOM_LOCK_AUTOZOOM_FREEMOVE 0
-#define ZOOM_LOCK_AUTOZOOM_ANGLELOCK 1
-#define ZOOM_LOCK_CENTER_VIEW 2
-#define ZOOM_LOCK_OFF 3
-
-#define AUTOZOOM_PIXEL_STEP_FACTOR 48
-
-#define AIMING_BEAM_ANGLE_CHANGE_THRESHOLD 0.1
-
-/obj/item/gun/energy/beam_rifle
- name = "particle acceleration rifle"
- desc = "An energy-based anti material marksman rifle that uses highly charged particle beams moving at extreme velocities to decimate whatever is unfortunate enough to be targeted by one."
- desc_controls = "Hold down left click while scoped to aim, when weapon is fully aimed (Tracer goes from red to green as it charges), release to fire. Moving while aiming or changing where you're pointing at while aiming will delay the aiming process depending on how much you changed."
+/obj/item/gun/energy/event_horizon
+ name = "\improper Event Horizon anti-existential beam rifle"
+ desc = "The deranged minds of Nanotrasen, in their great hubris and spite, have birthed forth the definitive conclusion to the arms race. Weaponized black holes, and a platform to deliver them.\
+ To look upon this existential maleficence is to know that the pursuit of profit has consigned all life to this pathetic conclusion; the destruction of reality itself."
icon = 'icons/obj/weapons/guns/energy.dmi'
icon_state = "esniper"
inhand_icon_state = null
worn_icon_state = null
fire_sound = 'sound/weapons/beam_sniper.ogg'
slot_flags = ITEM_SLOT_BACK
- force = 15
+ force = 20 //This is maybe the sanest part of this weapon.
custom_materials = null
- recoil = 4
+ recoil = 2
ammo_x_offset = 3
ammo_y_offset = 3
modifystate = FALSE
charge_sections = 1
weapon_weight = WEAPON_HEAVY
w_class = WEIGHT_CLASS_BULKY
- ammo_type = list(/obj/item/ammo_casing/energy/beam_rifle/hitscan)
- actions_types = list(/datum/action/item_action/zoom_lock_action)
- cell_type = /obj/item/stock_parts/cell/beam_rifle
- var/aiming = FALSE
- var/aiming_time = 12
- var/aiming_time_fire_threshold = 5
- var/aiming_time_left = 12
- var/aiming_time_increase_user_movement = 3
- var/scoped_slow = 1
- var/aiming_time_increase_angle_multiplier = 0.3
- var/last_process = 0
-
- var/lastangle = 0
- var/aiming_lastangle = 0
- var/mob/current_user = null
- var/list/obj/effect/projectile/tracer/current_tracers
-
- var/structure_piercing = 2 //Amount * 2. For some reason structures aren't respecting this unless you have it doubled. Probably with the objects in question's Bump() code instead of this but I'll deal with this later.
- var/structure_bleed_coeff = 0.7
- var/wall_pierce_amount = 0
- var/wall_devastate = 0
- var/aoe_structure_range = 1
- var/aoe_structure_damage = 50
- var/aoe_fire_range = 2
- var/aoe_fire_chance = 40
- var/aoe_mob_range = 1
- var/aoe_mob_damage = 30
- var/impact_structure_damage = 60
- var/projectile_damage = 30
- var/projectile_stun = 0
- var/projectile_setting_pierce = TRUE
- var/delay = 25
- var/lastfire = 0
-
- //ZOOMING
- var/zoom_current_view_increase = 0
- ///The radius you want to zoom by
- var/zoom_target_view_increase = 9.5
- var/zooming = FALSE
- var/zoom_lock = ZOOM_LOCK_OFF
- var/zooming_angle
- var/current_zoom_x = 0
- var/current_zoom_y = 0
-
- var/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/trace = null
-
-/obj/item/gun/energy/beam_rifle/apply_fantasy_bonuses(bonus)
- . = ..()
- delay = modify_fantasy_variable("delay", delay, -bonus * 2)
- aiming_time = modify_fantasy_variable("aiming_time", aiming_time, -bonus * 2)
- recoil = modify_fantasy_variable("recoil", recoil, round(-bonus / 2))
-
-/obj/item/gun/energy/beam_rifle/remove_fantasy_bonuses(bonus)
- delay = reset_fantasy_variable("delay", delay)
- aiming_time = reset_fantasy_variable("aiming_time", aiming_time)
- recoil = reset_fantasy_variable("recoil", recoil)
- return ..()
-
-/obj/item/gun/energy/beam_rifle/debug
- delay = 0
- cell_type = /obj/item/stock_parts/cell/infinite
- aiming_time = 0
- recoil = 0
- pin = /obj/item/firing_pin
-
-/obj/item/gun/energy/beam_rifle/equipped(mob/user)
- set_user(user)
- return ..()
-
-/obj/item/gun/energy/beam_rifle/pickup(mob/user)
- set_user(user)
- return ..()
-
-/obj/item/gun/energy/beam_rifle/dropped(mob/user)
- set_user()
- return ..()
-
-/obj/item/gun/energy/beam_rifle/ui_action_click(mob/user, actiontype)
- if(istype(actiontype, /datum/action/item_action/zoom_lock_action))
- zoom_lock++
- if(zoom_lock > 3)
- zoom_lock = 0
- switch(zoom_lock)
- if(ZOOM_LOCK_AUTOZOOM_FREEMOVE)
- to_chat(user, span_boldnotice("You switch [src]'s zooming processor to free directional."))
- if(ZOOM_LOCK_AUTOZOOM_ANGLELOCK)
- to_chat(user, span_boldnotice("You switch [src]'s zooming processor to locked directional."))
- if(ZOOM_LOCK_CENTER_VIEW)
- to_chat(user, span_boldnotice("You switch [src]'s zooming processor to center mode."))
- if(ZOOM_LOCK_OFF)
- to_chat(user, span_boldnotice("You disable [src]'s zooming system."))
- reset_zooming()
- return
-
- return ..()
+ ammo_type = list(/obj/item/ammo_casing/energy/event_horizon)
+ selfcharge = TRUE
+ self_charge_amount = STANDARD_ENERGY_GUN_SELF_CHARGE_RATE * 10
-/obj/item/gun/energy/beam_rifle/proc/set_autozoom_pixel_offsets_immediate(current_angle)
- if(zoom_lock == ZOOM_LOCK_CENTER_VIEW || zoom_lock == ZOOM_LOCK_OFF)
- return
- current_zoom_x = sin(current_angle) + sin(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
- current_zoom_y = cos(current_angle) + cos(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
-
-/obj/item/gun/energy/beam_rifle/proc/handle_zooming()
- if(!zooming || !check_user())
- return
- current_user.client.view_size.setTo(zoom_target_view_increase)
- zoom_current_view_increase = zoom_target_view_increase
- set_autozoom_pixel_offsets_immediate(zooming_angle)
-
-/obj/item/gun/energy/beam_rifle/proc/start_zooming()
- if(zoom_lock == ZOOM_LOCK_OFF)
- return
- zooming = TRUE
-
-/obj/item/gun/energy/beam_rifle/proc/stop_zooming(mob/user)
- if(zooming)
- zooming = FALSE
- reset_zooming(user)
-
-/obj/item/gun/energy/beam_rifle/proc/reset_zooming(mob/user)
- if(!user)
- user = current_user
- if(!user || !user.client)
- return FALSE
- user.client.view_size.zoomIn()
- zoom_current_view_increase = 0
- zooming_angle = 0
- current_zoom_x = 0
- current_zoom_y = 0
-
-/obj/item/gun/energy/beam_rifle/attack_self(mob/user)
- projectile_setting_pierce = !projectile_setting_pierce
- balloon_alert(user, "switched to [projectile_setting_pierce ? "pierce":"impact"] mode")
- aiming_beam()
-
-/obj/item/gun/energy/beam_rifle/proc/update_slowdown()
- if(aiming)
- slowdown = scoped_slow
- else
- slowdown = initial(slowdown)
-
-/obj/item/gun/energy/beam_rifle/Initialize(mapload)
+/obj/item/gun/energy/event_horizon/Initialize(mapload)
. = ..()
- fire_delay = delay
- current_tracers = list()
- START_PROCESSING(SSfastprocess, src)
-
-/obj/item/gun/energy/beam_rifle/Destroy()
- STOP_PROCESSING(SSfastprocess, src)
- set_user(null)
- QDEL_LIST(current_tracers)
- return ..()
-
-/obj/item/gun/energy/beam_rifle/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- chambered = null
- recharge_newshot()
-
-/obj/item/gun/energy/beam_rifle/proc/aiming_beam(force_update = FALSE)
- var/diff = abs(aiming_lastangle - lastangle)
- if(!check_user())
- return
- if(diff < AIMING_BEAM_ANGLE_CHANGE_THRESHOLD && !force_update)
- return
- aiming_lastangle = lastangle
- // ONLY ONE at once (since fire can sleep)
- if(trace)
- QDEL_NULL(trace)
- trace = new
- trace.gun = src
- trace.wall_pierce_amount = wall_pierce_amount
- trace.structure_pierce_amount = structure_piercing
- trace.do_pierce = projectile_setting_pierce
- if(aiming_time)
- var/percent = ((100/aiming_time)*aiming_time_left)
- trace.color = rgb(255 * percent,255 * ((100 - percent) / 100),0)
- else
- trace.color = rgb(0, 255, 0)
- var/turf/curloc = get_turf(src)
-
- var/atom/target_atom = current_user.client.mouse_object_ref?.resolve()
- var/turf/targloc = get_turf(target_atom)
- if(!istype(targloc))
- if(!istype(curloc))
- return
- targloc = get_turf_in_angle(lastangle, curloc, 10)
- var/mouse_modifiers = params2list(current_user.client.mouseParams)
- trace.preparePixelProjectile(targloc, current_user, mouse_modifiers, 0)
- trace.fire(lastangle)
- trace = null
+ AddComponent(/datum/component/scope, range_modifier = 4)
-/obj/item/gun/energy/beam_rifle/process()
- if(!aiming)
- last_process = world.time
- return
- check_user()
- handle_zooming()
- aiming_time_left = max(0, aiming_time_left - (world.time - last_process))
- aiming_beam(TRUE)
- last_process = world.time
-
-/obj/item/gun/energy/beam_rifle/proc/check_user(automatic_cleanup = TRUE)
- if(!istype(current_user) || !isturf(current_user.loc) || !(src in current_user.held_items) || current_user.incapacitated()) //Doesn't work if you're not holding it!
- if(automatic_cleanup)
- stop_aiming()
- return FALSE
- return TRUE
-
-/obj/item/gun/energy/beam_rifle/proc/process_aim(params)
- var/angle = mouse_angle_from_client(current_user?.client, params)
- current_user.setDir(angle2dir_cardinal(angle))
- var/difference = abs(closer_angle_difference(lastangle, angle))
- delay_penalty(difference * aiming_time_increase_angle_multiplier)
- lastangle = angle
-
-/obj/item/gun/energy/beam_rifle/proc/on_mob_move()
- SIGNAL_HANDLER
- check_user()
- if(aiming)
- delay_penalty(aiming_time_increase_user_movement)
- process_aim(current_user?.client?.mouseParams)
- INVOKE_ASYNC(src, PROC_REF(aiming_beam), TRUE)
-
-/obj/item/gun/energy/beam_rifle/proc/start_aiming(params)
- aiming_time_left = aiming_time
- aiming = TRUE
- process_aim(params)
- aiming_beam(TRUE)
- zooming_angle = lastangle
- start_zooming()
-
-/obj/item/gun/energy/beam_rifle/proc/stop_aiming(mob/user)
- set waitfor = FALSE
- aiming_time_left = aiming_time
- aiming = FALSE
- QDEL_LIST(current_tracers)
- QDEL_NULL(trace)
- stop_zooming(user)
-
-/obj/item/gun/energy/beam_rifle/proc/set_user(mob/user)
- if(user == current_user)
- return
- stop_aiming(current_user)
- if(istype(current_user))
- unregister_client_signals(current_user)
- UnregisterSignal(current_user, list(COMSIG_MOVABLE_MOVED, COMSIG_MOB_LOGIN, COMSIG_MOB_LOGOUT))
- current_user = null
- if(!istype(user))
- return
- current_user = user
- RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_mob_move))
- RegisterSignal(user, COMSIG_MOB_LOGIN, PROC_REF(register_client_signals))
- RegisterSignal(user, COMSIG_MOB_LOGOUT, PROC_REF(unregister_client_signals))
- if(user.client)
- register_client_signals(user)
+/obj/item/gun/energy/event_horizon/process_fire(atom/target, mob/living/user, message, params, zone_override, bonus_spread)
-/obj/item/gun/energy/beam_rifle/proc/register_client_signals(mob/source)
- SIGNAL_HANDLER
- RegisterSignal(source.client, COMSIG_CLIENT_MOUSEDOWN, PROC_REF(on_mouse_down))
-
-/obj/item/gun/energy/beam_rifle/proc/unregister_client_signals(mob/source)
- SIGNAL_HANDLER
- stop_aiming()
- if(QDELETED(source.client))
- return
- UnregisterSignal(source.client, list(COMSIG_CLIENT_MOUSEDOWN, COMSIG_CLIENT_MOUSEUP, COMSIG_CLIENT_MOUSEDRAG))
-
-///change the aiming beam angle to that of the mouse cursor.
-/obj/item/gun/energy/beam_rifle/proc/on_mouse_drag(client/source, src_object, over_object, src_location, over_location, src_control, over_control, params)
- SIGNAL_HANDLER
- if(aiming)
- process_aim(params)
- INVOKE_ASYNC(src, PROC_REF(aiming_beam))
- if(zoom_lock == ZOOM_LOCK_AUTOZOOM_FREEMOVE)
- zooming_angle = lastangle
- set_autozoom_pixel_offsets_immediate(zooming_angle)
-
-///Start aiming and charging the beam
-/obj/item/gun/energy/beam_rifle/proc/on_mouse_down(client/source, atom/movable/object, location, control, params)
- SIGNAL_HANDLER
- if(source.mob.get_active_held_item() != src)
- return
- if(!object.IsAutoclickable() || (object in source.mob.contents) || (object == source.mob))
+ if(!HAS_TRAIT(user, TRAIT_USER_SCOPED))
+ balloon_alert(user, "must be scoped!")
return
- INVOKE_ASYNC(src, PROC_REF(start_aiming), params)
- RegisterSignal(source, COMSIG_CLIENT_MOUSEDRAG, PROC_REF(on_mouse_drag))
- RegisterSignal(source, COMSIG_CLIENT_MOUSEUP, PROC_REF(on_mouse_up))
-
-///Stop aiming and fire the beam if charged enough
-/obj/item/gun/energy/beam_rifle/proc/on_mouse_up(client/source, atom/movable/object, location, control, params)
- SIGNAL_HANDLER
- if(!object.IsAutoclickable())
- return
- process_aim(params)
- UnregisterSignal(source, list(COMSIG_CLIENT_MOUSEDRAG, COMSIG_CLIENT_MOUSEUP))
- if(aiming_time_left <= aiming_time_fire_threshold && check_user())
- sync_ammo()
- var/atom/target = source.mouse_object_ref?.resolve()
- if(target)
- INVOKE_ASYNC(src, PROC_REF(try_fire_gun), target, source.mob, source.mouseParams, TRUE)
- stop_aiming()
- QDEL_LIST(current_tracers)
-
-/obj/item/gun/energy/beam_rifle/try_fire_gun(atom/target, mob/living/user, params, passthrough = FALSE)
- if(user.Adjacent(target)) //It's adjacent, is the user, or is on the user's person
- if(target in user.contents) //can't shoot stuff inside us.
- return FALSE
- if(!ismob(target) || user.combat_mode) //melee attack
- return FALSE
- if(target == user && user.zone_selected != BODY_ZONE_PRECISE_MOUTH) //so we can't shoot ourselves (unless mouth selected)
- return FALSE
- if(!passthrough && (aiming_time > aiming_time_fire_threshold))
- return FALSE
- if(lastfire > world.time + delay)
- return FALSE
- if(!..())
- return FALSE
- lastfire = world.time
- stop_aiming()
- return TRUE
-
-/obj/item/gun/energy/beam_rifle/proc/sync_ammo()
- for(var/obj/item/ammo_casing/energy/beam_rifle/AC in contents)
- AC.sync_stats()
-/obj/item/gun/energy/beam_rifle/proc/delay_penalty(amount)
- aiming_time_left = clamp(aiming_time_left + amount, 0, aiming_time)
-
-/obj/item/ammo_casing/energy/beam_rifle
- name = "particle acceleration lens"
- desc = "Don't look into barrel!"
- var/wall_pierce_amount = 0
- var/wall_devastate = 0
- var/aoe_structure_range = 1
- var/aoe_structure_damage = 30
- var/aoe_fire_range = 2
- var/aoe_fire_chance = 66
- var/aoe_mob_range = 1
- var/aoe_mob_damage = 20
- var/impact_structure_damage = 50
- var/projectile_damage = 40
- var/projectile_stun = 0
- var/structure_piercing = 2
- var/structure_bleed_coeff = 0.7
- var/do_pierce = TRUE
- var/obj/item/gun/energy/beam_rifle/host
-
-/obj/item/ammo_casing/energy/beam_rifle/proc/sync_stats()
- var/obj/item/gun/energy/beam_rifle/BR = loc
- if(!istype(BR))
- stack_trace("Beam rifle syncing error")
- host = BR
- do_pierce = BR.projectile_setting_pierce
- wall_pierce_amount = BR.wall_pierce_amount
- wall_devastate = BR.wall_devastate
- aoe_structure_range = BR.aoe_structure_range
- aoe_structure_damage = BR.aoe_structure_damage
- aoe_fire_range = BR.aoe_fire_range
- aoe_fire_chance = BR.aoe_fire_chance
- aoe_mob_range = BR.aoe_mob_range
- aoe_mob_damage = BR.aoe_mob_damage
- impact_structure_damage = BR.impact_structure_damage
- projectile_damage = BR.projectile_damage
- projectile_stun = BR.projectile_stun
- delay = BR.delay
- structure_piercing = BR.structure_piercing
- structure_bleed_coeff = BR.structure_bleed_coeff
-
-/obj/item/ammo_casing/energy/beam_rifle/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
. = ..()
- var/obj/projectile/beam/beam_rifle/hitscan/HS_BB = loaded_projectile
- if(!istype(HS_BB))
- return
- HS_BB.impact_direct_damage = projectile_damage
- HS_BB.stun = projectile_stun
- HS_BB.impact_structure_damage = impact_structure_damage
- HS_BB.aoe_mob_damage = aoe_mob_damage
- HS_BB.aoe_mob_range = clamp(aoe_mob_range, 0, 15) //Badmin safety lock
- HS_BB.aoe_fire_chance = aoe_fire_chance
- HS_BB.aoe_fire_range = aoe_fire_range
- HS_BB.aoe_structure_damage = aoe_structure_damage
- HS_BB.aoe_structure_range = clamp(aoe_structure_range, 0, 15) //Badmin safety lock
- HS_BB.wall_devastate = wall_devastate
- HS_BB.wall_pierce_amount = wall_pierce_amount
- HS_BB.structure_pierce_amount = structure_piercing
- HS_BB.structure_bleed_coeff = structure_bleed_coeff
- HS_BB.do_pierce = do_pierce
- HS_BB.gun = host
-
-/obj/item/ammo_casing/energy/beam_rifle/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread, atom/fired_from)
- var/turf/curloc = get_turf(user)
- if(!istype(curloc) || !loaded_projectile)
- return FALSE
- var/obj/item/gun/energy/beam_rifle/gun = loc
- if(!targloc && gun)
- targloc = get_turf_in_angle(gun.lastangle, curloc, 10)
- else if(!targloc)
- return FALSE
- var/firing_dir
- if(loaded_projectile.firer)
- firing_dir = loaded_projectile.firer.dir
- if(!loaded_projectile.suppressed && firing_effect_type)
- new firing_effect_type(get_turf(src), firing_dir)
- var/modifiers = params2list(params)
- loaded_projectile.preparePixelProjectile(target, user, modifiers, spread)
- loaded_projectile.fire(gun? gun.lastangle : null, null)
- loaded_projectile = null
- return TRUE
+ message_admins("[ADMIN_LOOKUPFLW(user)] has fired an anti-existential beam at [ADMIN_VERBOSEJMP(user)].")
-/obj/item/ammo_casing/energy/beam_rifle/hitscan
- projectile_type = /obj/projectile/beam/beam_rifle/hitscan
- select_name = "beam"
- e_cost = LASER_SHOTS(5, 50000) // Beam rifle has a custom cell
+/obj/item/ammo_casing/energy/event_horizon
+ projectile_type = /obj/projectile/beam/event_horizon
+ select_name = "doomsday"
+ e_cost = LASER_SHOTS(1, STANDARD_CELL_CHARGE)
fire_sound = 'sound/weapons/beam_sniper.ogg'
-/obj/projectile/beam/beam_rifle
- name = "particle beam"
+/obj/projectile/beam/event_horizon
+ name = "anti-existential beam"
icon = null
hitsound = 'sound/effects/explosion3.ogg'
- damage = 0 //Handled manually.
+ damage = 100 // Does it matter?
damage_type = BURN
armor_flag = ENERGY
range = 150
jitter = 20 SECONDS
- var/obj/item/gun/energy/beam_rifle/gun
- var/structure_pierce_amount = 0 //All set to 0 so the gun can manually set them during firing.
- var/structure_bleed_coeff = 0
- var/structure_pierce = 0
- var/do_pierce = TRUE
- var/wall_pierce_amount = 0
- var/wall_pierce = 0
- var/wall_devastate = 0
- var/aoe_structure_range = 0
- var/aoe_structure_damage = 0
- var/aoe_fire_range = 0
- var/aoe_fire_chance = 0
- var/aoe_mob_range = 0
- var/aoe_mob_damage = 0
- var/impact_structure_damage = 0
- var/impact_direct_damage = 0
- var/list/pierced = list()
-
-/obj/projectile/beam/beam_rifle/proc/AOE(turf/epicenter)
- if(!epicenter)
- return
- new /obj/effect/temp_visual/explosion/fast(epicenter)
- for(var/mob/living/L in range(aoe_mob_range, epicenter)) //handle aoe mob damage
- L.adjustFireLoss(aoe_mob_damage)
- to_chat(L, span_userdanger("\The [src] sears you!"))
- for(var/turf/T in RANGE_TURFS(aoe_fire_range, epicenter)) //handle aoe fire
- if(prob(aoe_fire_chance))
- new /obj/effect/hotspot(T)
- for(var/obj/O in range(aoe_structure_range, epicenter))
- if(!isitem(O))
- O.take_damage(aoe_structure_damage * get_damage_coeff(O), BURN, LASER, FALSE)
-
-/obj/projectile/beam/beam_rifle/prehit_pierce(atom/A)
- if(isclosedturf(A) && (wall_pierce < wall_pierce_amount))
- if(prob(wall_devastate))
- if(iswallturf(A))
- var/turf/closed/wall/W = A
- W.dismantle_wall(TRUE, TRUE)
- else
- SSexplosions.medturf += A
- ++wall_pierce
- return PROJECTILE_PIERCE_PHASE // yeah this gun is a snowflakey piece of garbage
- if(isobj(A) && (structure_pierce < structure_pierce_amount))
- ++structure_pierce
- var/obj/O = A
- O.take_damage((impact_structure_damage + aoe_structure_damage) * structure_bleed_coeff * get_damage_coeff(A), BURN, ENERGY, FALSE)
- return PROJECTILE_PIERCE_PHASE // ditto and this could be refactored to on_hit honestly
- return ..()
-
-/obj/projectile/beam/beam_rifle/proc/get_damage_coeff(atom/target)
- if(istype(target, /obj/machinery/door))
- return 0.4
- if(istype(target, /obj/structure/window))
- return 0.5
- return 1
-
-/obj/projectile/beam/beam_rifle/proc/handle_impact(atom/target)
- if(isobj(target))
- var/obj/O = target
- O.take_damage(impact_structure_damage * get_damage_coeff(target), BURN, LASER, FALSE)
- if(isliving(target))
- var/mob/living/L = target
- L.adjustFireLoss(impact_direct_damage)
- L.emote("scream")
-
-/obj/projectile/beam/beam_rifle/proc/handle_hit(atom/target, piercing_hit = FALSE)
- set waitfor = FALSE
- if(!is_hostile_projectile())
- return FALSE
- playsound(src, 'sound/effects/explosion3.ogg', 100, TRUE)
- if(!do_pierce)
- AOE(get_turf(target) || get_turf(src))
- if(!QDELETED(target))
- handle_impact(target)
-
-/obj/projectile/beam/beam_rifle/on_hit(atom/target, blocked = 0, pierce_hit)
- handle_hit(target, pierce_hit)
- return ..()
-
-/obj/projectile/beam/beam_rifle/is_hostile_projectile()
- return TRUE // on hit = boom fire
-
-/obj/projectile/beam/beam_rifle/hitscan
- icon_state = ""
hitscan = TRUE
tracer_type = /obj/effect/projectile/tracer/tracer/beam_rifle
- var/constant_tracer = FALSE
-/obj/projectile/beam/beam_rifle/hitscan/generate_hitscan_tracers(cleanup = TRUE, duration = 5, impacting = TRUE, highlander)
- set waitfor = FALSE
- if(isnull(highlander))
- highlander = constant_tracer
- if(highlander && istype(gun))
- QDEL_LIST(gun.current_tracers)
- for(var/datum/point/p in beam_segments)
- gun.current_tracers += generate_tracer_between_points(p, beam_segments[p], tracer_type, color, 0, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity)
- else
- for(var/datum/point/p in beam_segments)
- generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity)
- if(cleanup)
- QDEL_LIST(beam_segments)
- beam_segments = null
- QDEL_NULL(beam_index)
-
-/obj/projectile/beam/beam_rifle/hitscan/aiming_beam
- tracer_type = /obj/effect/projectile/tracer/tracer/aiming
- name = "aiming beam"
- hitsound = null
- hitsound_wall = null
- damage = 0
- constant_tracer = TRUE
- hitscan_light_range = 0
- hitscan_light_intensity = 0
- hitscan_light_color_override = "#99ff99"
- reflectable = REFLECT_FAKEPROJECTILE
-
-/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/is_hostile_projectile()
- return FALSE // just an aiming reticle
-
-/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/prehit_pierce(atom/target)
- return PROJECTILE_DELETE_WITHOUT_HITTING
+/obj/projectile/beam/event_horizon/on_hit(atom/target, blocked, pierce_hit)
+ . = ..()
-/obj/projectile/beam/beam_rifle/hitscan/aiming_beam/on_hit(atom/target, blocked = 0, pierce_hit)
- SHOULD_CALL_PARENT(FALSE) // This is some snowflake stuff so whatever
- qdel(src)
- return BULLET_ACT_BLOCK
+ // Where we droppin' boys?
+ var/turf/rift_loc = get_turf(target)
-#undef AIMING_BEAM_ANGLE_CHANGE_THRESHOLD
-#undef AUTOZOOM_PIXEL_STEP_FACTOR
-#undef ZOOM_LOCK_AUTOZOOM_ANGLELOCK
-#undef ZOOM_LOCK_AUTOZOOM_FREEMOVE
-#undef ZOOM_LOCK_CENTER_VIEW
-#undef ZOOM_LOCK_OFF
+ // Spawn our temporary rift, then activate it.
+ var/obj/reality_tear/temporary/tear = new(rift_loc)
+ tear.start_disaster()
+ message_admins("[ADMIN_LOOKUPFLW(target)] has been hit by an anti-existential beam at [ADMIN_VERBOSEJMP(rift_loc)], creating a singularity.")
diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm
index fec816b1e765f..78e75d1f30665 100644
--- a/code/modules/projectiles/guns/energy/energy_gun.dm
+++ b/code/modules/projectiles/guns/energy/energy_gun.dm
@@ -86,7 +86,7 @@
/obj/item/gun/energy/e_gun/dragnet
name = "\improper DRAGnet"
- desc = "The \"Dynamic Rapid-Apprehension of the Guilty\" net is a revolution in law enforcement technology."
+ desc = "The \"Dynamic Rapid-Apprehension of the Guilty\" net is a revolution in law enforcement technology. Can by synced with a DRAGnet beacon to set a teleport destination for snare rounds."
icon_state = "dragnet"
inhand_icon_state = "dragnet"
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
@@ -95,10 +95,35 @@
modifystate = FALSE
w_class = WEIGHT_CLASS_NORMAL
ammo_x_offset = 1
+ ///A dragnet beacon set to be the teleport destination for snare teleport rounds.
+ var/obj/item/dragnet_beacon/linked_beacon
/obj/item/gun/energy/e_gun/dragnet/add_seclight_point()
return
+/obj/item/gun/energy/e_gun/dragnet/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, /obj/item/dragnet_beacon))
+ link_beacon(user, tool)
+
+///Sets the linked_beacon var on the dragnet, which becomes the snare round's teleport destination.
+/obj/item/gun/energy/e_gun/dragnet/proc/link_beacon(mob/living/user, obj/item/dragnet_beacon/our_beacon)
+ if(linked_beacon)
+ if(our_beacon == linked_beacon)
+ balloon_alert(user, "already synced!")
+ return
+ else
+ UnregisterSignal(linked_beacon, COMSIG_QDELETING) //You're getting overridden dude.
+
+ linked_beacon = our_beacon
+ balloon_alert(user, "beacon synced")
+ RegisterSignal(our_beacon, COMSIG_QDELETING, PROC_REF(handle_beacon_disable))
+
+///Handles clearing the linked_beacon reference in the event that it is deleted.
+/obj/item/gun/energy/e_gun/dragnet/proc/handle_beacon_disable(datum/source)
+ SIGNAL_HANDLER
+ visible_message(span_warning("A light on the [src] flashes, indicating that it is no longer linked with a DRAGnet beacon!"))
+ linked_beacon = null
+
/obj/item/gun/energy/e_gun/dragnet/snare
name = "Energy Snare Launcher"
desc = "Fires an energy snare that slows the target down."
diff --git a/code/modules/projectiles/projectile/energy/net_snare.dm b/code/modules/projectiles/projectile/energy/net_snare.dm
index 1bb7988e4bae4..ac35fb5503e68 100644
--- a/code/modules/projectiles/projectile/energy/net_snare.dm
+++ b/code/modules/projectiles/projectile/energy/net_snare.dm
@@ -11,10 +11,15 @@
SpinAnimation()
/obj/projectile/energy/net/on_hit(atom/target, blocked = 0, pierce_hit)
+ var/obj/item/dragnet_beacon/destination_beacon = null
+ var/obj/item/gun/energy/e_gun/dragnet/our_dragnet = fired_from
+ if(our_dragnet && istype(our_dragnet))
+ destination_beacon = our_dragnet.linked_beacon
+
if(isliving(target))
var/turf/Tloc = get_turf(target)
if(!locate(/obj/effect/nettingportal) in Tloc)
- new /obj/effect/nettingportal(Tloc)
+ new /obj/effect/nettingportal(Tloc, destination_beacon)
. = ..()
/obj/projectile/energy/net/on_range()
@@ -29,26 +34,18 @@
light_range = 3
anchored = TRUE
-/obj/effect/nettingportal/Initialize(mapload)
+/obj/effect/nettingportal/Initialize(mapload, destination_beacon)
. = ..()
- var/obj/item/beacon/teletarget = null
- for(var/obj/machinery/computer/teleporter/com as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/computer/teleporter))
- var/atom/target = com.target_ref?.resolve()
- if(target)
- if(com.power_station && com.power_station.teleporter_hub && com.power_station.engaged)
- teletarget = target
- else
- com.target_ref = null
-
+ var/obj/item/dragnet_beacon/teletarget = destination_beacon
addtimer(CALLBACK(src, PROC_REF(pop), teletarget), 3 SECONDS)
/obj/effect/nettingportal/proc/pop(teletarget)
if(teletarget)
- for(var/mob/living/L in get_turf(src))
- do_teleport(L, teletarget, 2, channel = TELEPORT_CHANNEL_BLUESPACE)//teleport what's in the tile to the beacon
+ for(var/mob/living/living_mob in get_turf(src))
+ do_teleport(living_mob, get_turf(teletarget), 1, channel = TELEPORT_CHANNEL_BLUESPACE) //Teleport what's in the tile to the beacon
else
- for(var/mob/living/L in get_turf(src))
- do_teleport(L, L, 15, channel = TELEPORT_CHANNEL_BLUESPACE) //Otherwise it just warps you off somewhere.
+ for(var/mob/living/living_mob in get_turf(src))
+ do_teleport(living_mob, get_turf(living_mob), 15, channel = TELEPORT_CHANNEL_BLUESPACE) //Otherwise it just warps you off somewhere.
qdel(src)
@@ -58,6 +55,66 @@
/obj/effect/nettingportal/singularity_pull()
return
+/obj/item/dragnet_beacon
+ name = "\improper DRAGnet beacon"
+ desc = "Can be synced with a DRAGnet to set it as a designated teleporting point."
+ icon = 'icons/obj/devices/tracker.dmi'
+ icon_state = "dragnet_beacon"
+ inhand_icon_state = "beacon"
+ lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
+ ///Has a security ID been used to lock this in place?
+ var/locked = FALSE
+
+/obj/item/dragnet_beacon/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, /obj/item/gun/energy/e_gun/dragnet))
+ var/obj/item/gun/energy/e_gun/dragnet/dragnet_to_link = tool
+ dragnet_to_link.link_beacon(user, src)
+ return
+
+ if(isidcard(tool))
+ if(!anchored)
+ balloon_alert(user, "wrench the beacon first!")
+ return
+
+ if(obj_flags & EMAGGED)
+ balloon_alert(user, "the access control is fried!")
+ return
+
+ var/obj/item/card/id/id_card = tool
+ if((ACCESS_SECURITY in id_card.GetAccess()))
+ locked = !locked
+ balloon_alert(user, "beacon [locked ? "locked" : "unlocked"]")
+ else
+ balloon_alert(user, "no access!")
+
+/obj/item/dragnet_beacon/wrench_act(mob/living/user, obj/item/tool)
+ if(user.is_holding(src))
+ balloon_alert(user, "put it down first!")
+ return ITEM_INTERACT_BLOCKING
+
+ if(anchored && locked)
+ balloon_alert(user, "must be unlocked first!")
+ return ITEM_INTERACT_BLOCKING
+
+ if(isinspace() && !anchored)
+ balloon_alert(user, "nothing to anchor to!")
+ return ITEM_INTERACT_BLOCKING
+
+ set_anchored(!anchored)
+ tool.play_tool_sound(src, 75)
+ user.balloon_alert_to_viewers("[anchored ? "anchored" : "unanchored"]")
+
+/obj/item/dragnet_beacon/emag_act(mob/user, obj/item/card/emag/emag_card)
+ if(obj_flags & EMAGGED)
+ return FALSE
+ obj_flags |= EMAGGED
+ locked = FALSE
+ set_anchored(FALSE)
+ do_sparks(3, TRUE, src)
+ balloon_alert(user, "beacon unlocked")
+ return TRUE
+
/obj/projectile/energy/trap
name = "energy snare"
icon_state = "e_snare"
diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
index e206ffebbc9f8..a4fa10cb88c63 100644
--- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
+++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm
@@ -422,7 +422,7 @@
var/duration = time / speed
- Shake(duration = duration)
+ Shake(pixelshiftx = 1, pixelshifty = 0, duration = duration)
operating = TRUE
if(!juicing)
playsound(src, 'sound/machines/blender.ogg', 50, TRUE)
@@ -490,7 +490,7 @@
var/duration = time / speed
- Shake(duration = duration)
+ Shake(pixelshiftx = 1, pixelshifty = 0, duration = duration)
operating = TRUE
playsound(src, 'sound/machines/juicer.ogg', 20, TRUE)
diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm
index 387f3b84d4911..bb426436599c0 100644
--- a/code/modules/reagents/reagent_containers/spray.dm
+++ b/code/modules/reagents/reagent_containers/spray.dm
@@ -96,7 +96,6 @@
/obj/item/reagent_containers/spray/proc/do_spray(atom/target, wait_step, obj/effect/decal/chempuff/reagent_puff, range, puff_reagent_left, mob/user)
reagent_puff.user = user
reagent_puff.sprayer = src
- reagent_puff.lifetime = puff_reagent_left
reagent_puff.stream = stream_mode
var/turf/target_turf = get_turf(target)
diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm
index 7d4f613f05478..ef6c65d183175 100644
--- a/code/modules/research/designs/machine_designs.dm
+++ b/code/modules/research/designs/machine_designs.dm
@@ -747,7 +747,6 @@
name = "NTNet Relay Board"
desc = "The circuit board for a wireless network relay."
id = "ntnet_relay"
- build_type = IMPRINTER
build_path = /obj/item/circuitboard/machine/ntnet_relay
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_TELECOMMS
diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm
index 8ae869220deef..0ea6c41e16448 100644
--- a/code/modules/research/designs/misc_designs.dm
+++ b/code/modules/research/designs/misc_designs.dm
@@ -818,6 +818,19 @@
)
departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
+/datum/design/dragnet_beacon
+ name = "DRAGnet Beacon"
+ desc = "A beacon that can be used as a teleport destination for DRAGnet snare rounds. Remember to sync it with your DRAGnet first!"
+ id = "dragnet_beacon"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2)
+ build_path = /obj/item/dragnet_beacon
+ category = list(
+ RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SECURITY
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
/datum/design/inspector
name = "N-Spect Scanner"
desc = "Central Command-issued inspection device. Performs inspections according to Nanotrasen protocols when activated, then prints an encrypted report regarding the maintenance of the station. Definitely not giving you cancer."
diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm
index 00c7dba3946bd..35d95d82d3047 100644
--- a/code/modules/research/designs/weapon_designs.dm
+++ b/code/modules/research/designs/weapon_designs.dm
@@ -218,8 +218,8 @@
autolathe_exportable = FALSE
/datum/design/beamrifle
- name = "Beam Marksman Rifle Part Kit (Lethal)"
- desc = "The gunkit for a powerful long ranged anti-material rifle that fires charged particle beams to obliterate targets."
+ name = "Event Horizon Anti-Existential Beam Rifle Part Kit (DOOMSDAY DEVICE)"
+ desc = "The kit that produces a weapon made to end your foes on an existential level. Why the fuck can you make this?"
id = "beamrifle"
build_type = PROTOLATHE | AWAY_LATHE
materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5, /datum/material/glass =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/diamond =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/uranium = SHEET_MATERIAL_AMOUNT * 4, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 2.25, /datum/material/gold =SHEET_MATERIAL_AMOUNT * 2.5)
@@ -230,7 +230,6 @@
departmental_flags = DEPARTMENT_BITFLAG_SECURITY
autolathe_exportable = FALSE
-
/datum/design/rapidsyringe
name = "Rapid Syringe Gun"
desc = "A gun that fires many syringes."
diff --git a/code/modules/research/techweb/nodes/biology_nodes.dm b/code/modules/research/techweb/nodes/biology_nodes.dm
index 6a14d7d517c8f..4adc78691835a 100644
--- a/code/modules/research/techweb/nodes/biology_nodes.dm
+++ b/code/modules/research/techweb/nodes/biology_nodes.dm
@@ -6,6 +6,7 @@
design_ids = list(
"healthanalyzer",
"autopsyscanner",
+ "genescanner",
"medical_kiosk",
"chem_master",
"ph_meter",
@@ -54,7 +55,6 @@
"scan_console",
"dna_disk",
"dnainfuser",
- "genescanner",
"mod_dna_lock",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
diff --git a/code/modules/research/techweb/nodes/research_nodes.dm b/code/modules/research/techweb/nodes/research_nodes.dm
index b3af62c72e994..f68086ab5abdd 100644
--- a/code/modules/research/techweb/nodes/research_nodes.dm
+++ b/code/modules/research/techweb/nodes/research_nodes.dm
@@ -48,7 +48,7 @@
"bluespacebodybag",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS)
- required_experiments = list(/datum/experiment/scanning/bluespace_crystal)
+ required_experiments = list(/datum/experiment/scanning/points/bluespace_crystal)
/datum/techweb_node/bluespace_travel
id = "bluespace_travel"
diff --git a/code/modules/research/techweb/nodes/security_nodes.dm b/code/modules/research/techweb/nodes/security_nodes.dm
index 97092f2682a63..8b931fd2273e5 100644
--- a/code/modules/research/techweb/nodes/security_nodes.dm
+++ b/code/modules/research/techweb/nodes/security_nodes.dm
@@ -32,6 +32,7 @@
"scanner_gate",
"turret_control",
"pepperspray",
+ "dragnet_beacon",
"inspector",
"evidencebag",
"handcuffs_s",
diff --git a/code/modules/spells/spell_types/touch/_touch.dm b/code/modules/spells/spell_types/touch/_touch.dm
index 5045ea6522092..1ed5ce49511dd 100644
--- a/code/modules/spells/spell_types/touch/_touch.dm
+++ b/code/modules/spells/spell_types/touch/_touch.dm
@@ -127,7 +127,8 @@
/datum/action/cooldown/spell/touch/proc/register_hand_signals()
SHOULD_CALL_PARENT(TRUE)
- RegisterSignal(attached_hand, COMSIG_ITEM_AFTERATTACK, PROC_REF(on_hand_hit))
+ RegisterSignal(attached_hand, COMSIG_ITEM_INTERACTING_WITH_ATOM, PROC_REF(on_hand_hit))
+ RegisterSignal(attached_hand, COMSIG_ITEM_INTERACTING_WITH_ATOM_SECONDARY, PROC_REF(on_hand_hit_secondary))
RegisterSignal(attached_hand, COMSIG_ITEM_DROPPED, PROC_REF(on_hand_dropped))
RegisterSignal(attached_hand, COMSIG_QDELETING, PROC_REF(on_hand_deleted))
@@ -140,7 +141,8 @@
SHOULD_CALL_PARENT(TRUE)
UnregisterSignal(attached_hand, list(
- COMSIG_ITEM_AFTERATTACK,
+ COMSIG_ITEM_INTERACTING_WITH_ATOM,
+ COMSIG_ITEM_INTERACTING_WITH_ATOM_SECONDARY,
COMSIG_ITEM_DROPPED,
COMSIG_QDELETING,
COMSIG_ITEM_OFFER_TAKEN,
@@ -159,21 +161,34 @@
return ..()
/**
- * Signal proc for [COMSIG_ITEM_AFTERATTACK] from our attached hand.
+ * Signal proc for [COMSIG_ITEM_INTERACTING_WITH_ATOM] from our attached hand.
*
* When our hand hits an atom, we can cast do_hand_hit() on them.
*/
-/datum/action/cooldown/spell/touch/proc/on_hand_hit(datum/source, atom/victim, mob/caster, click_parameters)
+/datum/action/cooldown/spell/touch/proc/on_hand_hit(datum/source, mob/living/caster, atom/target, click_parameters)
SIGNAL_HANDLER
SHOULD_NOT_OVERRIDE(TRUE) // DEFINITELY don't put effects here, put them in cast_on_hand_hit
- if(!can_hit_with_hand(victim, caster))
+ if(!can_hit_with_hand(target, caster))
return
- if(LAZYACCESS(params2list(click_parameters), RIGHT_CLICK))
- INVOKE_ASYNC(src, PROC_REF(do_secondary_hand_hit), source, victim, caster)
- else
- INVOKE_ASYNC(src, PROC_REF(do_hand_hit), source, victim, caster)
+ INVOKE_ASYNC(src, PROC_REF(do_hand_hit), source, target, caster)
+ return ITEM_INTERACT_SUCCESS
+
+/**
+ * Signal proc for [COMSIG_ITEM_INTERACTING_WITH_ATOM_SECONDARY] from our attached hand.
+ *
+ * When our hand hits an atom, we can cast do_hand_hit() on them.
+ */
+/datum/action/cooldown/spell/touch/proc/on_hand_hit_secondary(datum/source, mob/living/caster, atom/target, click_parameters)
+ SIGNAL_HANDLER
+ SHOULD_NOT_OVERRIDE(TRUE)
+
+ if(!can_hit_with_hand(target, caster))
+ return
+
+ INVOKE_ASYNC(src, PROC_REF(do_secondary_hand_hit), source, target, caster)
+ return ITEM_INTERACT_SUCCESS
/// Checks if the passed victim can be cast on by the caster.
/datum/action/cooldown/spell/touch/proc/can_hit_with_hand(atom/victim, mob/caster)
@@ -206,6 +221,7 @@
log_combat(caster, victim, "cast the touch spell [name] on", hand)
spell_feedback(caster)
+ caster.do_attack_animation(victim)
remove_hand(caster)
/**
@@ -223,6 +239,7 @@
if(SECONDARY_ATTACK_CONTINUE_CHAIN)
log_combat(caster, victim, "cast the touch spell [name] on", hand, "(secondary / alt cast)")
spell_feedback(caster)
+ caster.do_attack_animation(victim)
remove_hand(caster)
// Call normal will call the normal cast proc
diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
index 03f53c962d59f..97fa50d76c85b 100644
--- a/code/modules/surgery/bodyparts/parts.dm
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -74,9 +74,11 @@
if(!ishuman(owner))
return null
var/mob/living/carbon/human/human_owner = owner
- var/butt_sprite = human_owner.physique == FEMALE ? BUTT_SPRITE_HUMAN_FEMALE : BUTT_SPRITE_HUMAN_MALE
var/obj/item/organ/external/tail/tail = human_owner.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL)
- return tail?.get_butt_sprite() || butt_sprite
+ if(tail)
+ return tail.get_butt_sprite()
+
+ return icon('icons/mob/butts.dmi', human_owner.physique == FEMALE ? BUTT_SPRITE_HUMAN_FEMALE : BUTT_SPRITE_HUMAN_MALE)
/obj/item/bodypart/chest/monkey
icon = 'icons/mob/human/species/monkey/bodyparts.dmi'
diff --git a/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm
index 30e91db21373a..157e5b04fe68e 100644
--- a/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/lizard_bodyparts.dm
@@ -11,7 +11,7 @@
wing_types = list(/obj/item/organ/external/wings/functional/dragon)
/obj/item/bodypart/chest/lizard/get_butt_sprite()
- return BUTT_SPRITE_LIZARD
+ return icon('icons/mob/butts.dmi', BUTT_SPRITE_LIZARD)
/obj/item/bodypart/arm/left/lizard
icon_greyscale = 'icons/mob/human/species/lizard/bodyparts.dmi'
diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
index 2730bc362c72c..fa3ab9cc49d39 100644
--- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
@@ -59,7 +59,7 @@
wing_types = NONE
/obj/item/bodypart/chest/abductor/get_butt_sprite()
- return BUTT_SPRITE_GREY
+ return icon('icons/mob/butts.dmi', BUTT_SPRITE_GREY)
/obj/item/bodypart/arm/left/abductor
limb_id = SPECIES_ABDUCTOR
@@ -97,7 +97,7 @@
wing_types = list(/obj/item/organ/external/wings/functional/slime)
/obj/item/bodypart/chest/jelly/get_butt_sprite()
- return BUTT_SPRITE_SLIME
+ return icon('icons/mob/butts.dmi', BUTT_SPRITE_SLIME)
/obj/item/bodypart/arm/left/jelly
biological_state = (BIO_FLESH|BIO_BLOODED)
@@ -216,7 +216,7 @@
wing_types = NONE
/obj/item/bodypart/chest/pod/get_butt_sprite()
- return BUTT_SPRITE_FLOWERPOT
+ return icon('icons/mob/butts.dmi', BUTT_SPRITE_FLOWERPOT)
/obj/item/bodypart/arm/left/pod
limb_id = SPECIES_PODPERSON
diff --git a/code/modules/surgery/bodyparts/species_parts/moth_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/moth_bodyparts.dm
index 011ee83368a63..375b37ca434d6 100644
--- a/code/modules/surgery/bodyparts/species_parts/moth_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/moth_bodyparts.dm
@@ -17,7 +17,7 @@
wing_types = list(/obj/item/organ/external/wings/functional/moth/megamoth, /obj/item/organ/external/wings/functional/moth/mothra)
/obj/item/bodypart/chest/moth/get_butt_sprite()
- return BUTT_SPRITE_FUZZY
+ return icon('icons/mob/butts.dmi', BUTT_SPRITE_FUZZY)
/obj/item/bodypart/arm/left/moth
icon = 'icons/mob/human/species/moth/bodyparts.dmi'
diff --git a/code/modules/surgery/bodyparts/species_parts/plasmaman_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/plasmaman_bodyparts.dm
index 40bf4a51c042e..b0acf914079f3 100644
--- a/code/modules/surgery/bodyparts/species_parts/plasmaman_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/plasmaman_bodyparts.dm
@@ -27,7 +27,7 @@
wing_types = NONE
/obj/item/bodypart/chest/plasmaman/get_butt_sprite()
- return BUTT_SPRITE_PLASMA
+ return icon('icons/mob/butts.dmi', BUTT_SPRITE_PLASMA)
/obj/item/bodypart/arm/left/plasmaman
icon = 'icons/mob/human/species/plasmaman/bodyparts.dmi'
diff --git a/code/modules/surgery/organs/external/tails.dm b/code/modules/surgery/organs/external/tails.dm
index 56479d0f22a10..e4cd3f50a4997 100644
--- a/code/modules/surgery/organs/external/tails.dm
+++ b/code/modules/surgery/organs/external/tails.dm
@@ -23,11 +23,12 @@
/obj/item/organ/external/tail/Insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(.)
- original_owner ||= WEAKREF(receiver)
-
receiver.clear_mood_event("tail_lost")
receiver.clear_mood_event("tail_balance_lost")
+ if(!special) // if some admin wants to give someone tail moodles for tail shenanigans, they can spawn it and do it by hand
+ original_owner ||= WEAKREF(receiver)
+
// If it's your tail, an infinite debuff is replaced with a timed one
// If it's not your tail but of same species, I guess it works, but we are more sad
// If it's not your tail AND of different species, we are horrified
@@ -161,7 +162,7 @@
return SSaccessories.tails_list_human
/obj/item/organ/external/tail/cat/get_butt_sprite()
- return BUTT_SPRITE_CAT
+ return icon('icons/mob/butts.dmi', BUTT_SPRITE_CAT)
///Cat tail bodypart overlay
/datum/bodypart_overlay/mutant/tail/cat
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index 76d193c80e87f..ed6d085abe593 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -271,7 +271,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
*/
var/max_amount = rand(CEILING(product_record.amount * 0.5, 1), product_record.amount)
product_record.amount = rand(0, max_amount)
- credits_contained += rand(0, 1) //randomly add a few credits to the machine to make it look like it's been used, proportional to the amount missing.
+ credits_contained += rand(1, 5) //randomly add a few credits to the machine to make it look like it's been used, proportional to the amount missing.
if(tiltable && prob(6)) // 1 in 17 chance to start tilted (as an additional hint to the station trait behind it)
INVOKE_ASYNC(src, PROC_REF(tilt), loc)
credits_contained = 0 // If it's tilted, it's been looted, so no credits for you.
@@ -1652,6 +1652,7 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
return
var/credits_to_remove = min(CREDITS_DUMP_THRESHOLD, round(credits_contained))
var/obj/item/holochip/holochip = new(loc, credits_to_remove)
+ playsound(src, 'sound/effects/cashregister.ogg', 40, TRUE)
credits_contained = max(0, credits_contained - credits_to_remove)
SSblackbox.record_feedback("amount", "vending machine looted", holochip.credits)
diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm
index 015c3d6f7e0cb..3f82a219e56c9 100644
--- a/code/modules/vending/wardrobes.dm
+++ b/code/modules/vending/wardrobes.dm
@@ -1,3 +1,7 @@
+GLOBAL_VAR_INIT(roaches_deployed, FALSE)
+#define MOTHROACH_START_CHANCE 5
+#define MAX_MOTHROACH_AMOUNT 3
+
/obj/item/vending_refill/wardrobe
icon_state = "refill_clothes"
@@ -8,6 +12,29 @@
panel_type = "panel19"
light_mask = "wardrobe-light-mask"
+/obj/machinery/vending/wardrobe/Initialize(mapload)
+ . = ..()
+ if(!mapload)
+ return
+ if(GLOB.roaches_deployed || !is_station_level(z) || !prob(MOTHROACH_START_CHANCE))
+ return
+ for(var/count in 1 to rand(1, MAX_MOTHROACH_AMOUNT))
+ new /mob/living/basic/mothroach(src)
+ GLOB.roaches_deployed = TRUE
+
+
+/obj/machinery/vending/wardrobe/on_dispense(obj/item/clothing/food)
+ if(!istype(food))
+ return
+ for(var/mob/living/basic/mothroach/roach in contents)
+ food.take_damage(food.get_integrity() * 0.5)
+
+/obj/machinery/vending/wardrobe/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
+ . = ..()
+ for(var/mob/living/basic/mothroach/roach in contents)
+ roach.ai_controller.set_blackboard_key(BB_BASIC_MOB_FLEE_TARGET, src) //scatter away!
+ roach.forceMove(drop_location())
+
/obj/machinery/vending/wardrobe/sec_wardrobe
name = "\improper SecDrobe"
desc = "A vending machine for security and security-related clothing!"
@@ -704,3 +731,6 @@
/obj/item/vending_refill/wardrobe/cent_wardrobe
machine_name = "CentDrobe"
light_color = LIGHT_COLOR_ELECTRIC_GREEN
+
+#undef MOTHROACH_START_CHANCE
+#undef MAX_MOTHROACH_AMOUNT
diff --git a/html/changelogs/AutoChangeLog-pr-83866.yml b/html/changelogs/AutoChangeLog-pr-83866.yml
deleted file mode 100644
index 77c38213c9f14..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-83866.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Absolucy"
-delete-after: True
-changes:
- - qol: "Prettied up the Chemical Analyzer's output in chat, making it easier to read, especially when scanning multiple things."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83925.yml b/html/changelogs/AutoChangeLog-pr-83925.yml
deleted file mode 100644
index 7698097a45a3a..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-83925.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Rhials"
-delete-after: True
-changes:
- - bugfix: "Fixes a door in the Fredington Fasting Bear Five Nights and Fnafbears map."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83986.yml b/html/changelogs/AutoChangeLog-pr-83986.yml
deleted file mode 100644
index d22b2d9744ab2..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-83986.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "GPeckman"
-delete-after: True
-changes:
- - bugfix: "The chaplain altar can once again be buckled to."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-83988.yml b/html/changelogs/AutoChangeLog-pr-83988.yml
deleted file mode 100644
index 90f0a198682e6..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-83988.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-author: "mc-oofert"
-delete-after: True
-changes:
- - bugfix: "wawa centcom interns may actually leave the stationside dock"
- - bugfix: "wawa hop office and cap office get keycard auths"
- - bugfix: "wawa disposals blast doors work properly"
- - bugfix: "wawa med elevator controls on the bottom floor are accessible"
- - bugfix: "sci entrance actually has access restrictions"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84006.yml b/html/changelogs/AutoChangeLog-pr-84006.yml
deleted file mode 100644
index 54a31eda940c7..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-84006.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "SyncIt21"
-delete-after: True
-changes:
- - bugfix: "no more runtimes when dragging turfs onto other stuff"
- - code_imp: "most actions now properly check for recursive locs & better adjacency"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84060.yml b/html/changelogs/AutoChangeLog-pr-84060.yml
new file mode 100644
index 0000000000000..1b2f4ac222b39
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84060.yml
@@ -0,0 +1,4 @@
+author: "Thunder12345"
+delete-after: True
+changes:
+ - rscdel: "Locker staffs have been removed from the Ragin' Mages deathmatch lootcrate pool."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84066.yml b/html/changelogs/AutoChangeLog-pr-84066.yml
new file mode 100644
index 0000000000000..0ea603f5e6b4a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84066.yml
@@ -0,0 +1,4 @@
+author: "AyIong"
+delete-after: True
+changes:
+ - qol: "Fullscreen mode can now be toggled by pressing F11 or the button at the top right"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84067.yml b/html/changelogs/AutoChangeLog-pr-84067.yml
deleted file mode 100644
index 2ab2ff078df5a..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-84067.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "SyncIt21"
-delete-after: True
-changes:
- - bugfix: "Techfabs now print 5x cable coil"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84071.yml b/html/changelogs/AutoChangeLog-pr-84071.yml
deleted file mode 100644
index 766856faa60c8..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-84071.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "SyncIt21"
-delete-after: True
-changes:
- - bugfix: "You can move around ui buttons in your action bar"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84074.yml b/html/changelogs/AutoChangeLog-pr-84074.yml
deleted file mode 100644
index 3ba774dadd0a4..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-84074.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "MTandi"
-delete-after: True
-changes:
- - bugfix: "Made 10 MJ & 20 MJ cells properly correspond to tiers 1 & 2 in lathes."
- - image: "Updated cell sprites to correspond to other stock parts of their tiers."
- - image: "Updated plasma cell, 500KJ cell and 2.5MJ cell sprites"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84091.yml b/html/changelogs/AutoChangeLog-pr-84091.yml
new file mode 100644
index 0000000000000..58059ae4af9d8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84091.yml
@@ -0,0 +1,4 @@
+author: "FlufflesTheDog"
+delete-after: True
+changes:
+ - bugfix: "sanitization on citation pda alerts"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84099.yml b/html/changelogs/AutoChangeLog-pr-84099.yml
deleted file mode 100644
index da8c2a27966e1..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-84099.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "MTandi"
-delete-after: True
-changes:
- - qol: "APC has wires for machinery/lights/environment channels"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84104.yml b/html/changelogs/AutoChangeLog-pr-84104.yml
deleted file mode 100644
index 580ba6a7626c3..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-84104.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "MTandi"
-delete-after: True
-changes:
- - bugfix: "Fixed techweb app showing wrong designs on Details button click"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84108.yml b/html/changelogs/AutoChangeLog-pr-84108.yml
deleted file mode 100644
index f2a3935790775..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-84108.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "mc-oofert"
-delete-after: True
-changes:
- - bugfix: "gasping makes sound now"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84109.yml b/html/changelogs/AutoChangeLog-pr-84109.yml
deleted file mode 100644
index 8297afe62bb77..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-84109.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "grungussuss"
-delete-after: True
-changes:
- - bugfix: "mimes can now break their vow while borged or an MMI"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84116.yml b/html/changelogs/AutoChangeLog-pr-84116.yml
deleted file mode 100644
index e11526c56cf50..0000000000000
--- a/html/changelogs/AutoChangeLog-pr-84116.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "MTandi"
-delete-after: True
-changes:
- - refactor: "Compressor UI to TypeScript"
- - qol: "Simplified Compressor UI layout"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84147.yml b/html/changelogs/AutoChangeLog-pr-84147.yml
new file mode 100644
index 0000000000000..d8500ff079b4d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84147.yml
@@ -0,0 +1,4 @@
+author: "mc-oofert"
+delete-after: True
+changes:
+ - bugfix: "borg factory gives you your preference borg name"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84156.yml b/html/changelogs/AutoChangeLog-pr-84156.yml
new file mode 100644
index 0000000000000..95d9e31986fb6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84156.yml
@@ -0,0 +1,4 @@
+author: "JupiterJaeden"
+delete-after: True
+changes:
+ - bugfix: "Conga lines of more than 2 no longer break when going up and down stairs."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84158.yml b/html/changelogs/AutoChangeLog-pr-84158.yml
new file mode 100644
index 0000000000000..90e914ceb5428
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84158.yml
@@ -0,0 +1,4 @@
+author: "Mothblocks"
+delete-after: True
+changes:
+ - qol: "Dramatically improves delete character UI and UX."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-84163.yml b/html/changelogs/AutoChangeLog-pr-84163.yml
new file mode 100644
index 0000000000000..6dbfc565dcbd0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-84163.yml
@@ -0,0 +1,4 @@
+author: "Bisar"
+delete-after: True
+changes:
+ - bugfix: "Felinids no longer remember losing their tail and regaining it roundstart; you need to do it during the round to get that mood event."
\ No newline at end of file
diff --git a/html/changelogs/archive/2024-06.yml b/html/changelogs/archive/2024-06.yml
index c1c7f194b7758..b77aab2bd31f1 100644
--- a/html/changelogs/archive/2024-06.yml
+++ b/html/changelogs/archive/2024-06.yml
@@ -843,3 +843,134 @@
by making each tier reduce cooldowns by 25-15-10% for each injector type.
jlsnow301:
- bugfix: TGUI say will no longer spill your /me contents when you get attacked
+2024-06-20:
+ Absolucy:
+ - qol: Prettied up the Chemical Analyzer's output in chat, making it easier to read,
+ especially when scanning multiple things.
+ Ben10Omintrix:
+ - rscadd: vendrobes may have mothroaches inside them
+ - rscadd: mothroaches will now seek out clothes to eat them
+ FlufflesTheDog:
+ - bugfix: limbs that are both robotic and something else can be repaired properly
+ GPeckman:
+ - bugfix: The chaplain altar can once again be buckled to.
+ LT3:
+ - spellcheck: Melon fruit bowl now comes with a side of foreshadowing for people
+ who want to experience an explosion of flavour
+ MTandi:
+ - bugfix: Fixed techweb app showing wrong designs on Details button click
+ - bugfix: Fixed new compressor UI
+ - refactor: Compressor UI to TypeScript
+ - qol: Simplified Compressor UI layout
+ - bugfix: Made 10 MJ & 20 MJ cells properly correspond to tiers 1 & 2 in lathes.
+ - image: Updated cell sprites to correspond to other stock parts of their tiers.
+ - image: Updated plasma cell, 500KJ cell and 2.5MJ cell sprites
+ - qol: APC has wires for machinery/lights/environment channels
+ Metekillot:
+ - bugfix: Sparks will no longer turn areas with wooden furniture or similar into
+ naught but a field of ashes; they no longer ignite furniture, (unless it's made
+ of plasma(?!)) and have a decreasing chance to ignite items bigger than small
+ size.
+ Rhials:
+ - bugfix: Fixes a door in the Fredington Fasting Bear Five Nights and Fnafbears
+ map.
+ - balance: The energy bola slowdown has been (roughly) halved, to allow for more
+ retaliation when used on a criminal.
+ SyncIt21:
+ - bugfix: Techfabs now print 5x cable coil
+ - bugfix: no more runtimes when dragging turfs onto other stuff
+ - code_imp: most actions now properly check for recursive locs & better adjacency
+ - bugfix: You can move around ui buttons in your action bar
+ Vishenka0704:
+ - qol: Ability to delete characters(yourself)
+ grungussuss:
+ - bugfix: mimes can now break their vow while borged or an MMI
+ mc-oofert:
+ - bugfix: wawa centcom interns may actually leave the stationside dock
+ - bugfix: wawa hop office and cap office get keycard auths
+ - bugfix: wawa disposals blast doors work properly
+ - bugfix: wawa med elevator controls on the bottom floor are accessible
+ - bugfix: sci entrance actually has access restrictions
+ - bugfix: gasping makes sound now
+2024-06-21:
+ 00-Steven:
+ - refactor: Updated cards/ids to use the proper item interaction system instead
+ of attackby, please report any issues.
+ - bugfix: You can no longer recolour an ID at any point if you open the menu but
+ then don't select anything until later.
+ - bugfix: ID cards can be recoloured using crayons/spraycans again.
+ - qol: Prisoner IDs show genpop sentence time in hours/minutes/seconds instead of
+ seconds.
+ - qol: Prisoner IDs have genpop usage tips in their examine.
+ ArcaneMusic:
+ - qol: Vending machines now give audio feedback when you restock a vending refill
+ and get a payout.
+ - qol: The Restock tracker NTOS app for tracking vending machine contents now works
+ on all consoles, and comes pre-installed on the cargochat cargo computers.
+ - balance: Vending machines now offer a bit more credits when missing contents at
+ the start of a round after getting restocked.
+ BeagleGaming1:
+ - rscadd: Added disks for accelerator modkits and crusher trophies to the bitrunning
+ vendor
+ Bisar:
+ - rscadd: Ashwalkers are now better at riding, taming animals, and fishing.
+ - code_imp: Behavior for the settler trait has been partially atomized into several
+ traits instead.
+ EdgeLordExe:
+ - rscadd: Adds Feast of Owls ritual to heretic which allows one to forsake their
+ ascension in exchange for immediate power.
+ GPeckman:
+ - bugfix: When on 'stream' mode, the cleaning spray from a bottle of space cleaner
+ should no longer be blocked by just about everything.
+ Goat:
+ - bugfix: Icebox's raptor den is now lined with asbestos and lead and no longer
+ gets hit with radiation.
+ JackEnoff:
+ - bugfix: Repurposed Glands (Adrenals) now show their correct duration and chemical
+ cost in its description.
+ LT3:
+ - bugfix: Fixed paramedics not having access to the Icebox NanoDrug using the west
+ airlock
+ MTandi:
+ - image: New gibber sprite
+ - image: New food/slime processor sprite
+ - balance: Added ordnance to extra access of geneticists and roboticists
+ - balance: Reduced parts scanning tests' machine count to 4 from 8
+ - balance: Reduced augmented organs scanning tests mob count to 1 from 2
+ - balance: Reduced equipped mech scanning test count to 1 from 2
+ - balance: Added polycrystal option to bluespace crystal scan test
+ - bugfix: Allowed NTNet relay in away circuit imprinter for NT Frontier app
+ - qol: NT Frontier app installed on RD and Scientists` PDAs by default
+ - qol: Updated NT Frontier app to be more user-friendly
+ Rex9001:
+ - bugfix: lunatics now get their hud properly
+ - bugfix: lunatics now get objectives
+ - qol: ascended moon heretics are now labelled as ringleaders and are easier for
+ lunatics to spot
+ Rhials:
+ - balance: DRAGnets now come with a beacon they can be synced to, which will set
+ the destination for the snare round's teleport ability.
+ SmArtKar:
+ - image: Resprited all jetpacks
+ SyncIt21:
+ - bugfix: breaking an APC will depower the area
+ Xander3359:
+ - bugfix: Fix rust heretic being unable to rust walls or floors
+ grungussuss:
+ - bugfix: Welding protection module for MODsuits protect flash-sensitives from welding
+ arcs
+ mc-oofert:
+ - rscadd: every engineering lobby starts with a flatpacked flatpacker and multitool
+ - bugfix: fixed wrong access on one door on wawastation and also made lights on
+ elevators not break (On Wawastation)
+ necromanceranne:
+ - rscadd: Replaces the Particle Acceleration Rifle with the Event Horizon anti-existential
+ beam rifle. It shoots black holes. You can make this in-game. That's right,
+ YOU!
+ - balance: Only one vortex anomaly can be made in a round.
+ projectkepler-ru:
+ - bugfix: Wawastation bridge now has the correct access on their suit storage and
+ medkit now actually starts filled
+ r3dj4ck0424:
+ - bugfix: puts tiles under the wawa tool storage doors
+ - bugfix: allows you to access a door and a fire alarm on wawa's prison second floor
diff --git a/icons/effects/random_spawners.dmi b/icons/effects/random_spawners.dmi
index ddd6fd6f608bd..ed6c0c8702e45 100644
Binary files a/icons/effects/random_spawners.dmi and b/icons/effects/random_spawners.dmi differ
diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi
index 52be2d07923e5..eb3f87d9c71f0 100644
Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ
diff --git a/icons/mob/huds/antag_hud.dmi b/icons/mob/huds/antag_hud.dmi
index c51fcc5baa8c6..2a916d01d9553 100644
Binary files a/icons/mob/huds/antag_hud.dmi and b/icons/mob/huds/antag_hud.dmi differ
diff --git a/icons/mob/inhands/equipment/jetpacks_lefthand.dmi b/icons/mob/inhands/equipment/jetpacks_lefthand.dmi
index 113a8349008b8..8096611173054 100644
Binary files a/icons/mob/inhands/equipment/jetpacks_lefthand.dmi and b/icons/mob/inhands/equipment/jetpacks_lefthand.dmi differ
diff --git a/icons/mob/inhands/equipment/jetpacks_righthand.dmi b/icons/mob/inhands/equipment/jetpacks_righthand.dmi
index 6337f0c0d2a02..8e2ce1de8b2a5 100644
Binary files a/icons/mob/inhands/equipment/jetpacks_righthand.dmi and b/icons/mob/inhands/equipment/jetpacks_righthand.dmi differ
diff --git a/icons/obj/canisters.dmi b/icons/obj/canisters.dmi
index 277833976adbb..436467648880b 100644
Binary files a/icons/obj/canisters.dmi and b/icons/obj/canisters.dmi differ
diff --git a/icons/obj/devices/tracker.dmi b/icons/obj/devices/tracker.dmi
index 59884c0aff881..39be63ef4de81 100644
Binary files a/icons/obj/devices/tracker.dmi and b/icons/obj/devices/tracker.dmi differ
diff --git a/icons/obj/machines/kitchen.dmi b/icons/obj/machines/kitchen.dmi
index c94afb8d78ad6..2142125c5aa5c 100644
Binary files a/icons/obj/machines/kitchen.dmi and b/icons/obj/machines/kitchen.dmi differ
diff --git a/interface/skin.dmf b/interface/skin.dmf
index b8d1d82f17558..8388f5107b64c 100644
--- a/interface/skin.dmf
+++ b/interface/skin.dmf
@@ -70,8 +70,8 @@ window "mainwindow"
menu = "menu"
elem "split"
type = CHILD
- pos = 3,0
- size = 634x440
+ pos = 0,0
+ size = 640x440
anchor1 = 0,0
anchor2 = 100,100
saved-params = "splitter"
@@ -159,58 +159,67 @@ window "infowindow"
is-vert = false
elem "changelog"
type = BUTTON
- pos = 16,5
- size = 104x20
- anchor1 = 3,0
- anchor2 = 19,0
+ pos = 5,5
+ size = 90x20
+ anchor1 = 1,0
+ anchor2 = 15,0
saved-params = "is-checked"
text = "Changelog"
command = "changelog"
elem "rules"
type = BUTTON
- pos = 120,5
- size = 100x20
- anchor1 = 19,0
- anchor2 = 34,0
+ pos = 95,5
+ size = 90x20
+ anchor1 = 15,0
+ anchor2 = 29,0
saved-params = "is-checked"
text = "Rules"
command = "rules"
elem "wiki"
type = BUTTON
- pos = 220,5
- size = 100x20
- anchor1 = 34,0
- anchor2 = 50,0
+ pos = 185,5
+ size = 90x20
+ anchor1 = 29,0
+ anchor2 = 43,0
saved-params = "is-checked"
text = "Wiki"
command = "wiki"
elem "forum"
type = BUTTON
- pos = 320,5
- size = 100x20
- anchor1 = 50,0
- anchor2 = 66,0
+ pos = 275,5
+ size = 90x20
+ anchor1 = 43,0
+ anchor2 = 57,0
saved-params = "is-checked"
text = "Forum"
command = "forum"
elem "github"
type = BUTTON
- pos = 420,5
- size = 100x20
- anchor1 = 66,0
- anchor2 = 81,0
+ pos = 365,5
+ size = 90x20
+ anchor1 = 57,0
+ anchor2 = 71,0
saved-params = "is-checked"
text = "Github"
command = "github"
elem "report-issue"
type = BUTTON
- pos = 520,5
- size = 100x20
- anchor1 = 81,0
- anchor2 = 97,0
+ pos = 455,5
+ size = 90x20
+ anchor1 = 71,0
+ anchor2 = 85,0
saved-params = "is-checked"
text = "Report Issue"
command = "report-issue"
+ elem "fullscreen-toggle"
+ type = BUTTON
+ pos = 545,5
+ size = 90x20
+ anchor1 = 85,0
+ anchor2 = 99,0
+ saved-params = "is-checked"
+ text = "Fullscreen"
+ command = "fullscreen"
window "outputwindow"
elem "outputwindow"
diff --git a/tgstation.dme b/tgstation.dme
index 5c2702885460a..94efbba308232 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -4959,9 +4959,10 @@
#include "code\modules\mob\living\basic\vermin\crab.dm"
#include "code\modules\mob\living\basic\vermin\frog.dm"
#include "code\modules\mob\living\basic\vermin\lizard.dm"
-#include "code\modules\mob\living\basic\vermin\mothroach.dm"
#include "code\modules\mob\living\basic\vermin\mouse.dm"
#include "code\modules\mob\living\basic\vermin\space_bat.dm"
+#include "code\modules\mob\living\basic\vermin\mothroach\mothroach.dm"
+#include "code\modules\mob\living\basic\vermin\mothroach\mothroach_ai.dm"
#include "code\modules\mob\living\brain\brain.dm"
#include "code\modules\mob\living\brain\brain_cybernetic.dm"
#include "code\modules\mob\living\brain\brain_item.dm"
@@ -5363,12 +5364,12 @@
#include "code\modules\power\lighting\light_items.dm"
#include "code\modules\power\lighting\light_mapping_helpers.dm"
#include "code\modules\power\lighting\light_wallframes.dm"
-#include "code\modules\power\singularity\boh_tear.dm"
#include "code\modules\power\singularity\containment_field.dm"
#include "code\modules\power\singularity\dark_matter_singularity.dm"
#include "code\modules\power\singularity\emitter.dm"
#include "code\modules\power\singularity\field_generator.dm"
#include "code\modules\power\singularity\narsie.dm"
+#include "code\modules\power\singularity\reality_tear.dm"
#include "code\modules\power\singularity\singularity.dm"
#include "code\modules\power\supermatter\supermatter.dm"
#include "code\modules\power\supermatter\supermatter_extra_effects.dm"
diff --git a/tgui/packages/tgui-panel/themes.ts b/tgui/packages/tgui-panel/themes.ts
index d8f0ce50b7974..67061ff1f4cf1 100644
--- a/tgui/packages/tgui-panel/themes.ts
+++ b/tgui/packages/tgui-panel/themes.ts
@@ -57,6 +57,8 @@ export const setClientTheme = (name) => {
'github.text-color': '#000000',
'report-issue.background-color': 'none',
'report-issue.text-color': '#000000',
+ 'fullscreen-toggle.background-color': 'none',
+ 'fullscreen-toggle.text-color': '#000000',
// Status and verb tabs
'output.background-color': 'none',
'output.text-color': '#000000',
@@ -109,6 +111,8 @@ export const setClientTheme = (name) => {
'github.text-color': COLOR_DARK_TEXT,
'report-issue.background-color': '#492020',
'report-issue.text-color': COLOR_DARK_TEXT,
+ 'fullscreen-toggle.background-color': '#494949',
+ 'fullscreen-toggle.text-color': COLOR_DARK_TEXT,
// Status and verb tabs
'output.background-color': COLOR_DARK_BG_DARKER,
'output.text-color': COLOR_DARK_TEXT,
diff --git a/tgui/packages/tgui/interfaces/NtosScipaper.jsx b/tgui/packages/tgui/interfaces/NtosScipaper.jsx
index 2af2cd12f43f9..4d12fdc638931 100644
--- a/tgui/packages/tgui/interfaces/NtosScipaper.jsx
+++ b/tgui/packages/tgui/interfaces/NtosScipaper.jsx
@@ -5,7 +5,6 @@ import {
Button,
Collapsible,
Dropdown,
- Icon,
Input,
LabeledList,
NoticeBox,
@@ -13,14 +12,13 @@ import {
Stack,
Table,
Tabs,
- Tooltip,
} from '../components';
import { TableCell, TableRow } from '../components/Table';
import { NtosWindow } from '../layouts';
export const NtosScipaper = (props) => {
return (
-
+
@@ -50,31 +48,132 @@ const PaperPublishing = (props) => {
return (
<>
-
-
+ {fileList.length === 0 && (
+
+ Use the File Manager app to download files from a disk.
+
+ )}
+
+
+ }
+ >
+
+
+ act('select_file', {
+ selected_uid: fileList[ordfile_name],
+ })
+ }
+ />
+
+
+
+ }
+ >
+
+
+ act('select_experiment', {
+ selected_expath: expList[experiment_name],
+ })
+ }
+ />
+
+
+
+ }
+ >
+
+ String(number))}
+ selected={String(tier)}
+ onSelected={(new_tier) =>
+ act('select_tier', {
+ selected_tier: Number(new_tier),
+ })
+ }
+ />
+
+
+
+ }
+ >
+
+
+ act('select_partner', {
+ selected_partner: allowedPartners[new_partner],
+ })
+ }
+ />
+
+
+ act('et_alia')}
+ />
+ }
+ >
act('rewrite', {
- title: value,
+ author: value,
})
}
/>
-
+
act('rewrite', {
- author: value,
+ title: value,
})
}
/>
-
{
}
/>
-
-
-
-
- act('select_file', {
- selected_uid: fileList[ordfile_name],
- })
- }
- />
-
-
-
-
-
-
-
-
-
-
-
-
- act('select_experiment', {
- selected_expath: expList[experiment_name],
- })
- }
- />
-
-
-
-
-
-
-
-
-
-
-
- String(number))}
- selected={String(tier)}
- onSelected={(new_tier) =>
- act('select_tier', {
- selected_tier: Number(new_tier),
- })
- }
- />
-
-
-
-
-
-
-
-
-
-
-
-
- act('select_partner', {
- selected_partner: allowedPartners[new_partner],
- })
- }
- />
-
-
-
-
-
-
-
-
-
-
-
+
{' Cooperation: '}
{gains[coopIndex - 1]}
-
-
-
+
{' Funding: '}
{gains[fundingIndex - 1]}
>
);
@@ -366,7 +367,7 @@ export const NtosScipaperContent = (props) => {
Please sync this application to a valid techweb to upload progress!
)}
-
+
@@ -385,7 +386,7 @@ export const NtosScipaperContent = (props) => {
})
}
>
- {'View Previous Publications'}
+ {'Publications'}
{
})
}
>
- {'View Available Experiments'}
+ {'Experiments'}
{
})
}
>
- {'View Scientific Partners'}
+ {'Scientific Partners'}
{currentTab === 1 && }
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx
index 1e6f779c847ac..8193384e6ae88 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx
@@ -101,15 +101,12 @@ export const CharacterPreferenceWindow = (props) => {
profiles={data.character_profiles}
/>
-
{!data.content_unlocked && (
Buy BYOND premium for more slots!
)}
-
-
@@ -168,9 +165,7 @@ export const CharacterPreferenceWindow = (props) => {
-
-
{pageContents}
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/DeleteCharacterPopup.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/DeleteCharacterPopup.tsx
new file mode 100644
index 0000000000000..3656465677b21
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/DeleteCharacterPopup.tsx
@@ -0,0 +1,57 @@
+import { useEffect, useState } from 'react';
+
+import { useBackend } from '../../backend';
+import { Box, Button, Modal, Stack } from '../../components';
+import { PreferencesMenuData } from './data';
+
+export const DeleteCharacterPopup = (props: { close: () => void }) => {
+ const { data, act } = useBackend();
+ const [secondsLeft, setSecondsLeft] = useState(3);
+
+ const { close } = props;
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setSecondsLeft((current) => current - 1);
+ }, 1000);
+
+ return () => clearInterval(interval);
+ });
+
+ return (
+
+
+
+ Wait!
+
+
+
+ {`You're about to delete ${data.character_preferences.names[data.name_to_use]} forever. Are you sure you want to do this?`}
+
+
+
+
+
+ {/* Explicit width so that the layout doesn't shift */}
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx
index c79827d2abe74..35ddf9d2a7326 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx
@@ -1,7 +1,7 @@
import { filter, map, sortBy } from 'common/collections';
import { classes } from 'common/react';
import { createSearch } from 'common/string';
-import { useState } from 'react';
+import { ReactNode, useState } from 'react';
import { sendAct, useBackend } from '../../backend';
import {
@@ -21,6 +21,7 @@ import {
RandomSetting,
ServerData,
} from './data';
+import { DeleteCharacterPopup } from './DeleteCharacterPopup';
import { MultiNameInput, NameInput } from './names';
import features from './preferences/features';
import {
@@ -383,6 +384,7 @@ export const PreferenceList = (props: {
preferences: Record;
randomizations: Record;
maxHeight: string;
+ children?: ReactNode;
}) => {
return (
+
+ {props.children}
);
};
@@ -478,6 +482,8 @@ export const MainPage = (props: { openSpecies: () => void }) => {
const [currentClothingMenu, setCurrentClothingMenu] = useState(
null,
);
+ const [deleteCharacterPopupOpen, setDeleteCharacterPopupOpen] =
+ useState(false);
const [multiNameInputOpen, setMultiNameInputOpen] = useState(false);
const [randomToggleEnabled] = useRandomToggleState();
@@ -549,6 +555,12 @@ export const MainPage = (props: { openSpecies: () => void }) => {
/>
)}
+ {deleteCharacterPopupOpen && (
+ setDeleteCharacterPopupOpen(false)}
+ />
+ )}
+
@@ -648,7 +660,21 @@ export const MainPage = (props: { openSpecies: () => void }) => {
)}
preferences={nonContextualPreferences}
maxHeight="auto"
- />
+ >
+
+
+
+
diff --git a/tgui/packages/tgui/interfaces/RestockTracker.jsx b/tgui/packages/tgui/interfaces/RestockTracker.jsx
index 7df606adffcd3..ab6f1d012edf0 100644
--- a/tgui/packages/tgui/interfaces/RestockTracker.jsx
+++ b/tgui/packages/tgui/interfaces/RestockTracker.jsx
@@ -81,7 +81,17 @@ export const RestockTracker = (props) => {
))}
+ {vending_list.length === 0 && }
);
};
+
+export const RestockTrackerFull = (props) => {
+ const { data } = useBackend();
+ return (
+
+ All vending machines stocked!
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/TankCompressor.tsx b/tgui/packages/tgui/interfaces/TankCompressor.tsx
index 3f516e3c01803..a0d2acc14fb4b 100644
--- a/tgui/packages/tgui/interfaces/TankCompressor.tsx
+++ b/tgui/packages/tgui/interfaces/TankCompressor.tsx
@@ -70,6 +70,7 @@ const TankCompressorContent = (props) => {
return (
+