diff --git a/.github/workflows/ci_suite.yml b/.github/workflows/ci_suite.yml
index 622cd98514a..414f5d59ea0 100644
--- a/.github/workflows/ci_suite.yml
+++ b/.github/workflows/ci_suite.yml
@@ -287,7 +287,7 @@ jobs:
completion_gate: # Serves as a non-moving target for branch rulesets
if: always() && !cancelled()
name: Completion Gate
- needs: [ test_windows, compare_screenshots, compile_all_maps, run_linters ]
+ needs: [ test_windows, compare_screenshots, compile_all_maps, run_all_tests, run_alternate_tests, run_linters ]
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
diff --git a/_maps/RandomRuins/SpaceRuins/meatderelict.dmm b/_maps/RandomRuins/SpaceRuins/meatderelict.dmm
index a4f95abdf0e..96959954e26 100644
--- a/_maps/RandomRuins/SpaceRuins/meatderelict.dmm
+++ b/_maps/RandomRuins/SpaceRuins/meatderelict.dmm
@@ -728,8 +728,8 @@
/obj/effect/turf_decal/siding/purple{
dir = 1
},
-/obj/item/ammo_casing/a357/spent,
-/obj/item/ammo_casing/a357/spent,
+/obj/item/ammo_casing/c357/spent,
+/obj/item/ammo_casing/c357/spent,
/turf/open/indestructible/white,
/area/ruin/space/has_grav/powered/biooutpost)
"oQ" = (
@@ -887,7 +887,7 @@
/obj/effect/turf_decal/siding/purple{
dir = 1
},
-/obj/item/ammo_casing/a357/spent,
+/obj/item/ammo_casing/c357/spent,
/turf/open/indestructible/white,
/area/ruin/space/has_grav/powered/biooutpost)
"rl" = (
@@ -1561,9 +1561,9 @@
/turf/open/indestructible/plating,
/area/ruin/space/has_grav/powered/biooutpost)
"DC" = (
-/obj/item/ammo_casing/a357/spent,
-/obj/item/ammo_casing/a357/spent,
-/obj/item/ammo_casing/a357/spent,
+/obj/item/ammo_casing/c357/spent,
+/obj/item/ammo_casing/c357/spent,
+/obj/item/ammo_casing/c357/spent,
/turf/open/indestructible/white,
/area/ruin/space/has_grav/powered/biooutpost)
"DJ" = (
diff --git a/_maps/RandomRuins/SpaceRuins/mimesvsclowns.dmm b/_maps/RandomRuins/SpaceRuins/mimesvsclowns.dmm
index c98a43e89c2..7a76e71debf 100644
--- a/_maps/RandomRuins/SpaceRuins/mimesvsclowns.dmm
+++ b/_maps/RandomRuins/SpaceRuins/mimesvsclowns.dmm
@@ -6,7 +6,7 @@
/area/ruin)
"dI" = (
/obj/item/grown/bananapeel,
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 9;
pixel_x = -13;
pixel_y = 10
@@ -78,19 +78,19 @@
},
/obj/effect/decal/cleanable/blood/gibs,
/obj/machinery/light/small/broken/directional/south,
-/obj/item/ammo_casing/a357/spent,
+/obj/item/ammo_casing/c357/spent,
/turf/open/floor/iron/checker/airless,
/area/ruin)
"uc" = (
/obj/effect/decal/cleanable/blood/footprints{
dir = 4
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = -5;
dir = 5;
pixel_y = 6
},
-/obj/item/ammo_casing/a357/spent,
+/obj/item/ammo_casing/c357/spent,
/obj/item/gps/spaceruin,
/turf/open/floor/plating/airless,
/area/ruin)
@@ -204,7 +204,7 @@
/obj/effect/mob_spawn/corpse/human/clown,
/obj/effect/decal/cleanable/blood/footprints,
/obj/effect/decal/cleanable/dirt,
-/obj/item/ammo_casing/a357/spent,
+/obj/item/ammo_casing/c357/spent,
/turf/open/floor/plating/airless,
/area/ruin)
"Ij" = (
@@ -253,7 +253,7 @@
"Pq" = (
/obj/machinery/light/broken/directional/north,
/obj/structure/reagent_dispensers/watertank,
-/obj/item/ammo_casing/a357/spent,
+/obj/item/ammo_casing/c357/spent,
/turf/open/floor/iron/checker/airless,
/area/ruin)
"Qb" = (
diff --git a/_maps/RandomRuins/SpaceRuins/old_infiltrator.dmm b/_maps/RandomRuins/SpaceRuins/old_infiltrator.dmm
index 4b42f668c33..85c2910d1fa 100644
--- a/_maps/RandomRuins/SpaceRuins/old_infiltrator.dmm
+++ b/_maps/RandomRuins/SpaceRuins/old_infiltrator.dmm
@@ -210,8 +210,8 @@
"nF" = (
/obj/item/radio/intercom/directional/south,
/obj/effect/decal/cleanable/blood/old,
-/obj/item/ammo_casing/a357/match,
-/obj/item/ammo_casing/a357/match{
+/obj/item/ammo_casing/c357/match,
+/obj/item/ammo_casing/c357/match{
pixel_y = -4;
pixel_x = -7
},
@@ -306,7 +306,7 @@
/area/ruin/space/unpowered)
"uS" = (
/obj/machinery/newscaster/directional/south,
-/obj/item/ammo_casing/a357/match,
+/obj/item/ammo_casing/c357/match,
/turf/open/floor/mineral/plastitanium/red,
/area/ruin/space/unpowered)
"vi" = (
@@ -425,7 +425,7 @@
/turf/open/floor/mineral/plastitanium/red/airless,
/area/ruin/space/unpowered)
"Cv" = (
-/obj/item/ammo_casing/a357/match,
+/obj/item/ammo_casing/c357/match,
/turf/open/floor/mineral/plastitanium/red,
/area/ruin/space/unpowered)
"CC" = (
diff --git a/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm b/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm
index d05c6135896..592b4253850 100644
--- a/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm
+++ b/_maps/RandomRuins/SpaceRuins/russian_derelict.dmm
@@ -4146,14 +4146,14 @@
"Eb" = (
/obj/structure/cable,
/obj/effect/mapping_helpers/burnt_floor,
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357{
pixel_x = -6;
pixel_y = 3
},
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357{
pixel_x = -5
},
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357{
pixel_x = 6;
dir = 9;
pixel_y = -3
@@ -5018,8 +5018,8 @@
/turf/open/floor/plating/airless,
/area/space/nearstation)
"IG" = (
-/obj/item/ammo_casing/a357,
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357,
+/obj/item/ammo_casing/c357{
pixel_x = 5;
pixel_y = 6
},
@@ -6330,11 +6330,11 @@
/turf/open/floor/iron/airless,
/area/ruin/space/ks13/hallway/aft)
"Pv" = (
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357{
pixel_x = 4;
pixel_y = -7
},
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357{
pixel_x = -5
},
/obj/structure/cable,
@@ -6847,12 +6847,12 @@
"Sk" = (
/obj/structure/cable,
/obj/effect/mapping_helpers/burnt_floor,
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357{
pixel_x = 6;
pixel_y = -4
},
-/obj/item/ammo_casing/a357,
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357,
+/obj/item/ammo_casing/c357{
pixel_x = -10;
pixel_y = 7;
dir = 9
@@ -7599,12 +7599,12 @@
/area/ruin/space/ks13/service/hydro)
"VH" = (
/obj/effect/mapping_helpers/burnt_floor,
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357{
pixel_x = 8;
dir = 8;
pixel_y = 6
},
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357{
pixel_x = 6;
pixel_y = -4
},
diff --git a/_maps/RandomRuins/SpaceRuins/the_faceoff.dmm b/_maps/RandomRuins/SpaceRuins/the_faceoff.dmm
index b31a11cba31..a358d59956e 100644
--- a/_maps/RandomRuins/SpaceRuins/the_faceoff.dmm
+++ b/_maps/RandomRuins/SpaceRuins/the_faceoff.dmm
@@ -1,15 +1,15 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"aj" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 5;
pixel_y = 12
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = -7;
pixel_y = 6
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 4;
pixel_x = 14;
pixel_y = 14
@@ -45,40 +45,40 @@
/turf/template_noop,
/area/template_noop)
"bM" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = 9
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = -1;
pixel_y = 5
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = -7;
pixel_y = 6
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 2;
pixel_y = -5
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 5;
pixel_y = 2
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 4;
pixel_x = 14;
pixel_y = 14
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 2;
pixel_y = -5
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 2;
pixel_y = 12
@@ -168,7 +168,7 @@
/turf/open/floor/iron/dark/airless,
/area/ruin/space)
"eI" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = -1;
pixel_y = 5
@@ -213,17 +213,17 @@
/turf/open/misc/asteroid/basalt/airless,
/area/ruin/space)
"gq" = (
-/obj/item/ammo_casing/a357/spent,
+/obj/item/ammo_casing/c357/spent,
/obj/structure/barricade/wooden,
/turf/open/floor/iron/dark/airless,
/area/ruin/space)
"gx" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = -7;
pixel_y = 6
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 5;
pixel_y = 2
},
@@ -280,41 +280,41 @@
/turf/open/floor/iron/dark/textured/airless,
/area/ruin/space)
"je" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 10;
pixel_y = 5
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 2;
pixel_y = 12
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = -2;
pixel_y = -5
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 5;
pixel_y = 2
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 2;
pixel_y = 1
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 2;
pixel_y = 6;
dir = 1
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 2;
pixel_y = -6
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_y = 13;
pixel_x = -5
@@ -322,7 +322,7 @@
/turf/open/floor/mineral/plastitanium/red/airless,
/area/ruin/space)
"jX" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = 9;
pixel_y = 9
@@ -354,7 +354,7 @@
/turf/open/floor/iron/dark/airless,
/area/ruin/space)
"mC" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 4;
pixel_x = 14;
pixel_y = 14
@@ -392,7 +392,7 @@
/turf/open/floor/iron/dark/airless,
/area/ruin/space)
"pl" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 2;
pixel_y = -5
@@ -434,11 +434,11 @@
/area/ruin/space)
"qX" = (
/obj/effect/mapping_helpers/broken_floor,
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 2;
pixel_y = 12
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = 9;
pixel_y = 9
@@ -455,7 +455,7 @@
/area/ruin/space)
"rU" = (
/obj/effect/mapping_helpers/broken_floor,
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = 9
},
@@ -482,7 +482,7 @@
/area/ruin/space)
"su" = (
/obj/effect/mapping_helpers/broken_floor,
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 2;
pixel_y = 12
@@ -541,7 +541,7 @@
/turf/open/misc/asteroid/basalt/airless,
/area/ruin/space)
"vA" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = -1;
pixel_y = 5
@@ -549,7 +549,7 @@
/turf/open/floor/iron/dark/airless,
/area/ruin/space)
"vZ" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = -2;
pixel_y = -5
@@ -588,7 +588,7 @@
/turf/open/floor/mineral/plastitanium/airless,
/area/ruin/space)
"xi" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 4;
pixel_x = 14;
pixel_y = 14
@@ -726,7 +726,7 @@
/area/ruin/space)
"Dm" = (
/obj/effect/decal/cleanable/dirt,
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 5;
pixel_y = 12
},
@@ -786,7 +786,7 @@
/turf/open/misc/asteroid/basalt/airless,
/area/ruin/space)
"FQ" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 5;
pixel_y = 2
},
@@ -804,7 +804,7 @@
/turf/open/floor/iron/dark/airless,
/area/ruin/space)
"GA" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 4;
pixel_x = 14;
pixel_y = 14
@@ -822,7 +822,7 @@
/area/ruin/space)
"HE" = (
/obj/structure/table/reinforced/plastitaniumglass,
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 2;
pixel_y = 12
},
@@ -881,7 +881,7 @@
/turf/open/floor/mineral/plastitanium/airless,
/area/ruin/space)
"JP" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 2;
pixel_y = 12
},
@@ -897,11 +897,11 @@
/turf/open/floor/mineral/plastitanium/red/airless,
/area/ruin/space)
"Kd" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = 9
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = 9;
pixel_y = 9
@@ -909,7 +909,7 @@
/turf/open/floor/iron/dark/airless,
/area/ruin/space)
"Kp" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 10;
pixel_y = 5
@@ -940,14 +940,14 @@
/turf/open/floor/iron/dark/textured/airless,
/area/ruin/space)
"LM" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 2;
pixel_y = 12
},
/turf/open/floor/iron/dark/airless,
/area/ruin/space)
"LX" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 2;
pixel_y = 12
@@ -973,7 +973,7 @@
/turf/open/floor/mineral/plastitanium/red/airless,
/area/ruin/space)
"Nc" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 10;
pixel_y = 5
@@ -1047,7 +1047,7 @@
/turf/open/floor/iron/dark/airless,
/area/ruin/space)
"TH" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = 9
},
@@ -1065,7 +1065,7 @@
/turf/open/floor/iron/dark/airless,
/area/ruin/space)
"Uy" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = -7;
pixel_y = 6
@@ -1102,36 +1102,36 @@
/turf/open/floor/mineral/plastitanium/airless,
/area/ruin/space)
"Vn" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 4;
pixel_x = 14;
pixel_y = 14
},
-/obj/item/ammo_casing/a357/spent,
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent,
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 10;
pixel_y = 5
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = 9
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 10;
pixel_y = 5
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = -7;
pixel_y = 6
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 2;
pixel_y = 12
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = 9;
pixel_y = 9
@@ -1152,7 +1152,7 @@
/turf/open/floor/mineral/plastitanium/airless,
/area/ruin/space)
"VB" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 5;
pixel_y = 2
},
@@ -1209,40 +1209,40 @@
/turf/open/floor/iron/dark/airless,
/area/ruin/space)
"Zg" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 10;
pixel_y = 5
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 5;
pixel_y = 7
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = -4;
pixel_y = -7
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 2;
pixel_y = 12
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 1;
pixel_x = -1;
pixel_y = 8
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = -2;
pixel_y = -4
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = -7;
pixel_y = 8
},
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_x = 5;
pixel_y = -4
},
@@ -1259,7 +1259,7 @@
/turf/open/floor/mineral/plastitanium/airless,
/area/ruin/space)
"ZP" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
dir = 8;
pixel_x = 2;
pixel_y = 1
diff --git a/_maps/RandomZLevels/moonoutpost19.dmm b/_maps/RandomZLevels/moonoutpost19.dmm
index a8f52784b34..c830fdc968f 100644
--- a/_maps/RandomZLevels/moonoutpost19.dmm
+++ b/_maps/RandomZLevels/moonoutpost19.dmm
@@ -4131,7 +4131,7 @@
/area/awaymission/moonoutpost19/arrivals/shed)
"Bf" = (
/obj/effect/decal/cleanable/dirt,
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357{
pixel_x = -2;
pixel_y = -7
},
@@ -6467,7 +6467,7 @@
"QZ" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/cable,
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357{
pixel_x = 11;
pixel_y = 8
},
@@ -7514,7 +7514,7 @@
/obj/item/stack/ore/glass{
pixel_x = 8
},
-/obj/item/ammo_casing/a357{
+/obj/item/ammo_casing/c357{
pixel_x = -10;
pixel_y = -7
},
diff --git a/_maps/deathmatch/arena_station.dmm b/_maps/deathmatch/arena_station.dmm
index 50089df45e8..8009387c502 100644
--- a/_maps/deathmatch/arena_station.dmm
+++ b/_maps/deathmatch/arena_station.dmm
@@ -79,7 +79,7 @@
/obj/structure/closet/crate/cardboard,
/obj/effect/turf_decal/tile/brown/fourcorners,
/obj/item/mail/junkmail,
-/obj/item/ammo_casing/a357,
+/obj/item/ammo_casing/c357,
/obj/item/reagent_containers/syringe/plasma,
/turf/open/indestructible,
/area/deathmatch)
diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm
index 54d9bde3bc2..de9e1338f29 100644
--- a/_maps/map_files/Birdshot/birdshot.dmm
+++ b/_maps/map_files/Birdshot/birdshot.dmm
@@ -10872,7 +10872,10 @@
/obj/effect/turf_decal/tile/yellow{
dir = 1
},
-/obj/machinery/door/window/right/directional/east,
+/obj/machinery/door/window/right/directional/east{
+ name = "Engineering Deliveries";
+ req_access = list("engineering")
+ },
/turf/open/floor/iron/smooth,
/area/station/engineering/main)
"ecn" = (
@@ -57400,6 +57403,9 @@
/obj/item/gun/energy/ionrifle{
pixel_y = 3
},
+/obj/item/gun/ballistic/automatic/battle_rifle{
+ pixel_y = 5
+ },
/obj/item/clothing/suit/hooded/ablative,
/turf/open/floor/iron/dark/small,
/area/station/ai_monitored/security/armory)
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index dcebb2cdce2..b0091430712 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -53109,6 +53109,9 @@
"nmT" = (
/obj/structure/rack,
/obj/item/gun/energy/ionrifle,
+/obj/item/gun/ballistic/automatic/battle_rifle{
+ pixel_y = 3
+ },
/obj/item/clothing/suit/hooded/ablative,
/obj/item/gun/energy/temperature/security,
/obj/structure/window/reinforced/spawner/directional/south,
@@ -84090,10 +84093,10 @@
/obj/structure/disposalpipe/trunk{
dir = 4
},
-/obj/machinery/disposal/delivery_chute{
+/obj/effect/turf_decal/box/red,
+/obj/structure/disposaloutlet{
dir = 8
},
-/obj/effect/turf_decal/box/red,
/turf/open/floor/engine/xenobio,
/area/station/science/xenobiology)
"uYg" = (
diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
index 5c5129c900a..aa3df4dfd8a 100644
--- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm
+++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
@@ -77464,6 +77464,9 @@
"wyF" = (
/obj/structure/rack,
/obj/item/gun/energy/ionrifle,
+/obj/item/gun/ballistic/automatic/battle_rifle{
+ pixel_y = 3
+ },
/obj/item/gun/energy/temperature/security,
/obj/item/clothing/suit/hooded/ablative,
/obj/effect/turf_decal/tile/red/half/contrasted{
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index 0063ca41fdd..6b4adfe0d48 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -11421,6 +11421,9 @@
"eew" = (
/obj/structure/rack,
/obj/item/gun/energy/ionrifle,
+/obj/item/gun/ballistic/automatic/battle_rifle{
+ pixel_y = 3
+ },
/obj/item/gun/energy/temperature/security,
/obj/item/clothing/suit/hooded/ablative,
/obj/effect/turf_decal/tile/red/half/contrasted{
diff --git a/_maps/map_files/NebulaStation/NebulaStation.dmm b/_maps/map_files/NebulaStation/NebulaStation.dmm
index 9e0fec80123..047b54e5fd4 100644
--- a/_maps/map_files/NebulaStation/NebulaStation.dmm
+++ b/_maps/map_files/NebulaStation/NebulaStation.dmm
@@ -10569,6 +10569,9 @@
"bDU" = (
/obj/structure/rack,
/obj/item/gun/energy/ionrifle,
+/obj/item/gun/ballistic/automatic/battle_rifle{
+ pixel_y = 3
+ },
/obj/item/clothing/suit/hooded/ablative,
/obj/item/gun/energy/temperature/security,
/obj/effect/turf_decal/bot/right,
@@ -12767,6 +12770,7 @@
/turf/open/floor/iron/dark/textured,
/area/station/engineering/atmos)
"bUo" = (
+/obj/structure/cable,
/turf/open/floor/iron/solarpanel/airless,
/area/station/solars/port/aft)
"bUs" = (
@@ -92156,7 +92160,8 @@
/area/station/security/checkpoint/science)
"nKF" = (
/obj/item/toy/katana{
- icon_state = "supermatter_sword"
+ icon_state = "supermatter_sword";
+ icon_angle = -45
},
/obj/structure/ladder,
/turf/open/floor/plating,
@@ -113586,7 +113591,7 @@
/turf/open/floor/engine/n2,
/area/station/engineering/atmos)
"qOe" = (
-/obj/item/ammo_casing/a357/spent{
+/obj/item/ammo_casing/c357/spent{
pixel_y = 6;
pixel_x = 9
},
diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm
index 4aab72d4be6..8f24d290d17 100644
--- a/_maps/map_files/generic/CentCom.dmm
+++ b/_maps/map_files/generic/CentCom.dmm
@@ -62229,7 +62229,7 @@ xt
xt
xt
xt
-xt
+PG
xt
Vd
zm
@@ -62486,7 +62486,7 @@ xt
xt
xt
xt
-rq
+xt
xt
xt
zl
diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm
index 528a6dff60e..5701e877f72 100644
--- a/_maps/map_files/tramstation/tramstation.dmm
+++ b/_maps/map_files/tramstation/tramstation.dmm
@@ -7855,6 +7855,9 @@
/obj/structure/rack,
/obj/item/clothing/suit/hooded/ablative,
/obj/item/gun/energy/ionrifle,
+/obj/item/gun/ballistic/automatic/battle_rifle{
+ pixel_y = 3
+ },
/obj/item/gun/energy/temperature/security,
/obj/structure/reagent_dispensers/wall/peppertank/directional/north,
/obj/effect/turf_decal/tile/neutral/fourcorners,
diff --git a/_maps/map_files/wawastation/wawastation.dmm b/_maps/map_files/wawastation/wawastation.dmm
index 75d1b066c6e..01d06908ce8 100644
--- a/_maps/map_files/wawastation/wawastation.dmm
+++ b/_maps/map_files/wawastation/wawastation.dmm
@@ -151,6 +151,9 @@
/obj/item/clothing/suit/hooded/ablative,
/obj/item/gun/energy/temperature/security,
/obj/item/gun/energy/ionrifle,
+/obj/item/gun/ballistic/automatic/battle_rifle{
+ pixel_y = 3
+ },
/obj/effect/turf_decal/tile/red/half/contrasted{
dir = 1
},
@@ -4661,7 +4664,7 @@
/obj/structure/chair{
dir = 4
},
-/obj/item/ammo_casing/a357/spent,
+/obj/item/ammo_casing/c357/spent,
/obj/effect/decal/cleanable/dirt/dust,
/turf/open/floor/iron/white/small,
/area/station/science/lobby)
@@ -21692,7 +21695,7 @@
/obj/effect/decal/cleanable/blood/tracks{
dir = 4
},
-/obj/item/ammo_casing/a357/spent,
+/obj/item/ammo_casing/c357/spent,
/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{
dir = 8
},
@@ -34884,7 +34887,7 @@
/area/station/engineering/atmos)
"moT" = (
/obj/effect/turf_decal/siding/white,
-/obj/item/ammo_casing/a357/spent,
+/obj/item/ammo_casing/c357/spent,
/turf/open/floor/iron/white/small,
/area/station/science/lobby)
"moU" = (
diff --git a/_maps/shuttles/emergency_lance.dmm b/_maps/shuttles/emergency_lance.dmm
index d83294d00ac..2f62bc945bd 100644
--- a/_maps/shuttles/emergency_lance.dmm
+++ b/_maps/shuttles/emergency_lance.dmm
@@ -62,7 +62,7 @@
/area/shuttle/escape)
"bV" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/obj/effect/turf_decal/trimline/dark_blue/arrow_ccw{
dir = 8
@@ -125,7 +125,7 @@
/area/shuttle/escape)
"dW" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 8
@@ -287,7 +287,7 @@
/area/shuttle/escape)
"jo" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/obj/effect/turf_decal/trimline/dark_blue/arrow_ccw{
dir = 8
@@ -533,7 +533,7 @@
/area/shuttle/escape)
"pu" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 4
@@ -573,7 +573,7 @@
/area/shuttle/escape)
"qe" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/obj/effect/mapping_helpers/airlock/access/any/engineering/general,
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
@@ -609,7 +609,7 @@
/area/shuttle/escape)
"rw" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/obj/effect/mapping_helpers/airlock/access/any/engineering/general,
/obj/effect/mapping_helpers/airlock/cyclelink_helper,
@@ -682,7 +682,7 @@
/area/shuttle/escape)
"uK" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 4
@@ -868,7 +868,7 @@
/area/shuttle/escape)
"Cx" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 8
@@ -887,7 +887,7 @@
/area/shuttle/escape)
"CR" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 4
@@ -1199,7 +1199,7 @@
/area/shuttle/escape)
"LD" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 8
diff --git a/_maps/shuttles/emergency_raven.dmm b/_maps/shuttles/emergency_raven.dmm
index 7e393756800..7eb255fc2d4 100644
--- a/_maps/shuttles/emergency_raven.dmm
+++ b/_maps/shuttles/emergency_raven.dmm
@@ -742,7 +742,7 @@
/area/shuttle/escape)
"cd" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/turf/open/floor/plating,
/area/shuttle/escape)
@@ -1456,7 +1456,7 @@
/area/shuttle/escape)
"eo" = (
/obj/machinery/door/airlock/external/ruin{
- name = "Emegency Shuttle External Airlock"
+ name = "Emergency Shuttle External Airlock"
},
/obj/docking_port/mobile/emergency{
name = "CentCom Raven Cruiser"
diff --git a/_maps/templates/lazy_templates/nukie_base.dmm b/_maps/templates/lazy_templates/nukie_base.dmm
index bb1d8bd4126..5178b0f9356 100644
--- a/_maps/templates/lazy_templates/nukie_base.dmm
+++ b/_maps/templates/lazy_templates/nukie_base.dmm
@@ -68,6 +68,17 @@
/obj/effect/turf_decal/siding/thinplating_new/light{
dir = 8
},
+/obj/structure/closet/syndicate/personal,
+/obj/item/reagent_containers/cup/beaker/large,
+/obj/item/reagent_containers/cup/beaker/large,
+/obj/item/reagent_containers/cup/beaker/large,
+/obj/item/reagent_containers/cup/beaker/large,
+/obj/item/reagent_containers/cup/beaker/large,
+/obj/item/reagent_containers/cup/beaker/large,
+/obj/item/reagent_containers/cup/beaker/large,
+/obj/item/reagent_containers/cup/beaker/large,
+/obj/item/reagent_containers/cup/beaker/large,
+/obj/item/reagent_containers/cup/beaker/large,
/turf/open/floor/mineral/plastitanium,
/area/centcom/syndicate_mothership/expansion_chemicalwarfare)
"bo" = (
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 2c20765d351..5cb838603c9 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -154,6 +154,13 @@ DEFINE_BITFIELD(status_flags, list(
#define ATTACK_EFFECT_MECHTOXIN "mech_toxin"
#define ATTACK_EFFECT_BOOP "boop" //Honk
+/// Attack animation for sharp items
+#define ATTACK_ANIMATION_SLASH "slash"
+/// Attack animation for pointy items
+#define ATTACK_ANIMATION_PIERCE "pierce"
+/// Animation for blunt attacks
+#define ATTACK_ANIMATION_BLUNT "blunt"
+
//the define for visible message range in combat
#define SAMETILE_MESSAGE_RANGE 1
#define COMBAT_MESSAGE_RANGE 3
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
index deb9188d76e..3569b9af1e6 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
@@ -244,6 +244,7 @@
///from /mob/living/proc/check_block(): (atom/hit_by, damage, attack_text, attack_type, armour_penetration, damage_type)
#define COMSIG_LIVING_CHECK_BLOCK "living_check_block"
+ #define FAILED_BLOCK NONE
#define SUCCESSFUL_BLOCK (1<<0)
///Hit by successful disarm attack (mob/living/attacker, zone_targeted, item/weapon)
diff --git a/code/__DEFINES/dcs/signals/signals_mod.dm b/code/__DEFINES/dcs/signals/signals_mod.dm
index 58fd8ca689e..c08377d50db 100644
--- a/code/__DEFINES/dcs/signals/signals_mod.dm
+++ b/code/__DEFINES/dcs/signals/signals_mod.dm
@@ -45,3 +45,5 @@
#define COMSIG_MOD_TETHER_SNAP "mod_tether_snap"
/// Called when a MOD module generats its worn overlay
#define COMSIG_MODULE_GENERATE_WORN_OVERLAY "mod_module_generate_worn_overlay"
+/// Called when the MOD control unit fetches its visor icon
+#define COMSIG_MOD_GET_VISOR_OVERLAY "mod_get_visor_overlay"
diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm
index 785d353982c..975b6037f6b 100644
--- a/code/__DEFINES/hud.dm
+++ b/code/__DEFINES/hud.dm
@@ -54,8 +54,10 @@
#define ui_combo "CENTER+4:24,SOUTH+1:7" //combo meter for martial arts
//Lower right, persistent menu
-#define ui_drop_throw "EAST-1:28,SOUTH+1:7"
+#define ui_rest "EAST-1:28,SOUTH+1:7"
+#define ui_drop_throw "EAST-1:28,SOUTH+1:24"
#define ui_above_movement "EAST-2:26,SOUTH+1:7"
+#define ui_above_movement_top "EAST-2:26, SOUTH+1:24"
#define ui_above_intent "EAST-3:24, SOUTH+1:7"
#define ui_movi "EAST-2:26,SOUTH:5"
#define ui_acti "EAST-3:24,SOUTH:5"
@@ -65,8 +67,7 @@
#define ui_crafting "EAST-4:22,SOUTH:5"
#define ui_building "EAST-4:22,SOUTH:21"
#define ui_language_menu "EAST-4:6,SOUTH:21"
-#define ui_navigate_menu "EAST-4:22,SOUTH:5"
-#define ui_floor_changer "EAST-3:24, SOUTH+1:3"
+#define ui_navigate_menu "EAST-4:6,SOUTH:5"
//Upper left (action buttons)
#define ui_action_palette "WEST+0:23,NORTH-1:5"
@@ -100,7 +101,11 @@
#define ui_living_healthdoll "EAST-1:28,CENTER-1:15"
//Humans
-#define ui_human_floor_changer "EAST-4:22, SOUTH+1:7"
+#define ui_human_floor_changer "EAST-4:22,SOUTH:5"
+#define ui_human_crafting "EAST-3:24,SOUTH+1:7"
+#define ui_human_navigate "EAST-3:7,SOUTH+1:7"
+#define ui_human_language "EAST-3:7,SOUTH+1:24"
+#define ui_human_area "EAST-3:24,SOUTH+1:24"
//Drones
#define ui_drone_drop "CENTER+1:18,SOUTH:5"
@@ -123,7 +128,7 @@
#define ui_borg_camera "CENTER+3:21,SOUTH:5"
#define ui_borg_alerts "CENTER+4:21,SOUTH:5"
#define ui_borg_language_menu "CENTER+4:19,SOUTH+1:6"
-#define ui_borg_navigate_menu "CENTER+4:19,SOUTH+1:6"
+#define ui_borg_navigate_menu "CENTER+4:3,SOUTH+1:6"
#define ui_borg_floor_changer "EAST-1:28,SOUTH+1:39"
//Aliens
@@ -132,7 +137,7 @@
#define ui_alien_queen_finder "EAST,CENTER-3:15"
#define ui_alien_storage_r "CENTER+1:18,SOUTH:5"
#define ui_alien_language_menu "EAST-4:20,SOUTH:5"
-#define ui_alien_navigate_menu "EAST-4:20,SOUTH:5"
+#define ui_alien_navigate_menu "EAST-4:4,SOUTH:5"
//AI
#define ui_ai_core "BOTTOM:6,RIGHT-4"
@@ -173,7 +178,7 @@
#define ui_pai_view_images "SOUTH:6,WEST+12"
#define ui_pai_radio "SOUTH:6,WEST+13"
#define ui_pai_language_menu "SOUTH+1:8,WEST+12:31"
-#define ui_pai_navigate_menu "SOUTH+1:8,WEST+12:31"
+#define ui_pai_navigate_menu "SOUTH+1:8,WEST+12:15"
//Ghosts
#define ui_ghost_spawners_menu "SOUTH:6,CENTER-3:24"
@@ -182,8 +187,8 @@
#define ui_ghost_teleport "SOUTH:6,CENTER:24"
#define ui_ghost_pai "SOUTH: 6, CENTER+1:24"
#define ui_ghost_minigames "SOUTH: 6, CENTER+2:24"
-#define ui_ghost_language_menu "SOUTH: 22, CENTER+3:22"
-#define ui_ghost_floor_changer "SOUTH: 6, CENTER+3:23"
+#define ui_ghost_language_menu "SOUTH: 6, CENTER+3:24"
+#define ui_ghost_floor_changer "SOUTH: 6, CENTER+3:8"
//Blobbernauts
#define ui_blobbernaut_overmind_health "EAST-1:28,CENTER+0:19"
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index ffc42371844..cb338595b4d 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -255,8 +255,6 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list(
#define ismecha(A) (istype(A, /obj/vehicle/sealed/mecha))
-#define ismopable(A) (A && ((PLANE_TO_TRUE(A.plane) == FLOOR_PLANE) ? (A.layer <= FLOOR_CLEAN_LAYER) : (A.layer <= GAME_CLEAN_LAYER))) //If something can be cleaned by floor-cleaning devices such as mops or clean bots
-
#define isorgan(A) (istype(A, /obj/item/organ))
#define isclothing(A) (istype(A, /obj/item/clothing))
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index 2c5d8722f11..35dd5cc35ce 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -156,9 +156,7 @@
#define CATWALK_LAYER (14 + TOPDOWN_LAYER)
#define LOWER_RUNE_LAYER (15 + TOPDOWN_LAYER)
#define RUNE_LAYER (16 + TOPDOWN_LAYER)
-/// [GAME_CLEAN_LAYER] but for floors.
-/// Basically any layer below this (numerically) is "on" a floor for the purposes of washing
-#define FLOOR_CLEAN_LAYER (21 + TOPDOWN_LAYER)
+#define CLEANABLE_FLOOR_OBJECT_LAYER (21 + TOPDOWN_LAYER)
//Placeholders in case the game plane and possibly other things between it and the floor plane are ever made into topdown planes
@@ -185,9 +183,7 @@
#define BOT_PATH_LAYER 2.497
#define LOW_OBJ_LAYER 2.5
#define HIGH_PIPE_LAYER 2.54
-// Anything above this layer is not "on" a turf for the purposes of washing
-// I hate this life of ours
-#define GAME_CLEAN_LAYER 2.55
+#define CLEANABLE_OBJECT_LAYER 2.55
#define TRAM_STRUCTURE_LAYER 2.57
#define TRAM_FLOOR_LAYER 2.58
#define TRAM_WALL_LAYER 2.59
@@ -201,6 +197,7 @@
#define DOOR_HELPER_LAYER 2.72 //keep this above DOOR_ACCESS_HELPER_LAYER and OPEN_DOOR_LAYER since the others tend to have tiny sprites that tend to be covered up.
#define PROJECTILE_HIT_THRESHHOLD_LAYER 2.75 //projectiles won't hit objects at or below this layer if possible
#define TABLE_LAYER 2.8
+#define GIB_LAYER 2.85 // sit on top of tables, but below machines
#define BELOW_OBJ_LAYER 2.9
#define LOW_ITEM_LAYER 2.95
//#define OBJ_LAYER 3 //For easy recordkeeping; this is a byond define
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 3e53268a0d5..3d42c989479 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -1407,4 +1407,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
///Trait given to atoms currently affected by projectile dampeners
#define TRAIT_GOT_DAMPENED "got_dampened"
+/// Apply to movables to say "hey, this movable is technically flat on the floor, so it'd be mopped up by a mop"
+#define TRAIT_MOPABLE "mopable"
+
// END TRAIT DEFINES
diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index efee782aaeb..aa13b74f0e1 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -215,3 +215,8 @@
var/position_a = fluids_priority.Find(initial(a.required_fluid_type))
var/position_b = fluids_priority.Find(initial(b.required_fluid_type))
return cmp_numeric_asc(position_a, position_b) || cmp_text_asc(initial(b.name), initial(a.name))
+
+///Sorts stock parts based on tier
+/proc/cmp_rped_sort(obj/item/first_item, obj/item/second_item)
+ ///even though stacks aren't stock parts, get_part_rating() is defined on the item level (see /obj/item/proc/get_part_rating()) and defaults to returning 0.
+ return second_item.get_part_rating() - first_item.get_part_rating()
diff --git a/code/__HELPERS/construction.dm b/code/__HELPERS/construction.dm
index 166a009f066..a0ebb3db034 100644
--- a/code/__HELPERS/construction.dm
+++ b/code/__HELPERS/construction.dm
@@ -46,9 +46,9 @@
return 0
/**
- * Splits a stack. we don't use /obj/item/stack/proc/fast_split_stack because Byond complains that should only be called asynchronously.
+ * Splits a stack. we don't use /obj/item/stack/proc/split_stack because Byond complains that should only be called asynchronously.
* This proc is also more faster because it doesn't deal with mobs, copying evidences or refreshing atom storages
- * Has special internal uses for e.g. by the material container
+ * Has special internal uses for e.g. by the material container & RPED
*
* Arguments:
* - [target][obj/item/stack]: the stack to split
diff --git a/code/__HELPERS/hud.dm b/code/__HELPERS/hud.dm
index 40a12767d28..d7d053c40bb 100644
--- a/code/__HELPERS/hud.dm
+++ b/code/__HELPERS/hud.dm
@@ -3,14 +3,10 @@
var/y_off = round((i-1) / 2)
return"CENTER+[x_off]:16,SOUTH+[y_off]:5"
-/proc/ui_equip_position(mob/M)
- var/y_off = round((M.held_items.len-1) / 2) //values based on old equip ui position (CENTER: +/-16,SOUTH+1:5)
- return "CENTER:-16,SOUTH+[y_off+1]:5"
-
/proc/ui_swaphand_position(mob/M, which = LEFT_HANDS) //values based on old swaphand ui positions (CENTER: +/-16,SOUTH+1:5)
- var/x_off = which == LEFT_HANDS ? -1 : 0
+ var/x_off = (which == LEFT_HANDS) ? -1 : null
var/y_off = round((M.held_items.len-1) / 2)
- return "CENTER+[x_off]:16,SOUTH+[y_off+1]:5"
+ return "CENTER[x_off]:16,SOUTH+[y_off+1]:5"
/proc/ui_perk_position(perk_count)
var/y_off = perk_count < 1 ? 0 : perk_count/2
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 357ff150415..b1ac9fdbac3 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -48,7 +48,6 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_CHASM_STOPPER" = TRAIT_CHASM_STOPPER,
"TRAIT_COMBAT_MODE_SKIP_INTERACTION" = TRAIT_COMBAT_MODE_SKIP_INTERACTION,
"TRAIT_DEL_ON_SPACE_DUMP" = TRAIT_DEL_ON_SPACE_DUMP,
- "TRAIT_VALID_DNA_INFUSION" = TRAIT_VALID_DNA_INFUSION,
"TRAIT_FROZEN" = TRAIT_FROZEN,
"TRAIT_HAS_LABEL" = TRAIT_HAS_LABEL,
"TRAIT_HEARING_SENSITIVE" = TRAIT_HEARING_SENSITIVE,
@@ -57,34 +56,36 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_IRRADIATED" = TRAIT_IRRADIATED,
"TRAIT_IS_AQUARIUM" = TRAIT_IS_AQUARIUM,
"TRAIT_LAVA_IMMUNE" = TRAIT_LAVA_IMMUNE,
+ "TRAIT_MOPABLE" = TRAIT_MOPABLE,
"TRAIT_MOVE_FLOATING" = TRAIT_MOVE_FLOATING,
"TRAIT_MOVE_FLYING" = TRAIT_MOVE_FLYING,
"TRAIT_MOVE_GROUND" = TRAIT_MOVE_GROUND,
"TRAIT_MOVE_PHASING" = TRAIT_MOVE_PHASING,
"TRAIT_MOVE_UPSIDE_DOWN" = TRAIT_MOVE_UPSIDE_DOWN,
- "TRAIT_MOVE_VENTCRAWLING" = TRAIT_MOVE_VENTCRAWLING,
"TRAIT_MOVE_UPSIDE_DOWN" = TRAIT_MOVE_UPSIDE_DOWN,
+ "TRAIT_MOVE_VENTCRAWLING" = TRAIT_MOVE_VENTCRAWLING,
+ "TRAIT_NOT_BARFABLE" = TRAIT_NOT_BARFABLE,
+ "TRAIT_NOT_ENGRAVABLE" = TRAIT_NOT_ENGRAVABLE,
"TRAIT_NO_FLOATING_ANIM" = TRAIT_NO_FLOATING_ANIM,
"TRAIT_NO_MANIFEST_CONTENTS_ERROR" = TRAIT_NO_MANIFEST_CONTENTS_ERROR,
"TRAIT_NO_MISSING_ITEM_ERROR" = TRAIT_NO_MISSING_ITEM_ERROR,
"TRAIT_NO_THROW_HITPUSH" = TRAIT_NO_THROW_HITPUSH,
- "TRAIT_NOT_BARFABLE" = TRAIT_NOT_BARFABLE,
- "TRAIT_NOT_ENGRAVABLE" = TRAIT_NOT_ENGRAVABLE,
"TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT" = TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT,
"TRAIT_ON_HIT_EFFECT" = TRAIT_ON_HIT_EFFECT,
"TRAIT_RUNECHAT_HIDDEN" = TRAIT_RUNECHAT_HIDDEN,
"TRAIT_SCARY_FISHERMAN" = TRAIT_SCARY_FISHERMAN,
"TRAIT_SECLUDED_LOCATION" = TRAIT_SECLUDED_LOCATION,
+ "TRAIT_SILENT_REACTIONS" = TRAIT_SILENT_REACTIONS,
"TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE,
"TRAIT_SPELLS_TRANSFER_TO_LOC" = TRAIT_SPELLS_TRANSFER_TO_LOC,
"TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH" = TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH,
"TRAIT_TELEKINESIS_CONTROLLED" = TRAIT_TELEKINESIS_CONTROLLED,
"TRAIT_UNDERFLOOR" = TRAIT_UNDERFLOOR,
"TRAIT_UNIQUE_IMMERSE" = TRAIT_UNIQUE_IMMERSE,
+ "TRAIT_VALID_DNA_INFUSION" = TRAIT_VALID_DNA_INFUSION,
"TRAIT_WADDLING" = TRAIT_WADDLING,
"TRAIT_WAS_RENAMED" = TRAIT_WAS_RENAMED,
"TRAIT_WEATHER_IMMUNE" = TRAIT_WEATHER_IMMUNE,
- "TRAIT_SILENT_REACTIONS" = TRAIT_SILENT_REACTIONS,
),
/datum/controller/subsystem/economy = list(
"TRAIT_MARKET_CRASHING" = TRAIT_MARKET_CRASHING,
diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/action_button.dm
index 562266f51c0..5d092cbc1f6 100644
--- a/code/_onclick/hud/action_button.dm
+++ b/code/_onclick/hud/action_button.dm
@@ -110,32 +110,37 @@
last_hovored_ref = null
if(!can_use(usr))
return
+
var/datum/hud/our_hud = usr.hud_used
if(over_object == src)
our_hud.hide_landings()
return
+
if(istype(over_object, /atom/movable/screen/action_landing))
var/atom/movable/screen/action_landing/reserve = over_object
reserve.hit_by(src)
- our_hud.hide_landings()
save_position()
+ our_hud.hide_landings()
return
- our_hud.hide_landings()
if(istype(over_object, /atom/movable/screen/button_palette) || istype(over_object, /atom/movable/screen/palette_scroll))
our_hud.position_action(src, SCRN_OBJ_IN_PALETTE)
save_position()
+ our_hud.hide_landings()
return
+
if(istype(over_object, /atom/movable/screen/movable/action_button))
var/atom/movable/screen/movable/action_button/button = over_object
our_hud.position_action_relative(src, button)
save_position()
+ our_hud.hide_landings()
return
. = ..()
our_hud.position_action(src, screen_loc)
save_position()
+ our_hud.hide_landings()
/atom/movable/screen/movable/action_button/proc/save_position()
var/mob/user = our_hud.mymob
@@ -290,6 +295,7 @@
/atom/movable/screen/button_palette/proc/set_hud(datum/hud/our_hud)
src.our_hud = our_hud
refresh_owner()
+ disable_landing() // If our hud already has elements, don't force hide us
/atom/movable/screen/button_palette/update_name(updates)
. = ..()
@@ -311,6 +317,15 @@
icon_state = "[ui_name]_palette"
+/atom/movable/screen/button_palette/proc/activate_landing()
+ // Reveal ourselves to the user
+ invisibility = INVISIBILITY_NONE
+
+/atom/movable/screen/button_palette/proc/disable_landing()
+ // If we have no elements in the palette, hide your ugly self please
+ if (!length(our_hud.palette_actions?.actions))
+ invisibility = INVISIBILITY_ABSTRACT
+
/atom/movable/screen/button_palette/MouseEntered(location, control, params)
. = ..()
if(QDELETED(src))
diff --git a/code/_onclick/hud/alien.dm b/code/_onclick/hud/alien.dm
index b9a0e3bf655..20e1e72f950 100644
--- a/code/_onclick/hud/alien.dm
+++ b/code/_onclick/hud/alien.dm
@@ -39,13 +39,13 @@
using = new /atom/movable/screen/swap_hand(null, src)
using.icon = ui_style
using.icon_state = "swap_1"
- using.screen_loc = ui_swaphand_position(owner,1)
+ using.screen_loc = ui_swaphand_position(owner, 1)
static_inventory += using
using = new /atom/movable/screen/swap_hand(null, src)
using.icon = ui_style
using.icon_state = "swap_2"
- using.screen_loc = ui_swaphand_position(owner,2)
+ using.screen_loc = ui_swaphand_position(owner, 2)
static_inventory += using
action_intent = new /atom/movable/screen/combattoggle/flashy(null, src)
@@ -61,7 +61,6 @@
floor_change = new /atom/movable/screen/floor_changer(null, src)
floor_change.icon = ui_style
- floor_change.screen_loc = ui_above_intent
static_inventory += floor_change
using = new/atom/movable/screen/language_menu(null, src)
@@ -77,11 +76,11 @@
using.screen_loc = ui_drop_throw
static_inventory += using
- using = new /atom/movable/screen/resist(null, src)
- using.icon = ui_style
- using.screen_loc = ui_above_movement
- using.update_appearance()
- hotkeybuttons += using
+ resist_icon = new /atom/movable/screen/resist(null, src)
+ resist_icon.icon = ui_style
+ resist_icon.screen_loc = ui_above_movement
+ resist_icon.update_appearance()
+ hotkeybuttons += resist_icon
throw_icon = new /atom/movable/screen/throw_catch(null, src)
throw_icon.icon = ui_style
diff --git a/code/_onclick/hud/alien_larva.dm b/code/_onclick/hud/alien_larva.dm
index bb2b9fcb14a..922953b01a3 100644
--- a/code/_onclick/hud/alien_larva.dm
+++ b/code/_onclick/hud/alien_larva.dm
@@ -12,7 +12,6 @@
floor_change = new /atom/movable/screen/floor_changer(null, src)
floor_change.icon = ui_style
- floor_change.screen_loc = ui_above_intent
static_inventory += floor_change
healths = new /atom/movable/screen/healths/alien(null, src)
diff --git a/code/_onclick/hud/generic_dextrous.dm b/code/_onclick/hud/generic_dextrous.dm
index 4048fd91b16..134f91a2b8d 100644
--- a/code/_onclick/hud/generic_dextrous.dm
+++ b/code/_onclick/hud/generic_dextrous.dm
@@ -3,11 +3,6 @@
..()
var/atom/movable/screen/using
- using = new /atom/movable/screen/drop(null, src)
- using.icon = ui_style
- using.screen_loc = ui_drone_drop
- static_inventory += using
-
pull_icon = new /atom/movable/screen/pull(null, src)
pull_icon.icon = ui_style
pull_icon.update_appearance()
@@ -16,21 +11,20 @@
build_hand_slots()
- using = new /atom/movable/screen/swap_hand(null, src)
+ using = new /atom/movable/screen/drop(null, src)
using.icon = ui_style
- using.icon_state = "swap_1_m"
- using.screen_loc = ui_swaphand_position(owner,1)
+ using.screen_loc = ui_swaphand_position(owner, 1)
static_inventory += using
using = new /atom/movable/screen/swap_hand(null, src)
using.icon = ui_style
- using.icon_state = "swap_2"
- using.screen_loc = ui_swaphand_position(owner,2)
+ using.icon_state = "act_swap"
+ using.screen_loc = ui_swaphand_position(owner, 2)
static_inventory += using
action_intent = new /atom/movable/screen/combattoggle/flashy(null, src)
action_intent.icon = ui_style
- action_intent.screen_loc = ui_combat_toggle
+ action_intent.screen_loc = ui_movi
static_inventory += action_intent
floor_change = new /atom/movable/screen/floor_changer(null, src)
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index ab1e5fccfbb..ae1e619763f 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -40,6 +40,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
var/atom/movable/screen/pull_icon
var/atom/movable/screen/rest_icon
var/atom/movable/screen/throw_icon
+ var/atom/movable/screen/resist_icon
var/atom/movable/screen/module_store_icon
var/atom/movable/screen/floor_change
@@ -505,12 +506,17 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
static_inventory += hand_box
hand_box.update_appearance()
- var/i = 1
- for(var/atom/movable/screen/swap_hand/SH in static_inventory)
- SH.screen_loc = ui_swaphand_position(mymob, IS_RIGHT_INDEX(i) ? RIGHT_HANDS : LEFT_HANDS)
- i++
- for(var/atom/movable/screen/human/equip/E in static_inventory)
- E.screen_loc = ui_equip_position(mymob)
+ var/num_of_swaps = 0
+ for(var/atom/movable/screen/swap_hand/swap_hands in static_inventory)
+ num_of_swaps += 1
+
+ var/hand_num = 1
+ for(var/atom/movable/screen/swap_hand/swap_hands in static_inventory)
+ var/hand_ind = RIGHT_HANDS
+ if (num_of_swaps > 1)
+ hand_ind = IS_RIGHT_INDEX(hand_num) ? LEFT_HANDS : RIGHT_HANDS
+ swap_hands.screen_loc = ui_swaphand_position(mymob, hand_ind)
+ hand_num += 1
if(ismob(mymob) && mymob.hud_used == src)
show_hud(hud_version)
@@ -581,11 +587,13 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
/datum/hud/proc/generate_landings(atom/movable/screen/movable/action_button/button)
listed_actions.generate_landing()
palette_actions.generate_landing()
+ toggle_palette.activate_landing()
/// Clears all currently visible landings
/datum/hud/proc/hide_landings()
listed_actions.clear_landing()
palette_actions.clear_landing()
+ toggle_palette.disable_landing()
// Updates any existing "owned" visuals, ensures they continue to be visible
/datum/hud/proc/update_our_owner()
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 50954584a0d..918f5f62a32 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -4,6 +4,7 @@
/atom/movable/screen/human/toggle
name = "toggle"
icon_state = "toggle"
+ base_icon_state = "toggle"
mouse_over_pointer = MOUSE_HAND_POINTER
/atom/movable/screen/human/toggle/Click()
@@ -23,17 +24,11 @@
usr.client.screen += targetmob.hud_used.toggleable_inventory
targetmob.hud_used.hidden_inventory_update(usr)
+ update_appearance()
-/atom/movable/screen/human/equip
- name = "equip"
- icon_state = "act_equip"
- mouse_over_pointer = MOUSE_HAND_POINTER
-
-/atom/movable/screen/human/equip/Click()
- if(ismecha(usr.loc)) // stops inventory actions in a mech
- return TRUE
- var/mob/living/carbon/human/H = usr
- H.quick_equip()
+/atom/movable/screen/human/toggle/update_icon_state()
+ icon_state = "[base_icon_state][hud?.inventory_shown ? "_active" : ""]"
+ return ..()
/atom/movable/screen/ling
icon = 'icons/hud/screen_changeling.dmi'
@@ -63,14 +58,17 @@
using = new /atom/movable/screen/language_menu(null, src)
using.icon = ui_style
+ using.screen_loc = ui_human_language
static_inventory += using
using = new /atom/movable/screen/navigate(null, src)
using.icon = ui_style
+ using.screen_loc = ui_human_navigate
static_inventory += using
using = new /atom/movable/screen/area_creator(null, src)
using.icon = ui_style
+ using.screen_loc = ui_human_area
static_inventory += using
action_intent = new /atom/movable/screen/combattoggle/flashy(null, src)
@@ -78,23 +76,17 @@
action_intent.screen_loc = ui_combat_toggle
static_inventory += action_intent
- floor_change = new /atom/movable/screen/floor_changer(null, src)
+ floor_change = new /atom/movable/screen/floor_changer/vertical(null, src)
floor_change.icon = ui_style
floor_change.screen_loc = ui_human_floor_changer
static_inventory += floor_change
-
using = new /atom/movable/screen/mov_intent(null, src)
using.icon = ui_style
using.icon_state = (owner.move_intent == MOVE_INTENT_RUN ? "running" : "walking")
using.screen_loc = ui_movi
static_inventory += using
- using = new /atom/movable/screen/drop(null, src)
- using.icon = ui_style
- using.screen_loc = ui_drop_throw
- static_inventory += using
-
inv_box = new /atom/movable/screen/inventory(null, src)
inv_box.name = "uniform"
inv_box.icon = ui_style
@@ -115,16 +107,15 @@
build_hand_slots()
- using = new /atom/movable/screen/swap_hand(null, src)
+ using = new /atom/movable/screen/drop(null, src)
using.icon = ui_style
- using.icon_state = "swap_1"
- using.screen_loc = ui_swaphand_position(owner,1)
+ using.screen_loc = ui_swaphand_position(owner, 1)
static_inventory += using
using = new /atom/movable/screen/swap_hand(null, src)
using.icon = ui_style
- using.icon_state = "swap_2"
- using.screen_loc = ui_swaphand_position(owner,2)
+ using.icon_state = "act_swap"
+ using.screen_loc = ui_swaphand_position(owner, 2)
static_inventory += using
inv_box = new /atom/movable/screen/inventory(null, src)
@@ -190,21 +181,16 @@
inv_box.slot_id = ITEM_SLOT_SUITSTORE
static_inventory += inv_box
- using = new /atom/movable/screen/resist(null, src)
- using.icon = ui_style
- using.screen_loc = ui_above_intent
- hotkeybuttons += using
+ resist_icon = new /atom/movable/screen/resist(null, src)
+ resist_icon.icon = ui_style
+ resist_icon.screen_loc = ui_above_movement
+ hotkeybuttons += resist_icon
using = new /atom/movable/screen/human/toggle(null, src)
using.icon = ui_style
using.screen_loc = ui_inventory
static_inventory += using
- using = new /atom/movable/screen/human/equip()
- using.icon = ui_style
- using.screen_loc = ui_equip_position(mymob)
- static_inventory += using
-
inv_box = new /atom/movable/screen/inventory(null, src)
inv_box.name = "gloves"
inv_box.icon = ui_style
@@ -266,7 +252,7 @@
rest_icon = new /atom/movable/screen/rest(null, src)
rest_icon.icon = ui_style
- rest_icon.screen_loc = ui_above_movement
+ rest_icon.screen_loc = ui_rest
rest_icon.update_appearance()
static_inventory += rest_icon
@@ -287,7 +273,7 @@
pull_icon = new /atom/movable/screen/pull(null, src)
pull_icon.icon = ui_style
- pull_icon.screen_loc = ui_above_intent
+ pull_icon.screen_loc = ui_above_movement_top
pull_icon.update_appearance()
static_inventory += pull_icon
diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm
index 01f79aa1476..594449575ea 100644
--- a/code/_onclick/hud/new_player.dm
+++ b/code/_onclick/hud/new_player.dm
@@ -259,12 +259,13 @@
icon = 'icons/hud/lobby/join.dmi'
icon_state = "" //Default to not visible
base_icon_state = "join_game"
+ enabled = null // set in init
/atom/movable/screen/lobby/button/join/Initialize(mapload, datum/hud/hud_owner)
. = ..()
- set_button_status(FALSE)
switch(SSticker.current_state)
if(GAME_STATE_PREGAME, GAME_STATE_STARTUP)
+ set_button_status(FALSE)
RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, PROC_REF(show_join_button))
if(GAME_STATE_SETTING_UP)
set_button_status(TRUE)
@@ -330,6 +331,7 @@
icon = 'icons/hud/lobby/observe.dmi'
icon_state = "observe_disabled"
base_icon_state = "observe"
+ enabled = null // set in init
/atom/movable/screen/lobby/button/observe/Initialize(mapload, datum/hud/hud_owner)
. = ..()
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index b72924fdbe5..fefd4ea889d 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -364,7 +364,7 @@
name = "change floor"
icon = 'icons/hud/screen_midnight.dmi'
icon_state = "floor_change"
- screen_loc = ui_floor_changer
+ screen_loc = ui_above_intent
mouse_over_pointer = MOUSE_HAND_POINTER
var/vertical = FALSE
@@ -439,10 +439,12 @@
name = "resist"
icon = 'icons/hud/screen_midnight.dmi'
icon_state = "act_resist"
+ base_icon_state = "act_resist"
plane = HUD_PLANE
mouse_over_pointer = MOUSE_HAND_POINTER
/atom/movable/screen/resist/Click()
+ flick("[base_icon_state]_on", src)
if(isliving(usr))
var/mob/living/L = usr
L.resist()
@@ -464,7 +466,7 @@
var/mob/living/user = hud?.mymob
if(!istype(user))
return ..()
- icon_state = "[base_icon_state][user.resting ? 0 : null]"
+ icon_state = "[base_icon_state][user.resting ? "_on" : null]"
return ..()
/atom/movable/screen/storage
@@ -537,7 +539,7 @@
/atom/movable/screen/throw_catch
name = "throw/catch"
icon = 'icons/hud/screen_midnight.dmi'
- icon_state = "act_throw_off"
+ icon_state = "act_throw"
mouse_over_pointer = MOUSE_HAND_POINTER
/atom/movable/screen/throw_catch/Click()
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 3d17d9abe89..70da5ac19de 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -444,21 +444,30 @@
else
return clamp(w_class * 6, 10, 100) // Multiply the item's weight class by 6, then clamp the value between 10 and 100
-/mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area, def_zone)
- if(!I.force && !length(I.attack_verb_simple) && !length(I.attack_verb_continuous))
+/mob/living/proc/send_item_attack_message(obj/item/weapon, mob/living/user, hit_area, def_zone)
+ if(!weapon.force && !length(weapon.attack_verb_simple) && !length(weapon.attack_verb_continuous))
return
- var/message_verb_continuous = length(I.attack_verb_continuous) ? "[pick(I.attack_verb_continuous)]" : "attacks"
- var/message_verb_simple = length(I.attack_verb_simple) ? "[pick(I.attack_verb_simple)]" : "attack"
- var/message_hit_area = get_hit_area_message(hit_area)
- var/attack_message_spectator = "[src] [message_verb_continuous][message_hit_area] with [I]!"
- var/attack_message_victim = "Something [message_verb_continuous] you[message_hit_area] with [I]!"
- var/attack_message_attacker = "You [message_verb_simple] [src][message_hit_area] with [I]!"
+ // Sanity in case one is null for some reason
+ var/picked_index = rand(max(length(weapon.attack_verb_simple), length(weapon.attack_verb_continuous)))
+
+ var/message_verb_continuous = "attacks"
+ var/message_verb_simple = "attack"
+ var/message_hit_area = get_hit_area_message(hit_area)
+ // Sanity in case one is... longer than the other?
+ if (picked_index && length(weapon.attack_verb_continuous) >= picked_index)
+ message_verb_continuous = weapon.attack_verb_continuous[picked_index]
+ if (picked_index && length(weapon.attack_verb_simple) >= picked_index)
+ message_verb_simple = weapon.attack_verb_simple[picked_index]
+
+ var/attack_message_spectator = "[src] [message_verb_continuous][message_hit_area] with [weapon]!"
+ var/attack_message_victim = "Something [message_verb_continuous] you[message_hit_area] with [weapon]!"
+ var/attack_message_attacker = "You [message_verb_simple] [src][message_hit_area] with [weapon]!"
if(user in viewers(src, null))
- attack_message_spectator = "[user] [message_verb_continuous] [src][message_hit_area] with [I]!"
- attack_message_victim = "[user] [message_verb_continuous] you[message_hit_area] with [I]!"
+ attack_message_spectator = "[user] [message_verb_continuous] [src][message_hit_area] with [weapon]!"
+ attack_message_victim = "[user] [message_verb_continuous] you[message_hit_area] with [weapon]!"
if(user == src)
- attack_message_victim = "You [message_verb_simple] yourself[message_hit_area] with [I]."
+ attack_message_victim = "You [message_verb_simple] yourself[message_hit_area] with [weapon]."
visible_message(span_danger("[attack_message_spectator]"),\
span_userdanger("[attack_message_victim]"), null, COMBAT_MESSAGE_RANGE, user)
if(is_blind())
diff --git a/code/controllers/subsystem/movement/newtonian_movement.dm b/code/controllers/subsystem/movement/newtonian_movement.dm
index 41db87d722b..e4143669678 100644
--- a/code/controllers/subsystem/movement/newtonian_movement.dm
+++ b/code/controllers/subsystem/movement/newtonian_movement.dm
@@ -39,7 +39,7 @@ MOVEMENT_SUBSYSTEM_DEF(newtonian_movement)
/datum/controller/subsystem/movement/newtonian_movement/proc/fire_moveloop(datum/move_loop/loop)
// Loop isn't even running right now
- if(!(loop.status & MOVELOOP_STATUS_QUEUED) || isnull(loop.queued_time))
+ if(!(loop.status & MOVELOOP_STATUS_QUEUED))
return
// Drop the loop, process it, and if its still valid - queue it again
dequeue_loop(loop)
diff --git a/code/datums/components/alternative_sharpness.dm b/code/datums/components/alternative_sharpness.dm
new file mode 100644
index 00000000000..e32c3ccf45d
--- /dev/null
+++ b/code/datums/components/alternative_sharpness.dm
@@ -0,0 +1,60 @@
+/// Allows items to have different sharpness for right click attacks
+/datum/component/alternative_sharpness
+ /// Sharpness we change the attack to
+ var/alt_sharpness = NONE
+ /// Overrides for continuous attack verbs when performing an alt attack
+ var/verbs_continuous = null
+ /// Overrides for simple attack verbs when performing an alt attack
+ var/verbs_simple = null
+ /// Value by which we offset our force during the attack
+ var/force_mod = 0
+ /// Are we currently performing an alt attack?
+ var/alt_attacking = FALSE
+ /// Trait required for us to trigger
+ var/required_trait = null
+ // Old values before we overrode them
+ var/base_continuous = null
+ var/base_simple = null
+ var/base_sharpness = NONE
+
+/datum/component/alternative_sharpness/Initialize(alt_sharpness, verbs_continuous = null, verbs_simple = null, force_mod = 0, required_trait = null)
+ if (!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+ var/obj/item/weapon = parent
+ src.alt_sharpness = alt_sharpness
+ src.verbs_continuous = verbs_continuous
+ src.verbs_simple = verbs_simple
+ src.force_mod = force_mod
+ src.required_trait = required_trait
+ base_continuous = weapon.attack_verb_continuous
+ base_simple = weapon.attack_verb_simple
+
+/datum/component/alternative_sharpness/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK_SECONDARY, PROC_REF(on_secondary_attack))
+
+/datum/component/alternative_sharpness/proc/on_secondary_attack(obj/item/source, atom/target, mob/user, params)
+ SIGNAL_HANDLER
+
+ if (alt_attacking || (required_trait && !HAS_TRAIT(source, required_trait)))
+ return
+
+ alt_attacking = TRUE
+ source.force += force_mod
+ base_sharpness = source.sharpness
+ source.sharpness = alt_sharpness
+ if (!isnull(verbs_continuous))
+ source.attack_verb_continuous = verbs_continuous
+
+ if (!isnull(verbs_simple))
+ source.attack_verb_simple = verbs_simple
+
+ // I absolutely despise this but this is geniunely the best way to do this without creating and hooking up to a dozen signals and still risking failure edge cases
+ addtimer(CALLBACK(src, PROC_REF(disable_alt_attack)), 1)
+
+/datum/component/alternative_sharpness/proc/disable_alt_attack()
+ var/obj/item/weapon = parent
+ alt_attacking = FALSE
+ weapon.force -= force_mod
+ weapon.attack_verb_continuous = base_continuous
+ weapon.attack_verb_simple = base_simple
+ weapon.sharpness = base_sharpness
diff --git a/code/datums/components/aquarium.dm b/code/datums/components/aquarium.dm
index 184513a2e79..f207ecd9f55 100644
--- a/code/datums/components/aquarium.dm
+++ b/code/datums/components/aquarium.dm
@@ -189,7 +189,7 @@
/datum/component/aquarium/proc/on_click_alt(atom/movable/source, mob/living/user)
SIGNAL_HANDLER
- if(!user.can_perform_action(src))
+ if(!user.can_perform_action(source))
return
var/closing = HAS_TRAIT(parent, TRAIT_AQUARIUM_PANEL_OPEN)
if(closing)
diff --git a/code/datums/components/cleaner.dm b/code/datums/components/cleaner.dm
index 7072f271c7a..035f0c054db 100644
--- a/code/datums/components/cleaner.dm
+++ b/code/datums/components/cleaner.dm
@@ -96,8 +96,8 @@
ADD_TRAIT(target, TRAIT_CURRENTLY_CLEANING, REF(src))
// We need to update our planes on overlay changes
RegisterSignal(target, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(cleaning_target_moved))
- var/mutable_appearance/low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, target, GAME_PLANE)
- var/mutable_appearance/high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, target, ABOVE_GAME_PLANE)
+ var/mutable_appearance/low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", CLEANABLE_OBJECT_LAYER, target, GAME_PLANE)
+ var/mutable_appearance/high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", CLEANABLE_OBJECT_LAYER, target, ABOVE_GAME_PLANE)
var/list/icon_offsets = target.get_oversized_icon_offsets()
low_bubble.pixel_x = icon_offsets["x"]
low_bubble.pixel_y = icon_offsets["y"]
@@ -140,13 +140,13 @@
if(same_z_layer)
return
// First, get rid of the old overlay
- var/mutable_appearance/old_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, old_turf, GAME_PLANE)
- var/mutable_appearance/old_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, old_turf, ABOVE_GAME_PLANE)
+ var/mutable_appearance/old_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", CLEANABLE_OBJECT_LAYER, old_turf, GAME_PLANE)
+ var/mutable_appearance/old_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", CLEANABLE_OBJECT_LAYER, old_turf, ABOVE_GAME_PLANE)
source.cut_overlay(old_low_bubble)
source.cut_overlay(old_high_bubble)
// Now, add the new one
- var/mutable_appearance/new_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, new_turf, GAME_PLANE)
- var/mutable_appearance/new_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, new_turf, ABOVE_GAME_PLANE)
+ var/mutable_appearance/new_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", CLEANABLE_OBJECT_LAYER, new_turf, GAME_PLANE)
+ var/mutable_appearance/new_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", CLEANABLE_OBJECT_LAYER, new_turf, ABOVE_GAME_PLANE)
source.add_overlay(new_low_bubble)
source.add_overlay(new_high_bubble)
diff --git a/code/datums/components/combustible_flooder.dm b/code/datums/components/combustible_flooder.dm
index 07df03671c1..c24fae55b84 100644
--- a/code/datums/components/combustible_flooder.dm
+++ b/code/datums/components/combustible_flooder.dm
@@ -57,7 +57,11 @@
message_admins(admin_message)
if(delete_parent && !QDELETED(parent))
- qdel(parent) // For things with the explodable component like plasma mats this isn't necessary, but there's no harm.
+ if(isobj(parent))
+ var/obj/obj_parent = parent
+ obj_parent.deconstruct(disassembled = FALSE)
+ else
+ qdel(parent) // For things with the explodable component like plasma mats this isn't necessary, but there's no harm.
qdel(src)
/// fire_act reaction.
diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm
index ca8c8fdc62f..04a53ce3a3a 100644
--- a/code/datums/components/crafting/crafting.dm
+++ b/code/datums/components/crafting/crafting.dm
@@ -1,16 +1,23 @@
-/datum/component/personal_crafting/Initialize()
+/datum/component/personal_crafting
+ /// Custom screen_loc for our element
+ var/screen_loc_override
+
+/datum/component/personal_crafting/Initialize(screen_loc_override)
+ src.screen_loc_override = screen_loc_override
if(ismob(parent))
RegisterSignal(parent, COMSIG_MOB_CLIENT_LOGIN, PROC_REF(create_mob_button))
-/datum/component/personal_crafting/proc/create_mob_button(mob/user, client/CL)
+/datum/component/personal_crafting/proc/create_mob_button(mob/user, client/user_client)
SIGNAL_HANDLER
- var/datum/hud/H = user.hud_used
- var/atom/movable/screen/craft/C = new()
- C.icon = H.ui_style
- H.static_inventory += C
- CL.screen += C
- RegisterSignal(C, COMSIG_SCREEN_ELEMENT_CLICK, PROC_REF(component_ui_interact))
+ var/datum/hud/hud = user.hud_used
+ var/atom/movable/screen/craft/craft_ui = new()
+ craft_ui.icon = hud.ui_style
+ if (screen_loc_override)
+ craft_ui.screen_loc = screen_loc_override
+ hud.static_inventory += craft_ui
+ user_client.screen += craft_ui
+ RegisterSignal(craft_ui, COMSIG_SCREEN_ELEMENT_CLICK, PROC_REF(component_ui_interact))
#define COOKING TRUE
#define CRAFTING FALSE
diff --git a/code/datums/components/explodable.dm b/code/datums/components/explodable.dm
index 9dc8db3bbc4..db77f865fd7 100644
--- a/code/datums/components/explodable.dm
+++ b/code/datums/components/explodable.dm
@@ -147,18 +147,20 @@
return // If we don't do this and this doesn't delete it can lock the MC into only processing Input, Timers, and Explosions.
var/atom/bomb = parent
- var/log = TRUE
- if(light_impact_range < 1)
- log = FALSE
+ var/do_log = light_impact_range >= 1
exploding = TRUE
- explosion(bomb, devastation_range, heavy_impact_range, light_impact_range, flame_range, flash_range, log, uncapped) //epic explosion time
+ explosion(bomb, devastation_range, heavy_impact_range, light_impact_range, flame_range, flash_range, do_log, uncapped) //epic explosion time
switch(delete_after)
if(EXPLODABLE_DELETE_SELF)
qdel(src)
if(EXPLODABLE_DELETE_PARENT)
- qdel(bomb)
+ if(isobj(bomb))
+ var/obj/obj_bomb = bomb
+ obj_bomb.deconstruct(disassembled = FALSE)
+ else
+ qdel(bomb)
else
addtimer(CALLBACK(src, PROC_REF(reset_exploding)), 0.1 SECONDS)
diff --git a/code/datums/components/food_storage.dm b/code/datums/components/food_storage.dm
index 843f611e5ff..32fb27c7b72 100644
--- a/code/datums/components/food_storage.dm
+++ b/code/datums/components/food_storage.dm
@@ -4,7 +4,7 @@
/datum/component/food_storage
/// Reference to what we have in our food.
- var/obj/item/stored_item
+ VAR_FINAL/obj/item/stored_item
/// The amount of volume the food has on creation - Used for probabilities
var/initial_volume = 10
/// Minimum size items that can be inserted
@@ -13,16 +13,14 @@
var/bad_chance_of_discovery = 0
/// What are the odds we see the stored item before we bite it?
var/good_chance_of_discovery = 100
- /// The stored item was found out somehow.
- var/discovered = FALSE
/datum/component/food_storage/Initialize(_minimum_weight_class = WEIGHT_CLASS_SMALL, _bad_chance = 0, _good_chance = 100)
RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY, PROC_REF(try_inserting_item))
+ RegisterSignal(parent, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_requesting_context_from_item))
RegisterSignal(parent, COMSIG_CLICK_CTRL, PROC_REF(try_removing_item))
RegisterSignal(parent, COMSIG_FOOD_EATEN, PROC_REF(consume_food_storage))
- RegisterSignal(parent, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_requesting_context_from_item))
-
+ RegisterSignals(parent, list(COMSIG_FOOD_CONSUMED, COMSIG_OBJ_DECONSTRUCT), PROC_REF(storage_consumed))
var/atom/food = parent
initial_volume = food.reagents.total_volume
@@ -33,12 +31,19 @@
food.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1
-/datum/component/food_storage/Destroy(force)
- if(stored_item)
- stored_item.forceMove(stored_item.drop_location())
- stored_item.dropped()
- stored_item = null
- . = ..()
+/datum/component/food_storage/UnregisterFromParent()
+ UnregisterSignal(parent, list(
+ COMSIG_ATOM_ITEM_INTERACTION_SECONDARY,
+ COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM,
+ COMSIG_CLICK_CTRL,
+ COMSIG_FOOD_CONSUMED,
+ COMSIG_FOOD_EATEN,
+ COMSIG_OBJ_DECONSTRUCT,
+ ))
+ if(QDELING(parent) || QDELETED(stored_item))
+ return
+ stored_item.forceMove(stored_item.drop_location())
+ stored_item = null
/** Begins the process of inserted an item.
*
@@ -60,19 +65,17 @@
return NONE
if(inserted_item.w_class > minimum_weight_class)
- to_chat(user, span_warning("\The [inserted_item.name] won't fit in \the [parent]."))
+ to_chat(user, span_warning("[inserted_item] won't fit in [parent]."))
return ITEM_INTERACT_BLOCKING
if(!QDELETED(stored_item))
- to_chat(user, span_warning("There's something in \the [parent]."))
- return ITEM_INTERACT_BLOCKING
-
- if(HAS_TRAIT(inserted_item, TRAIT_NODROP))
- to_chat(user, span_warning("\the [inserted_item] is stuck to your hand, you can't put into \the [parent]!"))
+ to_chat(user, span_warning("There's something in [parent]."))
return ITEM_INTERACT_BLOCKING
- user.visible_message(span_notice("[user.name] begins inserting [inserted_item.name] into \the [parent]."), \
- span_notice("You start to insert the [inserted_item.name] into \the [parent]."))
+ user.visible_message(
+ span_notice("[user] begins inserting [inserted_item] into [parent]."),
+ span_notice("You start to insert the [inserted_item] into [parent]."),
+ )
INVOKE_ASYNC(src, PROC_REF(insert_item), inserted_item, user)
return ITEM_INTERACT_SUCCESS
@@ -89,14 +92,11 @@
var/atom/food = parent
- if(QDELETED(stored_item))
- return CLICK_ACTION_BLOCKING
-
if(!food.can_interact(user))
return CLICK_ACTION_BLOCKING
- user.visible_message(span_notice("[user.name] begins tearing at \the [parent]."), \
- span_notice("You start to rip into \the [parent]."))
+ user.visible_message(span_notice("[user] begins tearing at [parent]."), \
+ span_notice("You start to rip into [parent]."))
INVOKE_ASYNC(src, PROC_REF(begin_remove_item), user)
return CLICK_ACTION_SUCCESS
@@ -110,9 +110,12 @@
/datum/component/food_storage/proc/insert_item(obj/item/inserted_item, mob/user)
if(!do_after(user, 1.5 SECONDS, target = parent))
return
+ if(!user.temporarilyRemoveItemFromInventory(inserted_item))
+ to_chat(user, span_warning("You can't seem to insert [inserted_item] into [parent]."))
+ return
var/atom/food = parent
- to_chat(user, span_notice("You slip [inserted_item.name] inside \the [parent]."))
+ to_chat(user, span_notice("You slip [inserted_item] inside [parent]."))
inserted_item.forceMove(food)
user.log_message("inserted [inserted_item] into [parent].", LOG_ATTACK)
food.add_fingerprint(user)
@@ -126,19 +129,22 @@
* user - person removing the item.
*/
/datum/component/food_storage/proc/begin_remove_item(mob/user)
- if(do_after(user, 10 SECONDS, target = parent))
- remove_item(user)
+ if(!do_after(user, 10 SECONDS, target = parent))
+ return
+ if(QDELETED(stored_item))
+ to_chat(user, span_warning("There's nothing in [parent]."))
+ return
+ remove_item(user)
/**
* Removes the stored item, putting it in user's hands or on the ground, then updates the reference.
*/
/datum/component/food_storage/proc/remove_item(mob/user)
if(user.put_in_hands(stored_item))
- user.visible_message(span_warning("[user.name] slowly pulls [stored_item.name] out of \the [parent]."), \
- span_warning("You slowly pull [stored_item.name] out of \the [parent]."))
+ user.visible_message(span_warning("[user] slowly pulls [stored_item] out of [parent]."), \
+ span_warning("You slowly pull [stored_item] out of [parent]."))
else
- stored_item.dropped()
- stored_item.visible_message(span_warning("[stored_item.name] falls out of \the [parent]."))
+ stored_item.visible_message(span_warning("[stored_item] falls out of [parent]."))
update_stored_item()
@@ -167,9 +173,10 @@
/// Chance of finding the held item = bad chance - 50
good_chance_of_discovery = bad_chance_of_discovery - 50
+ var/discovered = FALSE
if(prob(good_chance_of_discovery)) //finding the item, without biting it
discovered = TRUE
- to_chat(target, span_warning("It feels like there's something in \the [parent]...!"))
+ to_chat(target, span_warning("It feels like there's something in [parent]...!"))
else if(prob(bad_chance_of_discovery)) //finding the item, BY biting it
user.log_message("just fed [key_name(target)] \a [stored_item] which was hidden in [parent].", LOG_ATTACK)
@@ -179,6 +186,14 @@
if(!QDELETED(stored_item) && discovered)
INVOKE_ASYNC(src, PROC_REF(remove_item), user)
+/// When fully consumed, just drop the item out on the ground.
+/datum/component/food_storage/proc/storage_consumed(datum/source, mob/living/target, mob/living/user)
+ SIGNAL_HANDLER
+ if(QDELETED(stored_item))
+ return
+ stored_item.forceMove(stored_item.drop_location())
+ stored_item = null
+
/** Updates the reference of the stored item.
*
* Checks the food's contents for if an alternate item was placed into the food.
diff --git a/code/datums/components/jetpack.dm b/code/datums/components/jetpack.dm
index 1a44da7eb6d..c7ff096029b 100644
--- a/code/datums/components/jetpack.dm
+++ b/code/datums/components/jetpack.dm
@@ -110,6 +110,7 @@
RegisterSignal(user, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(pre_move_react))
RegisterSignal(user, COMSIG_MOB_CLIENT_MOVE_NOGRAV, PROC_REF(on_client_move))
RegisterSignal(user, COMSIG_MOB_ATTEMPT_HALT_SPACEMOVE, PROC_REF(on_pushoff))
+ RegisterSignal(user, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT, PROC_REF(on_input_block))
last_stabilization_tick = world.time
START_PROCESSING(SSnewtonian_movement, src)
if (effect_type)
@@ -164,6 +165,17 @@
var/max_drift_force = MOVE_DELAY_TO_DRIFT(user.cached_multiplicative_slowdown)
user.drift_handler.stabilize_drift(user.client.intended_direction ? dir2angle(user.client.intended_direction) : null, user.client.intended_direction ? max_drift_force : 0, stabilization_force * (seconds_per_tick * 1 SECONDS))
+/datum/component/jetpack/proc/on_input_block(mob/source)
+ SIGNAL_HANDLER
+
+ if (!should_trigger(source))
+ return
+
+ if (!check_on_move.Invoke(TRUE))
+ return
+
+ return DRIFT_ALLOW_INPUT
+
/datum/component/jetpack/proc/on_client_move(mob/source, list/move_args)
SIGNAL_HANDLER
@@ -179,11 +191,10 @@
var/max_drift_force = MOVE_DELAY_TO_DRIFT(source.cached_multiplicative_slowdown)
var/applied_force = drift_force
var/move_dir = source.client.intended_direction
- // We're not moving anywhere, try to see if we can simulate pushing off a wall
- if (isnull(source.drift_handler))
- var/atom/movable/backup = source.get_spacemove_backup(move_dir, FALSE)
- if (backup && !(backup.dir & move_dir))
- applied_force = max_drift_force
+ // Try to see if we can simulate pushing off a wall
+ var/atom/movable/backup = source.get_spacemove_backup(move_dir, FALSE, include_floors = TRUE)
+ if (backup && !(backup.dir & move_dir))
+ applied_force = max_drift_force
// We don't want to force the loop to fire before stabilizing if we're going to, otherwise its effects will be delayed until the next tick which is jank
var/force_stabilize = FALSE
diff --git a/code/datums/components/leanable.dm b/code/datums/components/leanable.dm
index b95fd734ad0..b823cf5ec50 100644
--- a/code/datums/components/leanable.dm
+++ b/code/datums/components/leanable.dm
@@ -2,26 +2,21 @@
/datum/component/leanable
/// How much will mobs that lean onto this object be offset
var/leaning_offset = 11
- /// List of click modifiers that are required to be present for leaning to trigger
- var/list/click_mods = null
- /// Callback called for additional checks if a lean is valid
- var/datum/callback/lean_check = null
- /// Whenever this object can be leaned on from the same turf as its' own. Do not use without a custom lean_check!
- var/same_turf = FALSE
/// List of mobs currently leaning on our parent
var/list/leaning_mobs = list()
-/datum/component/leanable/Initialize(leaning_offset = 11, list/click_mods = null, datum/callback/lean_check = null, same_turf = FALSE)
+/datum/component/leanable/Initialize(mob/living/leaner, leaning_offset = 11)
. = ..()
src.leaning_offset = leaning_offset
- src.click_mods = click_mods
- src.lean_check = lean_check
- src.same_turf = same_turf
+ mousedrop_receive(parent, leaner, leaner)
/datum/component/leanable/RegisterWithParent()
RegisterSignal(parent, COMSIG_MOUSEDROPPED_ONTO, PROC_REF(mousedrop_receive))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
+/datum/component/leanable/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_MOUSEDROPPED_ONTO, COMSIG_MOVABLE_MOVED))
+
/datum/component/leanable/Destroy(force)
for (var/mob/living/leaner as anything in leaning_mobs)
leaner.stop_leaning()
@@ -30,17 +25,13 @@
/datum/component/leanable/proc/on_moved(datum/source)
SIGNAL_HANDLER
+
for (var/mob/living/leaner as anything in leaning_mobs)
leaner.stop_leaning()
/datum/component/leanable/proc/mousedrop_receive(atom/source, atom/movable/dropped, mob/user, params)
if (dropped != user)
return
- if (islist(click_mods))
- var/list/modifiers = params2list(params)
- for (var/modifier in click_mods)
- if (!LAZYACCESS(modifiers, modifier))
- return
if (!iscarbon(dropped) && !iscyborg(dropped))
return
var/mob/living/leaner = dropped
@@ -49,9 +40,7 @@
if (HAS_TRAIT_FROM(leaner, TRAIT_UNDENSE, LEANING_TRAIT))
return
var/turf/checked_turf = get_step(leaner, REVERSE_DIR(leaner.dir))
- if (checked_turf != get_turf(source) && (!same_turf || get_turf(source) != get_turf(leaner)))
- return
- if (!isnull(lean_check) && !lean_check.Invoke(dropped, params))
+ if (checked_turf != get_turf(source))
return
leaner.start_leaning(source, leaning_offset)
leaning_mobs += leaner
@@ -63,6 +52,13 @@
leaning_mobs -= source
UnregisterSignal(source, list(COMSIG_LIVING_STOPPED_LEANING, COMSIG_QDELETING))
+/**
+ * Makes the mob lean on an atom
+ * Arguments
+ *
+ * * atom/lean_target - the target the mob is trying to lean on
+ * * leaning_offset - pixel offset to apply on the mob when leaning
+ */
/mob/living/proc/start_leaning(atom/lean_target, leaning_offset)
var/new_x = lean_target.pixel_x + base_pixel_x + body_position_pixel_x_offset
var/new_y = lean_target.pixel_y + base_pixel_y + body_position_pixel_y_offset
@@ -95,6 +91,7 @@
/// You fall on your face if you get teleported while leaning
/mob/living/proc/teleport_away_while_leaning()
SIGNAL_HANDLER
+
// Make sure we unregister signal handlers and reset animation
stop_leaning()
// -1000 aura
@@ -103,6 +100,7 @@
/mob/living/proc/stop_leaning()
SIGNAL_HANDLER
+
UnregisterSignal(src, list(
COMSIG_MOB_CLIENT_PRE_MOVE,
COMSIG_LIVING_DISARM_HIT,
@@ -117,5 +115,6 @@
/mob/living/proc/lean_dir_changed(atom/source, old_dir, new_dir)
SIGNAL_HANDLER
+
if (old_dir != new_dir)
INVOKE_ASYNC(src, PROC_REF(stop_leaning))
diff --git a/code/datums/components/mutant_hands.dm b/code/datums/components/mutant_hands.dm
index 66e19852ae7..b3478d41f31 100644
--- a/code/datums/components/mutant_hands.dm
+++ b/code/datums/components/mutant_hands.dm
@@ -147,6 +147,7 @@
icon = 'icons/effects/blood.dmi'
icon_state = "bloodhand_left"
base_icon_state = "bloodhand"
+ icon_angle = 90
item_flags = ABSTRACT | DROPDEL | HAND_ITEM
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
diff --git a/code/datums/drift_handler.dm b/code/datums/drift_handler.dm
index dc47f28819b..dcf0771d8a5 100644
--- a/code/datums/drift_handler.dm
+++ b/code/datums/drift_handler.dm
@@ -36,7 +36,6 @@
RegisterSignal(drifting_loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(before_move))
RegisterSignal(drifting_loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(after_move))
RegisterSignal(drifting_loop, COMSIG_QDELETING, PROC_REF(loop_death))
- RegisterSignal(parent, COMSIG_MOB_ATTEMPT_HALT_SPACEMOVE, PROC_REF(attempt_halt))
if(drifting_loop.status & MOVELOOP_STATUS_RUNNING)
drifting_start(drifting_loop) // There's a good chance it'll autostart, gotta catch that
@@ -208,28 +207,28 @@
if(world.time < block_inputs_until)
return COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE
-/datum/drift_handler/proc/attempt_halt(mob/source, movement_dir, continuous_move, atom/backup)
- SIGNAL_HANDLER
-
- if ((backup.density || !backup.CanPass(source, get_dir(backup, source))) && (get_dir(source, backup) == movement_dir || source.loc == backup.loc))
+/datum/drift_handler/proc/attempt_halt(movement_dir, continuous_move, atom/backup)
+ if ((backup.density || !backup.CanPass(parent, get_dir(backup, parent))) && (get_dir(parent, backup) == movement_dir || parent.loc == backup.loc))
if (drift_force >= INERTIA_FORCE_THROW_FLOOR)
- source.throw_at(backup, 1, floor(1 + (drift_force - INERTIA_FORCE_THROW_FLOOR) / INERTIA_FORCE_PER_THROW_FORCE), spin = FALSE)
- return
+ parent.throw_at(backup, 1, floor(1 + (drift_force - INERTIA_FORCE_THROW_FLOOR) / INERTIA_FORCE_PER_THROW_FORCE), spin = FALSE)
+ return FALSE
if (drift_force < INERTIA_FORCE_SPACEMOVE_GRAB || isnull(drifting_loop))
- return
+ return FALSE
- if (!isnull(source.client) && source.client.intended_direction)
- if ((source.client.intended_direction & movement_dir) && !(get_dir(source, backup) & movement_dir))
- return
+ if (ismob(parent))
+ var/mob/source_user = parent
+ if (!isnull(source_user.client) && source_user.client.intended_direction)
+ if ((source_user.client.intended_direction & movement_dir) && !(get_dir(source_user, backup) & movement_dir))
+ return FALSE
- if (drift_force <= INERTIA_FORCE_SPACEMOVE_REDUCTION / source.inertia_force_weight)
- glide_to_halt(get_loop_delay(source))
- return COMPONENT_PREVENT_SPACEMOVE_HALT
+ if (drift_force <= INERTIA_FORCE_SPACEMOVE_REDUCTION / parent.inertia_force_weight)
+ glide_to_halt(get_loop_delay(parent))
+ return TRUE
- drift_force -= INERTIA_FORCE_SPACEMOVE_REDUCTION / source.inertia_force_weight
- drifting_loop.set_delay(get_loop_delay(source))
- return COMPONENT_PREVENT_SPACEMOVE_HALT
+ drift_force -= INERTIA_FORCE_SPACEMOVE_REDUCTION / parent.inertia_force_weight
+ drifting_loop.set_delay(get_loop_delay(parent))
+ return TRUE
/datum/drift_handler/proc/get_loop_delay(atom/movable/movable)
return (DEFAULT_INERTIA_SPEED / ((1 - INERTIA_SPEED_COEF) + drift_force * INERTIA_SPEED_COEF)) * movable.inertia_move_multiplier
diff --git a/code/datums/elements/weapon_description.dm b/code/datums/elements/weapon_description.dm
index eda7ca59b49..64d044fb74a 100644
--- a/code/datums/elements/weapon_description.dm
+++ b/code/datums/elements/weapon_description.dm
@@ -73,9 +73,9 @@
// Doesn't show the base notes for items that have the override notes variable set to true
if(!source.override_notes)
- if (source.sharpness & SHARP_EDGED)
+ if (source.get_sharpness() & SHARP_EDGED)
readout += "It's sharp and could cause bleeding wounds."
- if (source.sharpness & SHARP_POINTY)
+ if (source.get_sharpness() & SHARP_POINTY)
readout += "It's pointy and could cause piercing wounds."
// Make sure not to divide by 0 on accident
if(source.force > 0)
diff --git a/code/datums/keybinding/living.dm b/code/datums/keybinding/living.dm
index f3d9cbf2989..f3b14c58dfa 100644
--- a/code/datums/keybinding/living.dm
+++ b/code/datums/keybinding/living.dm
@@ -16,10 +16,20 @@
. = ..()
if(.)
return
- var/mob/living/L = user.mob
- L.resist()
+ var/mob/living/owner = user.mob
+ owner.resist()
+ if (owner.hud_used?.resist_icon)
+ owner.hud_used.resist_icon.icon_state = "[owner.hud_used.resist_icon.base_icon_state]_on"
return TRUE
+/datum/keybinding/living/resist/up(client/user)
+ . = ..()
+ if(.)
+ return
+ var/mob/living/owner = user.mob
+ if (owner.hud_used?.resist_icon)
+ owner.hud_used.resist_icon.icon_state = owner.hud_used.resist_icon.base_icon_state
+
/datum/keybinding/living/look_up
hotkey_keys = list("P")
name = "look up"
diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm
index d0ad6544ca6..1dc2e69f2ca 100644
--- a/code/datums/looping_sounds/_looping_sound.dm
+++ b/code/datums/looping_sounds/_looping_sound.dm
@@ -174,7 +174,7 @@
if(!each_once)
. = play_from
while(!isfile(.) && !isnull(.))
- . = pick_weight(.)
+ . = pick_weight_recursive(.)
return .
if(in_order)
@@ -192,7 +192,7 @@
// Tree is a list of lists containign files
// If an entry in the tree goes to 0 length, we cut it from the list
tree += list(.)
- . = pick_weight(.)
+ . = pick_weight_recursive(.)
if(!isfile(.))
return
diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm
index c4648a929b3..92f78074c98 100644
--- a/code/datums/looping_sounds/machinery_sounds.dm
+++ b/code/datums/looping_sounds/machinery_sounds.dm
@@ -1,13 +1,17 @@
/datum/looping_sound/showering
start_sound = 'sound/machines/shower/shower_start.ogg'
start_length = 2
- mid_sounds = list('sound/machines/shower/shower_mid1.ogg' = 1, 'sound/machines/shower/shower_mid2.ogg' = 1, 'sound/machines/shower/shower_mid3.ogg' = 1)
+ mid_sounds = list(
+ 'sound/machines/shower/shower_mid1.ogg',
+ 'sound/machines/shower/shower_mid2.ogg',
+ 'sound/machines/shower/shower_mid3.ogg',
+ )
mid_length = 10
end_sound = 'sound/machines/shower/shower_end.ogg'
volume = 20
/datum/looping_sound/supermatter
- mid_sounds = list('sound/machines/sm/loops/calm.ogg' = 1)
+ mid_sounds = list('sound/machines/sm/loops/calm.ogg')
mid_length = 60
volume = 40
extra_range = 25
@@ -16,14 +20,14 @@
vary = TRUE
/datum/looping_sound/destabilized_crystal
- mid_sounds = list('sound/machines/sm/loops/delamming.ogg' = 1)
+ mid_sounds = list('sound/machines/sm/loops/delamming.ogg')
mid_length = 60
volume = 55
extra_range = 15
vary = TRUE
/datum/looping_sound/hypertorus
- mid_sounds = list('sound/machines/hypertorus/loops/hypertorus_nominal.ogg' = 1)
+ mid_sounds = list('sound/machines/hypertorus/loops/hypertorus_nominal.ogg')
mid_length = 60
volume = 55
extra_range = 15
@@ -32,35 +36,41 @@
/datum/looping_sound/generator
start_sound = 'sound/machines/generator/generator_start.ogg'
start_length = 4
- mid_sounds = list('sound/machines/generator/generator_mid1.ogg' = 1, 'sound/machines/generator/generator_mid2.ogg' = 1, 'sound/machines/generator/generator_mid3.ogg' = 1)
+ mid_sounds = list(
+ 'sound/machines/generator/generator_mid1.ogg',
+ 'sound/machines/generator/generator_mid2.ogg',
+ 'sound/machines/generator/generator_mid3.ogg',
+ )
mid_length = 4
end_sound = 'sound/machines/generator/generator_end.ogg'
volume = 40
-
/datum/looping_sound/deep_fryer
start_sound = 'sound/machines/fryer/deep_fryer_immerse.ogg' //my immersions
start_length = 10
- mid_sounds = list('sound/machines/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/fryer/deep_fryer_2.ogg' = 1)
+ mid_sounds = list(
+ 'sound/machines/fryer/deep_fryer_1.ogg',
+ 'sound/machines/fryer/deep_fryer_2.ogg',
+ )
mid_length = 2
end_sound = 'sound/machines/fryer/deep_fryer_emerge.ogg'
volume = 15
/datum/looping_sound/clock
- mid_sounds = list('sound/ambience/misc/ticking_clock.ogg' = 1)
+ mid_sounds = list('sound/ambience/misc/ticking_clock.ogg')
mid_length = 40
volume = 50
ignore_walls = FALSE
/datum/looping_sound/grill
- mid_sounds = list('sound/machines/grill/grillsizzle.ogg' = 1)
+ mid_sounds = list('sound/machines/grill/grillsizzle.ogg')
mid_length = 18
volume = 50
/datum/looping_sound/oven
start_sound = 'sound/machines/oven/oven_loop_start.ogg' //my immersions
start_length = 12
- mid_sounds = list('sound/machines/oven/oven_loop_mid.ogg' = 1)
+ mid_sounds = list('sound/machines/oven/oven_loop_mid.ogg')
mid_length = 13
end_sound = 'sound/machines/oven/oven_loop_end.ogg'
volume = 100
@@ -68,19 +78,25 @@
/datum/looping_sound/deep_fryer
mid_length = 2
- mid_sounds = list('sound/machines/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/fryer/deep_fryer_2.ogg' = 1)
+ mid_sounds = list(
+ 'sound/machines/fryer/deep_fryer_1.ogg',
+ 'sound/machines/fryer/deep_fryer_2.ogg',
+ )
volume = 30
/datum/looping_sound/microwave
start_sound = 'sound/machines/microwave/microwave-start.ogg'
start_length = 10
- mid_sounds = list('sound/machines/microwave/microwave-mid1.ogg' = 10, 'sound/machines/microwave/microwave-mid2.ogg' = 1)
+ mid_sounds = list(
+ 'sound/machines/microwave/microwave-mid1.ogg' = 10,
+ 'sound/machines/microwave/microwave-mid2.ogg' = 1,
+ )
mid_length = 10
end_sound = 'sound/machines/microwave/microwave-end.ogg'
volume = 90
/datum/looping_sound/lathe_print
- mid_sounds = list('sound/machines/lathe/lathe_print.ogg' = 1)
+ mid_sounds = list('sound/machines/lathe/lathe_print.ogg')
mid_length = 20
volume = 50
vary = TRUE
@@ -90,19 +106,19 @@
/datum/looping_sound/jackpot
mid_length = 11
- mid_sounds = list('sound/machines/roulette/roulettejackpot.ogg' = 1)
+ mid_sounds = list('sound/machines/roulette/roulettejackpot.ogg')
volume = 85
vary = TRUE
/datum/looping_sound/server
mid_sounds = list(
- 'sound/machines/tcomms/tcomms_mid1.ogg' = 1,
- 'sound/machines/tcomms/tcomms_mid2.ogg' = 1,
- 'sound/machines/tcomms/tcomms_mid3.ogg' = 1,
- 'sound/machines/tcomms/tcomms_mid4.ogg' = 1,
- 'sound/machines/tcomms/tcomms_mid5.ogg' = 1,
- 'sound/machines/tcomms/tcomms_mid6.ogg' = 1,
- 'sound/machines/tcomms/tcomms_mid7.ogg' = 1,
+ 'sound/machines/tcomms/tcomms_mid1.ogg',
+ 'sound/machines/tcomms/tcomms_mid2.ogg',
+ 'sound/machines/tcomms/tcomms_mid3.ogg',
+ 'sound/machines/tcomms/tcomms_mid4.ogg',
+ 'sound/machines/tcomms/tcomms_mid5.ogg',
+ 'sound/machines/tcomms/tcomms_mid6.ogg',
+ 'sound/machines/tcomms/tcomms_mid7.ogg',
)
mid_length = 1.8 SECONDS
extra_range = -8
@@ -116,7 +132,10 @@
start_sound = 'sound/machines/computer/computer_start.ogg'
start_length = 7.2 SECONDS
start_volume = 10
- mid_sounds = list('sound/machines/computer/computer_mid1.ogg' = 1, 'sound/machines/computer/computer_mid2.ogg' = 1)
+ mid_sounds = list(
+ 'sound/machines/computer/computer_mid1.ogg',
+ 'sound/machines/computer/computer_mid2.ogg',
+ )
mid_length = 1.8 SECONDS
end_sound = 'sound/machines/computer/computer_end.ogg'
end_volume = 10
@@ -141,7 +160,12 @@
falloff_exponent = 20
/datum/looping_sound/firealarm
- mid_sounds = list('sound/machines/fire_alarm/FireAlarm1.ogg' = 1,'sound/machines/fire_alarm/FireAlarm2.ogg' = 1,'sound/machines/fire_alarm/FireAlarm3.ogg' = 1,'sound/machines/fire_alarm/FireAlarm4.ogg' = 1)
+ mid_sounds = list(
+ 'sound/machines/fire_alarm/FireAlarm1.ogg',
+ 'sound/machines/fire_alarm/FireAlarm2.ogg',
+ 'sound/machines/fire_alarm/FireAlarm3.ogg',
+ 'sound/machines/fire_alarm/FireAlarm4.ogg',
+ )
mid_length = 2.4 SECONDS
volume = 30
@@ -151,30 +175,30 @@
falloff_exponent = 5
/datum/looping_sound/boiling
- mid_sounds = list('sound/effects/bubbles/bubbles2.ogg' = 1)
+ mid_sounds = list('sound/effects/bubbles/bubbles2.ogg')
mid_length = 7 SECONDS
volume = 25
/datum/looping_sound/typing
mid_sounds = list(
- 'sound/machines/terminal/terminal_button01.ogg' = 1,
- 'sound/machines/terminal/terminal_button02.ogg' = 1,
- 'sound/machines/terminal/terminal_button03.ogg' = 1,
- 'sound/machines/terminal/terminal_button04.ogg' = 1,
- 'sound/machines/terminal/terminal_button05.ogg' = 1,
- 'sound/machines/terminal/terminal_button06.ogg' = 1,
- 'sound/machines/terminal/terminal_button07.ogg' = 1,
- 'sound/machines/terminal/terminal_button08.ogg' = 1,
+ 'sound/machines/terminal/terminal_button01.ogg',
+ 'sound/machines/terminal/terminal_button02.ogg',
+ 'sound/machines/terminal/terminal_button03.ogg',
+ 'sound/machines/terminal/terminal_button04.ogg',
+ 'sound/machines/terminal/terminal_button05.ogg',
+ 'sound/machines/terminal/terminal_button06.ogg',
+ 'sound/machines/terminal/terminal_button07.ogg',
+ 'sound/machines/terminal/terminal_button08.ogg',
)
mid_length = 0.3 SECONDS
/datum/looping_sound/soup
mid_sounds = list(
- 'sound/effects/soup_boil/soup_boil1.ogg' = 1,
- 'sound/effects/soup_boil/soup_boil2.ogg' = 1,
- 'sound/effects/soup_boil/soup_boil3.ogg' = 1,
- 'sound/effects/soup_boil/soup_boil4.ogg' = 1,
- 'sound/effects/soup_boil/soup_boil5.ogg' = 1,
+ 'sound/effects/soup_boil/soup_boil1.ogg',
+ 'sound/effects/soup_boil/soup_boil2.ogg',
+ 'sound/effects/soup_boil/soup_boil3.ogg',
+ 'sound/effects/soup_boil/soup_boil4.ogg',
+ 'sound/effects/soup_boil/soup_boil5.ogg',
)
mid_length = 3 SECONDS
volume = 80
@@ -182,3 +206,19 @@
end_volume = 60
extra_range = MEDIUM_RANGE_SOUND_EXTRARANGE
falloff_exponent = 4
+
+/datum/looping_sound/cryo_cell
+ mid_sounds = list(
+ 'sound/machines/cryo/cryo_1.ogg',
+ 'sound/machines/cryo/cryo_2.ogg',
+ 'sound/machines/cryo/cryo_3.ogg',
+ 'sound/machines/cryo/cryo_4.ogg',
+ 'sound/machines/cryo/cryo_5.ogg',
+ 'sound/machines/cryo/cryo_6.ogg',
+ 'sound/machines/cryo/cryo_7.ogg',
+ 'sound/machines/cryo/cryo_8.ogg',
+ 'sound/machines/cryo/cryo_9.ogg',
+ 'sound/machines/cryo/cryo_10.ogg',
+ )
+ mid_length = 5 SECONDS
+ volume = 50
diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm
index d2142b02a8b..b5ff49037e6 100644
--- a/code/datums/martial/sleeping_carp.dm
+++ b/code/datums/martial/sleeping_carp.dm
@@ -254,6 +254,7 @@
icon = 'icons/obj/weapons/staff.dmi'
icon_state = "bostaff0"
base_icon_state = "bostaff"
+ icon_angle = -135
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
block_chance = 50
diff --git a/code/datums/mutations/tongue_spike.dm b/code/datums/mutations/tongue_spike.dm
index c6a48a32a14..663dcd2541a 100644
--- a/code/datums/mutations/tongue_spike.dm
+++ b/code/datums/mutations/tongue_spike.dm
@@ -45,6 +45,7 @@
desc = "Hardened biomass, shaped into a spike. Very pointy!"
icon = 'icons/obj/weapons/thrown.dmi'
icon_state = "tonguespike"
+ icon_angle = 45
force = 2
throwforce = 25
throw_speed = 4
diff --git a/code/datums/storage/subtypes/rped.dm b/code/datums/storage/subtypes/rped.dm
index 9931cff7372..a54d52ca359 100644
--- a/code/datums/storage/subtypes/rped.dm
+++ b/code/datums/storage/subtypes/rped.dm
@@ -6,7 +6,6 @@
#define MAX_STACK_PICKUP 30
/datum/storage/rped
- allow_quick_empty = TRUE
allow_quick_gather = TRUE
max_slots = 50
max_total_storage = 100
@@ -14,7 +13,7 @@
numerical_stacking = TRUE
/**
- * as of now only these stack components are required to build machines like[thermomaachine,crystallizer,electrolyzer]
+ * as of now only these stack components are required to build machines like[thermomachine,crystallizer,electrolyzer]
* so we limit the rped to pick up only these stack types so players dont cheat and use this as a general storage medium
*/
var/static/list/allowed_material_types = list(
@@ -33,11 +32,20 @@
)
/datum/storage/rped/can_insert(obj/item/to_insert, mob/user, messages = TRUE, force = FALSE)
- . = ..()
- if(!.)
- return .
+ //only stock parts permited
+ if(to_insert.get_part_rating())
+ return ..()
+
+ //some exceptions to non stock parts
+ var/static/list/obj/item/exceptions = list(
+ /obj/item/stack,
+ /obj/item/circuitboard/machine,
+ /obj/item/circuitboard/computer,
+ )
+
+ return is_type_in_list(to_insert, exceptions) ? ..() : FALSE
- //we check how much of glass,plasteel & cable the user can insert
+/datum/storage/rped/attempt_insert(obj/item/to_insert, mob/user, override, force, messages)
if(isstack(to_insert))
//user tried to insert invalid stacktype
if(!is_type_in_list(to_insert, allowed_material_types) && !is_type_in_list(to_insert, allowed_bluespace_types))
@@ -53,60 +61,58 @@
//if yes count total bluespace stuff is the RPED and then compare the total amount to the value the user is trying to insert
if(is_type_in_list(stack_content, allowed_bluespace_types))
present_amount += stack_content.amount
+
//count other normal stack stuff
- else if(istype(to_insert,stack_content.type))
+ else if(the_stack.merge_type == stack_content.merge_type)
present_amount = stack_content.amount
break
- //no more storage for this specific stack type
- if(MAX_STACK_PICKUP - present_amount == 0)
- return FALSE
+ var/available = MAX_STACK_PICKUP - present_amount
- //we want the user to insert the exact stack amount which is available so we dont have to bother subtracting & leaving left overs for the user
- var/available = MAX_STACK_PICKUP-present_amount
- if(available - the_stack.amount < 0)
+ //no more storage for this specific stack type
+ if(!available)
return FALSE
- else if(istype(to_insert, /obj/item/circuitboard/machine) || istype(to_insert, /obj/item/circuitboard/computer))
- return TRUE
+ var/obj/item/stack/target = the_stack
+ if(the_stack.amount > available) //take in only a portion of the stack that can fit in our quota
+ target = fast_split_stack(the_stack, available)
+ target.copy_evidences(the_stack)
- //check normal insertion of other stock parts
- else if(!to_insert.get_part_rating())
- return FALSE
+ . = ..(target, user, override, force, messages)
+ if(!. && target != the_stack) //in case of failure merge back the split amount into the original
+ the_stack.add(target.amount)
+ qdel(target)
- return .
-
-/datum/storage/rped/mass_empty(datum/source, mob/user)
- if(!allow_quick_empty)
return
- remove_lowest_tier(user.drop_location())
+ return ..()
-/**
- * Searches through everything currently in storage, calculates the lowest tier of parts inside of it,
- * and then dumps out every part that has the equal tier of parts. Likely a worse implementation of remove_all.
- *
- * Arguments
- * * atom/dump_loc - where we're placing the item
- */
-/datum/storage/rped/proc/remove_lowest_tier(atom/dump_loc = parent.drop_location())
+/datum/storage/rped/mass_empty(datum/source, mob/user)
var/list/obj/item/parts_list = list()
- var/current_lowest_tier = INFINITY
-
for(var/obj/item/thing in real_location)
parts_list += thing
+ if(!parts_list.len)
+ return
- if(parts_list.len > 0)
- parts_list = reverse_range(sortTim(parts_list, GLOBAL_PROC_REF(cmp_rped_sort)))
- current_lowest_tier = parts_list[1].get_part_rating()
- if(ismob(parent.loc))
- parent.balloon_alert(parent.loc, "dropping lowest rated parts...")
- for(var/obj/item/part in parts_list)
- if(part.get_part_rating() != current_lowest_tier)
- break
- if(!attempt_remove(part, dump_loc, silent = TRUE))
- continue
- part.pixel_x = part.base_pixel_x + rand(-8, 8)
- part.pixel_y = part.base_pixel_y + rand(-8, 8)
+ var/current_lowest_tier = INFINITY
+ parts_list = reverse_range(sortTim(parts_list, GLOBAL_PROC_REF(cmp_rped_sort)))
+ current_lowest_tier = parts_list[1].get_part_rating()
+ if(ismob(parent.loc))
+ parent.balloon_alert(parent.loc, "dropping lowest rated parts...")
+
+ var/dump_loc = user.drop_location()
+ for(var/obj/item/part in parts_list)
+ if(part.get_part_rating() != current_lowest_tier)
+ break
+ if(!attempt_remove(part, dump_loc, silent = TRUE))
+ continue
+ part.pixel_x = part.base_pixel_x + rand(-8, 8)
+ part.pixel_y = part.base_pixel_y + rand(-8, 8)
+
+///bluespace variant
+/datum/storage/rped/bluespace
+ max_slots = 400
+ max_total_storage = 800
+ max_specific_storage = WEIGHT_CLASS_GIGANTIC
#undef MAX_STACK_PICKUP
diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm
index bb4bc85d8e9..4bf2664fb34 100644
--- a/code/datums/wounds/pierce.dm
+++ b/code/datums/wounds/pierce.dm
@@ -17,6 +17,8 @@
/// How much blood we start losing when this wound is first applied
var/initial_flow
+ /// How much our blood_flow will naturally decrease per second, even without gauze
+ var/clot_rate
/// If gauzed, what percent of the internal bleeding actually clots of the total absorption rate
var/gauzed_clot_rate
@@ -72,8 +74,10 @@
return BLOOD_FLOW_STEADY
if(HAS_TRAIT(victim, TRAIT_BLOODY_MESS))
return BLOOD_FLOW_INCREASING
- if(limb.current_gauze)
+ if(limb.current_gauze || clot_rate > 0)
return BLOOD_FLOW_DECREASING
+ if(clot_rate < 0)
+ return BLOOD_FLOW_INCREASING
return BLOOD_FLOW_STEADY
/datum/wound/pierce/bleed/handle_process(seconds_per_tick, times_fired)
@@ -92,10 +96,16 @@
if(HAS_TRAIT(victim, TRAIT_BLOODY_MESS))
adjust_blood_flow(0.25 * seconds_per_tick) // old heparin used to just add +2 bleed stacks per tick, this adds 0.5 bleed flow to all open cuts which is probably even stronger as long as you can cut them first
+ //gauze always reduces blood flow, even for non bleeders
if(limb.current_gauze)
+ if(clot_rate > 0)
+ adjust_blood_flow(-clot_rate * seconds_per_tick)
var/gauze_power = limb.current_gauze.absorption_rate
limb.seep_gauze(gauze_power * seconds_per_tick)
adjust_blood_flow(-gauze_power * gauzed_clot_rate * seconds_per_tick)
+ //otherwise, only clot if it's a bleeder
+ else if(limb.can_bleed())
+ adjust_blood_flow(-clot_rate * seconds_per_tick)
/datum/wound/pierce/bleed/adjust_blood_flow(adjust_by, minimum)
. = ..()
@@ -174,12 +184,13 @@
cauterization, or in extreme circumstances, exposure to extreme cold or vaccuum. \
Follow with food and a rest period."
treat_text_short = "Apply bandaging or suturing."
- examine_desc = "has a small, circular hole, gently bleeding"
+ examine_desc = "has a small, torn hole, gently bleeding"
occur_text = "spurts out a thin stream of blood"
sound_effect = 'sound/effects/wounds/pierce1.ogg'
severity = WOUND_SEVERITY_MODERATE
initial_flow = 1.5
gauzed_clot_rate = 0.8
+ clot_rate = 0.03
internal_bleeding_chance = 30
internal_bleeding_coefficient = 1.25
threshold_penalty = 20
@@ -189,6 +200,11 @@
simple_treat_text = "Bandaging the wound will reduce blood loss, help the wound close by itself quicker, and speed up the blood recovery period. The wound itself can be slowly sutured shut."
homemade_treat_text = "Tea stimulates the body's natural healing systems, slightly fastening clotting. The wound itself can be rinsed off on a sink or shower as well. Other remedies are unnecessary."
+/datum/wound/pierce/bleed/moderate/update_descriptions()
+ if(!limb.can_bleed())
+ examine_desc = "has a small, torn hole"
+ occur_text = "splits a small hole open"
+
/datum/wound_pregen_data/flesh_pierce/breakage
abstract = FALSE
@@ -196,13 +212,35 @@
threshold_minimum = 30
-/datum/wound/pierce/bleed/moderate/update_descriptions()
+/datum/wound_pregen_data/flesh_pierce/breakage/get_weight(obj/item/bodypart/limb, woundtype, damage, attack_direction, damage_source)
+ if (isprojectile(damage_source))
+ return 0
+ return weight
+
+/datum/wound/pierce/bleed/moderate/projectile
+ name = "Minor Skin Penetration"
+ desc = "Patient's skin has been pierced through, causing severe bruising and minor internal bleeding in affected area."
+ treat_text = "Apply bandaging or suturing to the wound, make use of blood clotting agents, \
+ cauterization, or in extreme circumstances, exposure to extreme cold or vaccuum. \
+ Follow with food and a rest period."
+ examine_desc = "has a small, circular hole, gently bleeding"
+ clot_rate = 0
+
+/datum/wound/pierce/bleed/moderate/projectile/update_descriptions()
if(!limb.can_bleed())
examine_desc = "has a small, circular hole"
occur_text = "splits a small hole open"
+/datum/wound_pregen_data/flesh_pierce/breakage/projectile
+ wound_path_to_generate = /datum/wound/pierce/bleed/moderate/projectile
+
+/datum/wound_pregen_data/flesh_pierce/breakage/projectile/get_weight(obj/item/bodypart/limb, woundtype, damage, attack_direction, damage_source)
+ if (!isprojectile(damage_source))
+ return 0
+ return weight
+
/datum/wound/pierce/bleed/severe
- name = "Open Puncture"
+ name = "Open Stab Puncture"
desc = "Patient's internal tissue is penetrated, causing sizeable internal bleeding and reduced limb stability."
treat_text = "Swiftly apply bandaging or suturing to the wound, make use of blood clotting agents or saline-glucose, \
cauterization, or in extreme circumstances, exposure to extreme cold or vaccuum. \
@@ -214,6 +252,7 @@
severity = WOUND_SEVERITY_SEVERE
initial_flow = 2.25
gauzed_clot_rate = 0.6
+ clot_rate = 0.02
internal_bleeding_chance = 60
internal_bleeding_coefficient = 1.5
threshold_penalty = 35
@@ -223,6 +262,10 @@
simple_treat_text = "Bandaging the wound is essential, and will reduce blood loss. Afterwards, the wound can be sutured shut, preferably while the patient is resting and/or grasping their wound."
homemade_treat_text = "Bed sheets can be ripped up to make makeshift gauze. Flour, table salt, or salt mixed with water can be applied directly to stem the flow, though unmixed salt will irritate the skin and worsen natural healing. Resting and grabbing your wound will also reduce bleeding."
+/datum/wound/pierce/bleed/severe/update_descriptions()
+ if(!limb.can_bleed())
+ occur_text = "tears a hole open"
+
/datum/wound_pregen_data/flesh_pierce/open_puncture
abstract = FALSE
@@ -230,9 +273,23 @@
threshold_minimum = 50
-/datum/wound/pierce/bleed/severe/update_descriptions()
- if(!limb.can_bleed())
- occur_text = "tears a hole open"
+/datum/wound_pregen_data/flesh_pierce/open_puncture/get_weight(obj/item/bodypart/limb, woundtype, damage, attack_direction, damage_source)
+ if (isprojectile(damage_source))
+ return 0
+ return weight
+
+/datum/wound/pierce/bleed/severe/projectile
+ name = "Open Bullet Puncture"
+ examine_desc = "is pierced clear through, with bits of tissue obscuring the cleanly torn hole"
+ clot_rate = 0
+
+/datum/wound_pregen_data/flesh_pierce/open_puncture/projectile
+ wound_path_to_generate = /datum/wound/pierce/bleed/severe/projectile
+
+/datum/wound_pregen_data/flesh_pierce/open_puncture/projectile/get_weight(obj/item/bodypart/limb, woundtype, damage, attack_direction, damage_source)
+ if (!isprojectile(damage_source))
+ return 0
+ return weight
/datum/wound/pierce/bleed/severe/eye
name = "Eyeball Puncture"
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index fe3646cd062..7c2840afde1 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -1137,6 +1137,7 @@
pulledby.stop_pulling()
var/same_loc = oldloc == destination
+ var/movement_successful = TRUE
var/area/old_area = get_area(oldloc)
var/area/destarea = get_area(destination)
var/movement_dir = get_dir(src, destination)
@@ -1145,7 +1146,13 @@
loc = destination
- if(!same_loc)
+ if(!same_loc && loc == oldloc)
+ // when attempting to move an atom A into an atom B which already contains A, BYOND seems
+ // to silently refuse to move A to the new loc. This can really break stuff (see #77067)
+ stack_trace("Attempt to move [src] to [destination] was rejected by BYOND, possibly due to cyclic contents")
+ movement_successful = FALSE
+
+ if(movement_successful && !same_loc)
if(is_multi_tile && isturf(destination))
var/list/new_locs = block(
destination,
@@ -1174,7 +1181,7 @@
if(destarea && old_area != destarea)
destarea.Entered(src, old_area)
- . = TRUE
+ . = movement_successful
//If no destination, move the atom into nullspace (don't do this unless you know what you're doing)
else
@@ -1491,9 +1498,16 @@
return
-/atom/movable/proc/do_attack_animation(atom/attacked_atom, visual_effect_icon, obj/item/used_item, no_effect, fov_effect = TRUE)
+/atom/movable/proc/do_attack_animation(atom/attacked_atom, visual_effect_icon, obj/item/used_item, no_effect, fov_effect = TRUE, item_animation_override = null)
if(!no_effect && (visual_effect_icon || used_item))
- do_item_attack_animation(attacked_atom, visual_effect_icon, used_item)
+ var/animation_type = item_animation_override || ATTACK_ANIMATION_BLUNT
+ if (used_item && !item_animation_override)
+ switch(used_item.get_sharpness())
+ if (SHARP_EDGED)
+ animation_type = ATTACK_ANIMATION_SLASH
+ if (SHARP_POINTY)
+ animation_type = ATTACK_ANIMATION_PIERCE
+ do_item_attack_animation(attacked_atom, visual_effect_icon, used_item, animation_type = animation_type)
if(attacked_atom == src)
return //don't do an animation if attacking self
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 656ece7f8ee..0a7a8298aa3 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -493,7 +493,6 @@
///internal proc that removes all static power usage from the current area
/obj/machinery/proc/unset_static_power()
- PRIVATE_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
var/old_usage = static_power_usage
@@ -1020,21 +1019,16 @@
return TRUE
/obj/machinery/proc/exchange_parts(mob/user, obj/item/storage/part_replacer/replacer_tool)
- if(!istype(replacer_tool))
+ if(!istype(replacer_tool) || !component_parts)
return FALSE
- var/shouldplaysound = FALSE
- if(!component_parts)
- return FALSE
-
- if(!panel_open && !replacer_tool.works_from_distance)
+ var/works_from_distance = istype(replacer_tool, /obj/item/storage/part_replacer/bluespace)
+ if(!panel_open && !works_from_distance)
to_chat(user, display_parts(user))
- if(shouldplaysound)
- replacer_tool.play_rped_sound()
return FALSE
var/obj/item/circuitboard/machine/machine_board = locate(/obj/item/circuitboard/machine) in component_parts
- if(replacer_tool.works_from_distance)
+ if(works_from_distance)
to_chat(user, display_parts(user))
if(!machine_board)
return FALSE
@@ -1045,6 +1039,7 @@
* completly ignoring the tier 4 component inside
* we also ignore stack components inside the RPED cause we dont exchange that
*/
+ var/shouldplaysound = FALSE
var/list/part_list = replacer_tool.get_sorted_parts(ignore_stacks = TRUE)
if(!part_list.len)
return FALSE
@@ -1075,7 +1070,7 @@
if(!istype(secondary_part, required_type))
continue
// If it's a corrupt or rigged cell, attempting to send it through Bluespace could have unforeseen consequences.
- if(istype(secondary_part, /obj/item/stock_parts/power_store/cell) && replacer_tool.works_from_distance)
+ if(istype(secondary_part, /obj/item/stock_parts/power_store/cell) && works_from_distance)
var/obj/item/stock_parts/power_store/cell/checked_cell = secondary_part
// If it's rigged or corrupted, max the charge. Then explode it.
if(checked_cell.rigged || checked_cell.corrupted)
diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm
index 3e947d33d7b..45a21eafef8 100644
--- a/code/game/machinery/computer/buildandrepair.dm
+++ b/code/game/machinery/computer/buildandrepair.dm
@@ -125,8 +125,6 @@
if(add_cabling(user, cable, time = 0))
if(!no_sound)
replacer.play_rped_sound()
- if(replacer.works_from_distance)
- user.Beam(src, icon_state = "rped_upgrade", time = 0.5 SECONDS)
no_sound = TRUE
return install_parts_from_part_replacer(user, replacer, no_sound = no_sound) // Recursive call to handle the next part
@@ -140,8 +138,6 @@
if(add_glass(user, glass_sheets, time = 0))
if(!no_sound)
replacer.play_rped_sound()
- if(replacer.works_from_distance)
- user.Beam(src, icon_state = "rped_upgrade", time = 0.5 SECONDS)
return TRUE
return FALSE
diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index f0b3434ec85..b90302111ab 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -112,6 +112,16 @@
return install_board(user, tool, by_hand = TRUE) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
return NONE
+/obj/structure/frame/ranged_item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ . = NONE
+
+ if(!istype(tool, /obj/item/storage/part_replacer/bluespace))
+ return
+
+ . = item_interaction(user, tool, modifiers)
+ if(. & ITEM_INTERACT_ANY_BLOCKER)
+ user.Beam(tool, icon_state = "rped_upgrade", time = 0.5 SECONDS)
+
/**
* Installs the passed circuit board into the frame
*
@@ -173,7 +183,7 @@
if(QDELETED(target_board) || QDELETED(src) || QDELETED(user) || !(target_board in replacer) || !user.is_holding(replacer))
return FALSE
// User still within range?
- var/close_enough = replacer.works_from_distance || user.Adjacent(src)
+ var/close_enough = istype(replacer, /obj/item/storage/part_replacer/bluespace) || user.Adjacent(src)
if(!close_enough)
return FALSE
@@ -182,8 +192,6 @@
install_parts_from_part_replacer(user, replacer, no_sound = TRUE)
if(!no_sound)
replacer.play_rped_sound()
- if(replacer.works_from_distance)
- user.Beam(src, icon_state = "rped_upgrade", time = 0.5 SECONDS)
return TRUE
return FALSE
diff --git a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm
index 9a873b5e373..221881c6ea5 100644
--- a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm
@@ -98,6 +98,7 @@
name = "carp tooth"
desc = "Looks sharp. Sharp enough to poke someone's eye out. Holy fuck it's big."
icon_state = "carptooth"
+ icon_angle = -45
///carp brain. you need to occasionally go to a new zlevel. think of it as... walking your dog!
/obj/item/organ/brain/carp
diff --git a/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm b/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm
index 5a06aa8e8c2..e4315c4a5e2 100644
--- a/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/goliath_organs.dm
@@ -99,6 +99,7 @@
icon = 'icons/obj/weapons/goliath_hammer.dmi'
icon_state = "goliath_hammer"
inhand_icon_state = "goliath_hammer"
+ icon_angle = -90
lefthand_file = 'icons/mob/inhands/weapons/goliath_hammer_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/goliath_hammer_righthand.dmi'
item_flags = ABSTRACT | DROPDEL
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 7cbd5126ca4..1be4325638d 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1172,13 +1172,15 @@
return TRUE
-/obj/machinery/door/airlock/try_to_crowbar(obj/item/I, mob/living/user, forced = FALSE)
- if(I.tool_behaviour == TOOL_CROWBAR && should_try_removing_electronics() && !operating)
+/obj/machinery/door/airlock/try_to_crowbar(obj/item/tool, mob/living/user, forced = FALSE)
+ if(!isnull(tool) && tool.tool_behaviour == TOOL_CROWBAR && should_try_removing_electronics() && !operating)
user.visible_message(span_notice("[user] removes the electronics from the airlock assembly."), \
span_notice("You start to remove electronics from the airlock assembly..."))
- if(I.use_tool(src, user, 40, volume = 100))
+
+ if(tool.use_tool(src, user, 40, volume = 100))
deconstruct(TRUE, user)
return
+
if(seal)
to_chat(user, span_warning("Remove the seal first!"))
return
@@ -1188,37 +1190,48 @@
if(welded)
to_chat(user, span_warning("It's welded, it won't budge!"))
return
- if(hasPower())
- if(forced)
- var/check_electrified = isElectrified() //setting this so we can check if the mob got shocked during the do_after below
- if(check_electrified && shock(user,100))
- return //it's like sticking a fork in a power socket
- if(!density)//already open
- return
+ if(!hasPower())
+ if(operating)
+ return
- if(!prying_so_hard)
- var/time_to_open = 50
- playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 100, TRUE) //is it aliens or just the CE being a dick?
- prying_so_hard = TRUE
- if(I.use_tool(src, user, time_to_open, volume = 100))
- if(check_electrified && shock(user, 100))
- prying_so_hard = FALSE
- return
- open(BYPASS_DOOR_CHECKS)
- take_damage(25, BRUTE, 0, 0) // Enough to sometimes spark
- if(density && !open(BYPASS_DOOR_CHECKS))
- to_chat(user, span_warning("Despite your attempts, [src] refuses to open."))
- prying_so_hard = FALSE
- return
+ if(istype(tool, /obj/item/fireaxe) && !HAS_TRAIT(tool, TRAIT_WIELDED)) //being fireaxe'd
+ to_chat(user, span_warning("You need to be wielding [tool] to do that!"))
+ return
+
+ INVOKE_ASYNC(src, density ? PROC_REF(open) : PROC_REF(close), BYPASS_DOOR_CHECKS)
+ return
+
+ if(!forced)
to_chat(user, span_warning("The airlock's motors resist your efforts to force it!"))
return
- if(!operating)
- if(istype(I, /obj/item/fireaxe) && !HAS_TRAIT(I, TRAIT_WIELDED)) //being fireaxe'd
- to_chat(user, span_warning("You need to be wielding [I] to do that!"))
- return
- INVOKE_ASYNC(src, density ? PROC_REF(open) : PROC_REF(close), BYPASS_DOOR_CHECKS)
+ var/check_electrified = isElectrified() //setting this so we can check if the mob got shocked during the do_after below
+ if(check_electrified && shock(user,100))
+ return //it's like sticking a fork in a power socket
+
+ if(!density)//already open
+ return
+
+ if(prying_so_hard)
+ return
+
+ var/time_to_open = 5 SECONDS
+ playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 100, TRUE) //is it aliens or just the CE being a dick?
+ prying_so_hard = TRUE
+
+ if(!tool.use_tool(src, user, time_to_open, volume = 100))
+ prying_so_hard = FALSE
+ return
+
+ if(check_electrified && shock(user, 100))
+ prying_so_hard = FALSE
+ return
+
+ open(BYPASS_DOOR_CHECKS)
+ take_damage(25, BRUTE, 0, 0) // Enough to sometimes spark
+ if(density && !open(BYPASS_DOOR_CHECKS))
+ to_chat(user, span_warning("Despite your attempts, [src] refuses to open."))
/obj/machinery/door/airlock/open(forced = DEFAULT_DOOR_CHECKS)
if(cycle_pump && !operating && !welded && !seal && locked && density)
@@ -2227,7 +2240,7 @@
if(!hasPower())
to_chat(user, span_notice("You begin unlocking the airlock safety mechanism..."))
if(do_after(user, 15 SECONDS, target = src))
- try_to_crowbar(src, user, TRUE)
+ try_to_crowbar(null, user, TRUE)
return TRUE
else
// always open from the space side
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index b238f341668..5de6b4d61d3 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -548,7 +548,7 @@
if(welded || operating)
return
- var/atom/crowbar_owner = acting_object.loc //catchs mechs and any other non-mob using a crowbar
+ var/atom/crowbar_owner = acting_object?.loc || user // catches mechs and any other non-mob using a crowbar
if(density)
being_held_open = TRUE
diff --git a/code/game/machinery/machine_frame.dm b/code/game/machinery/machine_frame.dm
index d39d0652324..0f02d2ee3a7 100644
--- a/code/game/machinery/machine_frame.dm
+++ b/code/game/machinery/machine_frame.dm
@@ -259,8 +259,7 @@
if(play_sound && !no_sound)
replacer.play_rped_sound()
- if(replacer.works_from_distance)
- user.Beam(src, icon_state = "rped_upgrade", time = 0.5 SECONDS)
+
return TRUE
/obj/structure/frame/machine/can_be_unfasten_wrench(mob/user, silent)
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index 32e29a2e5dd..d1f785f9b53 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -483,13 +483,17 @@ DEFINE_BITFIELD(turret_flags, list(
else if(iscarbon(A))
var/mob/living/carbon/C = A
- //If not emagged, only target carbons that can use items
- if(mode != TURRET_LETHAL && (C.stat || C.handcuffed || !(C.mobility_flags & MOBILITY_USE)))
- continue
-
- //If emagged, target all but dead carbons
- if(mode == TURRET_LETHAL && C.stat == DEAD)
- continue
+ switch(mode)
+ //If not emagged, only target carbons that can use items
+ if(TURRET_STUN)
+ if(!(C.mobility_flags & MOBILITY_USE))
+ continue
+ if(HAS_TRAIT(C, TRAIT_INCAPACITATED))
+ continue
+ //If emagged, target all but dead carbons
+ if(TURRET_LETHAL)
+ if(C.stat == DEAD)
+ continue
//if the target is a human and not in our faction, analyze threat level
if(ishuman(C) && !in_faction(C))
@@ -840,6 +844,7 @@ DEFINE_BITFIELD(turret_flags, list(
return TRUE
/obj/machinery/porta_turret/ai
+ scan_range = /obj/projectile/energy/electrode/ai_turrets::range + 1
turret_flags = TURRET_FLAG_SHOOT_CRIMINALS | TURRET_FLAG_SHOOT_ANOMALOUS | TURRET_FLAG_SHOOT_HEADS
/obj/machinery/porta_turret/ai/assess_perp(mob/living/carbon/human/perp)
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
index 7454f185511..226e19bfe84 100755
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -17,6 +17,7 @@
/obj/item/melee/baton/security,
/obj/item/ammo_box/magazine/recharge,
/obj/item/modular_computer,
+ /obj/item/gun/ballistic/automatic/battle_rifle,
))
/obj/machinery/recharger/RefreshParts()
@@ -55,6 +56,11 @@
var/obj/item/ammo_box/magazine/recharge/power_pack = charging
. += span_notice("- \The [charging]'s cell is at [PERCENT(power_pack.stored_ammo.len/power_pack.max_ammo)]%.")
return
+ if(istype(charging, /obj/item/gun/ballistic/automatic/battle_rifle))
+ var/obj/item/gun/ballistic/automatic/battle_rifle/recalibrating_gun = charging
+ . += span_notice("- \The [charging]'s system degradation is at stage [recalibrating_gun.degradation_stage] of [recalibrating_gun.degradation_stage_max]%.")
+ . += span_notice("- \The [charging]'s degradation buffer is at [PERCENT(recalibrating_gun.shots_before_degradation/recalibrating_gun.max_shots_before_degradation)]%.")
+ return
. += span_notice("- \The [charging] is not reporting a power level.")
/obj/machinery/recharger/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
@@ -158,6 +164,23 @@
using_power = TRUE
update_appearance()
return
+
+ if(istype(charging, /obj/item/gun/ballistic/automatic/battle_rifle))
+ var/obj/item/gun/ballistic/automatic/battle_rifle/recalibrating_gun = charging
+
+ if(recalibrating_gun.degradation_stage)
+ recalibrating_gun.attempt_recalibration(FALSE)
+ use_energy(active_power_usage * recharge_coeff * seconds_per_tick)
+ using_power = TRUE
+
+ else if(recalibrating_gun.shots_before_degradation < recalibrating_gun.max_shots_before_degradation)
+ recalibrating_gun.attempt_recalibration(TRUE, 1 * recharge_coeff)
+ use_energy(active_power_usage * recharge_coeff * seconds_per_tick)
+ using_power = TRUE
+
+ update_appearance()
+ return
+
if(!using_power && !finished_recharging) //Inserted thing is at max charge/ammo, notify those around us
finished_recharging = TRUE
playsound(src, 'sound/machines/ping.ogg', 30, TRUE)
diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm
index 21eff5028b5..14e56b3ed69 100644
--- a/code/game/objects/effects/decals/cleanable.dm
+++ b/code/game/objects/effects/decals/cleanable.dm
@@ -1,6 +1,6 @@
/obj/effect/decal/cleanable
gender = PLURAL
- layer = FLOOR_CLEAN_LAYER
+ layer = CLEANABLE_FLOOR_OBJECT_LAYER
var/list/random_icon_states = null
///I'm sorry but cleanable/blood code is ass, and so is blood_DNA
var/blood_state = ""
@@ -15,6 +15,9 @@
var/datum/reagent/decal_reagent
///The amount of reagent this decal holds, if decal_reagent is defined
var/reagent_amount = 0
+ /// If TRUE, gains TRAIT_MOPABLE on init - thus this cleanable will cleaned if its turf is cleaned
+ /// Set to FALSE for things that hang high on the walls or things which generally shouldn't be mopped up
+ var/is_mopped = TRUE
/// Creates a cleanable decal on a turf
/// Use this if your decal is one of one, and thus we should not spawn it if it's there already
@@ -40,6 +43,9 @@
handle_merge_decal(C)
return INITIALIZE_HINT_QDEL
+ if(is_mopped)
+ ADD_TRAIT(src, TRAIT_MOPABLE, INNATE_TRAIT)
+
if(LAZYLEN(diseases))
var/list/datum/disease/diseases_to_add = list()
for(var/datum/disease/D in diseases)
diff --git a/code/game/objects/effects/decals/cleanable/aliens.dm b/code/game/objects/effects/decals/cleanable/aliens.dm
index bc7923ac0ed..f2543f33c86 100644
--- a/code/game/objects/effects/decals/cleanable/aliens.dm
+++ b/code/game/objects/effects/decals/cleanable/aliens.dm
@@ -24,10 +24,12 @@
icon = 'icons/effects/blood.dmi'
icon_state = "xgib1"
plane = GAME_PLANE
- layer = BELOW_OBJ_LAYER
+ layer = GIB_LAYER
random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6")
mergeable_decal = FALSE
+ is_mopped = TRUE // probably shouldn't be, but janitor powercreep
+
/obj/effect/decal/cleanable/xenoblood/xgibs/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_MOVABLE_PIPE_EJECTING, PROC_REF(on_pipe_eject))
diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm
index 38a7624a504..3a5c29c5572 100644
--- a/code/game/objects/effects/decals/cleanable/humans.dm
+++ b/code/game/objects/effects/decals/cleanable/humans.dm
@@ -75,6 +75,7 @@
plane = GAME_PLANE
vis_flags = VIS_INHERIT_PLANE
alpha = 180
+ is_mopped = FALSE
/obj/effect/decal/cleanable/blood/splatter/over_window/NeverShouldHaveComeHere(turf/here_turf)
return isgroundlessturf(here_turf)
@@ -116,7 +117,7 @@
desc = "They look bloody and gruesome."
icon = 'icons/effects/blood.dmi'
icon_state = "gib1"
- layer = BELOW_OBJ_LAYER
+ layer = GIB_LAYER
plane = GAME_PLANE
random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6")
mergeable_decal = FALSE
@@ -126,6 +127,8 @@
decal_reagent = /datum/reagent/consumable/liquidgibs
reagent_amount = 5
+ is_mopped = TRUE // probably shouldn't be, but janitor powercreep
+
/obj/effect/decal/cleanable/blood/gibs/Initialize(mapload, list/datum/disease/diseases)
. = ..()
AddElement(/datum/element/squish_sound)
@@ -361,6 +364,7 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache)
random_icon_states = list("hitsplatter1", "hitsplatter2", "hitsplatter3")
plane = GAME_PLANE
layer = ABOVE_WINDOW_LAYER
+ is_mopped = FALSE
/// The turf we just came from, so we can back up when we hit a wall
var/turf/prev_loc
/// The cached info about the blood
diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm
index caf7428ef01..65d22c8d856 100644
--- a/code/game/objects/effects/decals/cleanable/misc.dm
+++ b/code/game/objects/effects/decals/cleanable/misc.dm
@@ -11,7 +11,7 @@
icon = 'icons/obj/debris.dmi'
icon_state = "ash"
plane = GAME_PLANE
- layer = GAME_CLEAN_LAYER
+ layer = CLEANABLE_OBJECT_LAYER
mergeable_decal = FALSE
beauty = -50
decal_reagent = /datum/reagent/ash
@@ -153,6 +153,7 @@
resistance_flags = FLAMMABLE
beauty = -100
clean_type = CLEAN_TYPE_HARD_DECAL
+ is_mopped = FALSE
/obj/effect/decal/cleanable/cobweb/cobweb2
icon_state = "cobweb2"
@@ -164,7 +165,7 @@
icon = 'icons/effects/effects.dmi'
icon_state = "molten"
plane = GAME_PLANE
- layer = GAME_CLEAN_LAYER
+ layer = CLEANABLE_OBJECT_LAYER
mergeable_decal = FALSE
beauty = -150
clean_type = CLEAN_TYPE_HARD_DECAL
@@ -251,7 +252,7 @@
desc = "A pile of chemicals. You can't quite tell what's inside it."
gender = NEUTER
plane = GAME_PLANE
- layer = GAME_CLEAN_LAYER
+ layer = CLEANABLE_OBJECT_LAYER
icon = 'icons/obj/debris.dmi'
icon_state = "ash"
@@ -330,7 +331,7 @@
icon = 'icons/obj/debris.dmi'
icon_state = "paper_shreds"
plane = GAME_PLANE
- layer = GAME_CLEAN_LAYER
+ layer = CLEANABLE_OBJECT_LAYER
/obj/effect/decal/cleanable/wrapping/pinata
name = "pinata shreds"
@@ -349,7 +350,7 @@
icon = 'icons/obj/debris.dmi'
icon_state = "garbage"
plane = GAME_PLANE
- layer = GAME_CLEAN_LAYER
+ layer = CLEANABLE_OBJECT_LAYER
beauty = -150
clean_type = CLEAN_TYPE_HARD_DECAL
@@ -567,7 +568,9 @@
mergeable_decal = FALSE
beauty = -10
plane = GAME_PLANE
- layer = BELOW_OBJ_LAYER
+ layer = GIB_LAYER
+ clean_type = CLEAN_TYPE_HARD_DECAL
+ is_mopped = FALSE
/obj/effect/decal/cleanable/rubble/Initialize(mapload)
. = ..()
diff --git a/code/game/objects/effects/decals/cleanable/robots.dm b/code/game/objects/effects/decals/cleanable/robots.dm
index 3f2957a9c9e..7406cd910a1 100644
--- a/code/game/objects/effects/decals/cleanable/robots.dm
+++ b/code/game/objects/effects/decals/cleanable/robots.dm
@@ -6,7 +6,7 @@
icon = 'icons/mob/silicon/robots.dmi'
icon_state = "gib1"
plane = GAME_PLANE
- layer = BELOW_OBJ_LAYER
+ layer = GIB_LAYER
random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7")
blood_state = BLOOD_STATE_OIL
bloodiness = BLOOD_AMOUNT_PER_DECAL
@@ -14,6 +14,8 @@
beauty = -50
clean_type = CLEAN_TYPE_BLOOD
+ is_mopped = TRUE // probably shouldn't be, but janitor powercreep
+
/obj/effect/decal/cleanable/robot_debris/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_MOVABLE_PIPE_EJECTING, PROC_REF(on_pipe_eject))
diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm
index e27e6f91337..2e60cfb8bda 100644
--- a/code/game/objects/effects/decals/crayon.dm
+++ b/code/game/objects/effects/decals/crayon.dm
@@ -15,7 +15,7 @@
if(isclosedturf(loc) && loc.density)
// allows for wall graffiti to be seen
SET_PLANE_IMPLICIT(src, GAME_PLANE)
- layer = GAME_CLEAN_LAYER
+ layer = CLEANABLE_OBJECT_LAYER
if(e_name)
name = e_name
if(desc_override)
diff --git a/code/game/objects/effects/forcefields.dm b/code/game/objects/effects/forcefields.dm
index dc51e5079f4..bc9f8a0bfd6 100644
--- a/code/game/objects/effects/forcefields.dm
+++ b/code/game/objects/effects/forcefields.dm
@@ -84,7 +84,7 @@
icon = 'icons/effects/eldritch.dmi'
icon_state = "cosmic_carpet"
anchored = TRUE
- layer = BELOW_OBJ_LAYER
+ layer = GIB_LAYER
density = FALSE
can_atmos_pass = ATMOS_PASS_NO
initial_duration = 30 SECONDS
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index c613bdaaf73..99b8fef92c4 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -22,6 +22,9 @@
///Icon file for right inhand overlays
var/righthand_file = 'icons/mob/inhands/items_righthand.dmi'
+ /// Angle of the icon, used for piercing and slashing attack animations, clockwise from *east-facing* sprites
+ var/icon_angle = 0
+
///Icon file for mob worn overlays.
var/icon/worn_icon
///Icon state for mob worn overlays, if null the normal icon_state will be used.
@@ -178,7 +181,7 @@
///for flags such as [GLASSESCOVERSEYES]
var/flags_cover = 0
var/heat = 0
- ///All items with sharpness of SHARP_EDGED or higher will automatically get the butchering component.
+ /// All items with sharpness of SHARP_EDGED or higher will automatically get the butchering component.
var/sharpness = NONE
///How a tool acts when you use it on something, such as wirecutters cutting wires while multitools measure power
@@ -1594,44 +1597,162 @@
// This is instant on byond's end, but to our clients this looks like a quick drop
animate(src, alpha = old_alpha, pixel_x = old_x, pixel_y = old_y, transform = old_transform, time = 3, easing = CUBIC_EASING)
-/atom/movable/proc/do_item_attack_animation(atom/attacked_atom, visual_effect_icon, obj/item/used_item)
- var/image/attack_image
- if(visual_effect_icon)
- attack_image = image(icon = 'icons/effects/effects.dmi', icon_state = visual_effect_icon)
- else if(used_item)
- attack_image = image(icon = used_item)
+/atom/movable/proc/do_item_attack_animation(atom/attacked_atom, visual_effect_icon, obj/item/used_item, animation_type = ATTACK_ANIMATION_BLUNT)
+ if (visual_effect_icon)
+ var/image/attack_image = image(icon = 'icons/effects/effects.dmi', icon_state = visual_effect_icon)
attack_image.plane = attacked_atom.plane + 1
-
// Scale the icon.
attack_image.transform *= 0.4
// The icon should not rotate.
attack_image.appearance_flags = APPEARANCE_UI
+ var/atom/movable/flick_visual/attack = attacked_atom.flick_overlay_view(attack_image, 1 SECONDS)
+ var/matrix/copy_transform = new(transform)
+ animate(attack, alpha = 175, transform = copy_transform.Scale(0.75), time = 0.3 SECONDS)
+ animate(time = 0.1 SECONDS)
+ animate(alpha = 0, time = 0.3 SECONDS, easing = CIRCULAR_EASING|EASE_OUT)
+ return
- // Set the direction of the icon animation.
- var/direction = get_dir(src, attacked_atom)
- if(direction & NORTH)
- attack_image.pixel_y = -12
- else if(direction & SOUTH)
- attack_image.pixel_y = 12
-
- if(direction & EAST)
- attack_image.pixel_x = -14
- else if(direction & WEST)
- attack_image.pixel_x = 14
-
- if(!direction) // Attacked self?!
- attack_image.pixel_y = 12
- attack_image.pixel_x = 5 * (prob(50) ? 1 : -1)
-
- if(!attack_image)
+ if (isnull(used_item))
return
+ var/image/attack_image = image(icon = used_item)
+ attack_image.plane = attacked_atom.plane + 1
+ // Scale the icon.
+ attack_image.transform *= 0.5
+ // The icon should not rotate.
+ attack_image.appearance_flags = APPEARANCE_UI
+
var/atom/movable/flick_visual/attack = attacked_atom.flick_overlay_view(attack_image, 1 SECONDS)
var/matrix/copy_transform = new(transform)
+ var/x_sign = 0
+ var/y_sign = 0
+ var/direction = get_dir(src, attacked_atom)
+ if (direction & NORTH)
+ y_sign = -1
+ else if (direction & SOUTH)
+ y_sign = 1
+
+ if (direction & EAST)
+ x_sign = -1
+ else if (direction & WEST)
+ x_sign = 1
+
+ // Attacking self, or something on the same turf as us
+ if (!direction)
+ y_sign = 1
+ // Not a fan of this, but its the "cleanest" way to animate this
+ x_sign = 0.25 * (prob(50) ? 1 : -1)
+ // For piercing attacks
+ direction = SOUTH
+
// And animate the attack!
- animate(attack, alpha = 175, transform = copy_transform.Scale(0.75), pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 0.3 SECONDS)
- animate(time = 0.1 SECONDS)
- animate(alpha = 0, time = 0.3 SECONDS, easing = CIRCULAR_EASING|EASE_OUT)
+ switch (animation_type)
+ if (ATTACK_ANIMATION_BLUNT)
+ attack.pixel_x = 14 * x_sign
+ attack.pixel_y = 12 * y_sign
+ animate(attack, alpha = 175, transform = copy_transform.Scale(0.75), pixel_x = 4 * x_sign, pixel_y = 3 * y_sign, time = 0.2 SECONDS)
+ animate(time = 0.1 SECONDS)
+ animate(alpha = 0, time = 0.1 SECONDS, easing = CIRCULAR_EASING|EASE_OUT)
+
+ if (ATTACK_ANIMATION_PIERCE)
+ var/attack_angle = dir2angle(direction) + rand(-7, 7)
+ // Deducting 90 because we're assuming that icon_angle of 0 means an east-facing sprite
+ var/anim_angle = attack_angle - 90 - used_item.icon_angle
+ var/angle_mult = 1
+ if (x_sign && y_sign)
+ angle_mult = 1.4
+ attack.pixel_x = 22 * x_sign * angle_mult
+ attack.pixel_y = 18 * y_sign * angle_mult
+ attack.transform = attack.transform.Turn(anim_angle)
+ copy_transform = copy_transform.Turn(anim_angle)
+ animate(
+ attack,
+ pixel_x = (22 * x_sign - 12 * sin(attack_angle)) * angle_mult,
+ pixel_y = (18 * y_sign - 8 * cos(attack_angle)) * angle_mult,
+ time = 0.1 SECONDS,
+ easing = CUBIC_EASING|EASE_IN,
+ )
+ animate(
+ attack,
+ alpha = 175,
+ transform = copy_transform.Scale(0.75),
+ pixel_x = (22 * x_sign + 26 * sin(attack_angle)) * angle_mult,
+ pixel_y = (18 * y_sign + 22 * cos(attack_angle)) * angle_mult,
+ time = 0.3 SECONDS,
+ easing = CUBIC_EASING|EASE_OUT,
+ )
+ animate(
+ alpha = 0,
+ pixel_x = -3 * -(x_sign + sin(attack_angle)),
+ pixel_y = -2 * -(y_sign + cos(attack_angle)),
+ time = 0.1 SECONDS,
+ easing = CIRCULAR_EASING|EASE_OUT
+ )
+
+ if (ATTACK_ANIMATION_SLASH)
+ attack.pixel_x = 18 * x_sign
+ attack.pixel_y = 14 * y_sign
+ var/x_rot_sign = 0
+ var/y_rot_sign = 0
+ var/attack_dir = (prob(50) ? 1 : -1)
+ var/anim_angle = dir2angle(direction) - 90 - used_item.icon_angle
+
+ if (x_sign)
+ y_rot_sign = attack_dir
+ if (y_sign)
+ x_rot_sign = attack_dir
+
+ // Animations are flipped, so flip us too!
+ if (x_sign > 0 || y_sign < 0)
+ attack_dir *= -1
+
+ // We're swinging diagonally, use separate logic
+ var/anim_dir = attack_dir
+ if (x_sign && y_sign)
+ if (attack_dir < 0)
+ x_rot_sign = -x_sign * 1.4
+ y_rot_sign = 0
+ else
+ x_rot_sign = 0
+ y_rot_sign = -y_sign * 1.4
+
+ // Flip us if we've been flipped *unless* we're flipped due to both axis
+ if ((x_sign < 0 && y_sign > 0) || (x_sign > 0 && y_sign < 0))
+ anim_dir *= -1
+
+ attack.pixel_x += 10 * x_rot_sign
+ attack.pixel_y += 8 * y_rot_sign
+ attack.transform = attack.transform.Turn(anim_angle - 45 * anim_dir)
+ copy_transform = copy_transform.Scale(0.75)
+ animate(attack, alpha = 175, time = 0.3 SECONDS, flags = ANIMATION_PARALLEL)
+ animate(time = 0.1 SECONDS)
+ animate(alpha = 0, time = 0.1 SECONDS, easing = CIRCULAR_EASING|EASE_OUT)
+
+ animate(attack, transform = copy_transform.Turn(anim_angle + 45 * anim_dir), time = 0.3 SECONDS, flags = ANIMATION_PARALLEL)
+
+ var/x_return = 10 * -x_rot_sign
+ var/y_return = 8 * -y_rot_sign
+
+ if (!x_rot_sign)
+ x_return = 18 * x_sign
+ if (!y_rot_sign)
+ y_return = 14 * y_sign
+
+ var/angle_mult = 1
+ if (x_sign && y_sign)
+ angle_mult = 1.4
+ if (attack_dir > 0)
+ x_return = 8 * x_sign
+ y_return = 14 * y_sign
+ else
+ x_return = 18 * x_sign
+ y_return = 6 * y_sign
+
+ animate(attack, pixel_x = 4 * x_sign * angle_mult, time = 0.2 SECONDS, easing = CIRCULAR_EASING | EASE_IN, flags = ANIMATION_PARALLEL)
+ animate(pixel_x = x_return, time = 0.2 SECONDS, easing = CIRCULAR_EASING | EASE_OUT)
+
+ animate(attack, pixel_y = 3 * y_sign * angle_mult, time = 0.2 SECONDS, easing = CIRCULAR_EASING | EASE_IN, flags = ANIMATION_PARALLEL)
+ animate(pixel_y = y_return, time = 0.2 SECONDS, easing = CIRCULAR_EASING | EASE_OUT)
/// Common proc used by painting tools like spraycans and palettes that can access the entire 24 bits color space.
/obj/item/proc/pick_painting_tool_color(mob/user, default_color)
diff --git a/code/game/objects/items/boxcutter.dm b/code/game/objects/items/boxcutter.dm
index 58be269bacd..5452cd013fb 100644
--- a/code/game/objects/items/boxcutter.dm
+++ b/code/game/objects/items/boxcutter.dm
@@ -5,6 +5,7 @@
icon_state = "boxcutter"
inhand_icon_state = "boxcutter"
base_icon_state = "boxcutter"
+ icon_angle = -90
lefthand_file = 'icons/mob/inhands/equipment/boxcutter_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/boxcutter_righthand.dmi'
inhand_icon_state = null
diff --git a/code/game/objects/items/broom.dm b/code/game/objects/items/broom.dm
index 32636b1a99c..a48dfa1b94c 100644
--- a/code/game/objects/items/broom.dm
+++ b/code/game/objects/items/broom.dm
@@ -7,6 +7,7 @@
icon = 'icons/obj/service/janitor.dmi'
icon_state = "broom0"
base_icon_state = "broom"
+ icon_angle = 135
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
force = 8
diff --git a/code/game/objects/items/chainsaw.dm b/code/game/objects/items/chainsaw.dm
index 8045646b075..509a3a63856 100644
--- a/code/game/objects/items/chainsaw.dm
+++ b/code/game/objects/items/chainsaw.dm
@@ -5,6 +5,7 @@
desc = "A versatile power tool. Useful for limbing trees and delimbing humans."
icon = 'icons/obj/weapons/chainsaw.dmi'
icon_state = "chainsaw"
+ icon_angle = 180
lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
diff --git a/code/game/objects/items/courtroom.dm b/code/game/objects/items/courtroom.dm
index 8baba284314..c8907cbe936 100644
--- a/code/game/objects/items/courtroom.dm
+++ b/code/game/objects/items/courtroom.dm
@@ -7,6 +7,7 @@
desc = "Order, order! No bombs in my courthouse."
icon = 'icons/obj/weapons/hammer.dmi'
icon_state = "gavelhammer"
+ icon_angle = -135
force = 5
throwforce = 6
w_class = WEIGHT_CLASS_SMALL
diff --git a/code/game/objects/items/debug_items.dm b/code/game/objects/items/debug_items.dm
index fb6400fc7b3..9af69c33d7f 100644
--- a/code/game/objects/items/debug_items.dm
+++ b/code/game/objects/items/debug_items.dm
@@ -34,6 +34,7 @@
icon = 'icons/obj/weapons/club.dmi'
icon_state = "hypertool"
inhand_icon_state = "hypertool"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
toolspeed = 0.1
diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm
index 4d538f46184..bc3a12a4fe5 100644
--- a/code/game/objects/items/devices/multitool.dm
+++ b/code/game/objects/items/devices/multitool.dm
@@ -16,6 +16,7 @@
icon = 'icons/obj/devices/tool.dmi'
icon_state = "multitool"
inhand_icon_state = "multitool"
+ icon_angle = -90
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
force = 5
@@ -292,6 +293,7 @@
desc = "Optimised version of a regular multitool. Streamlines processes handled by its internal microchip."
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "toolkit_engiborg_multitool"
+ icon_angle = 0
toolspeed = 0.5
#undef PROXIMITY_NEAR
diff --git a/code/game/objects/items/devices/scanners/scanner_wand.dm b/code/game/objects/items/devices/scanners/scanner_wand.dm
index 18dfc820039..bba046bd2fe 100644
--- a/code/game/objects/items/devices/scanners/scanner_wand.dm
+++ b/code/game/objects/items/devices/scanners/scanner_wand.dm
@@ -3,6 +3,7 @@
icon = 'icons/obj/devices/scanner.dmi'
icon_state = "scanner_wand"
inhand_icon_state = "healthanalyzer"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
desc = "A wand that medically scans people. Inserting it into a medical kiosk makes it able to perform a health scan on the patient."
diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index d8b7c1999ae..341231d767a 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -329,7 +329,7 @@ effective or pretty fucking useless.
/obj/item/jammer
name = "radio jammer"
- desc = "Device used to disrupt nearby radio communication. Alternate function creates a powerful distruptor wave which disables all nearby listening devices."
+ desc = "Device used to disrupt nearby radio communication. Alternate function creates a powerful disruptor wave which disables all nearby listening devices."
icon = 'icons/obj/devices/syndie_gadget.dmi'
icon_state = "jammer"
var/active = FALSE
@@ -342,7 +342,7 @@ effective or pretty fucking useless.
register_context()
/obj/item/jammer/add_context(atom/source, list/context, obj/item/held_item, mob/user)
- context[SCREENTIP_CONTEXT_LMB] = "Release distruptor wave"
+ context[SCREENTIP_CONTEXT_LMB] = "Release disruptor wave"
context[SCREENTIP_CONTEXT_RMB] = "Toggle"
return CONTEXTUAL_SCREENTIP_SET
@@ -352,8 +352,8 @@ effective or pretty fucking useless.
user.balloon_alert(user, "on cooldown!")
return
- user.balloon_alert(user, "distruptor wave released!")
- to_chat(user, span_notice("You release a distruptor wave, disabling all nearby radio devices."))
+ user.balloon_alert(user, "disruptor wave released!")
+ to_chat(user, span_notice("You release a disruptor wave, disabling all nearby radio devices."))
for (var/atom/potential_owner in view(7, user))
disable_radios_on(potential_owner)
COOLDOWN_START(src, jam_cooldown, jam_cooldown_duration)
@@ -379,8 +379,8 @@ effective or pretty fucking useless.
user.balloon_alert(user, "out of reach!")
return
- interacting_with.balloon_alert(user, "radio distrupted!")
- to_chat(user, span_notice("You release a directed distruptor wave, disabling all radio devices on [interacting_with]."))
+ interacting_with.balloon_alert(user, "radio disrupted!")
+ to_chat(user, span_notice("You release a directed disruptor wave, disabling all radio devices on [interacting_with]."))
disable_radios_on(interacting_with)
return ITEM_INTERACT_SUCCESS
diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm
index aa98d325a7d..ef1e4ebdee4 100644
--- a/code/game/objects/items/dice.dm
+++ b/code/game/objects/items/dice.dm
@@ -89,6 +89,7 @@
result = rigged_value
. = result
+ playsound(src, 'sound/items/dice_roll.ogg', 50, TRUE)
var/fake_result = roll(sides)//Daredevil isn't as good as he used to be
var/comment = ""
diff --git a/code/game/objects/items/dualsaber.dm b/code/game/objects/items/dualsaber.dm
index 7f2e54984ca..dd5ef68f0d3 100644
--- a/code/game/objects/items/dualsaber.dm
+++ b/code/game/objects/items/dualsaber.dm
@@ -2,13 +2,14 @@
* Double-Bladed Energy Swords - Cheridan
*/
/obj/item/dualsaber
+ name = "double-bladed energy sword"
+ desc = "Handle with care."
icon = 'icons/obj/weapons/transforming_energy.dmi'
icon_state = "dualsaber0"
inhand_icon_state = "dualsaber0"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- name = "double-bladed energy sword"
- desc = "Handle with care."
force = 3
throwforce = 5
throw_speed = 3
@@ -73,7 +74,9 @@
set_light_on(FALSE)
/obj/item/dualsaber/get_sharpness()
- return HAS_TRAIT(src, TRAIT_WIELDED) && sharpness
+ if (!HAS_TRAIT(src, TRAIT_WIELDED))
+ return NONE
+ return ..()
/obj/item/dualsaber/update_icon_state()
icon_state = inhand_icon_state = HAS_TRAIT(src, TRAIT_WIELDED) ? "dualsaber[saber_color][HAS_TRAIT(src, TRAIT_WIELDED)]" : "dualsaber0"
diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm
index b4150ecb72f..a3b2422af61 100644
--- a/code/game/objects/items/extinguisher.dm
+++ b/code/game/objects/items/extinguisher.dm
@@ -5,6 +5,7 @@
icon_state = "fire_extinguisher0"
worn_icon_state = "fire_extinguisher"
inhand_icon_state = "fire_extinguisher"
+ icon_angle = 90
hitsound = 'sound/items/weapons/smash.ogg'
pickup_sound = 'sound/items/handling/gas_tank/gas_tank_pick_up.ogg'
drop_sound = 'sound/items/handling/gas_tank/gas_tank_drop.ogg'
diff --git a/code/game/objects/items/fireaxe.dm b/code/game/objects/items/fireaxe.dm
index 162af703ad2..3b6727d6923 100644
--- a/code/game/objects/items/fireaxe.dm
+++ b/code/game/objects/items/fireaxe.dm
@@ -4,13 +4,13 @@ GLOBAL_DATUM(bridge_axe, /obj/item/fireaxe)
* Fireaxe
*/
/obj/item/fireaxe // DEM AXES MAN, marker -Agouri
+ name = "fire axe"
+ desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?"
icon = 'icons/obj/weapons/fireaxe.dmi'
icon_state = "fireaxe0"
base_icon_state = "fireaxe"
lefthand_file = 'icons/mob/inhands/weapons/axes_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/axes_righthand.dmi'
- name = "fire axe"
- desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?"
force = 5
throwforce = 15
demolition_mod = 1.25
@@ -73,10 +73,11 @@ GLOBAL_DATUM(bridge_axe, /obj/item/fireaxe)
* Bone Axe
*/
/obj/item/fireaxe/boneaxe // Blatant imitation of the fireaxe, but made out of bone.
- icon_state = "bone_axe0"
- base_icon_state = "bone_axe"
name = "bone axe"
desc = "A large, vicious axe crafted out of several sharpened bone plates and crudely tied together. Made of monsters, by killing monsters, for killing monsters."
+ icon_state = "bone_axe0"
+ base_icon_state = "bone_axe"
+ icon_angle = 180
force_unwielded = 5
force_wielded = 23
@@ -84,10 +85,11 @@ GLOBAL_DATUM(bridge_axe, /obj/item/fireaxe)
* Metal Hydrogen Axe
*/
/obj/item/fireaxe/metal_h2_axe
- icon_state = "metalh2_axe0"
- base_icon_state = "metalh2_axe"
name = "metallic hydrogen axe"
desc = "A lightweight crowbar with an extreme sharp fire axe head attached. It trades its heft as a weapon by making it easier to carry around when holstered to suits without having to sacrifice your backpack."
+ icon_state = "metalh2_axe0"
+ base_icon_state = "metalh2_axe"
+ icon_angle = -45
force_unwielded = 5
force_wielded = 15
demolition_mod = 2
@@ -97,10 +99,10 @@ GLOBAL_DATUM(bridge_axe, /obj/item/fireaxe)
//boarding axe
/obj/item/fireaxe/boardingaxe
- icon_state = "boarding_axe0"
- base_icon_state = "boarding_axe"
name = "boarding axe"
desc = "A hulking cleaver that feels like a burden just looking at it. Seems excellent at halving obstacles like windows, airlocks, barricades and people."
+ icon_state = "boarding_axe0"
+ base_icon_state = "boarding_axe"
force_unwielded = 5
force_wielded = 30
demolition_mod = 3
diff --git a/code/game/objects/items/food/bread.dm b/code/game/objects/items/food/bread.dm
index 48e7a2a21b1..3a41514413c 100644
--- a/code/game/objects/items/food/bread.dm
+++ b/code/game/objects/items/food/bread.dm
@@ -420,6 +420,7 @@
/obj/item/food/baguette/combat
block_sound = 'sound/items/weapons/parry.ogg'
sharpness = SHARP_EDGED
+ icon_angle = -45
/// Force when wielded as a sword by a mime
var/active_force = 20
/// Block chance when wielded as a sword by a mime
diff --git a/code/game/objects/items/grenades/_grenade.dm b/code/game/objects/items/grenades/_grenade.dm
index 780311fa4d1..664a31c2262 100644
--- a/code/game/objects/items/grenades/_grenade.dm
+++ b/code/game/objects/items/grenades/_grenade.dm
@@ -159,7 +159,7 @@
if(istype(user))
user.add_mob_memory(/datum/memory/bomb_planted, antagonist = src)
active = TRUE
- icon_state = initial(icon_state) + "_active"
+ icon_state = (base_icon_state || initial(icon_state)) + "_active"
SEND_SIGNAL(src, COMSIG_GRENADE_ARMED, det_time, delayoverride)
addtimer(CALLBACK(src, PROC_REF(detonate)), isnull(delayoverride)? det_time : delayoverride)
@@ -255,7 +255,7 @@
if(det_time == 0)
det_time = "Instant"
else
- det_time = num2text(det_time * 0.1)
+ det_time = num2text(det_time * 0.1)
var/old_selection = possible_fuse_time.Find(det_time) //Position of det_time in the list
if(old_selection >= possible_fuse_time.len)
diff --git a/code/game/objects/items/grenades/flashbang.dm b/code/game/objects/items/grenades/flashbang.dm
index 2300d2c6717..2bc9401c774 100644
--- a/code/game/objects/items/grenades/flashbang.dm
+++ b/code/game/objects/items/grenades/flashbang.dm
@@ -54,6 +54,7 @@
/obj/item/grenade/stingbang
name = "stingbang"
icon_state = "timeg_locked"
+ base_icon_state = "timeg"
inhand_icon_state = "flashbang"
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
@@ -64,6 +65,8 @@
/obj/item/grenade/stingbang/mega
name = "mega stingbang"
+ icon_state = "timeg_mega_locked"
+ base_icon_state = "timeg_mega"
shrapnel_type = /obj/projectile/bullet/pellet/stingball/mega
shrapnel_radius = 12
@@ -122,6 +125,7 @@
name = "rotfrag grenade"
desc = "A grenade that generates more shrapnel the more you rotate it in your hand after pulling the pin. This one releases shrapnel shards."
icon_state = "timeg_locked"
+ base_icon_state = "timeg"
inhand_icon_state = "flashbang"
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm
index befd9a619a3..d5c2a58dcd8 100644
--- a/code/game/objects/items/hand_items.dm
+++ b/code/game/objects/items/hand_items.dm
@@ -515,7 +515,7 @@
to_chat(taker, span_nicegreen("[offerer] gives you \a [blown_kiss][cheek_kiss ? " on the cheek" : ""]!"))
offerer.face_atom(taker)
taker.face_atom(offerer)
- offerer.do_item_attack_animation(taker, used_item=src)
+ offerer.do_item_attack_animation(taker, used_item = src)
//We're still firing a shot here because I don't want to deal with some weird edgecase where direct impacting them with the projectile causes it to freak out because there's no angle or something
blown_kiss.original = taker
blown_kiss.fired_from = offerer
diff --git a/code/game/objects/items/inspector.dm b/code/game/objects/items/inspector.dm
index 7783dcff072..d1092c34326 100644
--- a/code/game/objects/items/inspector.dm
+++ b/code/game/objects/items/inspector.dm
@@ -8,7 +8,9 @@
*/
/obj/item/inspector
name = "\improper N-spect scanner"
- desc = "Central Command standard issue inspection device. Can perform either wide area scans that central command can use to verify the security of the station, or detailed scan. Can scan people for contraband on their person or items being contraband."
+ desc = "Central Command standard issue inspection device. \
+ Performs wide area scan reports for inspectors to use to verify the security and integrity of the station. \
+ Can additionally be used for precision scans to determine if an item contains, or is itself, contraband."
icon = 'icons/obj/devices/scanner.dmi'
icon_state = "inspector"
worn_icon_state = "salestagger"
@@ -88,14 +90,16 @@
/obj/item/inspector/examine(mob/user)
. = ..()
+ . += span_info("Use in-hand to scan the local area, creating an encrypted security inspection.")
+ . += span_info("Use on an item to scan if it contains, or is, contraband.")
if(!cell_cover_open)
- . += "Its cell cover is closed. It looks like it could be pried out, but doing so would require an appropriate tool."
+ . += span_notice("Its cell cover is closed. It looks like it could be pried out, but doing so would require an appropriate tool.")
return
- . += "Its cell cover is open, exposing the cell slot. It looks like it could be pried in, but doing so would require an appropriate tool."
+ . += span_notice("Its cell cover is open, exposing the cell slot. It looks like it could be pried in, but doing so would require an appropriate tool.")
if(!cell)
- . += "The slot for a cell is empty."
+ . += span_notice("The slot for a cell is empty.")
else
- . += "\The [cell] is firmly in place. [span_info("Ctrl-click with an empty hand to remove it.")]"
+ . += span_notice("\The [cell] is firmly in place. Ctrl-click with an empty hand to remove it.")
/obj/item/inspector/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!user.Adjacent(interacting_with))
diff --git a/code/game/objects/items/kitchen.dm b/code/game/objects/items/kitchen.dm
index 6689ec41a9a..40bcd25d735 100644
--- a/code/game/objects/items/kitchen.dm
+++ b/code/game/objects/items/kitchen.dm
@@ -22,6 +22,7 @@
name = "fork"
desc = "Pointy."
icon_state = "fork"
+ icon_angle = -90
force = 4
w_class = WEIGHT_CLASS_TINY
throwforce = 0
@@ -107,6 +108,7 @@
name = "Kitchen Toolset"
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "sili_knife"
+ icon_angle = 0
desc = "A breakthrough in synthetic engineering, this tool is a knife programmed to dull when not used for cooking purposes, and can exchange the blade for a rolling pin"
force = 0
throwforce = 0
@@ -161,6 +163,7 @@
icon_state = "rolling_pin"
worn_icon_state = "rolling_pin"
inhand_icon_state = "rolling_pin"
+ icon_angle = -45
force = 8
throwforce = 5
throw_speed = 3
@@ -194,6 +197,7 @@
desc = "Just be careful your food doesn't melt the spoon first."
icon_state = "spoon"
base_icon_state = "spoon"
+ icon_angle = -90
w_class = WEIGHT_CLASS_TINY
obj_flags = CONDUCTS_ELECTRICITY
force = 2
@@ -335,6 +339,7 @@
icon_state = "ladle"
base_icon_state = "ladle"
inhand_icon_state = "spoon"
+ icon_angle = 90
custom_price = PAYCHECK_LOWER * 4
spoon_sip_size = 3 // just a taste
diff --git a/code/game/objects/items/knives.dm b/code/game/objects/items/knives.dm
index e089a5bc55d..fc7836bbc04 100644
--- a/code/game/objects/items/knives.dm
+++ b/code/game/objects/items/knives.dm
@@ -7,6 +7,7 @@
righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi'
inhand_icon_state = "knife"
worn_icon_state = "knife"
+ icon_angle = -90
desc = "The original knife, it is said that all other knives are only copies of this one."
obj_flags = CONDUCTS_ELECTRICITY
force = 10
@@ -17,13 +18,15 @@
throw_speed = 3
throw_range = 6
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 6)
- attack_verb_continuous = list("slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
sharpness = SHARP_EDGED
armor_type = /datum/armor/item_knife
wound_bonus = 5
bare_wound_bonus = 15
tool_behaviour = TOOL_KNIFE
+ var/list/alt_continuous = list("stabs", "pierces", "shanks")
+ var/list/alt_simple = list("stab", "pierce", "shank")
/datum/armor/item_knife
fire = 50
@@ -33,6 +36,9 @@
. = ..()
AddElement(/datum/element/eyestab)
set_butchering()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ make_stabby()
///Adds the butchering component, used to override stats for special cases
/obj/item/knife/proc/set_butchering()
@@ -43,6 +49,10 @@
)
//bonus chance increases depending on force
+///Adds alt sharpness component, used for overrides
+/obj/item/knife/proc/make_stabby()
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple)
+
/obj/item/knife/suicide_act(mob/living/user)
user.visible_message(pick(span_suicide("[user] is slitting [user.p_their()] wrists with the [src.name]! It looks like [user.p_theyre()] trying to commit suicide."), \
span_suicide("[user] is slitting [user.p_their()] throat with the [src.name]! It looks like [user.p_theyre()] trying to commit suicide."), \
@@ -56,6 +66,7 @@
icon_state = "bone_blade"
inhand_icon_state = "bone_blade"
worn_icon_state = "bone_blade"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
@@ -72,6 +83,7 @@
icon = 'icons/obj/weapons/khopesh.dmi'
icon_state = "bloodletter"
worn_icon_state = "render"
+ icon_angle = -45
w_class = WEIGHT_CLASS_NORMAL
/// Bleed stacks applied when an organic mob target is hit
var/bleed_stacks_per_hit = 3
@@ -90,9 +102,10 @@
/obj/item/knife/butcher
name = "butcher's cleaver"
+ desc = "A huge thing used for chopping and chopping up meat. This includes clowns and clown by-products."
icon_state = "butch"
inhand_icon_state = "butch"
- desc = "A huge thing used for chopping and chopping up meat. This includes clowns and clown by-products."
+ icon_angle = -45
obj_flags = CONDUCTS_ELECTRICITY
force = 15
throwforce = 10
@@ -103,12 +116,16 @@
custom_price = PAYCHECK_CREW * 5
wound_bonus = 15
+/obj/item/knife/butcher/make_stabby()
+ return
+
/obj/item/knife/hunting
name = "hunting knife"
- icon = 'icons/obj/weapons/stabby.dmi'
desc = "Despite its name, it's mainly used for cutting meat from dead prey rather than actual hunting."
+ icon = 'icons/obj/weapons/stabby.dmi'
inhand_icon_state = "huntingknife"
icon_state = "huntingknife"
+ icon_angle = 180
wound_bonus = 10
/obj/item/knife/hunting/set_butchering()
@@ -118,12 +135,16 @@
bonus_modifier = force + 10, \
)
+/obj/item/knife/hunting/make_stabby()
+ return
+
/obj/item/knife/combat
name = "combat knife"
+ desc = "A military combat utility survival knife."
icon = 'icons/obj/weapons/stabby.dmi'
icon_state = "buckknife"
worn_icon_state = "buckknife"
- desc = "A military combat utility survival knife."
+ icon_angle = -45
embed_type = /datum/embed_data/combat_knife
force = 20
throwforce = 20
@@ -141,6 +162,9 @@
. = ..()
AddComponent(/datum/component/knockoff, 90, list(BODY_ZONE_PRECISE_MOUTH), slot_flags) //90% to knock off when wearing a mask
+/obj/item/knife/combat/make_stabby()
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -5)
+
/obj/item/knife/combat/dropped(mob/living/user, slot)
. = ..()
if(user.get_item_by_slot(ITEM_SLOT_MASK) == src && !user.has_status_effect(/datum/status_effect/choke) && prob(20))
@@ -158,36 +182,33 @@
/obj/item/knife/combat/survival
name = "survival knife"
- icon = 'icons/obj/weapons/stabby.dmi'
+ desc = "A hunting grade survival knife."
icon_state = "survivalknife"
worn_icon_state = "survivalknife"
embed_type = /datum/embed_data/combat_knife/weak
- desc = "A hunting grade survival knife."
force = 15
throwforce = 15
/obj/item/knife/combat/root
name = "cahn'root dagger"
- icon = 'icons/obj/weapons/stabby.dmi'
+ desc = "A root dagger, deceptively sharp. Perfect to hide and stab someone with, or make a couple and throw them at enemies."
icon_state = "rootdagger"
worn_icon_state = "root_dagger"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
inhand_icon_state = "rootshiv"
embed_type = /datum/embed_data/combat_knife/weak
- desc = "A root dagger, deceptively sharp. Perfect to hide and stab someone with, or make a couple and throw them at enemies."
force = 15
throwforce = 15
/obj/item/knife/combat/bone
name = "bone dagger"
+ desc = "A sharpened bone. The bare minimum in survival."
inhand_icon_state = "bone_dagger"
- icon = 'icons/obj/weapons/stabby.dmi'
icon_state = "bone_dagger"
worn_icon_state = "bone_dagger"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- desc = "A sharpened bone. The bare minimum in survival."
embed_type = /datum/embed_data/combat_knife/weak
obj_flags = parent_type::obj_flags & ~CONDUCTS_ELECTRICITY
force = 15
@@ -199,20 +220,21 @@
/obj/item/knife/combat/cyborg
name = "cyborg knife"
+ desc = "A cyborg-mounted plasteel knife. Extremely sharp and durable."
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "knife_cyborg"
worn_icon_state = "knife_cyborg" //error sprite - this shouldn't have been dropped
- desc = "A cyborg-mounted plasteel knife. Extremely sharp and durable."
slot_flags = NONE //you can't put this in your mouth
/obj/item/knife/shiv
name = "glass shiv"
+ desc = "A makeshift glass shiv."
icon = 'icons/obj/weapons/stabby.dmi'
icon_state = "shiv"
inhand_icon_state = "shiv"
+ icon_angle = -65
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- desc = "A makeshift glass shiv."
obj_flags = parent_type::obj_flags & ~CONDUCTS_ELECTRICITY
force = 8
throwforce = 12
@@ -221,11 +243,14 @@
armor_type = /datum/armor/none
custom_materials = list(/datum/material/glass = SMALL_MATERIAL_AMOUNT * 4)
+/obj/item/knife/shiv/make_stabby()
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -3)
+
/obj/item/knife/shiv/plasma
name = "plasma shiv"
+ desc = "A makeshift plasma glass shiv."
icon_state = "plasmashiv"
inhand_icon_state = "plasmashiv"
- desc = "A makeshift plasma glass shiv."
force = 9
throwforce = 13
armor_type = /datum/armor/shiv_plasma
@@ -242,9 +267,9 @@
/obj/item/knife/shiv/titanium
name = "titanium shiv"
+ desc = "A makeshift titanium-infused glass shiv."
icon_state = "titaniumshiv"
inhand_icon_state = "titaniumshiv"
- desc = "A makeshift titanium-infused glass shiv."
throwforce = 14
throw_range = 7
wound_bonus = 10
@@ -262,9 +287,9 @@
/obj/item/knife/shiv/plastitanium
name = "plastitanium shiv"
+ desc = "A makeshift titanium-infused plasma glass shiv."
icon_state = "plastitaniumshiv"
inhand_icon_state = "plastitaniumshiv"
- desc = "A makeshift titanium-infused plasma glass shiv."
force = 10
throwforce = 15
throw_speed = 4
@@ -285,9 +310,10 @@
/obj/item/knife/shiv/carrot
name = "carrot shiv"
+ desc = "Unlike other carrots, you should probably keep this far away from your eyes."
icon_state = "carrotshiv"
inhand_icon_state = "carrotshiv"
- desc = "Unlike other carrots, you should probably keep this far away from your eyes."
+ icon_angle = -45
custom_materials = null
/obj/item/knife/shiv/carrot/suicide_act(mob/living/carbon/user)
@@ -296,15 +322,17 @@
/obj/item/knife/shiv/parsnip
name = "parsnip shiv"
+ desc = "Truly putting 'snip' in the 'parsnip', and it's not sub-par either!"
icon_state = "parsnipshiv"
inhand_icon_state = "parsnipshiv"
- desc = "Truly putting 'snip' in the 'parsnip', and it's not sub-par either!"
+ icon_angle = -45
custom_materials = null
/obj/item/knife/shiv/root
name = "cahn'root shiv"
+ desc = "A root sharpened into a shiv. A root source of someone's stab wounds soon, most likely."
icon_state = "rootshiv"
inhand_icon_state = "rootshiv"
- desc = "A root sharpened into a shiv. A root source of someone's stab wounds soon, most likely."
+ icon_angle = -45
custom_materials = null
diff --git a/code/game/objects/items/maintenance_loot.dm b/code/game/objects/items/maintenance_loot.dm
index 9d1c4fe676b..1bba4f5b651 100644
--- a/code/game/objects/items/maintenance_loot.dm
+++ b/code/game/objects/items/maintenance_loot.dm
@@ -8,6 +8,7 @@
icon = 'icons/obj/maintenance_loot.dmi'
icon_state = "lead_pipe"
inhand_icon_state = "lead_pipe"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
//wow, lore
diff --git a/code/game/objects/items/melee/baton.dm b/code/game/objects/items/melee/baton.dm
index 775612026b1..03df0514038 100644
--- a/code/game/objects/items/melee/baton.dm
+++ b/code/game/objects/items/melee/baton.dm
@@ -6,6 +6,7 @@
icon_state = "classic_baton"
inhand_icon_state = "classic_baton"
worn_icon_state = "classic_baton"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
slot_flags = ITEM_SLOT_BELT
@@ -306,6 +307,7 @@
desc = "A compact yet robust personal defense weapon. Can be concealed when folded."
icon = 'icons/obj/weapons/baton.dmi'
icon_state = "telebaton"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
inhand_icon_state = null
@@ -396,6 +398,7 @@
icon = 'icons/obj/weapons/baton.dmi'
icon_state = "contractor_baton"
worn_icon_state = "contractor_baton"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
slot_flags = ITEM_SLOT_BELT
@@ -431,6 +434,7 @@
icon_state = "stunbaton"
inhand_icon_state = "baton"
worn_icon_state = "baton"
+ icon_angle = -45
force = 10
wound_bonus = 0
attack_verb_continuous = list("beats")
@@ -724,6 +728,7 @@
icon_state = "stunprod"
inhand_icon_state = "prod"
worn_icon_state = null
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
w_class = WEIGHT_CLASS_HUGE
diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm
index f78eec38987..a386375b827 100644
--- a/code/game/objects/items/melee/energy.dm
+++ b/code/game/objects/items/melee/energy.dm
@@ -1,5 +1,6 @@
/obj/item/melee/energy
icon = 'icons/obj/weapons/transforming_energy.dmi'
+ icon_angle = -45
max_integrity = 200
armor_type = /datum/armor/melee_energy
attack_verb_continuous = list("hits", "taps", "pokes")
@@ -63,8 +64,8 @@
sharpness_on = active_sharpness, \
hitsound_on = active_hitsound, \
w_class_on = active_w_class, \
- attack_verb_continuous_on = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts"), \
- attack_verb_simple_on = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut"), \
+ attack_verb_continuous_on = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts"), \
+ attack_verb_simple_on = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut"), \
)
RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform))
@@ -190,6 +191,14 @@
block_chance = 50
block_sound = 'sound/items/weapons/block_blade.ogg'
embed_type = /datum/embed_data/esword
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
+
+/obj/item/melee/energy/sword/Initialize(mapload)
+ . = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -10, TRAIT_TRANSFORM_ACTIVE)
/obj/item/melee/energy/sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
@@ -322,8 +331,8 @@
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
hitsound = 'sound/items/weapons/blade1.ogg'
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
force = 30
throwforce = 1 // Throwing or dropping the item deletes it.
throw_speed = 3
@@ -333,10 +342,15 @@
w_class = WEIGHT_CLASS_BULKY
/// Our linked spark system that emits from our sword.
var/datum/effect_system/spark_spread/spark_system
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
//Most of the other special functions are handled in their own files. aka special snowflake code so kewl
/obj/item/melee/energy/blade/Initialize(mapload)
. = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -10)
spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, src)
spark_system.attach(src)
@@ -356,3 +370,4 @@
icon_state = "lightblade"
inhand_icon_state = "lightblade"
base_icon_state = "lightblade"
+ icon_angle = 0
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index d8b69c53be8..237815016b6 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -9,6 +9,7 @@
icon_state = "chain"
inhand_icon_state = "chain"
worn_icon_state = "whip"
+ icon_angle = -90
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
@@ -34,18 +35,24 @@
icon = 'icons/obj/weapons/changeling_items.dmi'
icon_state = "arm_blade"
inhand_icon_state = "arm_blade"
+ icon_angle = 180
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
w_class = WEIGHT_CLASS_HUGE
force = 20
throwforce = 10
hitsound = 'sound/items/weapons/bladeslice.ogg'
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
sharpness = SHARP_EDGED
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
/obj/item/melee/synthetic_arm_blade/Initialize(mapload)
. = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -5)
AddComponent(/datum/component/butchering, \
speed = 6 SECONDS, \
effectiveness = 80, \
@@ -58,6 +65,7 @@
icon = 'icons/psychonaut/obj/weapons/sword.dmi'
icon_state = "sabre_red"
inhand_icon_state = "sabre_red"
+ icon_angle = -45
lefthand_file = 'icons/psychonaut/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/psychonaut/mob/inhands/weapons/swords_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY | UNIQUE_RENAME
@@ -171,6 +179,7 @@
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "parsnip_sabre"
inhand_icon_state = "parsnip_sabre"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
force = 15
@@ -212,6 +221,7 @@
icon_state = "beesword"
inhand_icon_state = "stinger"
worn_icon_state = "stinger"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
slot_flags = ITEM_SLOT_BELT
@@ -248,6 +258,7 @@
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "supermatter_sword_balanced"
inhand_icon_state = "supermatter_sword"
+ icon_angle = -90
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
slot_flags = null
@@ -304,6 +315,7 @@
..()
balanced = 0
icon_state = "supermatter_sword"
+ icon_angle = -45
/obj/item/melee/supermatter_sword/ex_act(severity, target)
visible_message(
@@ -361,6 +373,7 @@
icon = 'icons/obj/weapons/whip.dmi'
icon_state = "whip"
inhand_icon_state = "chain"
+ icon_angle = -90
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
worn_icon_state = "whip"
@@ -385,6 +398,7 @@
icon_state = "roastingstick"
inhand_icon_state = null
worn_icon_state = "tele_baton"
+ icon_angle = -45
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
item_flags = NONE
@@ -510,6 +524,7 @@
icon_state = "default"
inhand_icon_state = "default"
worn_icon_state = "default_worn"
+ icon_angle = -45
greyscale_config = /datum/greyscale_config/cleric_mace
greyscale_config_inhand_left = /datum/greyscale_config/cleric_mace_lefthand
diff --git a/code/game/objects/items/mop.dm b/code/game/objects/items/mop.dm
index b2bd6d55d5c..2896ce06301 100644
--- a/code/game/objects/items/mop.dm
+++ b/code/game/objects/items/mop.dm
@@ -4,6 +4,7 @@
icon = 'icons/obj/service/janitor.dmi'
icon_state = "mop"
inhand_icon_state = "mop"
+ icon_angle = 135
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
force = 8
diff --git a/code/game/objects/items/pitchfork.dm b/code/game/objects/items/pitchfork.dm
index 1ece740d4a6..99f714f09f3 100644
--- a/code/game/objects/items/pitchfork.dm
+++ b/code/game/objects/items/pitchfork.dm
@@ -8,6 +8,7 @@
icon = 'icons/obj/weapons/spear.dmi'
icon_state = "pitchfork0"
base_icon_state = "pitchfork"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
name = "pitchfork"
diff --git a/code/game/objects/items/powerfist.dm b/code/game/objects/items/powerfist.dm
index 77810ff3a89..871a6d2d3b2 100644
--- a/code/game/objects/items/powerfist.dm
+++ b/code/game/objects/items/powerfist.dm
@@ -12,6 +12,7 @@
icon = 'icons/obj/antags/syndicate_tools.dmi'
icon_state = "powerfist"
inhand_icon_state = "powerfist"
+ icon_angle = 180
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
diff --git a/code/game/objects/items/religion.dm b/code/game/objects/items/religion.dm
index 963274f26f2..61385378736 100644
--- a/code/game/objects/items/religion.dm
+++ b/code/game/objects/items/religion.dm
@@ -337,6 +337,7 @@
desc = "It's a stick..?"
icon = 'icons/obj/weapons/staff.dmi'
icon_state = "godstaff-red"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
var/conversion_color = "#ffffff"
@@ -427,8 +428,14 @@
force = 24
armour_penetration = 10
+/obj/item/claymore/weak/make_stabby()
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -9)
+
/obj/item/claymore/weak/ceremonial
desc = "A rusted claymore, once at the heart of a powerful scottish clan struck down and oppressed by tyrants, it has been passed down the ages as a symbol of defiance."
force = 15
block_chance = 30
armour_penetration = 5
+
+/obj/item/claymore/weak/ceremonial/make_stabby()
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -5)
diff --git a/code/game/objects/items/shrapnel.dm b/code/game/objects/items/shrapnel.dm
index 701a0a819d0..a4adc353db3 100644
--- a/code/game/objects/items/shrapnel.dm
+++ b/code/game/objects/items/shrapnel.dm
@@ -4,6 +4,7 @@
weak_against_armour = TRUE
icon = 'icons/obj/debris.dmi'
icon_state = "large"
+ icon_angle = -45
w_class = WEIGHT_CLASS_TINY
item_flags = DROPDEL
sharpness = SHARP_EDGED
diff --git a/code/game/objects/items/spear.dm b/code/game/objects/items/spear.dm
index 6ad49bf836f..2ac99231d73 100644
--- a/code/game/objects/items/spear.dm
+++ b/code/game/objects/items/spear.dm
@@ -1,11 +1,12 @@
//spears
/obj/item/spear
+ name = "spear"
+ desc = "A haphazardly-constructed yet still deadly weapon of ancient design."
icon = 'icons/obj/weapons/spear.dmi'
icon_state = "spearglass0"
lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
- name = "spear"
- desc = "A haphazardly-constructed yet still deadly weapon of ancient design."
+ icon_angle = -45
force = 10
w_class = WEIGHT_CLASS_BULKY
slot_flags = ITEM_SLOT_BACK
@@ -18,7 +19,7 @@
hitsound = 'sound/items/weapons/bladeslice.ogg'
attack_verb_continuous = list("attacks", "pokes", "jabs", "tears", "lacerates", "gores")
attack_verb_simple = list("attack", "poke", "jab", "tear", "lacerate", "gore")
- sharpness = SHARP_EDGED // i know the whole point of spears is that they're pointy, but edged is more devastating at the moment so
+ sharpness = SHARP_POINTY
max_integrity = 200
armor_type = /datum/armor/item_spear
wound_bonus = -15
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index 7c922d6687d..e5514380bf8 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -32,6 +32,10 @@
var/sanitization
/// How much we add to flesh_healing for burn wounds on application
var/flesh_regeneration
+ /// Verb used when applying this object to someone
+ var/apply_verb = "treating"
+ /// Whether this item can be used on dead bodies
+ var/works_on_dead = FALSE
/obj/item/stack/medical/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!isliving(interacting_with))
@@ -90,8 +94,8 @@
var/heal_zone = check_zone(user.zone_selected)
if(!try_heal_checks(patient, user, heal_zone))
return FALSE
- SSblackbox.record_feedback("nested tally", "medical_item_used", 1, list(type, auto_change_zone ? "auto" : "manual"))
- patient.balloon_alert(user, "treating [parse_zone(heal_zone)]...")
+ SSblackbox.record_feedback("nested tally", "medical_item_used", 1, list("[auto_change_zone ? "auto" : "manual"]", "[type]"))
+ patient.balloon_alert(user, "[apply_verb] [parse_zone(heal_zone)]...")
INVOKE_ASYNC(src, PROC_REF(try_heal), patient, user, heal_zone, FALSE, iscarbon(patient) && auto_change_zone) // auto change is useless for non-carbons
return TRUE
@@ -114,7 +118,6 @@
/obj/item/stack/medical/proc/try_heal(mob/living/patient, mob/living/user, healed_zone, silent = FALSE, auto_change_zone = TRUE)
if(patient == user)
if(!silent)
- user.balloon_alert(user, "treating [parse_zone(healed_zone)]...")
user.visible_message(
span_notice("[user] starts to apply [src] on [user.p_them()]self..."),
span_notice("You begin applying [src] on yourself..."),
@@ -134,7 +137,6 @@
else if(other_delay)
if(!silent)
- patient.balloon_alert(user, "treating [parse_zone(healed_zone)]...")
user.visible_message(
span_notice("[user] starts to apply [src] on [patient]."),
span_notice("You begin applying [src] on [patient]..."),
@@ -181,7 +183,7 @@
var/preferred_target = check_zone(user.zone_selected)
if(try_heal_checks(patient, user, preferred_target, silent = TRUE))
if(preferred_target != healed_zone)
- patient.balloon_alert(user, "treating [parse_zone(preferred_target)]...")
+ patient.balloon_alert(user, "[apply_verb] [parse_zone(preferred_target)]...")
try_heal(patient, user, preferred_target, TRUE, auto_change_zone)
return
@@ -211,7 +213,7 @@
var/next_picked = (preferred_target in other_affected_limbs) ? preferred_target : other_affected_limbs[1]
if(next_picked != last_zone)
- user.balloon_alert(user, "treating [parse_zone(next_picked)]...")
+ patient.balloon_alert(user, "[apply_verb] [parse_zone(next_picked)]...")
try_heal(patient, user, next_picked, silent = TRUE, auto_change_zone = TRUE)
/obj/item/stack/medical/proc/try_heal_manual_target(mob/living/carbon/patient, mob/living/user)
@@ -223,7 +225,7 @@
var/new_zone = check_zone(user.zone_selected)
if(!try_heal_checks(patient, user, new_zone))
return
- patient.balloon_alert(user, "treating [parse_zone(new_zone)]...")
+ patient.balloon_alert(user, "[apply_verb] [parse_zone(new_zone)]...")
try_heal(patient, user, new_zone, silent = TRUE, auto_change_zone = FALSE)
/// Checks if the passed patient can be healed by the passed user
@@ -233,10 +235,14 @@
/// Checks a bunch of stuff to see if we can heal the patient, including can_heal
/// Gives a feedback if we can't ultimatly heal the patient (unless silent is TRUE)
/obj/item/stack/medical/proc/try_heal_checks(mob/living/patient, mob/living/user, healed_zone, silent = FALSE)
+ if(!(healed_zone in GLOB.all_body_zones))
+ stack_trace("Invalid zone ([healed_zone || "null"]) passed to try_heal_checks.")
+ healed_zone = BODY_ZONE_CHEST
+
if(!can_heal(patient, user, healed_zone, silent))
// has its own feedback
return FALSE
- if(patient.stat == DEAD)
+ if(!works_on_dead && patient.stat == DEAD)
if(!silent)
patient.balloon_alert(user, "[patient.p_theyre()] dead!")
return FALSE
@@ -343,6 +349,7 @@
other_delay = 2 SECONDS
grind_results = list(/datum/reagent/medicine/c2/libital = 10)
merge_type = /obj/item/stack/medical/bruise_pack
+ apply_verb = "applying to"
/obj/item/stack/medical/bruise_pack/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is bludgeoning [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
@@ -365,6 +372,8 @@
splint_factor = 0.7
burn_cleanliness_bonus = 0.35
merge_type = /obj/item/stack/medical/gauze
+ apply_verb = "wrapping"
+ works_on_dead = TRUE
var/obj/item/bodypart/gauzed_bodypart
/obj/item/stack/medical/gauze/Destroy(force)
@@ -403,7 +412,7 @@
return FALSE
// gauze is only relevant for wounds, which are handled in the wounds themselves
-/obj/item/stack/medical/gauze/try_heal(mob/living/patient, mob/living/user, silent, healed_zone, auto_change_zone)
+/obj/item/stack/medical/gauze/try_heal(mob/living/patient, mob/living/user, healed_zone, silent, auto_change_zone)
var/obj/item/bodypart/limb = patient.get_bodypart(healed_zone)
var/treatment_delay = (user == patient ? self_delay : other_delay)
var/any_scanned = FALSE
@@ -436,8 +445,6 @@
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
- patient.balloon_alert(user, "wrapping [parse_zone(healed_zone)]...")
-
if(!do_after(user, treatment_delay, target = patient))
return
@@ -510,6 +517,7 @@
stop_bleeding = 0.6
grind_results = list(/datum/reagent/medicine/spaceacillin = 2)
merge_type = /obj/item/stack/medical/suture
+ apply_verb = "suturing"
/obj/item/stack/medical/suture/emergency
name = "emergency suture"
@@ -546,6 +554,7 @@
sanitization = 0.25
grind_results = list(/datum/reagent/medicine/c2/lenturi = 10)
merge_type = /obj/item/stack/medical/ointment
+ apply_verb = "applying to"
/obj/item/stack/medical/ointment/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is squeezing [src] into [user.p_their()] mouth! [user.p_do(TRUE)]n't [user.p_they()] know that stuff is toxic?"))
@@ -581,7 +590,7 @@
return ..()
icon_state = "regen_mesh_closed"
-/obj/item/stack/medical/mesh/try_heal_checks(mob/living/patient, mob/living/user, silent = FALSE)
+/obj/item/stack/medical/mesh/try_heal_checks(mob/living/patient, mob/living/user, healed_zone, silent = FALSE)
if(!is_open)
if(!silent)
balloon_alert(user, "open it first!")
@@ -643,6 +652,11 @@
heal_burn = 3
grind_results = list(/datum/reagent/consumable/aloejuice = 1)
merge_type = /obj/item/stack/medical/aloe
+ apply_verb = "applying to"
+
+/obj/item/stack/medical/aloe/Initialize(mapload, new_amount, merge, list/mat_override, mat_amt)
+ . = ..()
+ AddComponent(/datum/component/bakeable, /obj/item/food/badrecipe, rand(10 SECONDS, 15 SECONDS), FALSE)
/obj/item/stack/medical/aloe/fresh
amount = 2
@@ -663,6 +677,7 @@
grind_results = list(/datum/reagent/bone_dust = 10, /datum/reagent/carbon = 10)
novariants = TRUE
merge_type = /obj/item/stack/medical/bone_gel
+ apply_verb = "applying to"
/obj/item/stack/medical/bone_gel/get_surgery_tool_overlay(tray_extended)
return "gel" + (tray_extended ? "" : "_out")
@@ -715,6 +730,8 @@
mob_throw_hit_sound = 'sound/misc/moist_impact.ogg'
hitsound = 'sound/misc/moist_impact.ogg'
merge_type = /obj/item/stack/medical/poultice
+ apply_verb = "applying to"
+ works_on_dead = TRUE
/obj/item/stack/medical/poultice/post_heal_effects(amount_healed, mob/living/carbon/healed_mob, mob/living/user)
. = ..()
@@ -736,6 +753,7 @@
self_delay = 3 SECONDS
other_delay = 1 SECONDS
grind_results = list(/datum/reagent/medicine/c2/libital = 2)
+ apply_verb = "applying to"
/obj/item/stack/medical/bandage/makeshift
name = "makeshift bandage"
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index 709465314bc..db7f1732979 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -290,6 +290,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
desc = "A nasty looking shard of glass."
icon = 'icons/obj/debris.dmi'
icon_state = "large"
+ icon_angle = -45
w_class = WEIGHT_CLASS_TINY
force = 5
throwforce = 10
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index cb7957929d2..4e8f53ba85c 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -718,7 +718,7 @@
atom_storage.set_holdable(list(
/obj/item/ammo_casing/strilka310,
/obj/item/ammo_casing/shotgun,
- /obj/item/ammo_casing/a357,
+ /obj/item/ammo_casing/c357,
/obj/item/ammo_casing/junk,
))
diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm
index 3f813e50b45..d945a3fd711 100644
--- a/code/game/objects/items/storage/uplink_kits.dm
+++ b/code/game/objects/items/storage/uplink_kits.dm
@@ -461,7 +461,7 @@
/obj/item/storage/box/syndie_kit/chemical/Initialize(mapload)
. = ..()
- atom_storage.max_slots = 14
+ atom_storage.max_slots = 15
/obj/item/storage/box/syndie_kit/chemical/PopulateContents()
new /obj/item/reagent_containers/cup/bottle/polonium(src)
@@ -469,6 +469,7 @@
new /obj/item/reagent_containers/cup/bottle/fentanyl(src)
new /obj/item/reagent_containers/cup/bottle/formaldehyde(src)
new /obj/item/reagent_containers/cup/bottle/spewium(src)
+ new /obj/item/reagent_containers/cup/bottle/syndol(src)
new /obj/item/reagent_containers/cup/bottle/cyanide(src)
new /obj/item/reagent_containers/cup/bottle/histamine(src)
new /obj/item/reagent_containers/cup/bottle/initropidril(src)
diff --git a/code/game/objects/items/syndie_spraycan.dm b/code/game/objects/items/syndie_spraycan.dm
index 5690ecb7a28..bc1910595d4 100644
--- a/code/game/objects/items/syndie_spraycan.dm
+++ b/code/game/objects/items/syndie_spraycan.dm
@@ -171,7 +171,7 @@
/// Timer until the rune can be cleaned up off the floor
var/protected_timer
-/obj/effect/decal/cleanable/traitor_rune/traitor/Destroy()
+/obj/effect/decal/cleanable/traitor_rune/Destroy()
deltimer(protected_timer)
QDEL_NULL(demoraliser)
return ..()
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index 100f8a3319e..241fb28d11f 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -16,6 +16,7 @@
icon = 'icons/obj/canisters.dmi'
icon_state = "generic"
inhand_icon_state = "generic_tank"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/equipment/tanks_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tanks_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm
index 1c23937d2b5..eba0bc82054 100644
--- a/code/game/objects/items/tanks/watertank.dm
+++ b/code/game/objects/items/tanks/watertank.dm
@@ -290,6 +290,9 @@
/obj/item/extinguisher/mini/nozzle/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(AttemptRefill(interacting_with, user))
return NONE
+ return ..()
+
+/obj/item/extinguisher/mini/nozzle/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(nozzle_mode == EXTINGUISHER)
return ..()
diff --git a/code/game/objects/items/tongs.dm b/code/game/objects/items/tongs.dm
index 81f71c6b345..ec72133a257 100644
--- a/code/game/objects/items/tongs.dm
+++ b/code/game/objects/items/tongs.dm
@@ -6,6 +6,7 @@
icon_state = "tongs"
base_icon_state = "tongs"
inhand_icon_state = "fork" // close enough
+ icon_angle = -45
attack_verb_continuous = list("pinches", "tongs", "nips")
attack_verb_simple = list("pinch", "tong", "nip")
/// What are we holding in our tongs?
diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/crowbar.dm
index 38ca59038f0..9081277ad94 100644
--- a/code/game/objects/items/tools/crowbar.dm
+++ b/code/game/objects/items/tools/crowbar.dm
@@ -4,6 +4,7 @@
icon = 'icons/obj/tools.dmi'
icon_state = "crowbar"
inhand_icon_state = "crowbar"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
usesound = 'sound/items/tools/crowbar.ogg'
@@ -77,6 +78,7 @@
w_class = WEIGHT_CLASS_NORMAL
icon = 'icons/obj/weapons/hammer.dmi'
icon_state = "clawhammer"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi'
inhand_icon_state = "clawhammer"
@@ -112,6 +114,7 @@
icon_state = "jaws"
inhand_icon_state = "jawsoflife"
worn_icon_state = "jawsoflife"
+ icon_angle = 180
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*2.25, /datum/material/silver = SHEET_MATERIAL_AMOUNT*1.25, /datum/material/titanium = SHEET_MATERIAL_AMOUNT*1.75)
@@ -192,6 +195,7 @@
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "toolkit_engiborg_crowbar"
worn_icon_state = "toolkit_engiborg_crowbar" //error sprite - this shouldn't have been dropped
+ icon_angle = 0
usesound = 'sound/items/tools/jaws_pry.ogg'
force = 10
toolspeed = 0.5
@@ -203,6 +207,7 @@
base_icon_state = "mechremoval"
inhand_icon_state = null
icon = 'icons/obj/mechremoval.dmi'
+ icon_angle = -65
w_class = WEIGHT_CLASS_HUGE
slot_flags = NONE
toolspeed = 1.25
diff --git a/code/game/objects/items/tools/spess_knife.dm b/code/game/objects/items/tools/spess_knife.dm
index 3550f4df5ae..f1148fbd397 100644
--- a/code/game/objects/items/tools/spess_knife.dm
+++ b/code/game/objects/items/tools/spess_knife.dm
@@ -8,6 +8,7 @@
worn_icon_state = "spess_knife"
inside_belt_icon_state = "spess_knife"
inhand_icon_state = "spess_knife"
+ icon_angle = -90
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
w_class = WEIGHT_CLASS_TINY
diff --git a/code/game/objects/items/tools/wrench.dm b/code/game/objects/items/tools/wrench.dm
index 564403acf0e..25f7e75593f 100644
--- a/code/game/objects/items/tools/wrench.dm
+++ b/code/game/objects/items/tools/wrench.dm
@@ -5,6 +5,7 @@
icon_state = "wrench"
inhand_icon_state = "wrench"
worn_icon_state = "wrench"
+ icon_angle = -135
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
@@ -43,6 +44,7 @@
desc = "A polarized wrench. It causes anything placed between the jaws to turn."
icon = 'icons/obj/antags/abductor.dmi'
inside_belt_icon_state = "wrench_alien"
+ icon_angle = -135
custom_materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/silver = SHEET_MATERIAL_AMOUNT*1.25, /datum/material/plasma =HALF_SHEET_MATERIAL_AMOUNT, /datum/material/titanium =SHEET_MATERIAL_AMOUNT, /datum/material/diamond =SHEET_MATERIAL_AMOUNT)
usesound = 'sound/effects/empulse.ogg'
toolspeed = 0.1
@@ -89,6 +91,7 @@
desc = "An advanced robotic wrench, powered by internal hydraulics. Twice as fast as the handheld version."
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "toolkit_engiborg_wrench"
+ icon_angle = 0
toolspeed = 0.5
/obj/item/wrench/combat
@@ -97,6 +100,7 @@
icon_state = "wrench_combat"
inhand_icon_state = "wrench_combat"
inside_belt_icon_state = "wrench_combat"
+ icon_angle = -90
attack_verb_continuous = list("devastates", "brutalizes", "commits a war crime against", "obliterates", "humiliates")
attack_verb_simple = list("devastate", "brutalize", "commit a war crime against", "obliterate", "humiliate")
tool_behaviour = null
@@ -132,4 +136,5 @@
desc = "A wrench designed to grab into airlock's bolting system and raise it regardless of the airlock's power status."
icon_state = "bolter_wrench"
inhand_icon_state = "bolter_wrench"
+ icon_angle = -90
w_class = WEIGHT_CLASS_NORMAL
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 3b67c096c54..f3630424a65 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -565,6 +565,7 @@
desc = "A cheap, plastic replica of an energy sword. Realistic sounds! Ages 8 and up."
icon_state = "e_sword"
inhand_icon_state = "e_sword"
+ icon_angle = -45
icon = 'icons/obj/weapons/transforming_energy.dmi'
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
@@ -682,6 +683,7 @@
icon = 'icons/obj/toys/toy.dmi'
icon_state = "foamblade"
inhand_icon_state = "arm_blade"
+ icon_angle = -180
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
attack_verb_continuous = list("pricks", "absorbs", "gores")
@@ -781,6 +783,7 @@
icon_state = "katana"
inhand_icon_state = "katana"
worn_icon_state = "katana"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
@@ -788,9 +791,17 @@
force = 5
throwforce = 5
w_class = WEIGHT_CLASS_NORMAL
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices")
- attack_verb_simple = list("attack", "slash", "stab", "slice")
+ attack_verb_continuous = list("attacks", "slashes", "slices")
+ attack_verb_simple = list("attack", "slash", "slice")
hitsound = 'sound/items/weapons/bladeslice.ogg'
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
+
+/obj/item/toy/katana/Initialize(mapload)
+ . = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple)
/*
* Snap pops
@@ -1121,6 +1132,7 @@
icon = 'icons/obj/weapons/khopesh.dmi'
icon_state = "render"
inhand_icon_state = "cultdagger"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
diff --git a/code/game/objects/items/v8_engine.dm b/code/game/objects/items/v8_engine.dm
index c75f1ebc13a..20b7dd406a5 100644
--- a/code/game/objects/items/v8_engine.dm
+++ b/code/game/objects/items/v8_engine.dm
@@ -48,6 +48,7 @@
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "house_edge0"
inhand_icon_state = "house_edge0"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
w_class = WEIGHT_CLASS_HUGE
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index a731772b090..bc34783a853 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -3,6 +3,7 @@
name = "banhammer"
icon = 'icons/obj/weapons/hammer.dmi'
icon_state = "toyhammer"
+ icon_angle = -45
slot_flags = ITEM_SLOT_BELT
throwforce = 0
force = 1
@@ -47,6 +48,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon = 'icons/obj/weapons/hammer.dmi'
icon_state = "balloon_mallet"
inhand_icon_state = "balloon_mallet"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi'
siemens_coefficient = 0
@@ -88,6 +90,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "sord"
inhand_icon_state = "sord"
+ icon_angle = -35
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
slot_flags = ITEM_SLOT_BELT
@@ -109,6 +112,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "claymore"
inhand_icon_state = "claymore"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
hitsound = 'sound/items/weapons/bladeslice.ogg'
@@ -117,14 +121,16 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
force = 40
throwforce = 10
w_class = WEIGHT_CLASS_NORMAL
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
block_chance = 50
block_sound = 'sound/items/weapons/parry.ogg'
sharpness = SHARP_EDGED
max_integrity = 200
armor_type = /datum/armor/item_claymore
resistance_flags = FIRE_PROOF
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
/datum/armor/item_claymore
fire = 100
@@ -132,11 +138,18 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/claymore/Initialize(mapload)
. = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ make_stabby()
AddComponent(/datum/component/butchering, \
speed = 4 SECONDS, \
effectiveness = 105, \
)
+// Applies alt sharpness component, for overrides
+/obj/item/claymore/proc/make_stabby()
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -15)
+
/obj/item/claymore/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is falling on [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
return BRUTELOSS
@@ -356,6 +369,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon_state = "katana"
inhand_icon_state = "katana"
worn_icon_state = "katana"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
@@ -364,14 +378,22 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
throwforce = 10
w_class = WEIGHT_CLASS_HUGE
hitsound = 'sound/items/weapons/bladeslice.ogg'
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
block_chance = 50
block_sound = 'sound/items/weapons/parry.ogg'
sharpness = SHARP_EDGED
max_integrity = 200
armor_type = /datum/armor/item_katana
resistance_flags = FIRE_PROOF
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
+
+/obj/item/katana/Initialize(mapload)
+ . = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -15)
/datum/armor/item_katana
fire = 100
@@ -438,6 +460,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon_state = "carpenter_hammer"
inhand_icon_state = "carpenter_hammer"
worn_icon_state = "clawhammer" //plaecholder
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi'
desc = "Uncanny looking hammer."
@@ -475,6 +498,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "switchblade"
base_icon_state = "switchblade"
+ icon_angle = -90
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
desc = "A sharp, concealable, spring-loaded knife."
@@ -491,6 +515,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
resistance_flags = FIRE_PROOF
/// Whether the switchblade starts extended or not.
var/start_extended = FALSE
+ var/list/alt_continuous = list("stabs", "pierces", "shanks")
+ var/list/alt_simple = list("stab", "pierce", "shank")
/obj/item/switchblade/get_all_tool_behaviours()
return list(TOOL_KNIFE)
@@ -512,10 +538,14 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
sharpness_on = SHARP_EDGED, \
hitsound_on = 'sound/items/weapons/bladeslice.ogg', \
w_class_on = WEIGHT_CLASS_NORMAL, \
- attack_verb_continuous_on = list("slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts"), \
- attack_verb_simple_on = list("slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut"), \
+ attack_verb_continuous_on = list("slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts"), \
+ attack_verb_simple_on = list("slash", "slice", "tear", "lacerate", "rip", "dice", "cut"), \
)
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -5, TRAIT_TRANSFORM_ACTIVE)
+
RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform))
/obj/item/switchblade/proc/on_transform(obj/item/source, mob/user, active)
@@ -567,6 +597,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
base_icon_state = "bambostaff"
inhand_icon_state = "bambostaff0"
worn_icon_state = "bambostaff0"
+ icon_angle = -135
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
@@ -589,6 +620,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon = 'icons/obj/weapons/staff.dmi'
icon_state = "cane"
inhand_icon_state = "stick"
+ icon_angle = 135
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
force = 5
@@ -630,6 +662,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon = 'icons/obj/weapons/staff.dmi'
icon_state = "crutch_med"
inhand_icon_state = "crutch_med"
+ icon_angle = 45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
force = 12
@@ -682,6 +715,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
desc = "Traditionally used by the blind to help them see. Folds down to be easier to transport."
icon_state = "cane_white"
inhand_icon_state = "cane_white"
+ icon_angle = 45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
force = 1
@@ -756,6 +790,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon = 'icons/obj/weapons/staff.dmi'
icon_state = "cane"
inhand_icon_state = "stick"
+ icon_angle = 135
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
force = 3
@@ -788,6 +823,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
desc = "For the beating to death of lizards with their own tails."
icon = 'icons/obj/weapons/club.dmi'
icon_state = "tailclub"
+ icon_angle = -25
force = 14
throwforce = 1 // why are you throwing a club do you even weapon
throw_speed = 1
@@ -895,6 +931,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon = 'icons/obj/weapons/bat.dmi'
icon_state = "baseball_bat"
inhand_icon_state = "baseball_bat"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
force = 12
@@ -1045,6 +1082,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon = 'icons/obj/service/hydroponics/equipment.dmi'
icon_state = "flyswatter"
inhand_icon_state = "flyswatter"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
force = 1
@@ -1135,6 +1173,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon = 'icons/obj/weapons/club.dmi'
icon_state = "gohei"
inhand_icon_state = "gohei"
+ icon_angle = -65
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
@@ -1145,6 +1184,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon_state = "swordon"
inhand_icon_state = "swordon"
worn_icon_state = "swordon"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
w_class = WEIGHT_CLASS_BULKY
@@ -1155,8 +1195,16 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
force = 14
throwforce = 12
hitsound = 'sound/items/weapons/bladeslice.ogg'
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
+
+/obj/item/melee/moonlight_greatsword/Initialize(mapload)
+ . = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple)
//High Frequency Blade
@@ -1167,6 +1215,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "hfrequency0"
worn_icon_state = "hfrequency0"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
force = 10
diff --git a/code/game/objects/items/wizard_weapons.dm b/code/game/objects/items/wizard_weapons.dm
index e5750c06bb2..9cbbce1a4c8 100644
--- a/code/game/objects/items/wizard_weapons.dm
+++ b/code/game/objects/items/wizard_weapons.dm
@@ -4,6 +4,7 @@
icon = 'icons/obj/weapons/hammer.dmi'
icon_state = "singularity_hammer0"
base_icon_state = "singularity_hammer"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi'
worn_icon_state = "singularity_hammer"
@@ -77,6 +78,7 @@
icon_state = "mjollnir0"
base_icon_state = "mjollnir"
worn_icon_state = "mjollnir"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 91991bec3a7..7a58e3ef486 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -69,18 +69,29 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag)
return
var/total_force = (attacking_item.force * attacking_item.demolition_mod)
+ var/damage = take_damage(total_force, attacking_item.damtype, MELEE, TRUE, get_dir(src, user), attacking_item.armour_penetration)
- var/damage = take_damage(total_force, attacking_item.damtype, MELEE, 1, get_dir(src, user))
+ // Sanity in case one is null for some reason
+ var/picked_index = rand(max(length(attacking_item.attack_verb_simple), length(attacking_item.attack_verb_continuous)))
- var/damage_verb = "hit"
+ var/message_verb_continuous = "attacks"
+ var/message_verb_simple = "attack"
+ // Sanity in case one is... longer than the other?
+ if (picked_index && length(attacking_item.attack_verb_continuous) >= picked_index)
+ message_verb_continuous = attacking_item.attack_verb_continuous[picked_index]
+ if (picked_index && length(attacking_item.attack_verb_simple) >= picked_index)
+ message_verb_simple = attacking_item.attack_verb_simple[picked_index]
+
+ if(attacking_item.demolition_mod > 1 && prob(damage * 5))
+ message_verb_simple = "pulverise"
+ message_verb_continuous = "pulverises"
- if(attacking_item.demolition_mod > 1 && damage)
- damage_verb = "pulverise"
if(attacking_item.demolition_mod < 1)
- damage_verb = "ineffectively pierce"
+ message_verb_simple = "ineffectively " + message_verb_simple
+ message_verb_continuous = "ineffectively " + message_verb_continuous
- user.visible_message(span_danger("[user] [damage_verb][plural_s(damage_verb)] [src] with [attacking_item][damage ? "." : ", [no_damage_feedback]!"]"), \
- span_danger("You [damage_verb] [src] with [attacking_item][damage ? "." : ", [no_damage_feedback]!"]"), null, COMBAT_MESSAGE_RANGE)
+ user.visible_message(span_danger("[user] [message_verb_continuous] [src] with [attacking_item][damage ? "." : ", [no_damage_feedback]!"]"), \
+ span_danger("You [message_verb_simple] [src] with [attacking_item][damage ? "." : ", [no_damage_feedback]!"]"), null, COMBAT_MESSAGE_RANGE)
log_combat(user, src, "attacked", attacking_item)
/obj/assume_air(datum/gas_mixture/giver)
diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm
index 38aadbb2662..8cc1012995d 100644
--- a/code/game/objects/structures/beds_chairs/chair.dm
+++ b/code/game/objects/structures/beds_chairs/chair.dm
@@ -317,6 +317,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0)
icon = 'icons/obj/chairs.dmi'
icon_state = "chair_toppled"
inhand_icon_state = "chair"
+ icon_angle = 180
lefthand_file = 'icons/mob/inhands/items/chairs_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/chairs_righthand.dmi'
w_class = WEIGHT_CLASS_HUGE
diff --git a/code/game/objects/structures/lavaland/geyser.dm b/code/game/objects/structures/lavaland/geyser.dm
index 6a8dc8e31cd..9a546e8154d 100644
--- a/code/game/objects/structures/lavaland/geyser.dm
+++ b/code/game/objects/structures/lavaland/geyser.dm
@@ -121,6 +121,7 @@
icon = 'icons/obj/watercloset.dmi'
icon_state = "plunger"
worn_icon_state = "plunger"
+ icon_angle = 90
slot_flags = ITEM_SLOT_MASK
flags_inv = HIDESNOUT
diff --git a/code/game/objects/structures/showcase.dm b/code/game/objects/structures/showcase.dm
index 2158a88a6b6..c0f19da6dbd 100644
--- a/code/game/objects/structures/showcase.dm
+++ b/code/game/objects/structures/showcase.dm
@@ -127,8 +127,8 @@
/obj/structure/showcase/katana
name = "seppuku katana"
- density = 0
desc = "Welp, only one way to recover your honour."
+ density = 0
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "katana"
diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm
index 9f7660b05e6..ce6c987ba10 100644
--- a/code/game/objects/structures/shower.dm
+++ b/code/game/objects/structures/shower.dm
@@ -344,11 +344,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16))
return mode == SHOWER_MODE_FOREVER ? 0 : PROCESS_KILL
// Wash up.
- wash_atom(loc)
- for(var/atom/movable/movable_content as anything in loc)
- if(!ismopable(movable_content)) // Mopables will be cleaned anyways by the turf wash above
- wash_atom(movable_content) // Reagent exposure is handled in wash_atom
-
+ wash_atom(loc, TRUE)
reagents.remove_all(SHOWER_SPRAY_VOLUME)
/obj/machinery/shower/on_deconstruction(disassembled = TRUE)
diff --git a/code/game/objects/structures/water_structures/sink.dm b/code/game/objects/structures/water_structures/sink.dm
index 1cd3f7d7aaa..3a6dfbb2a2c 100644
--- a/code/game/objects/structures/water_structures/sink.dm
+++ b/code/game/objects/structures/water_structures/sink.dm
@@ -73,10 +73,13 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink, (-14))
if(busy)
to_chat(user, span_warning("Someone's already washing here!"))
return
+
var/selected_area = user.parse_zone_with_bodypart(user.zone_selected)
- var/washing_face = 0
+ var/washing_face = FALSE
if(selected_area in list(BODY_ZONE_HEAD, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_PRECISE_EYES))
- washing_face = 1
+ washing_face = TRUE
+
+ playsound(src, 'sound/machines/sink-faucet.ogg', 50)
user.visible_message(span_notice("[user] starts washing [user.p_their()] [washing_face ? "face" : "hands"]..."), \
span_notice("You start washing your [washing_face ? "face" : "hands"]..."))
busy = TRUE
@@ -206,6 +209,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink, (-14))
if(!user.combat_mode || (O.item_flags & NOBLUDGEON))
to_chat(user, span_notice("You start washing [O]..."))
+ playsound(src, 'sound/machines/sink-faucet.ogg', 50)
busy = TRUE
if(!do_after(user, 4 SECONDS, target = src))
busy = FALSE
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 71c3ef9b671..9924713d939 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -82,12 +82,11 @@
/obj/structure/window/mouse_drop_receive(atom/dropping, mob/user, params)
. = ..()
- if (added_leaning || (flags_1 & ON_BORDER_1))
+ if (flags_1 & ON_BORDER_1)
return
- /// For performance reasons and to cut down on init times we are "lazy-loading" the leaning component when someone drags their sprite onto us, and then calling dragging code again to trigger the component
- AddComponent(/datum/component/leanable, 11)
- added_leaning = TRUE
- dropping.base_mouse_drop_handler(src, null, null, params)
+
+ //Adds the component only once. We do it here & not in Initialize() because there are tons of windows & we don't want to add to their init times
+ LoadComponent(/datum/component/leanable, dropping)
/obj/structure/window/examine(mob/user)
. = ..()
diff --git a/code/game/say.dm b/code/game/say.dm
index d8cb91c4ea4..1e5b4bce450 100644
--- a/code/game/say.dm
+++ b/code/game/say.dm
@@ -165,7 +165,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
if(istype(dialect) && dialect.display_icon(src))
languageicon = "[dialect.get_icon()] "
- messagepart = " [say_emphasis(messagepart)]"
+ messagepart = " [messagepart]"
return "[spanpart1][spanpart2][freqpart][languageicon][compose_track_href(speaker, namepart)][namepart][compose_job(speaker, message_language, raw_message, radio_freq)][endspanpart][messagepart]"
@@ -223,8 +223,14 @@ GLOBAL_LIST_INIT(freqtospan, list(
if(copytext_char(input, -2) == "!!")
spans |= SPAN_YELL
- var/spanned = attach_spans(input, spans)
- return "[say_mod], \"[spanned]\""
+ /* all inputs should be fully figured out past this point */
+
+ var/processed_input = say_emphasis(input) //This MUST be done first so that we don't get clipped by spans
+ processed_input = attach_spans(processed_input, spans)
+
+ var/processed_say_mod = say_emphasis(say_mod)
+
+ return "[processed_say_mod], \"[processed_input]\""
/// Transforms the speech emphasis mods from [/atom/movable/proc/say_emphasis] into the appropriate HTML tags. Includes escaping.
#define ENCODE_HTML_EMPHASIS(input, char, html, varname) \
@@ -235,8 +241,8 @@ GLOBAL_LIST_INIT(freqtospan, list(
/atom/movable/proc/say_emphasis(input)
ENCODE_HTML_EMPHASIS(input, "\\|", "i", italics)
ENCODE_HTML_EMPHASIS(input, "\\+", "b", bold)
- ENCODE_HTML_EMPHASIS(input, "_", "u", underline)
- var/static/regex/remove_escape_backlashes = regex("\\\\(_|\\+|\\|)", "g") // Removes backslashes used to escape text modification.
+ ENCODE_HTML_EMPHASIS(input, "\\_", "u", underline)
+ var/static/regex/remove_escape_backlashes = regex("\\\\(\\_|\\+|\\|)", "g") // Removes backslashes used to escape text modification.
input = remove_escape_backlashes.Replace_char(input, "$1")
return input
diff --git a/code/game/turfs/closed/walls.dm b/code/game/turfs/closed/walls.dm
index 99ea3327d6c..402d24c6c83 100644
--- a/code/game/turfs/closed/walls.dm
+++ b/code/game/turfs/closed/walls.dm
@@ -51,13 +51,8 @@
underlays += underlay_appearance
/turf/closed/wall/mouse_drop_receive(atom/dropping, mob/user, params)
- . = ..()
- if (added_leaning)
- return
- /// For performance reasons and to cut down on init times we are "lazy-loading" the leaning component when someone drags their sprite onto us, and then calling dragging code again to trigger the component
- AddComponent(/datum/component/leanable, 11)
- added_leaning = TRUE
- dropping.base_mouse_drop_handler(src, null, null, params)
+ //Adds the component only once. We do it here & not in Initialize() because there are tons of walls & we don't want to add to their init times
+ LoadComponent(/datum/component/leanable, dropping)
/turf/closed/wall/atom_destruction(damage_flag)
. = ..()
diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm
index c30073b9907..b0c3bb9c883 100644
--- a/code/game/turfs/open/_open.dm
+++ b/code/game/turfs/open/_open.dm
@@ -340,11 +340,7 @@
for(var/atom/theatom as anything in src)
SEND_SIGNAL(theatom, COMSIG_ATOM_EXPOSED_WATER)
- wash(CLEAN_WASH)
- for(var/atom/movable/movable_content as anything in src)
- if(ismopable(movable_content)) // Will have already been washed by the wash call above at this point.
- continue
- movable_content.wash(CLEAN_WASH)
+ wash(CLEAN_WASH, TRUE)
return TRUE
/turf/open/handle_slip(mob/living/slipper, knockdown_amount, obj/slippable, lube, paralyze_amount, force_drop)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 318bb5ebb4c..b3cf3761e1d 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -702,19 +702,14 @@ GLOBAL_LIST_EMPTY(station_turfs)
var/reac_volume = reagents[reagent]
. |= reagent.expose_turf(src, reac_volume)
-/**
- * Called when this turf is being washed. Washing a turf will also wash any mopable floor decals
- */
-/turf/wash(clean_types)
+// When our turf is washed, we may wash everything on top of the turf
+// By default we will only wash mopable things (like blood or vomit)
+// but you may optionally pass in all_contents = TRUE to wash everything
+/turf/wash(clean_types, all_contents = FALSE)
. = ..()
-
- for(var/am in src)
- if(am == src)
- continue
- var/atom/movable/movable_content = am
- if(!ismopable(movable_content))
- continue
- movable_content.wash(clean_types)
+ for(var/atom/movable/to_clean as anything in src)
+ if(all_contents || HAS_TRAIT(to_clean, TRAIT_MOPABLE))
+ to_clean.wash(clean_types)
/turf/set_density(new_value)
var/old_density = density
diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm
index 73987622202..6bd97dcaa20 100644
--- a/code/modules/admin/permissionedit.dm
+++ b/code/modules/admin/permissionedit.dm
@@ -140,9 +140,14 @@ ADMIN_VERB(edit_admin_permissions, R_PERMISSIONS, "Permissions Panel", "Edit adm
permissions_assets.send(usr.client)
var/admin_key = href_list["key"]
var/admin_ckey = ckey(admin_key)
- var/datum/admins/D = GLOB.admin_datums[admin_ckey]
- var/use_db
+
var/task = href_list["editrights"]
+ var/datum/admins/target_admin_datum = GLOB.admin_datums[admin_ckey]
+ if(!target_admin_datum)
+ target_admin_datum = GLOB.deadmins[admin_ckey]
+ if (!target_admin_datum && task != "add")
+ return
+ var/use_db
var/skip
var/legacy_only
if(task == "activate" || task == "deactivate" || task == "sync" || task == "verify")
@@ -152,7 +157,7 @@ ADMIN_VERB(edit_admin_permissions, R_PERMISSIONS, "Permissions Panel", "Edit adm
to_chat(usr, "Editing the rank of this admin is blocked by server configuration.", confidential = TRUE)
return
if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions")
- if((D.ranks & GLOB.protected_ranks).len > 0)
+ if((target_admin_datum.ranks & GLOB.protected_ranks).len > 0)
to_chat(usr, "Editing the flags of this rank is blocked by server configuration.", confidential = TRUE)
return
if(CONFIG_GET(flag/load_legacy_ranks_only) && (task == "add" || task == "rank" || task == "permissions"))
@@ -173,16 +178,11 @@ ADMIN_VERB(edit_admin_permissions, R_PERMISSIONS, "Permissions Panel", "Edit adm
use_db = FALSE
if(QDELETED(usr))
return
- if(task != "add")
- D = GLOB.admin_datums[admin_ckey]
- if(!D)
- D = GLOB.deadmins[admin_ckey]
- if(!D)
- return
- if((task != "sync") && !check_if_greater_rights_than_holder(D))
- message_admins("[key_name_admin(usr)] attempted to change the rank of [admin_key] without sufficient rights.")
- log_admin("[key_name(usr)] attempted to change the rank of [admin_key] without sufficient rights.")
- return
+
+ if(target_admin_datum && (task != "sync" && task != "verify") && !check_if_greater_rights_than_holder(target_admin_datum))
+ message_admins("[key_name_admin(usr)] attempted to change the rank of [admin_key] without sufficient rights.")
+ log_admin("[key_name(usr)] attempted to change the rank of [admin_key] without sufficient rights.")
+ return
switch(task)
if("add")
admin_ckey = add_admin(admin_ckey, admin_key, use_db)
@@ -194,24 +194,24 @@ ADMIN_VERB(edit_admin_permissions, R_PERMISSIONS, "Permissions Panel", "Edit adm
change_admin_rank(admin_ckey, admin_key, use_db, null, legacy_only)
if("remove")
- remove_admin(admin_ckey, admin_key, use_db, D)
+ remove_admin(admin_ckey, admin_key, use_db, target_admin_datum)
if("rank")
- change_admin_rank(admin_ckey, admin_key, use_db, D, legacy_only)
+ change_admin_rank(admin_ckey, admin_key, use_db, target_admin_datum, legacy_only)
if("permissions")
- change_admin_flags(admin_ckey, admin_key, D)
+ change_admin_flags(admin_ckey, admin_key, target_admin_datum)
if("activate")
- force_readmin(admin_key, D)
+ force_readmin(admin_key, target_admin_datum)
if("deactivate")
- force_deadmin(admin_key, D)
+ force_deadmin(admin_key, target_admin_datum)
if("sync")
- sync_lastadminrank(admin_ckey, admin_key, D)
+ sync_lastadminrank(admin_ckey, admin_key, target_admin_datum)
if("verify")
var/msg = "has authenticated [admin_ckey]"
message_admins("[key_name_admin(usr)] [msg]")
log_admin("[key_name(usr)] [msg]")
- D.bypass_2fa = TRUE
- D.associate(GLOB.directory[admin_ckey])
+ target_admin_datum.bypass_2fa = TRUE
+ target_admin_datum.associate(GLOB.directory[admin_ckey])
edit_admin_permissions()
/datum/admins/proc/add_admin(admin_ckey, admin_key, use_db)
diff --git a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm
index d70dc3f0e67..b2900b810f3 100644
--- a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm
+++ b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm
@@ -29,6 +29,7 @@
desc = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras."
icon_state = "gizmo_scan"
inhand_icon_state = "silencer"
+ icon_angle = -45
var/mode = GIZMO_SCAN
var/datum/weakref/marked_target_weakref
var/obj/machinery/abductor/console/console
@@ -105,6 +106,7 @@
desc = "A compact device used to shut down communications equipment."
icon_state = "silencer"
inhand_icon_state = "gizmo"
+ icon_angle = -45
/obj/item/abductor/silencer/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!AbductorCheck(user))
@@ -145,6 +147,7 @@
or to send a command to a test subject with a charged gland."
icon_state = "mind_device_message"
inhand_icon_state = "silencer"
+ icon_angle = -45
var/mode = MIND_DEVICE_MESSAGE
/obj/item/abductor/mind_device/attack_self(mob/user)
@@ -297,6 +300,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
righthand_file = 'icons/mob/inhands/antag/abductor_righthand.dmi'
icon_state = "wonderprodStun"
inhand_icon_state = "wonderprod"
+ icon_angle = -45
force = 7
wound_bonus = FALSE
@@ -549,6 +553,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
name = "alien scalpel"
desc = "It's a gleaming sharp knife made out of silvery-green metal."
icon = 'icons/obj/antags/abductor.dmi'
+ icon_angle = 180
surgical_tray_overlay = "scalpel_alien"
toolspeed = 0.25
@@ -557,6 +562,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
desc = "You've never seen this before."
icon = 'icons/obj/antags/abductor.dmi'
surgical_tray_overlay = "hemostat_alien"
+ icon_angle = 180
toolspeed = 0.25
/obj/item/retractor/alien
@@ -564,6 +570,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
desc = "You're not sure if you want the veil pulled back."
icon = 'icons/obj/antags/abductor.dmi'
surgical_tray_overlay = "retractor_alien"
+ icon_angle = 180
toolspeed = 0.25
/obj/item/circular_saw/alien
@@ -571,6 +578,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
desc = "Do the aliens also lose this, and need to find an alien hatchet?"
icon = 'icons/obj/antags/abductor.dmi'
surgical_tray_overlay = "saw_alien"
+ icon_angle = 180
toolspeed = 0.25
/obj/item/surgicaldrill/alien
@@ -578,6 +586,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
desc = "Maybe alien surgeons have finally found a use for the drill."
icon = 'icons/obj/antags/abductor.dmi'
surgical_tray_overlay = "drill_alien"
+ icon_angle = 180
toolspeed = 0.25
/obj/item/cautery/alien
@@ -586,6 +595,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
Unless..."
icon = 'icons/obj/antags/abductor.dmi'
surgical_tray_overlay = "cautery_alien"
+ icon_angle = 180
toolspeed = 0.25
/obj/item/clothing/head/helmet/abductor
@@ -620,6 +630,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
desc = "Effectively just a Space Swiss Army Knife. Contains a multitude of integrated tools. Right-click it to switch which toolset is active."
icon_state = "omnitool"
inhand_icon_state = "silencer"
+ icon_angle = -45
toolspeed = 0.25
tool_behaviour = null
usesound = 'sound/items/pshoom/pshoom.ogg'
diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm
index 874141237a9..c1721cc57e6 100644
--- a/code/modules/antagonists/changeling/powers/mutations.dm
+++ b/code/modules/antagonists/changeling/powers/mutations.dm
@@ -188,6 +188,7 @@
icon = 'icons/obj/weapons/changeling_items.dmi'
icon_state = "arm_blade"
inhand_icon_state = "arm_blade"
+ icon_angle = 180
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
item_flags = NEEDS_PERMIT | ABSTRACT | DROPDEL
@@ -197,14 +198,16 @@
throw_range = 0
throw_speed = 0
hitsound = 'sound/items/weapons/bladeslice.ogg'
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
sharpness = SHARP_EDGED
wound_bonus = 10
bare_wound_bonus = 10
armour_penetration = 35
var/can_drop = FALSE
var/fake = FALSE
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
/obj/item/melee/arm_blade/Initialize(mapload,silent,synthetic)
. = ..()
@@ -213,6 +216,9 @@
loc.visible_message(span_warning("A grotesque blade forms around [loc.name]\'s arm!"), span_warning("Our arm twists and mutates, transforming it into a deadly blade."), span_hear("You hear organic matter ripping and tearing!"))
if(synthetic)
can_drop = TRUE
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -5)
AddComponent(/datum/component/butchering, \
speed = 6 SECONDS, \
effectiveness = 80, \
@@ -276,6 +282,7 @@
icon = 'icons/obj/weapons/changeling_items.dmi'
icon_state = "tentacle"
inhand_icon_state = "tentacle"
+ icon_angle = 180
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
item_flags = NEEDS_PERMIT | ABSTRACT | DROPDEL | NOBLUDGEON
@@ -356,26 +363,31 @@
chain = firer.Beam(src, icon_state = "tentacle", emissive = FALSE)
..()
-/obj/projectile/tentacle/proc/reset_throw(mob/living/carbon/human/H)
- if(H.throw_mode)
- H.throw_mode_off(THROW_MODE_TOGGLE) //Don't annoy the changeling if he doesn't catch the item
+/obj/projectile/tentacle/proc/reset_throw(mob/living/carbon/human/user)
+ if(user.throw_mode)
+ user.throw_mode_off(THROW_MODE_TOGGLE) //Don't annoy the changeling if he doesn't catch the item
-/obj/projectile/tentacle/proc/tentacle_grab(mob/living/carbon/human/H, mob/living/carbon/C)
- if(H.Adjacent(C))
- if(H.get_active_held_item() && !H.get_inactive_held_item())
- H.swap_hand()
- if(H.get_active_held_item())
+/obj/projectile/tentacle/proc/tentacle_grab(mob/living/carbon/human/user, mob/living/carbon/victim)
+ if(!user.Adjacent(victim))
+ return
+
+ if(user.get_active_held_item() && !user.get_inactive_held_item())
+ user.swap_hand()
+
+ if(user.get_active_held_item())
+ return
+
+ victim.grabbedby(user)
+ victim.grippedby(user, instant = TRUE) //instant aggro grab
+
+ for(var/obj/item/weapon in user.held_items)
+ if(weapon.get_sharpness())
+ victim.visible_message(span_danger("[user] impales [victim] with [user.p_their()] [weapon.name]!"), span_userdanger("[user] impales you with [user.p_their()] [weapon.name]!"))
+ victim.apply_damage(weapon.force, BRUTE, BODY_ZONE_CHEST, attacking_item = weapon)
+ user.do_item_attack_animation(victim, used_item = weapon, animation_type = ATTACK_ANIMATION_PIERCE)
+ user.add_mob_blood(victim)
+ playsound(get_turf(user),weapon.hitsound,75,TRUE)
return
- C.grabbedby(H)
- C.grippedby(H, instant = TRUE) //instant aggro grab
- for(var/obj/item/I in H.held_items)
- if(I.get_sharpness())
- C.visible_message(span_danger("[H] impales [C] with [H.p_their()] [I.name]!"), span_userdanger("[H] impales you with [H.p_their()] [I.name]!"))
- C.apply_damage(I.force, BRUTE, BODY_ZONE_CHEST, attacking_item = I)
- H.do_item_attack_animation(C, used_item = I)
- H.add_mob_blood(C)
- playsound(get_turf(H),I.hitsound,75,TRUE)
- return
/obj/projectile/tentacle/on_hit(atom/movable/target, blocked = 0, pierce_hit)
if(!isliving(firer) || !ismovable(target))
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index c332b2266ce..6d7bee5d583 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -14,6 +14,7 @@
icon_state = "render"
inhand_icon_state = "cultdagger"
worn_icon_state = "render"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
inhand_x_dimension = 32
@@ -61,6 +62,7 @@ Striking a noncultist, however, will tear their flesh."}
icon_state = "cultblade"
inhand_icon_state = "cultblade"
worn_icon_state = "cultblade"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
@@ -75,10 +77,12 @@ Striking a noncultist, however, will tear their flesh."}
bare_wound_bonus = 20
hitsound = 'sound/items/weapons/bladeslice.ogg'
block_sound = 'sound/items/weapons/parry.ogg'
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "rends")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "rend")
/// If TRUE, it can be used at will by anyone, non-cultists included
var/free_use = FALSE
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
/obj/item/melee/cultblade/Initialize(mapload)
. = ..()
@@ -86,6 +90,9 @@ Striking a noncultist, however, will tear their flesh."}
speed = 4 SECONDS, \
effectiveness = 100, \
)
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -5)
ADD_TRAIT(src, TRAIT_CONTRABAND, INNATE_TRAIT)
/obj/item/melee/cultblade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
@@ -1131,6 +1138,7 @@ Striking a noncultist, however, will tear their flesh."}
icon_state = "occultpoleaxe0"
base_icon_state = "occultpoleaxe"
inhand_icon_state = "occultpoleaxe0"
+ icon_angle = -45
w_class = WEIGHT_CLASS_HUGE
force = 17
throwforce = 40
diff --git a/code/modules/antagonists/cult/cult_structure_archives.dm b/code/modules/antagonists/cult/cult_structure_archives.dm
index d4867659651..050fe7361a5 100644
--- a/code/modules/antagonists/cult/cult_structure_archives.dm
+++ b/code/modules/antagonists/cult/cult_structure_archives.dm
@@ -49,8 +49,9 @@
/obj/structure/destructible/cult/item_dispenser/archives/succcess_message(mob/living/user, obj/item/spawned_item)
to_chat(user, span_cult_italic("You summon [spawned_item] from [src]!"))
-// Preset for the library that doesn't spawn runed metal on destruction.
+// Preset for the library that doesn't spawn runed metal on destruction, or glow.
/obj/structure/destructible/cult/item_dispenser/archives/library
+ icon_state = "tomealtar_off"
debris = list()
#undef CULT_BLINDFOLD
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index af7254119dd..f4b424e4f20 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -82,6 +82,7 @@ Runes can either be invoked by one's self or with many different cultists. Each
var/image/I = image(icon = 'icons/effects/blood.dmi', icon_state = null, loc = src)
I.override = TRUE
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "cult_runes", I)
+ ADD_TRAIT(src, TRAIT_MOPABLE, INNATE_TRAIT)
/obj/effect/rune/examine(mob/user)
. = ..()
diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm
index 7b608223a98..1cee767dc96 100644
--- a/code/modules/antagonists/heretic/items/heretic_blades.dm
+++ b/code/modules/antagonists/heretic/items/heretic_blades.dm
@@ -5,6 +5,7 @@
icon = 'icons/obj/weapons/khopesh.dmi'
icon_state = "eldritch_blade"
inhand_icon_state = "eldritch_blade"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
@@ -21,8 +22,8 @@
demolition_mod = 0.8
hitsound = 'sound/items/weapons/bladeslice.ogg'
armour_penetration = 35
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "rends")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "rend")
var/after_use_message = ""
/obj/item/melee/sickly_blade/examine(mob/user)
diff --git a/code/modules/antagonists/heretic/magic/cosmic_runes.dm b/code/modules/antagonists/heretic/magic/cosmic_runes.dm
index be8f103678e..7eee200dd4a 100644
--- a/code/modules/antagonists/heretic/magic/cosmic_runes.dm
+++ b/code/modules/antagonists/heretic/magic/cosmic_runes.dm
@@ -69,6 +69,7 @@
var/image/silicon_image = image(icon = 'icons/obj/service/hand_of_god_structures.dmi', icon_state = null, loc = src)
silicon_image.override = TRUE
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "cosmic", silicon_image)
+ ADD_TRAIT(src, TRAIT_MOPABLE, INNATE_TRAIT)
/obj/effect/cosmic_rune/attack_paw(mob/living/user, list/modifiers)
return attack_hand(user, modifiers)
diff --git a/code/modules/antagonists/heretic/rust_effect.dm b/code/modules/antagonists/heretic/rust_effect.dm
index 9af6c4f6d89..294fe42cff1 100644
--- a/code/modules/antagonists/heretic/rust_effect.dm
+++ b/code/modules/antagonists/heretic/rust_effect.dm
@@ -13,3 +13,4 @@
pixel_x = rand(-6, 6)
icon_state = "small_rune_[rand(1, 12)]"
update_appearance()
+ ADD_TRAIT(src, TRAIT_MOPABLE, INNATE_TRAIT)
diff --git a/code/modules/antagonists/heretic/structures/carving_knife.dm b/code/modules/antagonists/heretic/structures/carving_knife.dm
index f3d37b87682..eb8a3a4769b 100644
--- a/code/modules/antagonists/heretic/structures/carving_knife.dm
+++ b/code/modules/antagonists/heretic/structures/carving_knife.dm
@@ -5,6 +5,7 @@
but only few can evoke the dangers that lurk beneath reality."
icon = 'icons/obj/antags/eldritch.dmi'
icon_state = "rune_carver"
+ icon_angle = -45
obj_flags = CONDUCTS_ELECTRICITY
sharpness = SHARP_EDGED
w_class = WEIGHT_CLASS_SMALL
@@ -12,8 +13,8 @@
force = 10
throwforce = 20
hitsound = 'sound/items/weapons/bladeslice.ogg'
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "rends")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "rend")
actions_types = list(/datum/action/item_action/rune_shatter)
embed_type = /datum/embed_data/rune_carver
@@ -25,6 +26,14 @@
var/list/datum/weakref/current_runes = list()
/// Turfs that you cannot draw carvings on
var/static/list/blacklisted_turfs = typecacheof(list(/turf/open/space, /turf/open/openspace, /turf/open/lava))
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
+
+/obj/item/melee/rune_carver/Initialize(mapload)
+ . = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple)
/datum/embed_data/rune_carver
ignore_throwspeed_threshold = TRUE
diff --git a/code/modules/antagonists/heretic/transmutation_rune.dm b/code/modules/antagonists/heretic/transmutation_rune.dm
index a2bf4af77f4..ee25b0a18fd 100644
--- a/code/modules/antagonists/heretic/transmutation_rune.dm
+++ b/code/modules/antagonists/heretic/transmutation_rune.dm
@@ -17,6 +17,7 @@
var/image/silicon_image = image(icon = 'icons/effects/eldritch.dmi', icon_state = null, loc = src)
silicon_image.override = TRUE
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "heretic_rune", silicon_image)
+ ADD_TRAIT(src, TRAIT_MOPABLE, INNATE_TRAIT)
/obj/effect/heretic_rune/examine(mob/user)
. = ..()
diff --git a/code/modules/antagonists/nightmare/nightmare_equipment.dm b/code/modules/antagonists/nightmare/nightmare_equipment.dm
index 52a687f9ac7..1d356d3dd9d 100644
--- a/code/modules/antagonists/nightmare/nightmare_equipment.dm
+++ b/code/modules/antagonists/nightmare/nightmare_equipment.dm
@@ -6,6 +6,7 @@
icon = 'icons/obj/weapons/changeling_items.dmi'
icon_state = "arm_blade"
inhand_icon_state = "arm_blade"
+ icon_angle = 180
force = 25
armour_penetration = 35
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
diff --git a/code/modules/antagonists/ninja/energy_katana.dm b/code/modules/antagonists/ninja/energy_katana.dm
index efd99355091..c26cb44d39c 100644
--- a/code/modules/antagonists/ninja/energy_katana.dm
+++ b/code/modules/antagonists/ninja/energy_katana.dm
@@ -17,6 +17,7 @@
icon_state = "energy_katana"
inhand_icon_state = "energy_katana"
worn_icon_state = "energy_katana"
+ icon_angle = 35
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
force = 30
@@ -28,8 +29,8 @@
pickup_sound = 'sound/items/unsheath.ogg'
drop_sound = 'sound/items/sheath.ogg'
block_sound = 'sound/items/weapons/block_blade.ogg'
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
slot_flags = ITEM_SLOT_BACK|ITEM_SLOT_BELT
sharpness = SHARP_EDGED
max_integrity = 200
diff --git a/code/modules/antagonists/voidwalker/voidwalker_void_eater.dm b/code/modules/antagonists/voidwalker/voidwalker_void_eater.dm
index f88c09188cf..5367a5cd89c 100644
--- a/code/modules/antagonists/voidwalker/voidwalker_void_eater.dm
+++ b/code/modules/antagonists/voidwalker/voidwalker_void_eater.dm
@@ -7,6 +7,7 @@
icon = 'icons/obj/weapons/voidwalker_items.dmi'
icon_state = "tentacle"
inhand_icon_state = "tentacle"
+ icon_angle = 180
force = 25
armour_penetration = 35
lefthand_file = 'icons/mob/inhands/antag/voidwalker_lefthand.dmi'
diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm
index 465765c75a3..9b0b16ef9f6 100644
--- a/code/modules/antagonists/wizard/equipment/artefact.dm
+++ b/code/modules/antagonists/wizard/equipment/artefact.dm
@@ -10,6 +10,7 @@
icon_state = "bone_blade"
inhand_icon_state = "bone_blade"
worn_icon_state = "bone_blade"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
@@ -401,6 +402,7 @@
desc = "This scepter allows you to conjure, force push and detonate Runic Vendors. It can hold up to 3 charges that can be recovered with a simple magical channeling. A modern spin on the old Geomancy spells."
icon_state = "vendor_staff"
inhand_icon_state = "vendor_staff"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
icon = 'icons/obj/weapons/guns/magic.dmi'
diff --git a/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm b/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
index dfda45ec488..33ae8f2c871 100644
--- a/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
@@ -79,6 +79,7 @@
silicon_image.override = TRUE
add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "wizard_rune", silicon_image)
announce_rune()
+ ADD_TRAIT(src, TRAIT_MOPABLE, INNATE_TRAIT)
/// I cast Summon Security
/obj/effect/grand_rune/proc/announce_rune()
diff --git a/code/modules/art/statues.dm b/code/modules/art/statues.dm
index eeb0cfa9cb4..a6a15c97732 100644
--- a/code/modules/art/statues.dm
+++ b/code/modules/art/statues.dm
@@ -265,6 +265,7 @@
icon = 'icons/obj/art/statue.dmi'
icon_state = "chisel"
inhand_icon_state = "screwdriver_nuke"
+ icon_angle = -90
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index 97b9741701f..65a587578e5 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -101,6 +101,8 @@
var/datum/gas_machine_connector/internal_connector
/// Check if the machine has been turned on
var/on = FALSE
+ /// The sound loop that can be heard when the generator is processing.
+ var/datum/looping_sound/cryo_cell/soundloop
/datum/armor/unary_cryo_cell
energy = 100
@@ -119,6 +121,7 @@
occupant_vis = new(mapload, src)
vis_contents += occupant_vis
internal_connector = new(loc, src, dir, CELL_VOLUME * 0.5)
+ soundloop = new(src)
register_context()
@@ -130,6 +133,7 @@
QDEL_NULL(radio)
QDEL_NULL(beaker)
QDEL_NULL(internal_connector)
+ QDEL_NULL(soundloop)
return ..()
@@ -378,10 +382,14 @@
/obj/machinery/cryo_cell/begin_processing()
. = ..()
SSair.start_processing_machine(src)
+ if(soundloop)
+ soundloop.start()
/obj/machinery/cryo_cell/end_processing()
. = ..()
SSair.stop_processing_machine(src)
+ if(soundloop)
+ soundloop.stop()
/obj/machinery/cryo_cell/on_set_is_operational(old_value)
//Turned off
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/machine_connector.dm b/code/modules/atmospherics/machinery/components/unary_devices/machine_connector.dm
index 157cbae9af0..e852fccdc31 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/machine_connector.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/machine_connector.dm
@@ -57,8 +57,12 @@
/**
* Called when the machine has been moved, reconnect to the pipe network
*/
-/datum/gas_machine_connector/proc/moved_connected_machine()
+/datum/gas_machine_connector/proc/moved_connected_machine(obj/machinery/source, atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
SIGNAL_HANDLER
+ if(forced) // Called from parent doing abstract_move()
+ gas_connector.abstract_move(get_turf(connected_machine))
+ return // No side-effects means no disconnections
+
gas_connector.forceMove(get_turf(connected_machine))
reconnect_connector()
diff --git a/code/modules/cargo/goodies.dm b/code/modules/cargo/goodies.dm
index 0b6451e5698..5abf8f2dd27 100644
--- a/code/modules/cargo/goodies.dm
+++ b/code/modules/cargo/goodies.dm
@@ -32,6 +32,27 @@
access_view = ACCESS_WEAPONS
contains = list(/obj/item/ammo_box/c38/match/bouncy)
+/datum/supply_pack/goody/dumdum38br
+ name = ".38 DumDum Magazine Single-Pack"
+ desc = "Contains one magazine of .38 DumDum ammunition, good for embedding in soft targets."
+ cost = PAYCHECK_CREW * 2
+ access_view = ACCESS_WEAPONS
+ contains = list(/obj/item/ammo_box/magazine/m38/dumdum)
+
+/datum/supply_pack/goody/match38br
+ name = ".38 Match Grade Magazine Single-Pack"
+ desc = "Contains one magazine of match grade .38 ammunition, perfect for showing off trickshots."
+ cost = PAYCHECK_CREW * 2
+ access_view = ACCESS_WEAPONS
+ contains = list(/obj/item/ammo_box/magazine/m38/match)
+
+/datum/supply_pack/goody/rubber
+ name = ".38 Rubber Magazine Single-Pack"
+ desc = "Contains one magazine of bouncy rubber .38 ammunition, for when you want to bounce your shots off anything and everything."
+ cost = PAYCHECK_CREW * 1.5
+ access_view = ACCESS_WEAPONS
+ contains = list(/obj/item/ammo_box/magazine/m38/match/bouncy)
+
/datum/supply_pack/goody/mars_single
name = "Colt Detective Special Single-Pack"
desc = "The HoS took your gun and your badge? No problem! Just pay the absurd taxation fee and you too can be reunited with the lethal power of a .38!"
diff --git a/code/modules/cargo/packs/general.dm b/code/modules/cargo/packs/general.dm
index e294d1bd4a1..5f3a4645933 100644
--- a/code/modules/cargo/packs/general.dm
+++ b/code/modules/cargo/packs/general.dm
@@ -137,9 +137,9 @@
/datum/supply_pack/misc/funeral
- name = "Funeral Supply crate"
+ name = "Funeral Supplies Crate"
desc = "At the end of the day, someone's gonna want someone dead. Give them a proper send-off with these \
- funeral supplies! Contains a coffin with burial garmets and flowers."
+ funeral supplies! Contains a coffin with burial garments and flowers."
cost = CARGO_CRATE_VALUE * 1.6
access_view = ACCESS_CHAPEL_OFFICE
contains = list(/obj/item/clothing/under/misc/burial,
@@ -165,7 +165,7 @@
/datum/supply_pack/misc/religious_supplies
name = "Religious Supplies Crate"
desc = "Keep your local chaplain happy and well-supplied, lest they call down judgement upon your \
- cargo bay. Contains two bottles of holywater, bibles, chaplain robes, and burial garmets."
+ cargo bay. Contains two bottles of holy water, bibles, chaplain robes, and burial garments."
cost = CARGO_CRATE_VALUE * 6 // it costs so much because the Space Church needs funding to build a cathedral
access_view = ACCESS_CHAPEL_OFFICE
contains = list(/obj/item/reagent_containers/cup/glass/bottle/holywater = 2,
diff --git a/code/modules/cargo/packs/security.dm b/code/modules/cargo/packs/security.dm
index 1823ef5174f..b233ecd78dd 100644
--- a/code/modules/cargo/packs/security.dm
+++ b/code/modules/cargo/packs/security.dm
@@ -248,6 +248,30 @@
crate_name = "disabler smg crate"
crate_type = /obj/structure/closet/crate/secure/plasma
+/datum/supply_pack/security/armory/battle_rifle
+ name = "NT BR-38 Crate"
+ desc = "An experimental energy-based ballistc battle rifle. Only available to \
+ Nanotrasen stations for security purposes. DO NOT RESELL TO OUTSIDE COMPANIES. \
+ Contains three NT BR-38 rifles and three magazines containing .38 Standard."
+ cost = CARGO_CRATE_VALUE * 100
+ contains = list(
+ /obj/item/gun/ballistic/automatic/battle_rifle = 2,
+ /obj/item/ammo_box/magazine/m38 = 4,
+ )
+ crate_name = "battle rifle crate"
+
+/datum/supply_pack/security/armory/br_mag
+ name = "NT BR-38 Magazine Crate"
+ desc = "Six .38 magazines, able to fit into the NT BR-38. Contains \
+ two standard magazines, two Hot Shot magazines and two Iceblox magazines."
+ cost = CARGO_CRATE_VALUE * 7
+ contains = list(
+ /obj/item/ammo_box/magazine/m38 = 2,
+ /obj/item/ammo_box/magazine/m38/hotshot = 2,
+ /obj/item/ammo_box/magazine/m38/iceblox =2,
+ )
+ crate_name = ".38 magazine crate"
+
/datum/supply_pack/security/armory/exileimp
name = "Exile Implants Crate"
desc = "Contains five Exile implants."
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 05a0451be9e..560a61c9683 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -354,7 +354,7 @@
/obj/item/clothing/suit/armor/balloon_vest/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
if(isitem(hitby))
var/obj/item/item_hit = hitby
- if(item_hit.sharpness)
+ if(item_hit.get_sharpness())
pop()
if(istype(hitby, /obj/projectile/bullet))
diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm
index 0c09afb90cd..8f838ea339b 100644
--- a/code/modules/fishing/fish/_fish.dm
+++ b/code/modules/fishing/fish/_fish.dm
@@ -10,6 +10,7 @@
icon = 'icons/obj/aquarium/fish.dmi'
lefthand_file = 'icons/mob/inhands/fish_lefthand.dmi'
righthand_file = 'icons/mob/inhands/fish_righthand.dmi'
+ icon_angle = 180
force = 6
throwforce = 6
throw_range = 8
diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm
index a4b1e5924f8..5fcfdd07ff3 100644
--- a/code/modules/fishing/fishing_rod.dm
+++ b/code/modules/fishing/fishing_rod.dm
@@ -5,6 +5,7 @@
desc = "You can fish with this."
icon = 'icons/obj/fishing.dmi'
icon_state = "fishing_rod"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/equipment/fishing_rod_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/fishing_rod_righthand.dmi'
inhand_icon_state = "rod"
@@ -140,7 +141,7 @@
var/list/block = list()
var/get_percent = HAS_MIND_TRAIT(user, TRAIT_EXAMINE_DEEPER_FISH)
block += span_info("You think you can cast it up to [get_cast_range()] tiles away.")
- block += get_stat_info(get_percent, difficulty_modifier, "Fishing will be", "easier", "harder", "with this fishing rod")
+ block += get_stat_info(get_percent, difficulty_modifier * 0.01, "Fishing will be", "easier", "harder", "with this fishing rod", offset = 0)
block += get_stat_info(get_percent, experience_multiplier, "You will gain experience", "faster", "slower")
block += get_stat_info(get_percent, completion_speed_mult, "You should complete the minigame", "faster", "slower")
block += get_stat_info(get_percent, bait_speed_mult, "Reeling is", "faster", "slower")
@@ -148,6 +149,7 @@
block += get_stat_info(get_percent, bounciness_mult, "This fishing rod is ", "bouncier", "less bouncy", "than a normal one", less_is_better = TRUE)
block += get_stat_info(get_percent, gravity_mult, "The lure will sink", "faster", "slower", span_info = TRUE)
+ list_clear_nulls(block)
. += examine_block(block.Join("\n"))
if(get_percent && (material_flags & MATERIAL_EFFECTS) && length(custom_materials))
@@ -172,15 +174,16 @@
. += examine_block(block.Join("\n"))
///Used in examine_more to reduce all the copypasta when getting more information about the various stats of the fishing rod.
-/obj/item/fishing_rod/proc/get_stat_info(get_percent, value, prefix, easier, harder, suffix = "with this fishing rod", span_info = FALSE, less_is_better = FALSE)
+/obj/item/fishing_rod/proc/get_stat_info(get_percent, value, prefix, easier, harder, suffix = "with this fishing rod", span_info = FALSE, less_is_better = FALSE, offset = 1)
if(value == 1)
return
- var/percent = get_percent ? "[abs(value)]% " : ""
- var/harder_easier = value > 1 ? easier : harder
+ value -= offset
+ var/percent = get_percent ? "[abs(value * 100)]% " : ""
+ var/harder_easier = value > 0 ? easier : harder
. = "[prefix] [percent][harder_easier] [suffix]."
if(span_info)
return span_info(.)
- if(less_is_better ? value < 1 : value > 1)
+ if(less_is_better ? value < 0 : value > 0)
return span_nicegreen(.)
return span_danger(.)
diff --git a/code/modules/hallucination/delusions.dm b/code/modules/hallucination/delusions.dm
index 0760d05ff46..da12f117803 100644
--- a/code/modules/hallucination/delusions.dm
+++ b/code/modules/hallucination/delusions.dm
@@ -230,6 +230,22 @@
return ..()
+/datum/hallucination/delusion/preset/seccies
+ dynamic_delusion = TRUE
+ random_hallucination_weight = 0
+ delusion_name = "Security"
+ affects_others = TRUE
+ affects_us = FALSE
+
+/datum/hallucination/delusion/preset/seccies/make_delusion_image(mob/over_who)
+ delusion_appearance = get_dynamic_human_appearance(
+ outfit_path = /datum/outfit/job/security,
+ bloody_slots = prob(5) ? ALL : NONE,
+ r_hand = prob(15) ? /obj/item/melee/baton/security/loaded : null,
+ l_hand = prob(15) ? /obj/item/melee/baton/security/loaded : null,
+ )
+ return ..()
+
/// Hallucination used by the nightmare vision goggles to turn everyone except you into mares
/datum/hallucination/delusion/preset/mare
delusion_icon_file = 'icons/obj/clothing/masks.dmi'
diff --git a/code/modules/hallucination/stray_bullet.dm b/code/modules/hallucination/stray_bullet.dm
index b670cd869e9..33462d2ab43 100644
--- a/code/modules/hallucination/stray_bullet.dm
+++ b/code/modules/hallucination/stray_bullet.dm
@@ -218,32 +218,6 @@
afflicted.adjustStaminaLoss(20)
afflicted.adjust_eye_blur(4 SECONDS)
-/obj/projectile/hallucination/taser
- name = "electrode"
- damage_type = BURN
- hal_icon_state = "spark"
- color = COLOR_YELLOW
- hal_fire_sound = 'sound/items/weapons/taser.ogg'
- hal_hitsound = 'sound/items/weapons/taserhit.ogg'
- hal_hitsound_wall = null
- hal_impact_effect = null
- hal_impact_effect_wall = null
-
-/obj/projectile/hallucination/taser/apply_effect_to_hallucinator(mob/living/afflicted)
- afflicted.Paralyze(10 SECONDS)
- afflicted.adjust_stutter(40 SECONDS)
- if(HAS_TRAIT(afflicted, TRAIT_HULK))
- afflicted.say(pick(
- ";RAAAAAAAARGH!",
- ";HNNNNNNNNNGGGGGGH!",
- ";GWAAAAAAAARRRHHH!",
- "NNNNNNNNGGGGGGGGHH!",
- ";AAAAAAARRRGH!"),
- forced = "hulk (hallucinating)",
- )
- else if(!afflicted.check_stun_immunity(CANKNOCKDOWN))
- addtimer(CALLBACK(afflicted, TYPE_PROC_REF(/mob/living/carbon, do_jitter_animation), 20), 0.5 SECONDS)
-
/obj/projectile/hallucination/disabler
name = "disabler beam"
damage_type = STAMINA
diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm
index 1b81661d245..0ffc3372042 100644
--- a/code/modules/hydroponics/hydroitemdefines.dm
+++ b/code/modules/hydroponics/hydroitemdefines.dm
@@ -424,6 +424,7 @@
icon = 'icons/obj/service/hydroponics/equipment.dmi'
icon_state = "cultivator"
inhand_icon_state = "cultivator"
+ icon_angle = -135
lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
@@ -442,6 +443,7 @@
/obj/item/cultivator/rake
name = "rake"
icon_state = "rake"
+ icon_angle = -45
w_class = WEIGHT_CLASS_NORMAL
attack_verb_continuous = list("slashes", "slices", "bashes", "claws")
attack_verb_simple = list("slash", "slice", "bash", "claw")
@@ -473,6 +475,7 @@
name = "cyborg cultivator"
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "sili_cultivator"
+ icon_angle = 0
/obj/item/hatchet
name = "hatchet"
@@ -480,6 +483,7 @@
icon = 'icons/obj/service/hydroponics/equipment.dmi'
icon_state = "hatchet"
inhand_icon_state = "hatchet"
+ icon_angle = -135
lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
@@ -523,6 +527,7 @@
name = "cyborg hatchet"
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "sili_hatchet"
+ icon_angle = 0
/obj/item/scythe
name = "scythe"
@@ -530,6 +535,7 @@
icon = 'icons/obj/service/hydroponics/equipment.dmi'
icon_state = "scythe0"
inhand_icon_state = "scythe0"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
force = 15
@@ -594,6 +600,7 @@
icon_state = "secateurs"
inhand_icon_state = null
worn_icon_state = "cutters"
+ icon_angle = -135
lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
@@ -621,6 +628,7 @@
name = "cyborg secateurs"
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "sili_secateur"
+ icon_angle = 0
/obj/item/geneshears
name = "botanogenetic plant shears"
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
index 7a26e2bd959..ac5d2a4b323 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
@@ -108,6 +108,7 @@
icon_state = "claymore_gold"
inhand_icon_state = "claymore_gold"
worn_icon_state = "claymore_gold"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
w_class = WEIGHT_CLASS_BULKY
@@ -116,9 +117,17 @@
block_sound = 'sound/items/weapons/parry.ogg'
sharpness = SHARP_EDGED
hitsound = 'sound/items/weapons/bladeslice.ogg'
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
menu_description = "A sharp claymore which provides a low chance of blocking incoming melee attacks. Can be worn on the back or belt."
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
+
+/obj/item/nullrod/claymore/Initialize(mapload)
+ . = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -3)
/obj/item/nullrod/claymore/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK)
@@ -132,6 +141,7 @@
icon_state = "cultblade"
inhand_icon_state = "cultblade"
worn_icon_state = "cultblade"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
@@ -192,6 +202,7 @@
icon_state = "e_sword_on_blue"
inhand_icon_state = "e_sword_on_blue"
worn_icon_state = "swordblue"
+ icon_angle = -45
slot_flags = ITEM_SLOT_BELT
hitsound = 'sound/items/weapons/blade1.ogg'
block_sound = 'sound/items/weapons/block_blade.ogg'
@@ -221,6 +232,7 @@
icon_state = "hfrequency0"
inhand_icon_state = "hfrequency1"
worn_icon_state = "hfrequency0"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
w_class = WEIGHT_CLASS_BULKY
@@ -231,6 +243,14 @@
attack_verb_simple = list("chop", "slice", "cut", "zandatsu")
hitsound = 'sound/items/weapons/rapierhit.ogg'
menu_description = "A sharp blade which partially penetrates armor. Very effective at butchering bodies. Can be worn on the back."
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
+
+/obj/item/nullrod/vibro/Initialize(mapload)
+ . = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -3)
/obj/item/nullrod/vibro/Initialize(mapload)
. = ..()
@@ -258,6 +278,7 @@
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "talking_sword"
inhand_icon_state = "talking_sword"
+ icon_angle = 45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
worn_icon_state = "talking_sword"
@@ -320,6 +341,7 @@
icon = 'icons/obj/weapons/staff.dmi'
icon_state = "godstaff-red"
inhand_icon_state = "godstaff-red"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
w_class = WEIGHT_CLASS_HUGE
@@ -352,6 +374,7 @@
icon_state = "sord"
inhand_icon_state = "sord"
worn_icon_state = "sord"
+ icon_angle = -35
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
force = 4.13
@@ -376,6 +399,7 @@
icon_state = "hammeron"
inhand_icon_state = "hammeron"
worn_icon_state = "hammeron"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi'
w_class = WEIGHT_CLASS_BULKY
@@ -443,13 +467,14 @@
icon = 'icons/obj/weapons/khopesh.dmi'
icon_state = "clownrender"
inhand_icon_state = "cultdagger"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
worn_icon_state = "render"
hitsound = 'sound/items/bikehorn.ogg'
sharpness = SHARP_EDGED
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
menu_description = "A sharp dagger. Fits in pockets. Can be worn on the belt. Honk."
// Pride-struck Hammer - Transfers reagents in your body to those you hit.
@@ -463,6 +488,7 @@
icon_state = "pride"
inhand_icon_state = "pride"
worn_icon_state = "pride"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi'
force = 16
@@ -495,6 +521,7 @@
icon_state = "chain"
inhand_icon_state = "chain"
worn_icon_state = "whip"
+ icon_angle = -90
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
slot_flags = ITEM_SLOT_BELT
@@ -536,6 +563,7 @@
icon = 'icons/obj/weapons/changeling_items.dmi'
icon_state = "arm_blade"
inhand_icon_state = "arm_blade"
+ icon_angle = 180
lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi'
slot_flags = null
@@ -604,6 +632,7 @@
base_icon_state = "bostaff"
inhand_icon_state = "bostaff0"
worn_icon_state = "bostaff0"
+ icon_angle = -135
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
menu_description = "A staff which provides a medium-low chance of blocking incoming melee attacks and deals less damage, unless dual-wielded. Can be worn on the back."
@@ -632,20 +661,26 @@
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "crysknife"
inhand_icon_state = "crysknife"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
w_class = WEIGHT_CLASS_HUGE
sharpness = SHARP_EDGED
slot_flags = null
hitsound = 'sound/items/weapons/bladeslice.ogg'
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
item_flags = SLOWS_WHILE_IN_HAND
menu_description = "A sharp knife. Randomly speeds or slows its user at a regular intervals. Capable of butchering bodies. Cannot be worn anywhere."
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
/obj/item/nullrod/tribal_knife/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -3)
AddComponent(/datum/component/butchering, \
speed = 5 SECONDS, \
effectiveness = 100, \
@@ -670,6 +705,7 @@
icon = 'icons/obj/weapons/spear.dmi'
icon_state = "pitchfork0"
inhand_icon_state = "pitchfork0"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
worn_icon_state = "pitchfork0"
@@ -707,6 +743,7 @@
icon_state = "hypertool"
inhand_icon_state = "hypertool"
worn_icon_state = "hypertool"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
slot_flags = ITEM_SLOT_BELT
@@ -725,6 +762,7 @@
icon = 'icons/obj/weapons/spear.dmi'
icon_state = "ratvarian_spear"
inhand_icon_state = "ratvarian_spear"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
slot_flags = ITEM_SLOT_BELT
@@ -744,6 +782,7 @@
icon = 'icons/obj/weapons/spear.dmi'
icon_state = "ratvarian_spear"
inhand_icon_state = "ratvarian_spear"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
slot_flags = ITEM_SLOT_BELT
@@ -767,6 +806,7 @@
icon_state = "nullsword"
inhand_icon_state = "nullsword"
worn_icon_state = "nullsword"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
w_class = WEIGHT_CLASS_BULKY
@@ -775,14 +815,23 @@
bare_wound_bonus = 30
slot_flags = ITEM_SLOT_BELT
block_sound = 'sound/items/weapons/parry.ogg'
- sharpness = SHARP_POINTY
+ sharpness = SHARP_EDGED
hitsound = 'sound/items/weapons/bladeslice.ogg'
- attack_verb_continuous = list("attacks", "punctures", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "puncture", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slashes", "slice", "tear", "lacerate", "rip", "dice", "cut")
menu_description = "A blade that deals variable, low amounts of damage, but does easily inflict wounds. \
The stronger your swinging arm is, the stronger the blade is, though only slightly. \
Against debilitated targets, can also deal additional sneak attack damage with a very high wound chance."
+ var/list/alt_continuous = list("stabs", "pierces", "impales", "punctures")
+ var/list/alt_simple = list("stab", "pierce", "impale", "puncture")
+
+/obj/item/nullrod/nullblade/Initialize(mapload)
+ . = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple)
+
/obj/item/nullrod/nullblade/melee_attack_chain(mob/user, atom/target, params)
//Track our actual force separately
var/old_force = force
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
index ce4a30efa92..df2e4f277b7 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
@@ -30,6 +30,7 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and
icon_state = "vorpalscythe"
inhand_icon_state = "vorpalscythe"
worn_icon_state = null
+ icon_angle = -35 // Scythes look better when slightly angled
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
diff --git a/code/modules/library/book.dm b/code/modules/library/book.dm
index 0a190b94662..3a95bf56aaa 100644
--- a/code/modules/library/book.dm
+++ b/code/modules/library/book.dm
@@ -196,7 +196,7 @@
if(!user.combat_mode)
return FALSE
//special check for wirecutter's because they don't have a sharp edge
- if((carving_item.sharpness & SHARP_EDGED) || (carving_item.tool_behaviour == TOOL_WIRECUTTER))
+ if((carving_item.get_sharpness() & SHARP_EDGED) || (carving_item.tool_behaviour == TOOL_WIRECUTTER))
balloon_alert(user, "carving out...")
if(!do_after(user, 3 SECONDS, target = src))
balloon_alert(user, "interrupted!")
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm b/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm
index d6b2d9ffaa7..c531ae75e0d 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm
@@ -4,6 +4,7 @@
icon_state = "wrench_caravan"
desc = "A prototype of a new wrench design, allegedly the red color scheme makes it go faster."
name = "experimental wrench"
+ icon_angle = -90
toolspeed = 0.3
/obj/item/screwdriver/caravan
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm b/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm
index 2b221664148..440f7ad0578 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm
@@ -125,6 +125,7 @@
/obj/lightning_thrower/Destroy()
. = ..()
+ clear_signals()
signal_turfs = null
STOP_PROCESSING(SSprocessing, src)
@@ -132,6 +133,8 @@
var/list/dirs = throw_diagonals ? GLOB.diagonals : GLOB.cardinals
throw_diagonals = !throw_diagonals
playsound(src, 'sound/effects/magic/lightningbolt.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, ignore_walls = FALSE)
+ if(length(signal_turfs))
+ clear_signals()
for(var/direction in dirs)
var/victim_turf = get_step(src, direction)
if(isclosedturf(victim_turf))
@@ -143,8 +146,7 @@
shock_victim(null, victim)
addtimer(CALLBACK(src, PROC_REF(clear_signals)), shock_duration)
-/obj/lightning_thrower/proc/clear_signals(datum/source)
- SIGNAL_HANDLER
+/obj/lightning_thrower/proc/clear_signals()
for(var/turf in signal_turfs)
UnregisterSignal(turf, COMSIG_ATOM_ENTERED)
signal_turfs -= turf
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
index 88b9e9f9503..d348ad6ac19 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
@@ -100,7 +100,7 @@
stored_organ = pick_weight(allowed_organs)
/obj/structure/meateor_fluff/flesh_pod/attackby(obj/item/attacking_item, mob/user, params)
- if (attacking_item.sharpness & SHARP_EDGED)
+ if (attacking_item.get_sharpness() & SHARP_EDGED)
cut_open(user)
return
return ..()
diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm
index c5bdfa9daa2..09982715f23 100644
--- a/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher.dm
@@ -6,15 +6,16 @@
* a good tradeoff and a decent playstyle.
*/
/obj/item/kinetic_crusher
+ name = "proto-kinetic crusher"
+ desc = "An early design of the proto-kinetic accelerator, it is little more than a combination of various mining tools cobbled together, \
+ forming a high-tech club. While it is an effective mining tool, it did little to aid any but the most skilled and/or \
+ suicidal miners against local fauna."
icon = 'icons/obj/mining.dmi'
icon_state = "crusher"
inhand_icon_state = "crusher0"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/hammers_righthand.dmi'
- name = "proto-kinetic crusher"
- desc = "An early design of the proto-kinetic accelerator, it is little more than a combination of various mining tools cobbled together, \
- forming a high-tech club. While it is an effective mining tool, it did little to aid any but the most skilled and/or \
- suicidal miners against local fauna."
resistance_flags = FIRE_PROOF
force = 0 //You can't hit stuff unless wielded
w_class = WEIGHT_CLASS_BULKY
@@ -51,6 +52,17 @@
)
//technically it's huge and bulky, but this provides an incentive to use it
AddComponent(/datum/component/two_handed, force_unwielded=0, force_wielded=20)
+ register_context()
+
+/obj/item/kinetic_crusher/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = ..()
+ if(!held_item)
+ context[SCREENTIP_CONTEXT_RMB] = "Detach trophy"
+ return CONTEXTUAL_SCREENTIP_SET
+
+ if(istype(held_item) && held_item.tool_behaviour == TOOL_CROWBAR)
+ context[SCREENTIP_CONTEXT_LMB] = "Detach all trophies"
+ return CONTEXTUAL_SCREENTIP_SET
/obj/item/kinetic_crusher/Destroy()
QDEL_LIST(trophies)
@@ -85,6 +97,46 @@
crusher_trophy.remove_from(src, user)
return ITEM_INTERACT_SUCCESS
+// adapted from kinetic accelerator attack_hand_secodary
+/obj/item/kinetic_crusher/attack_hand_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+ if(!LAZYLEN(trophies))
+ return SECONDARY_ATTACK_CONTINUE_CHAIN
+
+ var/list/display_names = list()
+ var/list/items = list()
+ for(var/trophies_length in 1 to length(trophies))
+ var/obj/item/crusher_trophy/trophy = trophies[trophies_length]
+ display_names[trophy.name] = REF(trophy)
+ var/image/item_image = image(icon = trophy.icon, icon_state = trophy.icon_state)
+ if(length(trophy.overlays))
+ item_image.copy_overlays(trophy)
+ items["[trophy.name]"] = item_image
+
+ var/pick = show_radial_menu(user, src, items, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE, tooltips = TRUE)
+ if(!pick)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+ var/trophy_reference = display_names[pick]
+ var/obj/item/crusher_trophy/trophy_to_remove = locate(trophy_reference) in trophies
+ if(!istype(trophy_to_remove))
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ trophy_to_remove.remove_from(src, user)
+ if(!user.put_in_hands(trophy_to_remove))
+ trophy_to_remove.forceMove(drop_location())
+
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+/obj/item/kinetic_crusher/proc/check_menu(mob/living/carbon/human/user)
+ if(!istype(user))
+ return FALSE
+ if(user.incapacitated)
+ return FALSE
+ return TRUE
+
/obj/item/kinetic_crusher/pre_attack(atom/A, mob/living/user, params)
. = ..()
if(.)
diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm
index 06f8ec1828d..23d59fbfb71 100644
--- a/code/modules/mining/equipment/mining_tools.dm
+++ b/code/modules/mining/equipment/mining_tools.dm
@@ -4,6 +4,7 @@
icon = 'icons/obj/mining.dmi'
icon_state = "pickaxe"
inhand_icon_state = "pickaxe"
+ icon_angle = -45
obj_flags = CONDUCTS_ELECTRICITY
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK
force = 15
@@ -65,6 +66,7 @@
name = "mining drill"
icon_state = "handdrill"
inhand_icon_state = "handdrill"
+ icon_angle = 0
slot_flags = ITEM_SLOT_BELT
toolspeed = 0.6 //available from roundstart, faster than a pickaxe.
usesound = 'sound/items/weapons/drill.ogg'
@@ -121,6 +123,7 @@
icon = 'icons/obj/mining.dmi'
icon_state = "shovel"
inhand_icon_state = "shovel"
+ icon_angle = 135
lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
@@ -157,6 +160,7 @@
desc = "A small tool for digging and moving dirt."
icon_state = "spade"
inhand_icon_state = "spade"
+ icon_angle = -135
lefthand_file = 'icons/mob/inhands/equipment/hydroponics_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/hydroponics_righthand.dmi'
force = 5
@@ -167,6 +171,7 @@
name = "cyborg spade"
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "sili_shovel"
+ icon_angle = 0
toolspeed = 0.6
worn_icon_state = null
@@ -212,6 +217,7 @@
icon = 'icons/obj/mining.dmi'
icon_state = "trench_tool"
inhand_icon_state = "trench_tool"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
@@ -301,6 +307,7 @@
desc = "A gigantic wrench made illegal because of its many incidents involving this tool."
icon_state = "giant_wrench"
icon = 'icons/obj/weapons/giant_wrench.dmi'
+ icon_angle = 0
inhand_icon_state = null
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
diff --git a/code/modules/mining/lavaland/megafauna_loot.dm b/code/modules/mining/lavaland/megafauna_loot.dm
index 3b33119aa24..cfe087a8ab3 100644
--- a/code/modules/mining/lavaland/megafauna_loot.dm
+++ b/code/modules/mining/lavaland/megafauna_loot.dm
@@ -46,6 +46,7 @@
desc = "The strange technology of this large club allows various nigh-magical teleportation feats. It used to beat you, but now you can set the beat."
icon_state = "hierophant_club_ready_beacon"
inhand_icon_state = "hierophant_club_ready_beacon"
+ icon_angle = -135
icon = 'icons/obj/mining_zones/artefacts.dmi'
lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
@@ -649,6 +650,7 @@
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "spectral"
inhand_icon_state = "spectral"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
@@ -658,17 +660,22 @@
throwforce = 1
hitsound = 'sound/effects/ghost2.ogg'
block_sound = 'sound/items/weapons/parry.ogg'
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "rends")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "rend")
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/summon_cooldown = 0
var/list/mob/dead/observer/spirits
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
/obj/item/melee/ghost_sword/Initialize(mapload)
. = ..()
spirits = list()
START_PROCESSING(SSobj, src)
SSpoints_of_interest.make_point_of_interest(src)
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple)
AddComponent(\
/datum/component/butchering, \
speed = 15 SECONDS, \
@@ -784,6 +791,7 @@
desc = "The ability to fill the emergency shuttle with lava. What more could you want out of life?"
icon_state = "lavastaff"
inhand_icon_state = "lavastaff"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
icon = 'icons/obj/weapons/guns/magic.dmi'
@@ -985,6 +993,7 @@
desc = "An ancient staff retrieved from the remains of Legion. The wind stirs as you move it."
icon_state = "staffofstorms"
inhand_icon_state = "staffofstorms"
+ icon_angle = -45
icon = 'icons/obj/weapons/guns/magic.dmi'
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
diff --git a/code/modules/mining/lavaland/tendril_loot.dm b/code/modules/mining/lavaland/tendril_loot.dm
index bb36b6d9102..31c4c1177ac 100644
--- a/code/modules/mining/lavaland/tendril_loot.dm
+++ b/code/modules/mining/lavaland/tendril_loot.dm
@@ -73,6 +73,7 @@
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
icon_state = "asclepius_dormant"
inhand_icon_state = "asclepius_dormant"
+ icon_angle = -45
var/activated = FALSE
/obj/item/rod_of_asclepius/Initialize(mapload)
@@ -990,6 +991,7 @@
Even with the weapon destroyed, all the pieces containing the creature have coagulated back together to find a new host."
icon = 'icons/obj/mining_zones/artefacts.dmi'
icon_state = "cursed_katana"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
force = 15
@@ -998,8 +1000,8 @@
block_sound = 'sound/items/weapons/parry.ogg'
sharpness = SHARP_EDGED
w_class = WEIGHT_CLASS_HUGE
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
hitsound = 'sound/items/weapons/bladeslice.ogg'
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | FREEZE_PROOF
var/shattered = FALSE
@@ -1012,9 +1014,14 @@
ATTACK_CLOAK = list(COMBO_STEPS = list(LEFT_ATTACK, RIGHT_ATTACK, LEFT_ATTACK, RIGHT_ATTACK), COMBO_PROC = PROC_REF(cloak)),
ATTACK_SHATTER = list(COMBO_STEPS = list(RIGHT_ATTACK, LEFT_ATTACK, RIGHT_ATTACK, LEFT_ATTACK), COMBO_PROC = PROC_REF(shatter)),
)
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
/obj/item/cursed_katana/Initialize(mapload)
. = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple)
AddComponent( \
/datum/component/combo_attacks, \
combos = combo_list, \
@@ -1078,6 +1085,7 @@
user.visible_message(span_warning("[user] does a wide slice!"),
span_notice("You do a wide slice!"))
playsound(src, 'sound/items/weapons/bladeslice.ogg', 50, TRUE)
+ user.do_item_attack_animation(target, used_item = src, animation_type = ATTACK_ANIMATION_SLASH)
var/turf/user_turf = get_turf(user)
var/dir_to_target = get_dir(user_turf, get_turf(target))
var/static/list/cursed_katana_slice_angles = list(0, -45, 45, -90, 90) //so that the animation animates towards the target clicked and not towards a side target
@@ -1117,6 +1125,7 @@
user.visible_message(span_warning("[user] cuts [target]'s tendons!"),
span_notice("You tendon cut [target]!"))
to_chat(target, span_userdanger("Your tendons have been cut by [user]!"))
+ user.do_item_attack_animation(target, used_item = src, animation_type = ATTACK_ANIMATION_SLASH)
target.apply_damage(damage = 15, sharpness = SHARP_EDGED, wound_bonus = 15)
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
playsound(src, 'sound/items/weapons/rapierhit.ogg', 50, TRUE)
diff --git a/code/modules/mob/dead/observer/orbit.dm b/code/modules/mob/dead/observer/orbit.dm
index d2823eae2b5..5e77627e00d 100644
--- a/code/modules/mob/dead/observer/orbit.dm
+++ b/code/modules/mob/dead/observer/orbit.dm
@@ -104,7 +104,7 @@ GLOBAL_DATUM_INIT(orbit_menu, /datum/orbit_menu, new)
if(isliving(mob_poi))
serialized += get_living_data(mob_poi)
- var/list/antag_data = get_antag_data(mob_poi.mind)
+ var/list/antag_data = get_antag_data(mob_poi.mind, user?.client?.holder)
if(length(antag_data))
serialized += antag_data
antagonists += list(serialized)
@@ -151,11 +151,11 @@ GLOBAL_DATUM_INIT(orbit_menu, /datum/orbit_menu, new)
/// Helper function to get threat type, group, overrides for job and icon
-/datum/orbit_menu/proc/get_antag_data(datum/mind/poi_mind) as /list
+/datum/orbit_menu/proc/get_antag_data(datum/mind/poi_mind, is_admin) as /list
var/list/serialized = list()
for(var/datum/antagonist/antag as anything in poi_mind.antag_datums)
- if(!antag.show_to_ghosts)
+ if(!antag.show_to_ghosts && !is_admin)
continue
serialized["antag"] = antag.name
diff --git a/code/modules/mob/living/basic/drone/drone_tools.dm b/code/modules/mob/living/basic/drone/drone_tools.dm
index 8408738bf6a..dfb05a8d3a9 100644
--- a/code/modules/mob/living/basic/drone/drone_tools.dm
+++ b/code/modules/mob/living/basic/drone/drone_tools.dm
@@ -34,6 +34,7 @@
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "toolkit_engiborg_crowbar"
inhand_icon_state = "crowbar"
+ icon_angle = 0
item_flags = NO_MAT_REDEMPTION
/obj/item/screwdriver/drone
@@ -61,6 +62,7 @@
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "toolkit_engiborg_wrench"
inhand_icon_state = "wrench"
+ icon_angle = 0
item_flags = NO_MAT_REDEMPTION
/obj/item/weldingtool/drone
@@ -84,6 +86,7 @@
desc = "A multitool built into your chassis."
icon = 'icons/obj/items_cyborg.dmi'
icon_state = "toolkit_engiborg_multitool"
+ icon_angle = 0
item_flags = NO_MAT_REDEMPTION
toolspeed = 0.5
diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm
index 014cfb626be..9b1916c9ac0 100644
--- a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm
+++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm
@@ -21,7 +21,7 @@
icon_state = "brimdust"
icon = 'icons/obj/mining.dmi'
plane = GAME_PLANE
- layer = GAME_CLEAN_LAYER
+ layer = CLEANABLE_OBJECT_LAYER
mergeable_decal = FALSE
/obj/effect/decal/cleanable/brimdust/Initialize(mapload)
diff --git a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm
index eae137787ed..07ad70a29e3 100644
--- a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm
+++ b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm
@@ -179,10 +179,12 @@
if(SEND_SIGNAL(target, COMSIG_RAT_INTERACT, src) & COMPONENT_RAT_INTERACTED)
return FALSE
- if(isnull(mind) || !combat_mode)
+ if(isnull(mind) || combat_mode)
return TRUE
- poison_target(target)
+ if(poison_target(target))
+ return FALSE
+
return TRUE
/// Checks if we are allowed to attack this mob. Will return TRUE if we are potentially allowed to attack, but if we end up in a case where we should NOT attack, return FALSE.
@@ -204,10 +206,17 @@
return TRUE
-/// Attempts to add rat spit to a target, effectively poisoning it to whoever eats it. Yuckers.
+/**
+ * Attempts to add rat spit to a target, effectively poisoning it to whoever eats it. Yuckers.
+ * Returns TRUE if the target is valid for adding rat spit
+ * Returns FALSE if the target is invalid for adding rat spit
+ * Arguments
+ *
+ * * atom/lean_target - the target we try to add the spit to
+ */
/mob/living/basic/regal_rat/proc/poison_target(atom/target)
if(isnull(target.reagents) || !target.is_injectable(src, allowmobs = TRUE))
- return
+ return FALSE
visible_message(
span_warning("[src] starts licking [target] passionately!"),
@@ -216,10 +225,11 @@
)
if (!do_after(src, 2 SECONDS, target, interaction_key = REGALRAT_INTERACTION))
- return
+ return TRUE // don't return false here because they tried to lick and the do_after was interrupted, otherwise cancelling the do_after will make them hit the target.
target.reagents.add_reagent(/datum/reagent/rat_spit, rand(1,3), no_react = TRUE)
balloon_alert(src, "licked")
+ return TRUE
/**
* Conditionally "eat" cheese object and heal, if injured.
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index dedecacc6ab..b32517875e4 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -471,9 +471,9 @@
/obj/item/organ/brain/felinid //A bit smaller than average
brain_size = 0.8
-/obj/item/organ/brain/lizard //A bit smaller than average
+/obj/item/organ/brain/lizard
name = "lizard brain"
- desc = "This juicy piece of meat has a oversized brain stem and cerebellum, with not much of a limbic system to speak of at all. You would expect it's owner to be pretty cold blooded."
+ desc = "This juicy piece of meat has a oversized brain stem and cerebellum, with not much of a limbic system to speak of at all. You would expect its owner to be pretty cold blooded."
organ_traits = list(TRAIT_TACKLING_TAILED_DEFENDER)
/obj/item/organ/brain/abductor
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 6e0f6716778..66b4ad2cfd0 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -77,7 +77,7 @@
if(!hurt)
return
- if(victim.check_block(src, 0, "[name]", LEAP_ATTACK))
+ if(. == SUCCESSFUL_BLOCK || victim.check_block(src, 0, "[name]", LEAP_ATTACK))
blocked = TRUE
take_bodypart_damage(10 + 5 * extra_speed, check_armor = TRUE, wound_bonus = extra_speed * 5)
@@ -120,7 +120,7 @@
return
throw_mode = THROW_MODE_DISABLED
if(hud_used)
- hud_used.throw_icon.icon_state = "act_throw_off"
+ hud_used.throw_icon.icon_state = "act_throw"
SEND_SIGNAL(src, COMSIG_LIVING_THROW_MODE_TOGGLE, throw_mode)
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 2c5d2df0517..ee06686faaf 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -84,19 +84,28 @@
return TRUE
return ..()
-/mob/living/carbon/send_item_attack_message(obj/item/I, mob/living/user, hit_area, def_zone)
- if(!I.force && !length(I.attack_verb_simple) && !length(I.attack_verb_continuous))
+/mob/living/carbon/send_item_attack_message(obj/item/weapon, mob/living/user, hit_area, def_zone)
+ if(!weapon.force && !length(weapon.attack_verb_simple) && !length(weapon.attack_verb_continuous))
return
var/obj/item/bodypart/hit_bodypart = get_bodypart(def_zone)
if(isnull(hit_bodypart)) // ??
return ..()
- var/message_verb_continuous = length(I.attack_verb_continuous) ? "[pick(I.attack_verb_continuous)]" : "attacks"
- var/message_verb_simple = length(I.attack_verb_simple) ? "[pick(I.attack_verb_simple)]" : "attack"
+ // Sanity in case one is null for some reason
+ var/picked_index = rand(max(length(weapon.attack_verb_simple), length(weapon.attack_verb_continuous)))
+
+ var/message_verb_continuous = "attacks"
+ var/message_verb_simple = "attack"
+ var/message_hit_area = get_hit_area_message(hit_area)
+ // Sanity in case one is... longer than the other?
+ if (picked_index && length(weapon.attack_verb_continuous) >= picked_index)
+ message_verb_continuous = weapon.attack_verb_continuous[picked_index]
+ if (picked_index && length(weapon.attack_verb_simple) >= picked_index)
+ message_verb_simple = weapon.attack_verb_simple[picked_index]
var/extra_wound_details = ""
- if(I.damtype == BRUTE && hit_bodypart.can_dismember())
+ if(weapon.damtype == BRUTE && hit_bodypart.can_dismember())
var/mangled_state = hit_bodypart.get_mangled_state()
@@ -111,24 +120,21 @@
var/dismemberable = ((hit_bodypart.dismemberable_by_wound()) || hit_bodypart.dismemberable_by_total_damage())
if (dismemberable)
extra_wound_details = ", threatening to sever it entirely"
- else if((has_interior && (has_exterior && exterior_ready_to_dismember) && I.get_sharpness()))
+ else if((has_interior && (has_exterior && exterior_ready_to_dismember) && weapon.get_sharpness()))
var/bone_text = hit_bodypart.get_internal_description()
- extra_wound_details = ", [I.get_sharpness() == SHARP_EDGED ? "slicing" : "piercing"] through to the [bone_text]"
- else if(has_exterior && ((has_interior && interior_ready_to_dismember) && I.get_sharpness()))
+ extra_wound_details = ", [weapon.get_sharpness() == SHARP_EDGED ? "slicing" : "piercing"] through to the [bone_text]"
+ else if(has_exterior && ((has_interior && interior_ready_to_dismember) && weapon.get_sharpness()))
var/tissue_text = hit_bodypart.get_external_description()
- extra_wound_details = ", [I.get_sharpness() == SHARP_EDGED ? "slicing" : "piercing"] at the remaining [tissue_text]"
-
- var/message_hit_area = ""
- if(hit_area)
- message_hit_area = " in the [hit_area]"
- var/attack_message_spectator = "[src] [message_verb_continuous][message_hit_area] with [I][extra_wound_details]!"
- var/attack_message_victim = "You're [message_verb_continuous][message_hit_area] with [I][extra_wound_details]!"
- var/attack_message_attacker = "You [message_verb_simple] [src][message_hit_area] with [I][extra_wound_details]!"
+ extra_wound_details = ", [weapon.get_sharpness() == SHARP_EDGED ? "slicing" : "piercing"] at the remaining [tissue_text]"
+
+ var/attack_message_spectator = "[src] [message_verb_continuous][message_hit_area] with [weapon][extra_wound_details]!"
+ var/attack_message_victim = "You're [message_verb_continuous][message_hit_area] with [weapon][extra_wound_details]!"
+ var/attack_message_attacker = "You [message_verb_simple] [src][message_hit_area] with [weapon][extra_wound_details]!"
if(user in viewers(src, null))
- attack_message_spectator = "[user] [message_verb_continuous] [src][message_hit_area] with [I][extra_wound_details]!"
- attack_message_victim = "[user] [message_verb_continuous] you[message_hit_area] with [I][extra_wound_details]!"
+ attack_message_spectator = "[user] [message_verb_continuous] [src][message_hit_area] with [weapon][extra_wound_details]!"
+ attack_message_victim = "[user] [message_verb_continuous] you[message_hit_area] with [weapon][extra_wound_details]!"
if(user == src)
- attack_message_victim = "You [message_verb_simple] yourself[message_hit_area] with [I][extra_wound_details]!"
+ attack_message_victim = "You [message_verb_simple] yourself[message_hit_area] with [weapon][extra_wound_details]!"
visible_message(span_danger("[attack_message_spectator]"),\
span_userdanger("[attack_message_victim]"), null, COMBAT_MESSAGE_RANGE, user)
if(user != src)
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index c13ac14b100..56c687df7bb 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -11,6 +11,7 @@
usable_hands = 0 //Populated on init through list/bodyparts
mobility_flags = MOBILITY_FLAGS_CARBON_DEFAULT
blocks_emissive = EMISSIVE_BLOCK_NONE
+ mouse_drop_zone = TRUE
// STOP_OVERLAY_UPDATE_BODY_PARTS is removed after we call update_body_parts() during init.
living_flags = ALWAYS_DEATHGASP|STOP_OVERLAY_UPDATE_BODY_PARTS
///List of [/obj/item/organ]s in the mob. They don't go in the contents for some reason I don't want to know.
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 8cd4881999f..b66032462f2 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -24,7 +24,7 @@
. = ..()
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_FACE_ACT, PROC_REF(clean_face))
- AddComponent(/datum/component/personal_crafting)
+ AddComponent(/datum/component/personal_crafting, ui_human_crafting)
AddElement(/datum/element/footstep, FOOTSTEP_MOB_HUMAN, 1, -6)
AddComponent(/datum/component/bloodysoles/feet, FOOTPRINT_SPRITE_SHOES)
AddElement(/datum/element/ridable, /datum/component/riding/creature/human)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index e6c7f9edd9b..73f63b8bfe1 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -85,8 +85,8 @@
/mob/living/carbon/human/check_block(atom/hit_by, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0, damage_type = BRUTE)
. = ..()
- if(.)
- return TRUE
+ if(. == SUCCESSFUL_BLOCK)
+ return SUCCESSFUL_BLOCK
var/block_chance_modifier = round(damage / -3)
for(var/obj/item/worn_thing in get_equipped_items(INCLUDE_HELD))
@@ -100,9 +100,9 @@
var/final_block_chance = worn_thing.block_chance - (clamp((armour_penetration - worn_thing.armour_penetration) / 2, 0, 100)) + block_chance_modifier
if(worn_thing.hit_reaction(src, hit_by, attack_text, final_block_chance, damage, attack_type, damage_type))
- return TRUE
+ return SUCCESSFUL_BLOCK
- return FALSE
+ return FAILED_BLOCK
/mob/living/carbon/human/grippedby(mob/living/carbon/user, instant = FALSE)
if(w_uniform)
@@ -191,7 +191,7 @@
var/damage = HAS_TRAIT(user, TRAIT_PERFECT_ATTACKER) ? monkey_mouth.unarmed_damage_high : rand(monkey_mouth.unarmed_damage_low, monkey_mouth.unarmed_damage_high)
if(!damage)
return FALSE
- if(check_block(user, damage, "the [user.name]"))
+ if(check_block(user, damage, "the [user.name]", attack_type = UNARMED_ATTACK))
return FALSE
apply_damage(damage, BRUTE, affecting, run_armor_check(affecting, MELEE))
return TRUE
@@ -248,9 +248,6 @@
return TRUE
apply_damage(damage, BRUTE, affecting, armor_block)
-
-
-
/mob/living/carbon/human/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers)
. = ..()
if(!.)
@@ -258,7 +255,7 @@
var/damage = rand(L.melee_damage_lower, L.melee_damage_upper)
if(!damage)
return
- if(check_block(L, damage, "the [L.name]"))
+ if(check_block(L, damage, "the [L.name]", attack_type = UNARMED_ATTACK))
return FALSE
if(stat != DEAD)
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index 81355918311..6b4af60788b 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -733,6 +733,9 @@
if(!emote_is_valid(user, our_message))
return FALSE
+ if(type_override)
+ emote_type = type_override
+
if(!params)
var/user_emote_type = get_custom_emote_type_from_user()
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index c27980f68b1..bcdca5dbb59 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -128,6 +128,7 @@
bare_wound_bonus = proj.bare_wound_bonus,
sharpness = proj.sharpness,
attack_direction = get_dir(proj.starting, src),
+ attacking_item = proj,
)
apply_effects(
@@ -206,10 +207,11 @@
/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(!isitem(AM))
// Filled with made up numbers for non-items.
- if(check_block(AM, 30, "\the [AM.name]", THROWN_PROJECTILE_ATTACK, 0, BRUTE))
+ if(check_block(AM, 30, "\the [AM.name]", THROWN_PROJECTILE_ATTACK, 0, BRUTE) & SUCCESSFUL_BLOCK)
hitpush = FALSE
skipcatch = TRUE
blocked = TRUE
+ return SUCCESSFUL_BLOCK
else
playsound(loc, 'sound/items/weapons/genhit.ogg', 50, TRUE, -1) //Item sounds are handled in the item itself
if(!isvendor(AM) && !iscarbon(AM)) //Vendors have special interactions, while carbon mobs already generate visible messages!
@@ -234,7 +236,7 @@
hitpush = FALSE
if(blocked)
- return TRUE
+ return SUCCESSFUL_BLOCK
var/mob/thrown_by = thrown_item.thrownby?.resolve()
if(thrown_by)
@@ -296,7 +298,7 @@
return FALSE
if(SEND_SIGNAL(src, COMSIG_LIVING_GRAB, target) & (COMPONENT_CANCEL_ATTACK_CHAIN|COMPONENT_SKIP_ATTACK))
return FALSE
- if(target.check_block(src, 0, "[src]'s grab"))
+ if(target.check_block(src, 0, "[src]'s grab", UNARMED_ATTACK))
return FALSE
target.grabbedby(src)
return TRUE
@@ -403,7 +405,7 @@
return FALSE
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
- if(check_block(user, damage, "[user]'s [user.attack_verb_simple]", MELEE_ATTACK/*or UNARMED_ATTACK?*/, user.armour_penetration, user.melee_damage_type))
+ if(check_block(user, damage, "[user]'s [user.attack_verb_simple]", UNARMED_ATTACK, user.armour_penetration, user.melee_damage_type))
return FALSE
if(user.attack_sound)
@@ -518,7 +520,7 @@
/mob/living/attack_alien(mob/living/carbon/alien/adult/user, list/modifiers)
SEND_SIGNAL(src, COMSIG_MOB_ATTACK_ALIEN, user, modifiers)
if(LAZYACCESS(modifiers, RIGHT_CLICK))
- if(check_block(user, 0, "[user]'s tackle", MELEE_ATTACK, 0, BRUTE))
+ if(check_block(user, 0, "[user]'s tackle", UNARMED_ATTACK, 0, BRUTE))
return FALSE
user.do_attack_animation(src, ATTACK_EFFECT_DISARM)
return TRUE
@@ -527,7 +529,7 @@
if(HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, span_warning("You don't want to hurt anyone!"))
return FALSE
- if(check_block(user, user.melee_damage_upper, "[user]'s slash", MELEE_ATTACK, 0, BRUTE))
+ if(check_block(user, user.melee_damage_upper, "[user]'s slash", UNARMED_ATTACK, 0, BRUTE))
return FALSE
user.do_attack_animation(src)
return TRUE
@@ -811,6 +813,6 @@
/mob/living/proc/check_block(atom/hit_by, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0, damage_type = BRUTE)
if(SEND_SIGNAL(src, COMSIG_LIVING_CHECK_BLOCK, hit_by, damage, attack_text, attack_type, armour_penetration, damage_type) & SUCCESSFUL_BLOCK)
- return TRUE
+ return SUCCESSFUL_BLOCK
- return FALSE
+ return FAILED_BLOCK
diff --git a/code/modules/mob/living/silicon/robot/robot_defines.dm b/code/modules/mob/living/silicon/robot/robot_defines.dm
index 83f14301519..daf66bd8257 100644
--- a/code/modules/mob/living/silicon/robot/robot_defines.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defines.dm
@@ -16,6 +16,7 @@
has_limbs = TRUE
hud_type = /datum/hud/robot
unique_name = TRUE
+ mouse_drop_zone = TRUE
///Represents the cyborg's model (engineering, medical, etc.)
var/obj/item/robot_model/model = null
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index 404b0324f71..7a78cb4be05 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -82,13 +82,13 @@
/mob/living/silicon/check_block(atom/hitby, damage, attack_text, attack_type, armour_penetration, damage_type, attack_flag)
. = ..()
- if(.)
- return TRUE
+ if(. == SUCCESSFUL_BLOCK)
+ return SUCCESSFUL_BLOCK
if(damage_type == BRUTE && attack_type == UNARMED_ATTACK && attack_flag == MELEE && damage <= 10)
playsound(src, 'sound/effects/bang.ogg', 10, TRUE)
visible_message(span_danger("[attack_text] doesn't leave a dent on [src]!"), vision_distance = COMBAT_MESSAGE_RANGE)
- return TRUE
- return FALSE
+ return SUCCESSFUL_BLOCK
+ return FAILED_BLOCK
/mob/living/silicon/attack_drone(mob/living/basic/drone/user)
if(user.combat_mode)
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index f19a3991ca3..743bdad8c45 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -281,6 +281,9 @@
if (SEND_SIGNAL(src, COMSIG_MOB_ATTEMPT_HALT_SPACEMOVE, movement_dir, continuous_move, backup) & COMPONENT_PREVENT_SPACEMOVE_HALT)
return FALSE
+ if (drift_handler?.attempt_halt(movement_dir, continuous_move, backup))
+ return FALSE
+
if(continuous_move || !istype(backup) || !movement_dir || backup.anchored)
return TRUE
@@ -300,8 +303,9 @@
/**
* Finds a target near a mob that is viable for pushing off when moving.
* Takes the intended movement direction as input, alongside if the context is checking if we're allowed to continue drifting
+ * If include_floors is TRUE, includes floors *with gravity*
*/
-/mob/get_spacemove_backup(moving_direction, continuous_move)
+/mob/get_spacemove_backup(moving_direction, continuous_move, include_floors = FALSE)
var/atom/secondary_backup
var/list/priority_dirs = (moving_direction in GLOB.cardinals) ? GLOB.cardinals : GLOB.diagonals
for(var/atom/pushover as anything in range(1, get_turf(src)))
@@ -309,13 +313,15 @@
continue
if(isarea(pushover))
continue
+ var/is_priority = pushover.loc == loc || (get_dir(src, pushover) in priority_dirs)
if(isturf(pushover))
var/turf/turf = pushover
if(isspaceturf(turf))
continue
if(!turf.density && !mob_negates_gravity())
- continue
- if (get_dir(src, pushover) in priority_dirs)
+ if (!include_floors || !turf.has_gravity())
+ continue
+ if (is_priority)
return pushover
secondary_backup = pushover
continue
@@ -343,13 +349,13 @@
if(moving_direction == get_dir(src, pushover)) // Can't push "off" of something that you're walking into
continue
if(rebound.anchored)
- if (get_dir(src, rebound) in priority_dirs)
+ if (is_priority)
return rebound
secondary_backup = rebound
continue
if(pulling == rebound)
continue
- if (get_dir(src, rebound) in priority_dirs)
+ if (is_priority)
return rebound
secondary_backup = rebound
return secondary_backup
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
index 8d5c5c20954..3b1f2a648bc 100644
--- a/code/modules/mod/mod_control.dm
+++ b/code/modules/mod/mod_control.dm
@@ -771,3 +771,10 @@
mod_link.end_call()
mod_link.frequency = null
+
+/obj/item/mod/control/proc/get_visor_overlay(mutable_appearance/standing)
+ var/list/overrides = list()
+ SEND_SIGNAL(src, COMSIG_MOD_GET_VISOR_OVERLAY, standing, overrides)
+ if (length(overrides))
+ return overrides[1]
+ return mutable_appearance(worn_icon, "[skin]-helmet-visor", layer = standing.layer + 0.1)
diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm
index 3eef7d47f15..ba5e1711fcb 100644
--- a/code/modules/mod/modules/modules_antag.dm
+++ b/code/modules/mod/modules/modules_antag.dm
@@ -102,6 +102,17 @@
else
REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, REF(src))
+/obj/item/mod/module/armor_booster/on_install()
+ RegisterSignal(mod, COMSIG_MOD_GET_VISOR_OVERLAY, PROC_REF(on_visor_overlay))
+
+/obj/item/mod/module/armor_booster/on_uninstall(deleting)
+ UnregisterSignal(mod, COMSIG_MOD_GET_VISOR_OVERLAY)
+
+/obj/item/mod/module/armor_booster/proc/on_visor_overlay(datum/source, mutable_appearance/standing, list/overrides)
+ SIGNAL_HANDLER
+ if (active)
+ overrides += mutable_appearance(overlay_icon_file, "module_armorbooster_visor-[mod.skin]", layer = standing.layer + 0.1)
+
///Energy Shield - Gives you a rechargeable energy shield that nullifies attacks.
/obj/item/mod/module/energy_shield
name = "MOD energy shield module"
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
index 3f4dfe405f7..a766e3f6e4d 100644
--- a/code/modules/mod/modules/modules_general.dm
+++ b/code/modules/mod/modules/modules_general.dm
@@ -664,13 +664,15 @@
complexity = 1
idle_power_cost = DEFAULT_CHARGE_DRAIN * 0.3
incompatible_modules = list(/obj/item/mod/module/plasma_stabilizer)
- overlay_state_inactive = "module_plasma"
required_slots = list(ITEM_SLOT_HEAD)
-/obj/item/mod/module/plasma_stabilizer/generate_worn_overlay()
- if(locate(/obj/item/mod/module/infiltrator) in mod.modules)
+/obj/item/mod/module/plasma_stabilizer/generate_worn_overlay(mutable_appearance/standing)
+ if (!active)
return list()
- return ..()
+ var/mutable_appearance/visor_overlay = mod.get_visor_overlay(standing)
+ visor_overlay.appearance_flags |= RESET_COLOR
+ visor_overlay.color = COLOR_VOID_PURPLE
+ return list(visor_overlay)
/obj/item/mod/module/plasma_stabilizer/on_equip()
ADD_TRAIT(mod.wearer, TRAIT_HEAD_ATMOS_SEALED, REF(src))
diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm
index 48089b0125c..7344993802c 100644
--- a/code/modules/mod/modules/modules_maint.dm
+++ b/code/modules/mod/modules/modules_maint.dm
@@ -87,7 +87,6 @@
desc = "A Super Cool Awesome Visor (SCAV), intended for modular suits."
icon_state = "rave_visor"
complexity = 1
- overlay_state_inactive = "module_rave"
required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_MASK)
/// The client colors applied to the wearer.
var/datum/client_colour/rave_screen
@@ -132,9 +131,13 @@
SEND_SOUND(mod.wearer, sound('sound/machines/terminal/terminal_off.ogg', volume = 50, channel = CHANNEL_JUKEBOX))
/obj/item/mod/module/visor/rave/generate_worn_overlay(mutable_appearance/standing)
- . = ..()
- for(var/mutable_appearance/appearance as anything in .)
- appearance.color = isnull(music_player.active_song_sound) ? null : rainbow_order[rave_number]
+ if (!active)
+ return list()
+ var/mutable_appearance/visor_overlay = mod.get_visor_overlay(standing)
+ visor_overlay.appearance_flags |= RESET_COLOR
+ if (!isnull(music_player.active_song_sound))
+ visor_overlay.color = rainbow_order[rave_number]
+ return list(visor_overlay)
/obj/item/mod/module/visor/rave/on_active_process(seconds_per_tick)
rave_number++
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 60c6aeb4dfe..ff113e2b2cd 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -11,12 +11,13 @@
* Pens
*/
/obj/item/pen
- desc = "It's a normal black ink pen."
name = "pen"
+ desc = "It's a normal black ink pen."
icon = 'icons/obj/service/bureaucracy.dmi'
icon_state = "pen"
inhand_icon_state = "pen"
worn_icon_state = "pen"
+ icon_angle = -135
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_EARS
throwforce = 0
w_class = WEIGHT_CLASS_TINY
@@ -304,8 +305,8 @@
* (Alan) Edaggers
*/
/obj/item/pen/edagger
- attack_verb_continuous = list("slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") //these won't show up if the pen is off
- attack_verb_simple = list("slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts") //these won't show up if the pen is off
+ attack_verb_simple = list("slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
sharpness = SHARP_POINTY
armour_penetration = 20
bare_wound_bonus = 10
@@ -322,9 +323,14 @@
var/hidden_desc = "It's a normal black ink pe- Wait. That's a thing used to stab people!"
/// The real icons used when extended.
var/hidden_icon = "edagger"
+ var/list/alt_continuous = list("stabs", "pierces", "shanks")
+ var/list/alt_simple = list("stab", "pierce", "shank")
/obj/item/pen/edagger/Initialize(mapload)
. = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -5, TRAIT_TRANSFORM_ACTIVE)
AddComponent(/datum/component/butchering, \
speed = 6 SECONDS, \
butcher_sound = 'sound/items/weapons/blade1.ogg', \
diff --git a/code/modules/power/lighting/light_items.dm b/code/modules/power/lighting/light_items.dm
index 357507d0aa4..a5b40e0534f 100644
--- a/code/modules/power/lighting/light_items.dm
+++ b/code/modules/power/lighting/light_items.dm
@@ -55,6 +55,7 @@
icon_state = "ltube"
base_state = "ltube"
inhand_icon_state = "ltube"
+ icon_angle = -45
brightness = 8
custom_price = PAYCHECK_CREW * 0.5
@@ -75,6 +76,7 @@
desc = "A replacement light bulb."
icon_state = "lbulb"
base_state = "lbulb"
+ icon_angle = -90
inhand_icon_state = "contvapour"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
diff --git a/code/modules/power/turbine/turbine.dm b/code/modules/power/turbine/turbine.dm
index e839800158f..2112002e507 100644
--- a/code/modules/power/turbine/turbine.dm
+++ b/code/modules/power/turbine/turbine.dm
@@ -1,11 +1,18 @@
+///Minimum pressure of gases pumped through the turbine
#define MINIMUM_TURBINE_PRESSURE 0.01
+///Returns the minimum pressure if it falls below the value
#define PRESSURE_MAX(value)(max((value), MINIMUM_TURBINE_PRESSURE))
+///Use emissive for overlays
+#define EMISSIVE_OVERLAY (1 << 0)
+///No turned off overlay
+#define NO_INACTIVE_OVERLAY (1 << 1)
/obj/machinery/power/turbine
+ icon = 'icons/obj/machines/engine/turbine.dmi'
density = TRUE
resistance_flags = FIRE_PROOF
can_atmos_pass = ATMOS_PASS_DENSITY
- processing_flags = NONE
+ processing_flags = START_PROCESSING_MANUALLY
///Checks if the machine is processing or not
var/active = FALSE
@@ -17,15 +24,8 @@
var/obj/item/turbine_parts/part_path
///The gas mixture this turbine part is storing
var/datum/gas_mixture/machine_gasmix
-
- ///Our overlay when active
- var/active_overlay = ""
- ///Our overlay when off
- var/off_overlay = ""
- ///Our overlay when open
- var/open_overlay = ""
- ///Should we use emissive appearance?
- var/emissive = FALSE
+ ///Flags for our overlays
+ var/overlay_flags = NONE
/obj/machinery/power/turbine/Initialize(mapload, gas_theoretical_volume)
. = ..()
@@ -59,41 +59,6 @@
deactivate_parts()
return ..()
-/**
- * Handles all the calculations needed for the gases, work done, temperature increase/decrease
- *
- * Arguments
- * * datum/gas_mixture/input_mix - the gas from the environment or from another part of the turbine
- * * datum/gas_mixture/output_mix - the gas that got pumped into this part from the input mix.
- * ideally should be same as input mix but varying texmperatur & pressures can cause varying results
- * * work_amount_to_remove - the amount of work to subtract from the actual work done to pump in the input mixture.
- * For e.g. if gas was transfered from the inlet compressor to the rotor we want to subtract the work done
- * by the inlet from the rotor to get the true work done
- * * intake_size - the percentage of gas to be fed into an turbine part, controlled by turbine computer for inlet compressor only
- */
-/obj/machinery/power/turbine/proc/transfer_gases(datum/gas_mixture/input_mix, datum/gas_mixture/output_mix, work_amount_to_remove, intake_size = 1)
- //pump gases. if no gases were transferred then no work was done
- var/output_pressure = PRESSURE_MAX(output_mix.return_pressure())
- var/datum/gas_mixture/transferred_gases = input_mix.pump_gas_to(output_mix, input_mix.return_pressure() * intake_size)
- if(!transferred_gases)
- return 0
-
- //compute work done
- var/work_done = QUANTIZE(transferred_gases.total_moles()) * R_IDEAL_GAS_EQUATION * transferred_gases.temperature * log((transferred_gases.volume * PRESSURE_MAX(transferred_gases.return_pressure())) / (output_mix.volume * output_pressure)) * TURBINE_WORK_CONVERSION_MULTIPLIER
- if(work_amount_to_remove)
- work_done = work_done - work_amount_to_remove
-
- //compute temperature & work from temperature if that is a lower value
- var/output_mix_heat_capacity = output_mix.heat_capacity()
- if(!output_mix_heat_capacity)
- return 0
- work_done = min(work_done, (output_mix_heat_capacity * output_mix.temperature - output_mix_heat_capacity * TCMB) / TURBINE_HEAT_CONVERSION_MULTIPLIER)
- output_mix.temperature = max((output_mix.temperature * output_mix_heat_capacity + work_done * TURBINE_HEAT_CONVERSION_MULTIPLIER) / output_mix_heat_capacity, TCMB)
- return work_done
-
-/obj/machinery/power/turbine/block_superconductivity()
- return TRUE
-
/obj/machinery/power/turbine/add_context(atom/source, list/context, obj/item/held_item, mob/user)
if(isnull(held_item))
return NONE
@@ -128,7 +93,7 @@
. = ..()
if(installed_part)
. += span_notice("Currently at tier [installed_part.current_tier].")
- if(installed_part.current_tier + 1 < installed_part.max_tier)
+ if(installed_part.current_tier + 1 < TURBINE_PART_TIER_FOUR)
. += span_notice("Can be upgraded by using a tier [installed_part.current_tier + 1] part.")
. += span_notice("The [installed_part.name] can be [EXAMINE_HINT("pried")] out.")
else
@@ -140,15 +105,54 @@
/obj/machinery/power/turbine/update_overlays()
. = ..()
+
if(panel_open)
- . += open_overlay
+ . += "[base_icon_state]_open"
if(active)
- . += active_overlay
- if(emissive)
- . += emissive_appearance(icon, active_overlay, src)
- else
- . += off_overlay
+ . += "[base_icon_state]_on"
+ if(overlay_flags & EMISSIVE_OVERLAY)
+ . += emissive_appearance(icon, "[base_icon_state]_on", src)
+ else if(!(overlay_flags & NO_INACTIVE_OVERLAY))
+ . += "[base_icon_state]_off"
+
+
+/**
+ * Handles all the calculations needed for the gases, work done, temperature increase/decrease
+ *
+ * Arguments
+ * * datum/gas_mixture/input_mix - the gas from the environment or from another part of the turbine
+ * * datum/gas_mixture/output_mix - the gas that got pumped into this part from the input mix.
+ * ideally should be same as input mix but varying texmperatur & pressures can cause varying results
+ * * work_amount_to_remove - the amount of work to subtract from the actual work done to pump in the input mixture.
+ * For e.g. if gas was transfered from the inlet compressor to the rotor we want to subtract the work done
+ * by the inlet from the rotor to get the true work done
+ * * intake_size - the percentage of gas to be fed into an turbine part, controlled by turbine computer for inlet compressor only
+ */
+/obj/machinery/power/turbine/proc/transfer_gases(datum/gas_mixture/input_mix, datum/gas_mixture/output_mix, work_amount_to_remove, intake_size = 1)
+ PROTECTED_PROC(TRUE)
+
+ //pump gases. if no gases were transferred then no work was done
+ var/output_pressure = PRESSURE_MAX(output_mix.return_pressure())
+ var/datum/gas_mixture/transferred_gases = input_mix.pump_gas_to(output_mix, input_mix.return_pressure() * intake_size)
+ if(!transferred_gases)
+ return 0
+
+ //compute work done
+ var/work_done = QUANTIZE(transferred_gases.total_moles()) * R_IDEAL_GAS_EQUATION * transferred_gases.temperature * log((transferred_gases.volume * PRESSURE_MAX(transferred_gases.return_pressure())) / (output_mix.volume * output_pressure)) * TURBINE_WORK_CONVERSION_MULTIPLIER
+ if(work_amount_to_remove)
+ work_done = work_done - work_amount_to_remove
+
+ //compute temperature & work from temperature if that is a lower value
+ var/output_mix_heat_capacity = output_mix.heat_capacity()
+ if(!output_mix_heat_capacity)
+ return 0
+ work_done = min(work_done, (output_mix_heat_capacity * output_mix.temperature - output_mix_heat_capacity * TCMB) / TURBINE_HEAT_CONVERSION_MULTIPLIER)
+ output_mix.temperature = max((output_mix.temperature * output_mix_heat_capacity + work_done * TURBINE_HEAT_CONVERSION_MULTIPLIER) / output_mix_heat_capacity, TCMB)
+ return work_done
+
+/obj/machinery/power/turbine/block_superconductivity()
+ return TRUE
/obj/machinery/power/turbine/screwdriver_act(mob/living/user, obj/item/tool)
. = ITEM_INTERACT_BLOCKING
@@ -165,7 +169,7 @@
deactivate_parts(user)
else
activate_parts(user)
- update_appearance()
+ update_appearance(UPDATE_OVERLAYS)
return ITEM_INTERACT_SUCCESS
@@ -229,22 +233,22 @@
if(gone == installed_part)
installed_part = null
-/obj/machinery/power/turbine/attackby(obj/item/turbine_parts/object, mob/user, params)
- //not the correct part
+/obj/machinery/power/turbine/item_interaction(mob/living/user, obj/item/turbine_parts/object, list/modifiers)
+ . = NONE
if(!istype(object, part_path))
return ..()
//not in a state to accep the part. return TRUE so we don't bash the machine and damage it
if(active)
balloon_alert(user, "turn off the machine first!")
- return TRUE
+ return ITEM_INTERACT_BLOCKING
if(!panel_open)
balloon_alert(user, "open the maintenance hatch first!")
- return TRUE
+ return ITEM_INTERACT_BLOCKING
//install the part
if(!do_after(user, 2 SECONDS, src))
- return TRUE
+ return ITEM_INTERACT_BLOCKING
if(installed_part)
user.put_in_hands(installed_part)
balloon_alert(user, "replaced part with the one in hand")
@@ -252,22 +256,21 @@
balloon_alert(user, "installed new part")
user.transferItemToLoc(object, src)
installed_part = object
- return TRUE
+ return ITEM_INTERACT_SUCCESS
/// Gets the efficiency of the installed part, returns 0 if no part is installed
/obj/machinery/power/turbine/proc/get_efficiency()
+ SHOULD_BE_PURE(TRUE)
+
return installed_part?.part_efficiency || 0
/obj/machinery/power/turbine/inlet_compressor
name = "inlet compressor"
desc = "The input side of a turbine generator, contains the compressor."
- icon = 'icons/obj/machines/engine/turbine.dmi'
icon_state = "inlet_compressor"
+ base_icon_state = "inlet"
circuit = /obj/item/circuitboard/machine/turbine_compressor
part_path = /obj/item/turbine_parts/compressor
- active_overlay = "inlet_animation"
- off_overlay = "inlet_off"
- open_overlay = "inlet_open"
/// The rotor this inlet is linked to
var/obj/machinery/power/turbine/core_rotor/rotor
@@ -296,6 +299,8 @@
* Returns temperature of the gas mix absorbed only if some work was done
*/
/obj/machinery/power/turbine/inlet_compressor/proc/compress_gases()
+ SHOULD_NOT_OVERRIDE(TRUE)
+
compressor_work = 0
compressor_pressure = MINIMUM_TURBINE_PRESSURE
if(QDELETED(input_turf))
@@ -314,16 +319,14 @@
return input_turf_mixture.temperature
+//===========================OUTLET==============================================
/obj/machinery/power/turbine/turbine_outlet
name = "turbine outlet"
desc = "The output side of a turbine generator, contains the turbine and the stator."
- icon = 'icons/obj/machines/engine/turbine.dmi'
icon_state = "turbine_outlet"
+ base_icon_state = "outlet"
circuit = /obj/item/circuitboard/machine/turbine_stator
part_path = /obj/item/turbine_parts/stator
- active_overlay = "outlet_animation"
- off_overlay = "outlet_off"
- open_overlay = "outlet_open"
/// The rotor this outlet is linked to
var/obj/machinery/power/turbine/core_rotor/rotor
@@ -343,6 +346,8 @@
/// push gases from its gas mix to output turf
/obj/machinery/power/turbine/turbine_outlet/proc/expel_gases()
+ SHOULD_NOT_OVERRIDE(TRUE)
+
if(QDELETED(output_turf))
output_turf = get_step(loc, dir)
//turf is blocked don't eject gases
@@ -358,18 +363,16 @@
//return ejected gases
return ejected_gases
+//===========================================CORE ROTOR=========================================
/obj/machinery/power/turbine/core_rotor
name = "core rotor"
desc = "The middle part of a turbine generator, contains the rotor and the main computer."
- icon = 'icons/obj/machines/engine/turbine.dmi'
icon_state = "core_rotor"
- active_overlay = "core_light"
- open_overlay = "core_open"
- active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION
- emissive = TRUE
+ base_icon_state = "core"
can_change_cable_layer = TRUE
circuit = /obj/item/circuitboard/machine/turbine_rotor
part_path = /obj/item/turbine_parts/rotor
+ overlay_flags = EMISSIVE_OVERLAY | NO_INACTIVE_OVERLAY
///ID to easily connect the main part of the turbine to the computer
var/mapping_id
@@ -378,9 +381,9 @@
///Reference to the turbine
var/obj/machinery/power/turbine/turbine_outlet/turbine
///Rotation per minute the machine is doing
- var/rpm
+ var/rpm = 0
///Amount of power the machine is producing
- var/produced_energy
+ var/produced_energy = 0
///Check to see if all parts are connected to the core
var/all_parts_connected = FALSE
///Max rmp that the installed parts can handle, limits the rpms
@@ -462,16 +465,21 @@
//works same as regular left click
return multitool_act(user, tool)
-/// convinience proc for balloon alert which returns if viewer is null
+/**
+ * convinience proc for balloon alert which returns if viewer is null
+ * Arguments
+ *
+ * * mob/viewer - the player receiving the message
+ * * text - the message
+ */
/obj/machinery/power/turbine/core_rotor/proc/feedback(mob/viewer, text)
+ PRIVATE_PROC(TRUE)
+
if(isnull(viewer))
return
balloon_alert(viewer, text)
-/**
- * Called to activate the complete machine, checks for part presence, correct orientation and installed parts
- * Registers the input/output turfs
- */
+///Called to activate the complete machine, checks for part presence, correct orientation and installed parts
/obj/machinery/power/turbine/core_rotor/activate_parts(mob/user, check_only = FALSE)
//if this is not a checkup and all parts are connected then we have nothing to do
if(!check_only && all_parts_connected)
@@ -479,13 +487,18 @@
//locate compressor & turbine, when checking we simply check to see if they are still there
if(!check_only)
- compressor = locate(/obj/machinery/power/turbine/inlet_compressor) in get_step(src, REVERSE_DIR(dir))
- turbine = locate(/obj/machinery/power/turbine/turbine_outlet) in get_step(src, dir)
+ compressor = locate() in get_step(src, REVERSE_DIR(dir))
+ turbine = locate() in get_step(src, dir)
- //maybe look for them the other way around. we want the rotor to allign with them either way for player convinience
- if(!compressor && !turbine)
- compressor = locate(/obj/machinery/power/turbine/inlet_compressor) in get_step(src, dir)
- turbine = locate(/obj/machinery/power/turbine/turbine_outlet) in get_step(src, REVERSE_DIR(dir))
+ //maybe look for them the other way around. this means the rotor is facing the wrong way
+ if(QDELETED(compressor) && QDELETED(turbine))
+ compressor = locate() in get_step(src, dir)
+ turbine = locate() in get_step(src, REVERSE_DIR(dir))
+
+ //show corrective actions
+ if(!QDELETED(compressor) || !QDELETED(turbine))
+ feedback(user, "rotor is facing the wrong way!")
+ return (all_parts_connected = FALSE)
//sanity checks for compressor
if(QDELETED(compressor))
@@ -505,17 +518,17 @@
if(QDELETED(turbine))
feedback(user, "missing turbine!")
return (all_parts_connected = FALSE)
- if(turbine.dir != dir && turbine.dir != REVERSE_DIR(dir))
+ if(turbine.dir != dir && turbine.dir != REVERSE_DIR(dir)) //make sure it's not perpendicular to the rotor
feedback(user, "turbine not aligned with rotor!")
return (all_parts_connected = FALSE)
if(!turbine.can_connect)
- feedback(user, "turbine panel is either open or is misplaced!") //we say misplaced because can_connect becomes FALSE when this turbine is moved
+ feedback(user, "close turbine panel!") //we say misplaced because can_connect becomes FALSE when this turbine is moved
return (all_parts_connected = FALSE)
if(!turbine.installed_part)
feedback(user, "turbine is missing stator part!")
return (all_parts_connected = FALSE)
- //final sanity check to make sure turbine & compressor are facing the same direction. From an visual perspective they will appear facing away from each other actually. I know blame spriter's
+ //sanity check to make sure turbine & compressor are facing the same direction. From an visual perspective they will appear facing away from each other actually. I know blame spriter's
if(compressor.dir != turbine.dir)
feedback(user, "turbine & compressor are not facing away from each other!")
return (all_parts_connected = FALSE)
@@ -537,85 +550,60 @@
* Allows to null the various machines and references from the main core
*/
/obj/machinery/power/turbine/core_rotor/deactivate_parts()
- if(all_parts_connected)
- power_off()
+ toggle_power(force_off = TRUE)
compressor?.rotor = null
compressor = null
turbine?.rotor = null
turbine = null
all_parts_connected = FALSE
disconnect_from_network()
- SSair.stop_processing_machine(src)
/obj/machinery/power/turbine/core_rotor/on_deconstruction(disassembled)
deactivate_parts()
return ..()
/// Toggle power on and off, not safe
-/obj/machinery/power/turbine/core_rotor/proc/toggle_power()
- if(active)
- power_off()
- return
- power_on()
-
-/**
- * Activate all three parts, not safe, it assumes the machine already connected and properly working
- * It does a minimun check to ensure the parts still exist
- */
-/obj/machinery/power/turbine/core_rotor/proc/power_on()
- if(active || QDELETED(compressor) || QDELETED(turbine))
- return
- active = TRUE
- compressor.active = TRUE
- turbine.active = TRUE
- call_parts_update_appearance()
- SSair.start_processing_machine(src)
-
-/// Calls all parts update appearance proc.
-/obj/machinery/power/turbine/core_rotor/proc/call_parts_update_appearance()
- update_appearance()
- if(!QDELETED(compressor))
- compressor.update_appearance()
- if(!QDELETED(turbine))
- turbine.update_appearance()
+/obj/machinery/power/turbine/core_rotor/proc/toggle_power(force_off)
+ SHOULD_NOT_OVERRIDE(TRUE)
+
+ //toggle status
+ if(force_off)
+ if(!active) //was already off
+ return
+ active = FALSE
+ else
+ active = !active
-/**
- * Deactivate all three parts, not safe, it assumes the machine already connected and properly working
- * will try to turn off whatever components are left of this machine
- */
-/obj/machinery/power/turbine/core_rotor/proc/power_off()
- if(!active)
- return
- active = FALSE
+ //update operation status of parts
+ update_appearance(UPDATE_OVERLAYS)
if(!QDELETED(compressor))
- compressor.active = FALSE
+ compressor.active = active
+ compressor.update_appearance(UPDATE_OVERLAYS)
if(!QDELETED(turbine))
- turbine.active = FALSE
- call_parts_update_appearance()
- SSair.stop_processing_machine(src)
+ turbine.active = active
+ turbine.update_appearance(UPDATE_OVERLAYS)
-/// Returns true if all parts have their panel closed
-/obj/machinery/power/turbine/core_rotor/proc/all_parts_ready()
- if(QDELETED(compressor))
- return FALSE
- if(QDELETED(turbine))
- return FALSE
- return !panel_open && !compressor.panel_open && !turbine.panel_open
+ //start or stop processing
+ if(active)
+ update_mode_power_usage(ACTIVE_POWER_USE, active_power_usage)
+ begin_processing()
+ else
+ unset_static_power()
+ end_processing()
/// Getter for turbine integrity, return the amount in %
/obj/machinery/power/turbine/core_rotor/proc/get_turbine_integrity()
+ SHOULD_NOT_OVERRIDE(TRUE)
+
var/integrity = damage / 500
integrity = max(round(100 - integrity * 100, 0.01), 0)
return integrity
-/obj/machinery/power/turbine/core_rotor/process_atmos()
+/obj/machinery/power/turbine/core_rotor/process(seconds_per_tick)
if(!active || !activate_parts(check_only = TRUE) || (machine_stat & BROKEN) || !powered(ignore_use_power = TRUE))
- power_off()
+ deactivate_parts()
return PROCESS_KILL
- //use power to operate internal electronics & stuff
- update_mode_power_usage(ACTIVE_POWER_USE, active_power_usage)
-
//===============COMPRESSOR WORKING========//
//Transfer gases from turf to compressor
var/temperature = compressor.compress_gases()
@@ -668,9 +656,9 @@
work_done = max(work_done - compressor.compressor_work * TURBINE_COMPRESSOR_STATOR_INTERACTION_MULTIPLIER - turbine_work, 0)
//calculate final acheived rpm
rpm = ((work_done * compressor.get_efficiency()) ** turbine.get_efficiency()) * get_efficiency() / TURBINE_RPM_CONVERSION
- rpm = FLOOR(min(rpm, max_allowed_rpm), 1)
+ rpm = min(ROUND_UP(rpm), max_allowed_rpm)
//add energy into the grid, also use part of it for turbine operation
- produced_energy = rpm * TURBINE_ENERGY_RECTIFICATION_MULTIPLIER * TURBINE_RPM_CONVERSION
+ produced_energy = rpm * TURBINE_ENERGY_RECTIFICATION_MULTIPLIER * TURBINE_RPM_CONVERSION * seconds_per_tick
add_avail(produced_energy)
/obj/item/paper/guides/jobs/atmos/turbine
@@ -686,3 +674,5 @@
#undef PRESSURE_MAX
#undef MINIMUM_TURBINE_PRESSURE
+#undef EMISSIVE_OVERLAY
+#undef NO_INACTIVE_OVERLAY
diff --git a/code/modules/power/turbine/turbine_computer.dm b/code/modules/power/turbine/turbine_computer.dm
index 36a556daefb..7771bda03f8 100644
--- a/code/modules/power/turbine/turbine_computer.dm
+++ b/code/modules/power/turbine/turbine_computer.dm
@@ -20,8 +20,8 @@
register_machine(main)
break
-/obj/machinery/computer/turbine_computer/multitool_act(mob/living/user, obj/item/tool)
- var/obj/item/multitool/multitool = tool
+/obj/machinery/computer/turbine_computer/multitool_act(mob/living/user, obj/item/multitool/multitool)
+ . = ITEM_INTERACT_FAILURE
if(!istype(multitool.buffer, /obj/machinery/power/turbine/core_rotor))
to_chat(user, span_notice("Wrong machine type in [multitool] buffer..."))
return
@@ -29,12 +29,21 @@
to_chat(user, span_notice("Changing [src] bluespace network..."))
if(!do_after(user, 0.2 SECONDS, src))
return
+
playsound(get_turf(user), 'sound/machines/click.ogg', 10, TRUE)
register_machine(multitool.buffer)
to_chat(user, span_notice("You link [src] to the console in [multitool]'s buffer."))
- return TRUE
+ return ITEM_INTERACT_SUCCESS
+
+/**
+ * Links the rotor with this computer
+ * Arguments
+ *
+ * * obj/machinery/power/turbine/core_rotor/machine - the machine to link
+ */
+/obj/machinery/computer/turbine_computer/proc/register_machine(obj/machinery/power/turbine/core_rotor/machine)
+ PRIVATE_PROC(TRUE)
-/obj/machinery/computer/turbine_computer/proc/register_machine(machine)
turbine_core = WEAKREF(machine)
/obj/machinery/computer/turbine_computer/ui_interact(mob/user, datum/tgui/ui)
@@ -45,26 +54,27 @@
ui.open()
/obj/machinery/computer/turbine_computer/ui_data(mob/user)
- var/list/data = list()
+ . = list()
+ //do we have the main rotor with all parts connected
var/obj/machinery/power/turbine/core_rotor/main_control = turbine_core?.resolve()
- data["connected"] = !!QDELETED(main_control)
- if(!main_control)
+ if(QDELETED(main_control) || !main_control.all_parts_connected)
+ .["connected"] = FALSE
return
+ else
+ .["connected"] = TRUE
- data["active"] = main_control.active
- data["rpm"] = main_control.rpm ? main_control.rpm : 0
- data["power"] = main_control.produced_energy ? main_control.produced_energy : 0
- data["integrity"] = main_control.get_turbine_integrity()
- data["parts_linked"] = main_control.all_parts_connected
- data["parts_ready"] = main_control.all_parts_ready()
-
- data["max_rpm"] = main_control.max_allowed_rpm
- data["max_temperature"] = main_control.max_allowed_temperature
- data["temp"] = main_control.compressor?.input_turf?.air.temperature || 0
- data["regulator"] = QDELETED(main_control.compressor) ? 0 : main_control.compressor.intake_regulator
+ //operation status
+ .["active"] = main_control.active
+ .["rpm"] = main_control.rpm
+ .["power"] = energy_to_power(main_control.produced_energy)
+ .["integrity"] = main_control.get_turbine_integrity()
- return data
+ //running parameters
+ .["max_rpm"] = main_control.max_allowed_rpm
+ .["max_temperature"] = main_control.max_allowed_temperature
+ .["temp"] = main_control.compressor.input_turf?.air.temperature || 0
+ .["regulator"] = main_control.compressor.intake_regulator
/obj/machinery/computer/turbine_computer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
@@ -74,19 +84,35 @@
switch(action)
if("toggle_power")
var/obj/machinery/power/turbine/core_rotor/main_control = turbine_core?.resolve()
- if(!main_control || !main_control.all_parts_connected || main_control.rpm > 1000)
- return TRUE
- if(!main_control.activate_parts(usr, check_only = TRUE))
- return TRUE
+ if(!main_control)
+ return FALSE
+
+ if(!main_control.active) //turning on the machine requires all part to be linked
+ if(!main_control.activate_parts(ui.user, check_only = TRUE))
+ return FALSE
+ else if(main_control.rpm > 1000) //turning off requires rpm to be less than 1000
+ return FALSE
+
main_control.toggle_power()
main_control.rpm = 0
main_control.produced_energy = 0
- . = TRUE
+ return TRUE
+
if("regulate")
- var/intake_size = text2num(params["regulate"])
+ var/intake_size = params["regulate"]
+ if(isnull(intake_size))
+ return FALSE
+
+ intake_size = text2num(intake_size)
+ if(isnull(intake_size))
+ return FALSE
+
var/obj/machinery/power/turbine/core_rotor/main_control = turbine_core?.resolve()
- if(intake_size == null || !main_control)
- return
- if(!QDELETED(main_control.compressor))
- main_control.compressor.intake_regulator = clamp(intake_size, 0.01, 1)
- . = TRUE
+ if(!main_control)
+ return FALSE
+
+ if(QDELETED(main_control.compressor))
+ return FALSE
+
+ main_control.compressor.intake_regulator = clamp(intake_size, 0.01, 1)
+ return TRUE
diff --git a/code/modules/power/turbine/turbine_parts.dm b/code/modules/power/turbine/turbine_parts.dm
index 4215fccf39f..e568f9e0964 100644
--- a/code/modules/power/turbine/turbine_parts.dm
+++ b/code/modules/power/turbine/turbine_parts.dm
@@ -1,3 +1,8 @@
+///String to access turbine part typepath to upgrade
+#define TURBINE_UPGRADE_PART "part"
+///String to access turbine part required amount to upgrade
+#define TURBINE_UPGRADE_AMOUNT "amount"
+
/obj/item/turbine_parts
name = "turbine parts"
desc = "you really should call an admin"
@@ -8,90 +13,62 @@
var/part_efficiency = 0
///Efficiency increase amount for each tier
var/part_efficiency_increase_amount = 0
-
///Current part tier
var/current_tier = TURBINE_PART_TIER_ONE
- ///Max part tier
- var/max_tier = TURBINE_PART_TIER_FOUR
-
- ///Stores the path of the material for the second tier upgrade
- var/obj/item/stack/sheet/second_tier_material = /obj/item/stack/sheet/plasteel
- ///Amount of second tier material for the upgrade
- var/second_tier_material_amount = 10
-
- ///Stores the path of the material for the third tier upgrade
- var/obj/item/stack/sheet/third_tier_material = /obj/item/stack/sheet/mineral/titanium
- ///Amount of third tier material for the upgrade
- var/third_tier_material_amount = 10
-
- ///Stores the path of the material for the fourth tier upgrade
- var/obj/item/stack/sheet/fourth_tier_material = /obj/item/stack/sheet/mineral/metal_hydrogen
- ///Amount of fourth tier material for the upgrade
- var/fourth_tier_material_amount = 5
-
///Max rpm reachable by the part
var/max_rpm = 35000
- ///Multiplier to increase the max rpm per tier, max should be around 500000 rpm
- var/max_rpm_tier_multiplier = 2.5
-
///Max temperature achievable by the part before the turbine starts to take damage
var/max_temperature = 50000
- ///Max temperature exponential value per tier
- var/max_temperature_tier_exponential = 1.2
/obj/item/turbine_parts/examine(mob/user)
. = ..()
- . += "This is a tier [current_tier] turbine part, rated for [max_rpm] rpm and [max_temperature] K."
- var/upgrade_material_name_amount
- switch(current_tier)
- if(TURBINE_PART_TIER_ONE)
- upgrade_material_name_amount = "[second_tier_material_amount] [initial(second_tier_material.name)] sheets"
- if(TURBINE_PART_TIER_TWO)
- upgrade_material_name_amount = "[third_tier_material_amount] [initial(third_tier_material.name)] sheets"
- if(TURBINE_PART_TIER_THREE)
- upgrade_material_name_amount = "[fourth_tier_material_amount] [initial(fourth_tier_material.name)] sheets"
+ . += span_notice("This is a tier [current_tier] turbine part, rated for [max_rpm] rpm and [max_temperature] K.")
- if(upgrade_material_name_amount)
- . += "Can be upgraded with [upgrade_material_name_amount]."
+ var/list/required_parts = get_tier_upgrades()
+ if(length(required_parts))
+ var/obj/item/stack/material = required_parts[TURBINE_UPGRADE_PART]
+ . += span_notice("Can be upgraded with [required_parts[TURBINE_UPGRADE_AMOUNT]] [initial(material.name)] sheets.")
else
- . += "Is already at max tier."
+ . += span_notice("Is already at max tier.")
+
+///Returns a list containing the typepath & amount of it required to upgrade to the next tier
+/obj/item/turbine_parts/proc/get_tier_upgrades()
+ PROTECTED_PROC(TRUE)
+ SHOULD_BE_PURE(TRUE)
+ RETURN_TYPE(/list)
-/obj/item/turbine_parts/attackby(obj/item/attacking_item, mob/user, params)
- if(current_tier >= max_tier)
- return FALSE
switch(current_tier)
if(TURBINE_PART_TIER_ONE)
- if(!istype(attacking_item, second_tier_material))
- return
- var/obj/item/stack/sheet/second_tier = attacking_item
- if(do_after(user, 1 SECONDS, src) && second_tier.use(second_tier_material_amount))
- current_tier = 2
- part_efficiency += part_efficiency_increase_amount
- max_rpm *= max_rpm_tier_multiplier
- max_temperature = max_temperature ** max_temperature_tier_exponential
- return TRUE
+ return list(TURBINE_UPGRADE_PART = /obj/item/stack/sheet/plasteel, TURBINE_UPGRADE_AMOUNT = 10)
if(TURBINE_PART_TIER_TWO)
- if(!istype(attacking_item, third_tier_material))
- return
- var/obj/item/stack/sheet/third_tier = attacking_item
- if(do_after(user, 2 SECONDS, src) && third_tier.use(third_tier_material_amount))
- current_tier = 3
- part_efficiency += part_efficiency_increase_amount
- max_rpm *= max_rpm_tier_multiplier
- max_temperature = max_temperature ** max_temperature_tier_exponential
- return TRUE
+ return list(TURBINE_UPGRADE_PART = /obj/item/stack/sheet/mineral/titanium, TURBINE_UPGRADE_AMOUNT = 10)
if(TURBINE_PART_TIER_THREE)
- if(!istype(attacking_item, fourth_tier_material))
- return
- var/obj/item/stack/sheet/fourth_tier = attacking_item
- if(do_after(user, 3 SECONDS, src) && fourth_tier.use(fourth_tier_material_amount))
- current_tier = 4
- part_efficiency += part_efficiency_increase_amount
- max_rpm *= max_rpm_tier_multiplier
- max_temperature = max_temperature ** max_temperature_tier_exponential
- return TRUE
-
- return ..()
+ return list(TURBINE_UPGRADE_PART = /obj/item/stack/sheet/mineral/metal_hydrogen, TURBINE_UPGRADE_AMOUNT = 5)
+
+/obj/item/turbine_parts/item_interaction(mob/living/user, obj/item/attacking_item, list/modifiers)
+ . = NONE
+
+ var/list/required_parts = get_tier_upgrades()
+ if(!length(required_parts))
+ balloon_alert(user, "already at max tier!")
+ return ITEM_INTERACT_FAILURE
+
+ var/obj/item/stack/sheet/material = attacking_item
+ if(!istype(material, required_parts[TURBINE_UPGRADE_PART]))
+ balloon_alert(user, "incorrect part!")
+ return ITEM_INTERACT_FAILURE
+
+ var/amount = required_parts[TURBINE_UPGRADE_AMOUNT]
+ if(material.amount < amount)
+ balloon_alert(user, "requires [amount] sheets!")
+ return ITEM_INTERACT_FAILURE
+
+ if(do_after(user, current_tier SECONDS, src) && material.use(amount))
+ current_tier += 1
+ part_efficiency += part_efficiency_increase_amount
+ max_rpm *= 2.5
+ max_temperature = max_temperature ** 1.2
+ return ITEM_INTERACT_SUCCESS
/obj/item/turbine_parts/compressor
name = "compressor part"
@@ -113,9 +90,15 @@
icon_state = "stator_part"
part_efficiency = 0.85
part_efficiency_increase_amount = 0.015
- second_tier_material = /obj/item/stack/sheet/mineral/titanium
- third_tier_material = /obj/item/stack/sheet/mineral/metal_hydrogen
- fourth_tier_material = /obj/item/stack/sheet/mineral/zaukerite
- second_tier_material_amount = 15
- third_tier_material_amount = 15
- fourth_tier_material_amount = 10
+
+/obj/item/turbine_parts/stator/get_tier_upgrades()
+ switch(current_tier)
+ if(TURBINE_PART_TIER_ONE)
+ return list(TURBINE_UPGRADE_PART = /obj/item/stack/sheet/mineral/titanium, TURBINE_UPGRADE_AMOUNT = 15)
+ if(TURBINE_PART_TIER_TWO)
+ return list(TURBINE_UPGRADE_PART = /obj/item/stack/sheet/mineral/metal_hydrogen, TURBINE_UPGRADE_AMOUNT = 15)
+ if(TURBINE_PART_TIER_THREE)
+ return list(TURBINE_UPGRADE_PART = /obj/item/stack/sheet/mineral/zaukerite, TURBINE_UPGRADE_AMOUNT = 10)
+
+#undef TURBINE_UPGRADE_PART
+#undef TURBINE_UPGRADE_AMOUNT
diff --git a/code/modules/projectiles/ammunition/_firing.dm b/code/modules/projectiles/ammunition/_firing.dm
index 5a704354ed3..25973e92e7a 100644
--- a/code/modules/projectiles/ammunition/_firing.dm
+++ b/code/modules/projectiles/ammunition/_firing.dm
@@ -61,6 +61,8 @@
loaded_projectile.damage *= gun.projectile_damage_multiplier * integrity_mult
loaded_projectile.stamina *= gun.projectile_damage_multiplier * integrity_mult
+ loaded_projectile.speed *= gun.projectile_speed_multiplier * integrity_mult
+
loaded_projectile.wound_bonus += gun.projectile_wound_bonus
loaded_projectile.wound_bonus *= loaded_projectile.wound_bonus >= 0 ? 1 : 2 - integrity_mult
loaded_projectile.bare_wound_bonus += gun.projectile_wound_bonus
diff --git a/code/modules/projectiles/ammunition/ballistic/revolver.dm b/code/modules/projectiles/ammunition/ballistic/revolver.dm
index 0126a461723..6e0c26af735 100644
--- a/code/modules/projectiles/ammunition/ballistic/revolver.dm
+++ b/code/modules/projectiles/ammunition/ballistic/revolver.dm
@@ -1,26 +1,26 @@
// .357 (Syndie Revolver)
-/obj/item/ammo_casing/a357
+/obj/item/ammo_casing/c357
name = ".357 bullet casing"
desc = "A .357 bullet casing."
caliber = CALIBER_357
- projectile_type = /obj/projectile/bullet/a357
+ projectile_type = /obj/projectile/bullet/c357
-/obj/item/ammo_casing/a357/spent
+/obj/item/ammo_casing/c357/spent
projectile_type = null
-/obj/item/ammo_casing/a357/match
+/obj/item/ammo_casing/c357/match
name = ".357 match bullet casing"
desc = "A .357 bullet casing, manufactured to exceedingly high standards."
- projectile_type = /obj/projectile/bullet/a357/match
+ projectile_type = /obj/projectile/bullet/c357/match
-/obj/item/ammo_casing/a357/phasic
+/obj/item/ammo_casing/c357/phasic
name = ".357 phasic bullet casing"
- projectile_type = /obj/projectile/bullet/a357/phasic
+ projectile_type = /obj/projectile/bullet/c357/phasic
-/obj/item/ammo_casing/a357/heartseeker
+/obj/item/ammo_casing/c357/heartseeker
name = ".357 heartseeker bullet casing"
- projectile_type = /obj/projectile/bullet/a357/heartseeker
+ projectile_type = /obj/projectile/bullet/c357/heartseeker
// 7.62x38mmR (Nagant Revolver)
@@ -53,6 +53,11 @@
desc = "A .38 rubber bullet casing, manufactured to exceedingly bouncy standards."
projectile_type = /obj/projectile/bullet/c38/match/bouncy
+/obj/item/ammo_casing/c38/match/true
+ name = ".38 True Strike bullet casing"
+ desc = "A .38 True Strike bullet casing."
+ projectile_type = /obj/projectile/bullet/c38/match/true
+
/obj/item/ammo_casing/c38/dumdum
name = ".38 DumDum bullet casing"
desc = "A .38 DumDum bullet casing."
diff --git a/code/modules/projectiles/ammunition/energy/stun.dm b/code/modules/projectiles/ammunition/energy/stun.dm
index 7fb22e42ef5..6fa8ee80c6d 100644
--- a/code/modules/projectiles/ammunition/energy/stun.dm
+++ b/code/modules/projectiles/ammunition/energy/stun.dm
@@ -16,6 +16,9 @@
/obj/item/ammo_casing/energy/electrode/old
e_cost = LASER_SHOTS(1, STANDARD_CELL_CHARGE)
+/obj/item/ammo_casing/energy/electrode/ai_turrets
+ projectile_type = /obj/projectile/energy/electrode/ai_turrets
+
/obj/item/ammo_casing/energy/disabler
projectile_type = /obj/projectile/beam/disabler
select_name = "disable"
diff --git a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm
index bbd89389eb8..b664be74e87 100644
--- a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm
+++ b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm
@@ -2,7 +2,7 @@
name = "speed loader (.357)"
desc = "Designed to quickly reload revolvers."
icon_state = "357"
- ammo_type = /obj/item/ammo_casing/a357
+ ammo_type = /obj/item/ammo_casing/c357
max_ammo = 7
caliber = CALIBER_357
multiple_sprites = AMMO_BOX_PER_BULLET
@@ -13,13 +13,13 @@
/obj/item/ammo_box/a357/match
name = "speed loader (.357 Match)"
desc = "Designed to quickly reload revolvers. These rounds are manufactured within extremely tight tolerances, making them easy to show off trickshots with."
- ammo_type = /obj/item/ammo_casing/a357/match
+ ammo_type = /obj/item/ammo_casing/c357/match
ammo_band_color = "#77828a"
/obj/item/ammo_box/a357/phasic
name = "speed loader (.357 Phasic)"
desc = "Designed to quickly reload revolvers. Holds phasic ammo, also known as 'Ghost Lead', allowing it to pass through non-organic material."
- ammo_type = /obj/item/ammo_casing/a357/phasic
+ ammo_type = /obj/item/ammo_casing/c357/phasic
ammo_band_color = "#693a6a"
/obj/item/ammo_box/a357/heartseeker
@@ -27,7 +27,7 @@
desc = "Designed to quickly reload revolvers. Holds heartseeker ammo, which veers into targets with exceptional precision using \
an unknown method. It apparently predicts movement using neural pulses in the brain, but that's less marketable. \
As seen in the hit NTFlik horror-space western film, Forget-Me-Not! Brought to you by Roseus Galactic!"
- ammo_type = /obj/item/ammo_casing/a357/heartseeker
+ ammo_type = /obj/item/ammo_casing/c357/heartseeker
ammo_band_color = "#a91e1e"
/obj/item/ammo_box/c38
@@ -60,6 +60,12 @@
ammo_type = /obj/item/ammo_casing/c38/match/bouncy
ammo_band_color = "#556696"
+/obj/item/ammo_box/c38/true
+ name = "speed loader (.38 True Strike)"
+ desc = "Designed to quickly reload revolvers. Bullets bounce towards new targets with surprising accuracy."
+ ammo_type = /obj/item/ammo_casing/c38/match/true
+ ammo_band_color = "#d647b0"
+
/obj/item/ammo_box/c38/dumdum
name = "speed loader (.38 DumDum)"
desc = "Designed to quickly reload revolvers. These rounds expand on impact, allowing them to shred the target and cause massive bleeding. Very weak against armor and distant targets."
diff --git a/code/modules/projectiles/boxes_magazines/external/rifle.dm b/code/modules/projectiles/boxes_magazines/external/rifle.dm
index 882fefedec1..96916fe9bb5 100644
--- a/code/modules/projectiles/boxes_magazines/external/rifle.dm
+++ b/code/modules/projectiles/boxes_magazines/external/rifle.dm
@@ -21,3 +21,66 @@
/obj/item/ammo_box/magazine/m223/phasic
name = "toploader magazine (.223 Phasic)"
ammo_type = /obj/item/ammo_casing/a223/phasic
+
+// .38 (Battle Rifle) //
+
+/obj/item/ammo_box/magazine/m38
+ name = "battle rifle magazine (.38)"
+ desc = "A magazine for a BR-38 battle rifle."
+ icon_state = "38mag"
+ base_icon_state = "38mag"
+ w_class = WEIGHT_CLASS_NORMAL
+ ammo_type = /obj/item/ammo_casing/c38
+ caliber = CALIBER_38
+ max_ammo = 15
+ ammo_band_icon = "+38mag_ammo_band"
+ ammo_band_color = null
+
+/obj/item/ammo_box/magazine/m38/update_icon_state()
+ . = ..()
+ icon_state = "[base_icon_state][ammo_count() ? "-ammo" : ""]"
+
+/obj/item/ammo_box/magazine/m38/empty
+ start_empty = TRUE
+
+/obj/item/ammo_box/magazine/m38/trac
+ name = "battle rifle magazine (.38 TRAC)"
+ desc = "A magazine for a BR-38 battle rifle. TRAC bullets embed a tracking implant within the target's body and are entirely nonlethal."
+ ammo_type = /obj/item/ammo_casing/c38/trac
+ ammo_band_color = "#7b6383"
+
+/obj/item/ammo_box/magazine/m38/match
+ name = "battle rifle magazine (.38 Match)"
+ desc = "A magazine for a BR-38 battle rifle. These rounds are manufactured within extremely tight tolerances, making them easy to show off trickshots with."
+ ammo_type = /obj/item/ammo_casing/c38/match
+ ammo_band_color = "#7b6383"
+
+/obj/item/ammo_box/magazine/m38/match/bouncy
+ name = "battle rifle magazine (.38 Rubber)"
+ desc = "A magazine for a BR-38 battle rifle. These rounds are incredibly bouncy and MOSTLY nonlethal, making them great to show off trickshots with."
+ ammo_type = /obj/item/ammo_casing/c38/match/bouncy
+ ammo_band_color = "#556696"
+
+/obj/item/ammo_box/magazine/m38/true
+ name = "battle rifle magazine (.38 True Strike)"
+ desc = "A magazine for a BR-38 battle rifle. Bullets bounce towards new targets with surprising accuracy."
+ ammo_type = /obj/item/ammo_casing/c38/match/true
+ ammo_band_color = "#d647b0"
+
+/obj/item/ammo_box/magazine/m38/dumdum
+ name = "battle rifle magazine (.38 DumDum)"
+ desc = "A magazine for a BR-38 battle rifle. These rounds expand on impact, allowing them to shred the target and cause massive bleeding. Very weak against armor and distant targets."
+ ammo_type = /obj/item/ammo_casing/c38/dumdum
+ ammo_band_color = "#969578"
+
+/obj/item/ammo_box/magazine/m38/hotshot
+ name = "battle rifle magazine (.38 Hot Shot)"
+ desc = "A magazine for a BR-38 battle rifle. Hot Shot bullets contain an incendiary payload."
+ ammo_type = /obj/item/ammo_casing/c38/hotshot
+ ammo_band_color = "#805a57"
+
+/obj/item/ammo_box/magazine/m38/iceblox
+ name = "battle rifle magazine (.38 Iceblox)"
+ desc = "A magazine for a BR-38 battle rifle. Iceblox bullets contain a cryogenic payload."
+ ammo_type = /obj/item/ammo_casing/c38/iceblox
+ ammo_band_color = "#658e94"
diff --git a/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm b/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm
index 28df0262352..7f467881a9d 100644
--- a/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm
+++ b/code/modules/projectiles/boxes_magazines/internal/_cylinder.dm
@@ -1,6 +1,6 @@
/obj/item/ammo_box/magazine/internal/cylinder
name = "revolver cylinder"
- ammo_type = /obj/item/ammo_casing/a357
+ ammo_type = /obj/item/ammo_casing/c357
caliber = CALIBER_357
max_ammo = 7
diff --git a/code/modules/projectiles/boxes_magazines/internal/revolver.dm b/code/modules/projectiles/boxes_magazines/internal/revolver.dm
index ced68ca5cb4..1e891abeef2 100644
--- a/code/modules/projectiles/boxes_magazines/internal/revolver.dm
+++ b/code/modules/projectiles/boxes_magazines/internal/revolver.dm
@@ -12,7 +12,7 @@
/obj/item/ammo_box/magazine/internal/cylinder/rus357
name = "\improper Russian revolver cylinder"
- ammo_type = /obj/item/ammo_casing/a357
+ ammo_type = /obj/item/ammo_casing/c357
caliber = CALIBER_357
max_ammo = 6
multiload = FALSE
@@ -21,8 +21,8 @@
/obj/item/ammo_box/magazine/internal/cylinder/rus357/Initialize(mapload)
. = ..()
for (var/i in 1 to max_ammo - 1)
- stored_ammo += new /obj/item/ammo_casing/a357/spent(src)
- stored_ammo += new /obj/item/ammo_casing/a357(src)
+ stored_ammo += new /obj/item/ammo_casing/c357/spent(src)
+ stored_ammo += new /obj/item/ammo_casing/c357(src)
/obj/item/ammo_box/magazine/internal/cylinder/peashooter
name = "peashooter cylinder"
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index f52bd88cd54..76cf2d18113 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -54,6 +54,10 @@
/// Even snowflakier way to modify projectile wounding bonus/potential for projectiles fired from this gun.
var/projectile_wound_bonus = 0
+ /// The most reasonable way to modify projectile speed values for projectile fired from this gun. Honest.
+ /// Lower values are better, higher values are worse.
+ var/projectile_speed_multiplier = 1
+
var/spread = 0 //Spread induced by the gun itself.
var/randomspread = 1 //Set to 0 for shotguns. This is used for weapons that don't fire all their bullets at once.
diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm
index 45c1e9c65ea..93ef571258b 100644
--- a/code/modules/projectiles/guns/ballistic.dm
+++ b/code/modules/projectiles/guns/ballistic.dm
@@ -477,7 +477,7 @@
/obj/item/gun/ballistic/shoot_live_shot(mob/living/user, pointblank = 0, atom/pbtarget = null, message = 1)
if(isnull(chambered))
return ..()
- if(can_misfire && chambered.can_misfire != FALSE)
+ if(can_misfire)
misfire_probability += misfire_percentage_increment
misfire_probability = clamp(misfire_probability, 0, misfire_probability_cap)
if(chambered.can_misfire)
diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm
index 1c158cf4a87..89c6deaa4d2 100644
--- a/code/modules/projectiles/guns/ballistic/automatic.dm
+++ b/code/modules/projectiles/guns/ballistic/automatic.dm
@@ -352,3 +352,196 @@
actions_types = list()
fire_sound = 'sound/items/weapons/laser.ogg'
casing_ejector = FALSE
+
+// NT Battle Rifle //
+
+/obj/item/gun/ballistic/automatic/battle_rifle
+ name = "\improper NT BR-38 battle rifle"
+ desc = "Nanotrasen's prototype security weapon, found exclusively in the hands of their private security teams. Chambered in .38 pistol rounds. \
+ Ignore that this makes it technically a carbine. And that it functions as a designated marksman rifle. Marketing weren't being very co-operative \
+ when it came time to name the gun. That, and the endless arguments in board rooms about exactly what designation the gun is meant to be."
+ icon = 'icons/obj/weapons/guns/wide_guns.dmi'
+ icon_state = "battle_rifle"
+ inhand_icon_state = "battle_rifle"
+ base_icon_state = "battle_rifle"
+ worn_icon = 'icons/mob/clothing/back.dmi'
+ worn_icon_state = "battle_rifle"
+ slot_flags = ITEM_SLOT_BACK
+
+ weapon_weight = WEAPON_HEAVY
+ accepted_magazine_type = /obj/item/ammo_box/magazine/m38
+ w_class = WEIGHT_CLASS_BULKY
+ force = 15 //this thing is kind of oversized, okay?
+ mag_display = TRUE
+ projectile_damage_multiplier = 1.2
+ projectile_speed_multiplier = 1.2
+ fire_delay = 2
+ burst_size = 1
+ actions_types = list()
+ spread = 10 //slightly inaccurate in burst fire mode, mostly important for long range shooting
+ fire_sound = 'sound/items/weapons/thermalpistol.ogg'
+ suppressor_x_offset = 8
+
+ /// Determines how many shots we can make before the weapon needs to be maintained.
+ var/shots_before_degradation = 10
+ /// The max number of allowed shots this gun can have before degradation.
+ var/max_shots_before_degradation = 10
+ /// Determines the degradation stage. The higher the value, the more poorly the weapon performs.
+ var/degradation_stage = 0
+ /// Maximum degradation stage.
+ var/degradation_stage_max = 5
+ /// The probability of degradation increasing per shot.
+ var/degradation_probability = 10
+ /// The maximum speed malus for projectile flight speed. Projectiles probably shouldn't move too slowly or else they will start to cause problems.
+ var/maximum_speed_malus = 0.7
+ /// What is our damage multiplier if the gun is emagged?
+ var/emagged_projectile_damage_multiplier = 1.6
+
+ /// Whether or not our gun is suffering an EMP related malfunction.
+ var/emp_malfunction = FALSE
+
+ /// Our timer for when our gun is suffering an extreme malfunction. AKA it is going to explode
+ var/explosion_timer
+
+ SET_BASE_PIXEL(-8, 0)
+
+/obj/item/gun/ballistic/automatic/battle_rifle/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/scope, range_modifier = 2)
+ register_context()
+
+/obj/item/gun/ballistic/automatic/battle_rifle/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = ..()
+
+ if(held_item?.tool_behaviour == TOOL_MULTITOOL && shots_before_degradation < max_shots_before_degradation)
+ context[SCREENTIP_CONTEXT_LMB] = "Reset System"
+ return CONTEXTUAL_SCREENTIP_SET
+
+/obj/item/gun/ballistic/automatic/battle_rifle/examine_more(mob/user)
+ . = ..()
+ . += span_notice("Looking down at the [name], you recall something you read in a promotional pamphlet... ")
+
+ . += span_info("The BR-38 possesses an acceleration rail that launches bullets at higher than typical velocity.\
+ This allows even less powerful cartridges to put out significant amounts of stopping power.")
+
+ . += span_notice("However, you also remember some of the rumors... ")
+
+ . += span_notice("In a sour twist of irony for Nanotrasen's historical issues with ballistics-based security weapons, the BR-38 has one significant flaw. \
+ It is possible for the weapon to suffer from unintended discombulations due to closed heat distribution systems should the weapon be tampered with. \
+ R&D are working on this issue before the weapon sees commercial sales. That, and trying to work out why the weapon's onboard computation systems suffer \
+ from so many calculation errors.")
+
+/obj/item/gun/ballistic/automatic/battle_rifle/examine(mob/user)
+ . = ..()
+ if(shots_before_degradation)
+ . += span_notice("[src] can fire [shots_before_degradation] more times before risking system degradation.")
+ else
+ . += span_notice("[src] is in the process of system degradation. It is currently at stage [degradation_stage] of [degradation_stage_max]. Use a multitool on [src] to recalibrate. Alternatively, insert it into a weapon recharger.")
+
+/obj/item/gun/ballistic/automatic/battle_rifle/update_icon_state()
+ . = ..()
+ if(!shots_before_degradation)
+ inhand_icon_state = "[base_icon_state]-empty"
+ else
+ inhand_icon_state = "[base_icon_state]"
+
+/obj/item/gun/ballistic/automatic/battle_rifle/update_overlays()
+ . = ..()
+ if(degradation_stage)
+ . += "[base_icon_state]_empty"
+ else if(shots_before_degradation)
+ var/ratio_for_overlay = CEILING(clamp(shots_before_degradation / max_shots_before_degradation, 0, 1) * 3, 1)
+ . += "[icon_state]_stage_[ratio_for_overlay]"
+
+/obj/item/gun/ballistic/automatic/battle_rifle/emp_act(severity)
+ . = ..()
+ if (!(. & EMP_PROTECT_SELF) && prob(50 / severity))
+ shots_before_degradation = 0
+ emp_malfunction = TRUE
+ attempt_degradation(TRUE)
+
+/obj/item/gun/ballistic/automatic/battle_rifle/emag_act(mob/user, obj/item/card/emag/emag_card)
+ . = ..()
+ if(obj_flags & EMAGGED)
+ return FALSE
+ obj_flags |= EMAGGED
+ projectile_damage_multiplier = emagged_projectile_damage_multiplier
+ balloon_alert(user, "heat distribution systems deactivated")
+ return TRUE
+
+/obj/item/gun/ballistic/automatic/battle_rifle/multitool_act(mob/living/user, obj/item/tool)
+ if(!tool.use_tool(src, user, 20 SECONDS, volume = 50))
+ balloon_alert(user, "interrupted!")
+ return ITEM_INTERACT_BLOCKING
+
+ emp_malfunction = FALSE
+ shots_before_degradation = initial(shots_before_degradation)
+ degradation_stage = initial(degradation_stage)
+ projectile_speed_multiplier = initial(projectile_speed_multiplier)
+ fire_delay = initial(fire_delay)
+ update_appearance()
+ balloon_alert(user, "system reset")
+ return ITEM_INTERACT_SUCCESS
+
+/obj/item/gun/ballistic/automatic/battle_rifle/try_fire_gun(atom/target, mob/living/user, params)
+ . = ..()
+ if(!chambered || (chambered && !chambered.loaded_projectile))
+ return
+
+ if(shots_before_degradation)
+ shots_before_degradation --
+ return
+
+ else if ((obj_flags & EMAGGED) && degradation_stage == degradation_stage_max && !explosion_timer)
+ perform_extreme_malfunction(user)
+
+ else
+ attempt_degradation(FALSE)
+
+
+/obj/item/gun/ballistic/automatic/battle_rifle/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
+ if(chambered.loaded_projectile && prob(75) && (emp_malfunction || degradation_stage == degradation_stage_max))
+ balloon_alert_to_viewers("*click*")
+ playsound(src, dry_fire_sound, dry_fire_sound_volume, TRUE)
+ return
+
+ return ..()
+
+/// Proc to handle weapon degradation. Called when attempting to fire or immediately after an EMP takes place.
+/obj/item/gun/ballistic/automatic/battle_rifle/proc/attempt_degradation(force_increment = FALSE)
+ if(!prob(degradation_probability) && !force_increment || degradation_stage == degradation_stage_max)
+ return //Only update if we actually increment our degradation stage
+
+ degradation_stage = clamp(degradation_stage + (obj_flags & EMAGGED ? 2 : 1), 0, degradation_stage_max)
+ projectile_speed_multiplier = clamp(initial(projectile_speed_multiplier) + degradation_stage * 0.1, initial(projectile_speed_multiplier), maximum_speed_malus)
+ fire_delay = initial(fire_delay) + (degradation_stage * 0.5)
+ do_sparks(1, TRUE, src)
+ update_appearance()
+
+/// Called by /obj/machinery/recharger while inserted: attempts to recalibrate our gun but reducing degradation.
+/obj/item/gun/ballistic/automatic/battle_rifle/proc/attempt_recalibration(restoring_shots_before_degradation = FALSE, recharge_rate = 1)
+ emp_malfunction = FALSE
+
+ if(restoring_shots_before_degradation)
+ shots_before_degradation = clamp(round(shots_before_degradation + recharge_rate, 1), 0, max_shots_before_degradation)
+
+ else
+ degradation_stage = clamp(degradation_stage - 1, 0, degradation_stage_max)
+ if(degradation_stage)
+ projectile_speed_multiplier = clamp(initial(projectile_speed_multiplier) - degradation_stage * 0.1, maximum_speed_malus, initial(projectile_speed_multiplier))
+ fire_delay = initial(fire_delay) + (degradation_stage * 0.5)
+ else
+ projectile_speed_multiplier = initial(projectile_speed_multiplier)
+ fire_delay = initial(fire_delay)
+
+ update_appearance()
+
+/// Proc to handle the countdown for our detonation
+/obj/item/gun/ballistic/automatic/battle_rifle/proc/perform_extreme_malfunction(mob/living/user)
+ balloon_alert(user, "gun is exploding, throw it!")
+ explosion_timer = addtimer(CALLBACK(src, PROC_REF(fucking_explodes_you)), 5 SECONDS, (TIMER_UNIQUE|TIMER_OVERRIDE))
+ playsound(src, 'sound/items/weapons/gun/general/empty_alarm.ogg', 50, FALSE)
+
+/// proc to handle our detonation
+/obj/item/gun/ballistic/automatic/battle_rifle/proc/fucking_explodes_you()
+ explosion(src, devastation_range = 1, heavy_impact_range = 3, light_impact_range = 6, explosion_cause = src)
diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm
index e826e1392bf..f6421ab7e1b 100644
--- a/code/modules/projectiles/guns/energy/energy_gun.dm
+++ b/code/modules/projectiles/guns/energy/energy_gun.dm
@@ -136,7 +136,7 @@
inhand_icon_state = "turretlaser"
slot_flags = null
w_class = WEIGHT_CLASS_HUGE
- ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser)
+ ammo_type = list(/obj/item/ammo_casing/energy/electrode/ai_turrets, /obj/item/ammo_casing/energy/laser)
weapon_weight = WEAPON_HEAVY
trigger_guard = TRIGGER_GUARD_NONE
ammo_x_offset = 2
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index d542f229914..ba63e41af4c 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -225,6 +225,11 @@
update_appearance()
/obj/projectile/kinetic/on_range()
+ if(!pressure_decrease_active && !lavaland_equipment_pressure_check(get_turf(src)))
+ name = "weakened [name]"
+ damage = damage * pressure_decrease
+ pressure_decrease_active = TRUE
+
strike_thing(loc)
..()
diff --git a/code/modules/projectiles/guns/magic/arcane_barrage.dm b/code/modules/projectiles/guns/magic/arcane_barrage.dm
index 74be54a6323..3534b99675d 100644
--- a/code/modules/projectiles/guns/magic/arcane_barrage.dm
+++ b/code/modules/projectiles/guns/magic/arcane_barrage.dm
@@ -6,6 +6,7 @@
icon_state = "arcane_barrage"
inhand_icon_state = "arcane_barrage"
base_icon_state = "arcane_barrage"
+ icon_angle = 90
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
slot_flags = null
diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm
index 6a383befa54..e84bb9cd1f3 100644
--- a/code/modules/projectiles/guns/magic/staff.dm
+++ b/code/modules/projectiles/guns/magic/staff.dm
@@ -3,6 +3,7 @@
ammo_type = /obj/item/ammo_casing/magic/nothing
worn_icon_state = null
icon_state = "staff"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
item_flags = NEEDS_PERMIT | NO_MAT_REDEMPTION
@@ -242,6 +243,7 @@
ammo_type = /obj/item/ammo_casing/magic/spellblade
icon_state = "spellblade"
inhand_icon_state = "spellblade"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
hitsound = 'sound/items/weapons/rapierhit.ogg'
diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm
index 0a82f44318e..6f59998ba61 100644
--- a/code/modules/projectiles/guns/magic/wand.dm
+++ b/code/modules/projectiles/guns/magic/wand.dm
@@ -4,6 +4,7 @@
ammo_type = /obj/item/ammo_casing/magic
icon_state = "nothingwand"
inhand_icon_state = "wand"
+ icon_angle = -45
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
base_icon_state = "nothingwand"
diff --git a/code/modules/projectiles/guns/special/meat_hook.dm b/code/modules/projectiles/guns/special/meat_hook.dm
index c3462abdd91..2034b400e17 100644
--- a/code/modules/projectiles/guns/special/meat_hook.dm
+++ b/code/modules/projectiles/guns/special/meat_hook.dm
@@ -8,6 +8,7 @@
ammo_type = /obj/item/ammo_casing/magic/hook
icon_state = "hook"
inhand_icon_state = "hook"
+ icon_angle = 45
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
fire_sound = 'sound/items/weapons/batonextend.ogg'
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 1a21ac4f82d..065a4899fc7 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -420,12 +420,18 @@
// Shooting yourself point-blank
if (firer == original)
original = null
+ if (firer == fired_from)
+ fired_from = null
firer = null
/obj/projectile/proc/original_deleted(datum/source)
SIGNAL_HANDLER
original = null
+/obj/projectile/proc/fired_from_deleted(datum/source)
+ SIGNAL_HANDLER
+ fired_from = null
+
/obj/projectile/proc/on_ricochet(atom/target)
ricochets++
if(!ricochet_auto_aim_angle || !ricochet_auto_aim_range)
@@ -752,11 +758,13 @@
/obj/projectile/proc/fire(fire_angle, atom/direct_target)
LAZYINITLIST(impacted)
- if (fired_from)
- SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_BEFORE_FIRE, src, original)
if (firer)
RegisterSignal(firer, COMSIG_QDELETING, PROC_REF(firer_deleted))
SEND_SIGNAL(firer, COMSIG_PROJECTILE_FIRER_BEFORE_FIRE, src, fired_from, original)
+ if (fired_from)
+ if (firer != fired_from)
+ RegisterSignal(fired_from, COMSIG_QDELETING, PROC_REF(fired_from_deleted))
+ SEND_SIGNAL(fired_from, COMSIG_PROJECTILE_BEFORE_FIRE, src, original)
if (original)
if (firer != original)
RegisterSignal(original, COMSIG_QDELETING, PROC_REF(original_deleted))
diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm
index 096acf5acae..6513be9cfb7 100644
--- a/code/modules/projectiles/projectile/bullets/revolver.dm
+++ b/code/modules/projectiles/projectile/bullets/revolver.dm
@@ -48,7 +48,6 @@
name = ".38 Rubber bullet"
damage = 10
stamina = 30
- weak_against_armour = TRUE
ricochets_max = 6
ricochet_incidence_leeway = 0
ricochet_chance = 130
@@ -57,6 +56,16 @@
sharpness = NONE
embed_type = null
+/obj/projectile/bullet/c38/match/true
+ name = ".38 True Strike bullet"
+ damage = 15
+ ricochet_auto_aim_range = 3
+ ricochet_auto_aim_angle = 100
+ ricochet_incidence_leeway = 0
+ ricochet_shoots_firer = FALSE
+ shrapnel_type = null
+ embed_type = null
+
// premium .38 ammo from cargo, weak against armor, lower base damage, but excellent at embedding and causing slice wounds at close range
/obj/projectile/bullet/c38/dumdum
name = ".38 DumDum bullet"
@@ -106,9 +115,9 @@
/obj/projectile/bullet/c38/hotshot/on_hit(atom/target, blocked = 0, pierce_hit)
. = ..()
if(iscarbon(target))
- var/mob/living/carbon/M = target
- M.adjust_fire_stacks(6)
- M.ignite_mob()
+ var/mob/living/carbon/criminal_scum = target
+ criminal_scum.adjust_fire_stacks(6)
+ criminal_scum.ignite_mob()
/obj/projectile/bullet/c38/iceblox //see /obj/projectile/temp for the original code
name = ".38 Iceblox bullet"
@@ -119,24 +128,24 @@
/obj/projectile/bullet/c38/iceblox/on_hit(atom/target, blocked = 0, pierce_hit)
. = ..()
if(isliving(target))
- var/mob/living/M = target
- M.adjust_bodytemperature(((100-blocked)/100)*(temperature - M.bodytemperature))
+ var/mob/living/criminal_scum = target
+ criminal_scum.adjust_bodytemperature(((100-blocked)/100)*(temperature - criminal_scum.bodytemperature))
// .357 (Syndie Revolver)
-/obj/projectile/bullet/a357
+/obj/projectile/bullet/c357
name = ".357 bullet"
damage = 60
wound_bonus = -30
-/obj/projectile/bullet/a357/phasic
+/obj/projectile/bullet/c357/phasic
name = ".357 phasic bullet"
icon_state = "gaussphase"
damage = 35
armour_penetration = 100
projectile_phasing = PASSTABLE | PASSGLASS | PASSGRILLE | PASSCLOSEDTURF | PASSMACHINE | PASSSTRUCTURE | PASSDOORS
-/obj/projectile/bullet/a357/heartseeker
+/obj/projectile/bullet/c357/heartseeker
name = ".357 heartseeker bullet"
icon_state = "gauss"
damage = 50
@@ -144,7 +153,7 @@
homing_turn_speed = 120
// admin only really, for ocelot memes
-/obj/projectile/bullet/a357/match
+/obj/projectile/bullet/c357/match
name = ".357 match bullet"
ricochets_max = 5
ricochet_chance = 140
diff --git a/code/modules/projectiles/projectile/energy/stun.dm b/code/modules/projectiles/projectile/energy/stun.dm
index fb5c041b338..168e7b0a989 100644
--- a/code/modules/projectiles/projectile/energy/stun.dm
+++ b/code/modules/projectiles/projectile/energy/stun.dm
@@ -2,28 +2,397 @@
name = "electrode"
icon_state = "spark"
color = COLOR_YELLOW
- paralyze = 10 SECONDS
- stutter = 10 SECONDS
- jitter = 40 SECONDS
hitsound = 'sound/items/weapons/taserhit.ogg'
- range = 7
+ range = 5
+ reflectable = FALSE
tracer_type = /obj/effect/projectile/tracer/stun
muzzle_type = /obj/effect/projectile/muzzle/stun
impact_type = /obj/effect/projectile/impact/stun
+ /// How much stamina damage will the tase deal in 1 second
+ VAR_PROTECTED/tase_stamina = 60
+ /// Electrodes that follow the projectile
+ VAR_PRIVATE/datum/weakref/beam_weakref
+ /// We need to track who was the ORIGINAL firer of the projectile specifically to ensure deflects work correctly
+ VAR_PRIVATE/datum/weakref/initial_firer_weakref
-/obj/projectile/energy/electrode/on_hit(atom/target, blocked = 0, pierce_hit)
+/obj/projectile/energy/electrode/is_hostile_projectile()
+ return TRUE
+
+/obj/projectile/energy/electrode/Destroy()
+ QDEL_NULL(beam_weakref)
+ return ..()
+
+/obj/projectile/energy/electrode/fire(fire_angle, atom/direct_target)
+ if(firer)
+ beam_weakref = WEAKREF(firer.Beam(
+ BeamTarget = src,
+ icon = 'icons/effects/beam.dmi',
+ icon_state = "electrodes_nozap",
+ maxdistance = maximum_range + 1,
+ beam_type = /obj/effect/ebeam/electrodes_nozap,
+ ))
+ initial_firer_weakref = WEAKREF(firer)
+ return ..()
+
+/obj/projectile/energy/electrode/on_hit(mob/living/target, blocked = 0, pierce_hit)
. = ..()
- if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - burst into sparks!
- do_sparks(1, TRUE, src)
- else if(iscarbon(target))
- var/mob/living/carbon/C = target
- C.add_mood_event("tased", /datum/mood_event/tased)
- SEND_SIGNAL(C, COMSIG_LIVING_MINOR_SHOCK)
- if(HAS_TRAIT(C, TRAIT_HULK))
- C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk")
- else if(!C.check_stun_immunity(CANKNOCKDOWN))
- addtimer(CALLBACK(C, TYPE_PROC_REF(/mob/living/carbon, do_jitter_animation), 20), 0.5 SECONDS)
+ if(pierce_hit)
+ return
+ if(. == BULLET_ACT_BLOCK || blocked >= 100 || !isliving(target))
+ return
+ // we need a "from", otherwise, where does the electricity come from?
+ if(isnull(fired_from))
+ target.visible_message(
+ span_warning("[src]\s collide with [target] harmlessly[isfloorturf(target.loc) ? ", before falling to [target.loc]" : ""]."),
+ span_notice("[src] collide with you harmlessly[isfloorturf(target.loc) ? ", before falling to [target.loc]" : ""]."),
+ )
+ return
+
+ do_sparks(1, TRUE, src)
+ do_sparks(1, TRUE, fired_from)
+ target.apply_status_effect(
+ /*type = *//datum/status_effect/tased,
+ /*taser = */fired_from,
+ /*firer = */initial_firer_weakref?.resolve() || firer,
+ /*tase_stamina = */tase_stamina,
+ /*energy_drain = */STANDARD_CELL_CHARGE * 0.05,
+ /*electrode_name = */"\the [src]\s",
+ /*tase_range = */maximum_range + 1,
+ )
/obj/projectile/energy/electrode/on_range() //to ensure the bolt sparks when it reaches the end of its range if it didn't hit a target yet
do_sparks(1, TRUE, src)
- ..()
+ return ..()
+
+/obj/projectile/energy/electrode/ai_turrets
+ tase_stamina = 120
+
+/// Status effect tracking being tased by someone!
+/datum/status_effect/tased
+ id = "being_tased"
+ status_type = STATUS_EFFECT_MULTIPLE
+ alert_type = null
+ tick_interval = 0.25 SECONDS
+ on_remove_on_mob_delete = TRUE
+ /// What atom is tasing us?
+ VAR_PRIVATE/datum/taser
+ /// What atom is using the atom tasing us? Sometimes the same as the taser, such as with turrets.
+ VAR_PRIVATE/atom/movable/firer
+ /// The beam datum representing the taser electrodes
+ VAR_PRIVATE/datum/beam/tase_line
+ /// How much stamina damage does it aim to cause in a second?
+ VAR_FINAL/stamina_per_second = 80
+ /// How much energy does the taser use per tick?
+ VAR_FINAL/energy_drain = STANDARD_CELL_CHARGE * 0.05
+ /// What do we name the electrodes?
+ VAR_FINAL/electrode_name
+ /// How far can the taser reach?
+ VAR_FINAL/tase_range = 6
+
+/datum/status_effect/tased/on_creation(
+ mob/living/new_owner,
+ datum/fired_from,
+ atom/movable/firer,
+ tase_stamina = 80,
+ energy_drain = STANDARD_CELL_CHARGE * 0.05,
+ electrode_name = "the electrodes",
+ tase_range = 6,
+)
+ if(isnull(fired_from) || isnull(firer) || !can_tase_with(fired_from))
+ qdel(src)
+ return
+
+ src.stamina_per_second = tase_stamina
+ src.energy_drain = energy_drain
+ src.electrode_name = electrode_name
+ src.tase_range = tase_range
+
+ . = ..()
+ if(!.)
+ return
+
+ set_taser(fired_from)
+ set_firer(firer)
+
+/// Checks if the passed atom is captable of being used to tase someone
+/datum/status_effect/tased/proc/can_tase_with(datum/with_what)
+ if(istype(with_what, /obj/item/gun/energy))
+ var/obj/item/gun/energy/taser_gun = with_what
+ if(isnull(taser_gun.cell))
+ return FALSE
+
+ else if(istype(with_what, /obj/machinery))
+ var/obj/machinery/taser_machine = with_what
+ if(!taser_machine.is_operational)
+ return FALSE
+
+ return TRUE
+
+/// Actually does the tasing with the passed atom
+/// Returns TRUE if the tasing was successful, FALSE if it failed
+/datum/status_effect/tased/proc/do_tase_with(atom/with_what, seconds_between_ticks)
+ if(!can_see(taser, owner, 5))
+ return FALSE
+ if(istype(with_what, /obj/item/gun/energy))
+ var/obj/item/gun/energy/taser_gun = with_what
+ if(!taser_gun.cell?.use(energy_drain * seconds_between_ticks))
+ return FALSE
+ taser_gun.update_appearance()
+ return TRUE
+
+ if(istype(taser, /obj/machinery))
+ var/obj/machinery/taser_machine = taser
+ if(!taser_machine.is_operational)
+ return FALSE
+ if(!taser_machine.use_energy(energy_drain * seconds_between_ticks, force = FALSE))
+ return FALSE
+ return TRUE
+
+ if(istype(taser, /obj/item/mecha_parts/mecha_equipment))
+ var/obj/item/mecha_parts/mecha_equipment/taser_equipment = taser
+ if(!taser_equipment.chassis \
+ || !taser_equipment.active \
+ || taser_equipment.get_integrity() <= 1 \
+ || taser_equipment.chassis.is_currently_ejecting \
+ || taser_equipment.chassis.equipment_disabled \
+ || !taser_equipment.chassis.use_energy(energy_drain * seconds_between_ticks))
+ return FALSE
+ return TRUE
+
+ return TRUE
+
+/datum/status_effect/tased/on_apply()
+ if(issilicon(owner) || isbot(owner) || isdrone(owner) || HAS_TRAIT(owner, TRAIT_PIERCEIMMUNE))
+ owner.visible_message(span_warning("[capitalize(electrode_name)] fail to catch [owner][isfloorturf(owner.loc) ? ", falling to [owner.loc]" : ""]!"))
+ return FALSE
+
+ RegisterSignal(owner, COMSIG_LIVING_RESIST, PROC_REF(try_remove_taser))
+ RegisterSignal(owner, COMSIG_CARBON_PRE_MISC_HELP, PROC_REF(someome_removing_taser))
+ SEND_SIGNAL(owner, COMSIG_LIVING_MINOR_SHOCK)
+ if(!owner.has_status_effect(type))
+ // does not use the status effect api because we snowflake it a bit
+ owner.throw_alert(type, /atom/movable/screen/alert/tazed)
+ owner.add_mood_event("tased", /datum/mood_event/tased)
+ owner.add_movespeed_modifier(/datum/movespeed_modifier/being_tased)
+ if(!HAS_TRAIT(owner, TRAIT_ANALGESIA))
+ owner.emote("scream")
+ if(HAS_TRAIT(owner, TRAIT_HULK))
+ owner.say(pick(
+ ";RAAAAAAAARGH!",
+ ";HNNNNNNNNNGGGGGGH!",
+ ";GWAAAAAAAARRRHHH!",
+ "NNNNNNNNGGGGGGGGHH!",
+ ";AAAAAAARRRGH!",
+ ), forced = "hulk")
+ if(ishuman(owner))
+ var/mob/living/carbon/human/human_owner = owner
+ human_owner.force_say()
+ return TRUE
+
+/datum/status_effect/tased/on_remove()
+ if(istype(taser, /obj/machinery/porta_turret))
+ var/obj/machinery/porta_turret/taser_turret = taser
+ taser_turret.manual_control = initial(taser_turret.manual_control)
+ taser_turret.always_up = initial(taser_turret.always_up)
+ taser_turret.check_should_process()
+ else if(istype(taser, /obj/machinery/power/emitter))
+ var/obj/machinery/power/emitter/taser_emitter = taser
+ taser_emitter.manual = initial(taser_emitter.manual)
+
+ var/mob/living/mob_firer = firer
+ if(istype(mob_firer))
+ mob_firer.remove_movespeed_modifier(/datum/movespeed_modifier/tasing_someone)
+
+ if(!QDELING(owner) && !owner.has_status_effect(type))
+ owner.adjust_jitter_up_to(10 SECONDS, 1 MINUTES)
+ owner.remove_movespeed_modifier(/datum/movespeed_modifier/being_tased)
+ owner.clear_alert(type)
+
+ taser = null
+ firer = null
+ QDEL_NULL(tase_line)
+
+/datum/status_effect/tased/tick(seconds_between_ticks)
+ if(!do_tase_with(taser, seconds_between_ticks))
+ end_tase()
+ return
+
+ owner.adjust_stutter_up_to(10 SECONDS, 20 SECONDS)
+ owner.adjust_jitter_up_to(20 SECONDS, 30 SECONDS)
+ if(owner.stat <= SOFT_CRIT)
+ owner.do_jitter_animation(INFINITY) // maximum POWER
+
+ // You are damp, that's bad when you're being tased
+ if(owner.fire_stacks < 0)
+ owner.apply_damage(max(1, owner.fire_stacks * -0.5 * seconds_between_ticks), FIRE, spread_damage = TRUE)
+ if(SPT_PROB(25, seconds_between_ticks))
+ do_sparks(1, FALSE, owner)
+
+ // clumsy people might hit their head while being tased
+ if(HAS_TRAIT(owner, TRAIT_CLUMSY) && owner.body_position == LYING_DOWN && SPT_PROB(20, seconds_between_ticks))
+ owner.apply_damage(10, BRUTE, BODY_ZONE_HEAD)
+ playsound(owner, 'sound/effects/tableheadsmash.ogg', 75, TRUE)
+
+ // the actual stunning is here
+ if(!owner.check_stun_immunity(CANSTUN|CANKNOCKDOWN))
+ owner.apply_damage(stamina_per_second * seconds_between_ticks, STAMINA)
+
+/// Sets the passed atom as the "taser"
+/datum/status_effect/tased/proc/set_taser(datum/new_taser)
+ taser = new_taser
+ RegisterSignals(taser, list(COMSIG_QDELETING, COMSIG_ITEM_DROPPED, COMSIG_ITEM_EQUIPPED), PROC_REF(end_tase))
+ RegisterSignal(taser, COMSIG_GUN_TRY_FIRE, PROC_REF(block_firing))
+ // snowflake cases! yay!
+ if(istype(taser, /obj/machinery/porta_turret))
+ var/obj/machinery/porta_turret/taser_turret = taser
+ taser_turret.manual_control = TRUE
+ taser_turret.always_up = TRUE
+ else if(istype(taser, /obj/machinery/power/emitter))
+ var/obj/machinery/power/emitter/taser_emitter = taser
+ taser_emitter.manual = TRUE
+
+/// Sets the passed atom as the person operating the taser, the "firer"
+/datum/status_effect/tased/proc/set_firer(atom/new_firer)
+ firer = new_firer
+ if(taser != firer) // Turrets, notably, are both
+ RegisterSignal(firer, COMSIG_QDELETING, PROC_REF(end_tase))
+
+ RegisterSignal(firer, COMSIG_MOB_CLICKON, PROC_REF(user_cancel_tase))
+
+ // Ensures AI mobs or turrets don't tase players until they run out of power
+ var/mob/living/mob_firer = new_firer
+ if(!istype(mob_firer) || isnull(mob_firer.client))
+ // If multiple things are tasing the same mob, give up sooner, so they can select a new target potentially
+ addtimer(CALLBACK(src, PROC_REF(end_tase)), (owner.has_status_effect(type) != src) ? 2 SECONDS : 8 SECONDS)
+ if(istype(mob_firer))
+ mob_firer.add_movespeed_modifier(/datum/movespeed_modifier/tasing_someone)
+
+ if(firer == owner)
+ return
+
+ tase_line = firer.Beam(
+ BeamTarget = owner,
+ icon = 'icons/effects/beam.dmi',
+ icon_state = "electrodes",
+ maxdistance = tase_range,
+ beam_type = /obj/effect/ebeam/reacting/electrodes,
+ )
+ RegisterSignal(tase_line, COMSIG_BEAM_ENTERED, PROC_REF(disrupt_tase))
+ RegisterSignal(tase_line, COMSIG_QDELETING, PROC_REF(end_tase))
+ // moves the tase beam up or down if the target moves up or down
+ tase_line.RegisterSignal(owner, COMSIG_LIVING_SET_BODY_POSITION, TYPE_PROC_REF(/datum/beam, redrawing))
+
+/datum/status_effect/tased/proc/block_firing(...)
+ SIGNAL_HANDLER
+ return COMPONENT_CANCEL_GUN_FIRE
+
+/datum/status_effect/tased/proc/user_cancel_tase(mob/living/source, atom/clicked_on, modifiers)
+ SIGNAL_HANDLER
+ if(clicked_on != owner)
+ return NONE
+ if(LAZYACCESS(modifiers, SHIFT_CLICK))
+ return NONE
+ end_tase()
+ source.changeNext_move(CLICK_CD_GRABBING)
+ return COMSIG_MOB_CANCEL_CLICKON
+
+/datum/status_effect/tased/proc/end_tase(...)
+ SIGNAL_HANDLER
+ if(QDELING(src))
+ return
+ owner.visible_message(
+ span_warning("[capitalize(electrode_name)] stop shocking [owner][isfloorturf(owner.loc) ? ", falling to [owner.loc]" : ""]."),
+ span_notice("[capitalize(electrode_name)] stop shocking you[isfloorturf(owner.loc) ? ", falling to [owner.loc]" : ""]."),
+ )
+ qdel(src)
+
+/datum/status_effect/tased/proc/try_remove_taser(datum/source)
+ SIGNAL_HANDLER
+ INVOKE_ASYNC(src, PROC_REF(try_remove_taser_async), owner)
+
+/datum/status_effect/tased/proc/someome_removing_taser(datum/source, mob/living/helper)
+ SIGNAL_HANDLER
+ INVOKE_ASYNC(src, PROC_REF(try_remove_taser_async), helper)
+ return COMPONENT_BLOCK_MISC_HELP
+
+/datum/status_effect/tased/proc/try_remove_taser_async(mob/living/remover)
+ if(DOING_INTERACTION(remover, id))
+ return
+ owner.shake_up_animation()
+ playsound(owner, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ remover.visible_message(
+ span_warning("[owner] tries to remove [electrode_name][remover == owner ? "" : " from [owner]"]!"),
+ span_notice("You try to remove [electrode_name][remover == owner ? "" : " from [owner]"]!"),
+ )
+ // If embedding was less... difficult to work with, I would make tasers rely on an embedded object to handle this
+ if(!do_after(remover, 5 SECONDS, owner, extra_checks = CALLBACK(src, PROC_REF(try_remove_taser_checks)), interaction_key = id))
+ return
+ remover.visible_message(
+ span_warning("[owner] removes [electrode_name] from [remover == owner ? "[owner.p_their()]" : "[owner]'s"] body!"),
+ span_notice("You remove [electrode_name][remover == owner ? "" : " from [owner]'s body"]!"),
+ )
+ end_tase()
+
+/datum/status_effect/tased/proc/try_remove_taser_checks()
+ return !QDELETED(src)
+
+/datum/status_effect/tased/proc/disrupt_tase(datum/beam/source, obj/effect/ebeam/beam_effect, atom/movable/entering)
+ SIGNAL_HANDLER
+
+ if(!isliving(entering) || entering == taser || entering == firer || entering == owner)
+ return
+ if(entering.pass_flags & (PASSMOB|PASSGRILLE|PASSTABLE))
+ return
+ var/mob/living/disruptor = entering
+ if(!HAS_TRAIT(entering, TRAIT_CLUMSY) || prob(50))
+ if(isliving(firer))
+ // taser firer can lie down so people can cross over it!
+ var/mob/living/firer_living = firer
+ if(firer_living.body_position != disruptor.body_position)
+ return
+ else
+ // otherwise you can limbo under it
+ if(disruptor.body_position == LYING_DOWN)
+ return
+ disruptor.visible_message(
+ span_warning("[disruptor] gets tangled in [electrode_name]!"),
+ span_warning("You get tangled in [electrode_name]!"),
+ )
+ if(!disruptor.check_stun_immunity(CANSTUN|CANKNOCKDOWN))
+ disruptor.apply_damage(90, STAMINA)
+ disruptor.Knockdown(5 SECONDS)
+ disruptor.adjust_jitter_up_to(10 SECONDS, 30 SECONDS)
+ qdel(src)
+
+/// Screen alert for being tased, clicking does a resist
+/atom/movable/screen/alert/tazed
+ name = "Tased!"
+ desc = "You're being tased! You can click this or resist to attempt to stop it, assuming you've not already collapsed."
+ icon_state = "stun"
+ clickable_glow = TRUE
+
+/atom/movable/screen/alert/tazed/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+ var/mob/living/clicker = usr
+ clicker.resist()
+
+/obj/effect/ebeam/electrodes_nozap
+ name = "electrodes"
+ alpha = 192
+
+/obj/effect/ebeam/reacting/electrodes
+ name = "electrodes"
+ light_system = OVERLAY_LIGHT
+ light_on = TRUE
+ light_color = COLOR_YELLOW
+ light_power = 1
+ light_range = 1.5
+
+// movespeed mods
+/datum/movespeed_modifier/tasing_someone
+ multiplicative_slowdown = 2
+
+/datum/movespeed_modifier/being_tased
+ multiplicative_slowdown = 4
diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm
index 28c299c87f0..4a4bf137b3c 100644
--- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm
@@ -865,3 +865,54 @@
)
new /obj/structure/bouncy_castle(gored.loc, gored)
gored.gib()
+
+/datum/reagent/drug/syndol
+ name = "Syndol"
+ description = "A potent and addictive hallucinogen used by syndicate agents disorient certain targets. \
+ It is said that the hallucinations it causes are tailored to the user's fears, but tests have been inconclusive, \
+ with subjects in security and assistants reporting wildly different experiences."
+ color = "#c90000"
+ taste_description = "metallic"
+ ph = 7
+ overdose_threshold = 10
+ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
+ addiction_types = list(/datum/addiction/hallucinogens = 20)
+ /// Track the active hallucination we're giving out so we don't replace it by accident
+ VAR_PRIVATE/datum/weakref/active_hallucination_weakref
+
+/datum/reagent/drug/syndol/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
+ . = ..()
+ var/obj/item/organ/liver = affected_mob.get_organ_slot(ORGAN_SLOT_LIVER)
+ if(isnull(liver) || !(liver.organ_flags & affected_organ_flags))
+ return
+ // non-trivial but not immediately dangerous liver damage
+ liver.apply_organ_damage(0.5 * REM * seconds_per_tick)
+ // anti-hallucinogens can counteract the effects
+ if(HAS_TRAIT(affected_mob, TRAIT_HALLUCINATION_IMMUNE) || affected_mob.reagents.has_reagent(/datum/reagent/medicine/haloperidol, amount = 3, needs_metabolizing = TRUE))
+ QDEL_NULL(active_hallucination_weakref)
+ return
+
+ // and the main event, funny hallucinations
+ if(active_hallucination_weakref?.resolve())
+ return
+ var/greatest_fear
+ if(HAS_TRAIT(liver, TRAIT_LAW_ENFORCEMENT_METABOLISM))
+ greatest_fear = /datum/hallucination/delusion/preset/syndies
+ else if(HAS_TRAIT(liver, TRAIT_MAINTENANCE_METABOLISM) || HAS_TRAIT(liver, TRAIT_COMEDY_METABOLISM))
+ greatest_fear = /datum/hallucination/delusion/preset/seccies
+
+ if(greatest_fear)
+ // 5 minutes = 15 units, roughly. we cancel the hallucination early when we exit the mob, anyway
+ active_hallucination_weakref = WEAKREF(affected_mob.cause_hallucination(greatest_fear, name, duration = 5 MINUTES, skip_nearby = !overdosed))
+ else
+ // if they're just some random schmuck, give them random hallucinations
+ affected_mob.adjust_hallucinations_up_to(4 SECONDS * REM * seconds_per_tick, 20 SECONDS)
+
+/datum/reagent/drug/syndol/on_mob_end_metabolize(mob/living/affected_mob)
+ . = ..()
+ affected_mob.adjust_hallucinations(-16 SECONDS)
+ QDEL_NULL(active_hallucination_weakref)
+
+/datum/reagent/drug/syndol/overdose_start(mob/living/affected_mob)
+ // no message, just refresh the hallucination
+ QDEL_NULL(active_hallucination_weakref)
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index d82f8d8d59a..51ca08f6b8d 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -1400,12 +1400,7 @@
if(reac_volume < 1)
return
- exposed_turf.wash(clean_types)
- for(var/am in exposed_turf)
- var/atom/movable/movable_content = am
- if(ismopable(movable_content)) // Mopables will be cleaned anyways by the turf wash
- continue
- movable_content.wash(clean_types)
+ exposed_turf.wash(clean_types, TRUE)
for(var/mob/living/basic/slime/exposed_slime in exposed_turf)
exposed_slime.adjustToxLoss(rand(5,10))
diff --git a/code/modules/reagents/reagent_containers/cups/bottle.dm b/code/modules/reagents/reagent_containers/cups/bottle.dm
index 4c757e0e146..2a81c39886b 100644
--- a/code/modules/reagents/reagent_containers/cups/bottle.dm
+++ b/code/modules/reagents/reagent_containers/cups/bottle.dm
@@ -37,6 +37,11 @@
desc = "A small bottle of spewium."
list_reagents = list(/datum/reagent/toxin/spewium = 30)
+/obj/item/reagent_containers/cup/bottle/syndol
+ name = "syndol bottle"
+ desc = "A small bottle of syndol."
+ list_reagents = list(/datum/reagent/drug/syndol = 30)
+
/obj/item/reagent_containers/cup/bottle/morphine
name = "morphine bottle"
desc = "A small bottle of morphine."
diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
index bd1a1cd5a35..93f16140d54 100644
--- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm
+++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
@@ -10,6 +10,7 @@
icon = 'icons/obj/drinks/bottles.dmi'
icon_state = "glassbottle"
worn_icon_state = "bottle"
+ icon_angle = 90
fill_icon_thresholds = list(0, 10, 20, 30, 40, 50, 60, 70, 80, 90)
custom_price = PAYCHECK_CREW * 1.1
amount_per_transfer_from_this = 10
@@ -699,7 +700,7 @@
if(spillable)
return
- if(attacking_item.sharpness != SHARP_EDGED)
+ if(attacking_item.get_sharpness() != SHARP_EDGED)
return
if(attacking_item != user.get_active_held_item()) //no TK allowed
diff --git a/code/modules/religion/sparring/ceremonial_gear.dm b/code/modules/religion/sparring/ceremonial_gear.dm
index 2c7e73b5a75..d755704a6d3 100644
--- a/code/modules/religion/sparring/ceremonial_gear.dm
+++ b/code/modules/religion/sparring/ceremonial_gear.dm
@@ -5,6 +5,7 @@
icon_state = "default"
inhand_icon_state = "default"
icon = 'icons/obj/weapons/ritual_weapon.dmi'
+ icon_angle = -45
//does the exact thing we want so heck why not
greyscale_config = /datum/greyscale_config/ceremonial_blade
@@ -18,8 +19,8 @@
throwforce = 1 //10
wound_bonus = CANT_WOUND // bad for sparring
w_class = WEIGHT_CLASS_NORMAL
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
block_chance = 3 //30
block_sound = 'sound/items/weapons/parry.ogg'
sharpness = SHARP_EDGED
@@ -27,6 +28,8 @@
material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_GREYSCALE //doesn't affect stats of the weapon as to avoid gamering your opponent with a dope weapon
armor_type = /datum/armor/item_ceremonial_blade
resistance_flags = FIRE_PROOF
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
/datum/armor/item_ceremonial_blade
fire = 100
@@ -34,6 +37,9 @@
/obj/item/ceremonial_blade/Initialize(mapload)
. = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple)
AddComponent(/datum/component/butchering, \
speed = 4 SECONDS, \
effectiveness = 105, \
diff --git a/code/modules/research/designs/autolathe/security_designs.dm b/code/modules/research/designs/autolathe/security_designs.dm
index b3bc0d8101d..7fbf06e122f 100644
--- a/code/modules/research/designs/autolathe/security_designs.dm
+++ b/code/modules/research/designs/autolathe/security_designs.dm
@@ -179,7 +179,7 @@
id = "a357"
build_type = AUTOLATHE
materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*2)
- build_path = /obj/item/ammo_casing/a357
+ build_path = /obj/item/ammo_casing/c357
category = list(
RND_CATEGORY_HACKED,
RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO,
diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm
index ebda0f1e3b4..872821e9827 100644
--- a/code/modules/research/designs/weapon_designs.dm
+++ b/code/modules/research/designs/weapon_designs.dm
@@ -3,7 +3,7 @@
/////////////////////////////////////////
/datum/design/c38/sec
- id = "sec_38"
+ id = "c38_sec"
build_type = PROTOLATHE | AWAY_LATHE
category = list(
RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
@@ -16,7 +16,11 @@
desc = "Designed to quickly reload revolvers. TRAC bullets embed a tracking implant within the target's body. The implant's signal is incompatible with teleporters."
id = "c38_trac"
build_type = PROTOLATHE | AWAY_LATHE
- materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 10, /datum/material/silver =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/gold =HALF_SHEET_MATERIAL_AMOUNT)
+ materials = list(
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 10,
+ /datum/material/silver = HALF_SHEET_MATERIAL_AMOUNT * 2.5,
+ /datum/material/gold = HALF_SHEET_MATERIAL_AMOUNT,
+ )
build_path = /obj/item/ammo_box/c38/trac
category = list(
RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
@@ -28,7 +32,10 @@
desc = "Designed to quickly reload revolvers. Hot Shot bullets contain an incendiary payload."
id = "c38_hotshot"
build_type = PROTOLATHE | AWAY_LATHE
- materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 10, /datum/material/plasma = SHEET_MATERIAL_AMOUNT * 2.5)
+ materials = list(
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 10,
+ /datum/material/plasma = HALF_SHEET_MATERIAL_AMOUNT * 2.5,
+ )
build_path = /obj/item/ammo_box/c38/hotshot
category = list(
RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
@@ -40,7 +47,10 @@
desc = "Designed to quickly reload revolvers. Iceblox bullets contain a cryogenic payload."
id = "c38_iceblox"
build_type = PROTOLATHE | AWAY_LATHE
- materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 10, /datum/material/plasma = SHEET_MATERIAL_AMOUNT * 2.5)
+ materials = list(
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 10,
+ /datum/material/plasma = HALF_SHEET_MATERIAL_AMOUNT * 2.5,
+ )
build_path = /obj/item/ammo_box/c38/iceblox
category = list(
RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
@@ -52,13 +62,125 @@
desc = "Designed to quickly reload revolvers. Rubber bullets are bouncy and less-than-lethal."
id = "c38_rubber"
build_type = PROTOLATHE | AWAY_LATHE
- materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 10)
+ materials = list(/datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 10)
build_path = /obj/item/ammo_box/c38/match/bouncy
category = list(
RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
)
departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+/datum/design/c38_true
+ name = "Speedloader (.38 True Strike) (Lethal)"
+ desc = "Designed to quickly reload revolvers. Bullets bounce towards new targets with surprising accuracy."
+ id = "c38_true_strike"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 10,
+ /datum/material/bluespace = HALF_SHEET_MATERIAL_AMOUNT,
+ )
+ build_path = /obj/item/ammo_box/magazine/m38/true
+ category = list(
+ RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
+/datum/design/c38_mag
+ name = "Magazine (.38) (Lethal)"
+ desc = "Designed to tactically reload a NT BR-38 Battle Rifle. Less powerful by design, guns chambered in .38 caliber rounds are still quite popular for use by police forces, \
+ private security firms and organizations unable to access energy-based nonlethal weaponry. The lower (relative) penetrative power is useful for preventing potential hull damage \
+ aboard space stations and shuttles."
+ id = "c38_mag"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 30,
+ /datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT * 3,
+ )
+ build_path = /obj/item/ammo_box/magazine/m38
+ category = list(
+ RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
+/datum/design/c38_trac_mag
+ name = "Magazine (.38 TRAC) (Less Lethal)"
+ desc = "Designed to tactically reload a NT BR-38 Battle Rifle. TRAC bullets embed a tracking implant within the target's body. The implant's signal is incompatible with teleporters."
+ id = "c38_trac_mag"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 30,
+ /datum/material/silver = HALF_SHEET_MATERIAL_AMOUNT * 2.5,
+ /datum/material/gold = HALF_SHEET_MATERIAL_AMOUNT,
+ /datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT * 3,
+ )
+ build_path = /obj/item/ammo_box/magazine/m38/trac
+ category = list(
+ RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
+/datum/design/c38_hotshot_mag
+ name = "Magazine (.38 Hot Shot) (Very Lethal)"
+ desc = "Designed to tactically reload a NT BR-38 Battle Rifle. Hot Shot bullets contain an incendiary payload."
+ id = "c38_hotshot_mag"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 30,
+ /datum/material/plasma = HALF_SHEET_MATERIAL_AMOUNT * 2.5,
+ /datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT * 3,
+ )
+ build_path = /obj/item/ammo_box/magazine/m38/hotshot
+ category = list(
+ RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
+/datum/design/c38_iceblox_mag
+ name = "Magazine (.38 Iceblox) (Lethal/Very Lethal (Lizardpeople))"
+ desc = "Designed to tactically reload a NT BR-38 Battle Rifle. Iceblox bullets contain a cryogenic payload."
+ id = "c38_iceblox_mag"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 30,
+ /datum/material/plasma = HALF_SHEET_MATERIAL_AMOUNT * 2.5,
+ /datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT * 3,
+ )
+ build_path = /obj/item/ammo_box/magazine/m38/iceblox
+ category = list(
+ RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
+/datum/design/c38_rubber_mag
+ name = "Magazine (.38 Rubber) (Less Lethal)"
+ desc = "Designed to tactically reload a NT BR-38 Battle Rifle. Rubber bullets are bouncy and less-than-lethal."
+ id = "c38_rubber_mag"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 30,
+ /datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT * 3,
+ )
+ build_path = /obj/item/ammo_box/magazine/m38/match/bouncy
+ category = list(
+ RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
+/datum/design/c38_true_mag
+ name = "Magazine (.38 Truee Strike) (Lethal)"
+ desc = "Designed to tactically reload a NT BR-38 Battle Rifle. Bullets bounce towards new targets with surprising accuracy."
+ id = "c38_true_strike_mag"
+ build_type = PROTOLATHE | AWAY_LATHE
+ materials = list(
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 30,
+ /datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT * 3,
+ /datum/material/bluespace = HALF_SHEET_MATERIAL_AMOUNT,
+ )
+ build_path = /obj/item/ammo_box/magazine/m38/true
+ category = list(
+ RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_SECURITY
+
/datum/design/rubbershot/sec
id = "sec_rshot"
desc = "Rubbershot shotgun shells. Fires a cloud of pellets. Rubber bullets are bouncy and less-than-lethal."
diff --git a/code/modules/research/part_replacer.dm b/code/modules/research/part_replacer.dm
new file mode 100644
index 00000000000..6c5bb5f6464
--- /dev/null
+++ b/code/modules/research/part_replacer.dm
@@ -0,0 +1,186 @@
+///RPED. Allows installing & exchaging parts on machines
+/obj/item/storage/part_replacer
+ name = "rapid part exchange device"
+ desc = "Special mechanical module made to store, sort, and apply standard machine parts."
+ icon_state = "RPED"
+ inhand_icon_state = "RPED"
+ worn_icon_state = "RPED"
+ lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
+ w_class = WEIGHT_CLASS_HUGE
+ storage_type = /datum/storage/rped
+
+/obj/item/storage/part_replacer/interact_with_atom(obj/attacked_object, mob/living/user, list/modifiers)
+ if(user.combat_mode || !istype(attacked_object) || HAS_TRAIT(attacked_object, TRAIT_COMBAT_MODE_SKIP_INTERACTION))
+ return ITEM_INTERACT_SKIP_TO_ATTACK
+
+ //its very important to NOT block so frames can still interact with it
+ if(!ismachinery(attacked_object) || istype(attacked_object, /obj/machinery/computer))
+ return NONE
+
+ var/obj/machinery/attacked_machinery = attacked_object
+ if(!LAZYLEN(attacked_machinery.component_parts))
+ return ITEM_INTERACT_FAILURE
+
+ return attacked_machinery.exchange_parts(user, src) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_FAILURE
+
+///Plays the sound for RPED exhanging or installing parts.
+/obj/item/storage/part_replacer/proc/play_rped_sound()
+ playsound(src, 'sound/items/tools/rped.ogg', 40, TRUE)
+
+/**
+ * Gets parts sorted in order of their tier
+ * Arguments
+ *
+ * * ignore_stacks - should the final list contain stacks
+ */
+/obj/item/storage/part_replacer/proc/get_sorted_parts(ignore_stacks = FALSE)
+ RETURN_TYPE(/list/obj/item)
+
+ var/list/obj/item/part_list = list()
+ //Assemble a list of current parts, then sort them by their rating!
+ for(var/obj/item/component_part in contents)
+ //No need to put circuit boards in this list or stacks when exchanging parts
+ if(istype(component_part, /obj/item/circuitboard) || (ignore_stacks && istype(component_part, /obj/item/stack)))
+ continue
+ part_list += component_part
+ //Sort the parts. This ensures that higher tier items are applied first.
+ sortTim(part_list, GLOBAL_PROC_REF(cmp_rped_sort))
+
+ return part_list
+
+///Bluespace RPED. Allows exchanging parts from a distance & through cameras
+/obj/item/storage/part_replacer/bluespace
+ name = "bluespace rapid part exchange device"
+ desc = "A version of the RPED that allows for replacement of parts and scanning from a distance, along with higher capacity for parts."
+ icon_state = "BS_RPED"
+ inhand_icon_state = "BS_RPED"
+ w_class = WEIGHT_CLASS_NORMAL
+ storage_type = /datum/storage/rped/bluespace
+
+/obj/item/storage/part_replacer/bluespace/Initialize(mapload)
+ . = ..()
+
+ RegisterSignal(src, COMSIG_ATOM_ENTERED, PROC_REF(on_part_entered))
+ RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_part_exited))
+
+/obj/item/storage/part_replacer/bluespace/interact_with_atom(obj/attacked_object, mob/living/user, list/modifiers)
+ . = ..()
+ if(. & ITEM_INTERACT_ANY_BLOCKER)
+ user.Beam(attacked_object, icon_state = "rped_upgrade", time = 0.5 SECONDS)
+
+/obj/item/storage/part_replacer/bluespace/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ return interact_with_atom(interacting_with, user, modifiers)
+
+/obj/item/storage/part_replacer/bluespace/play_rped_sound()
+ if(prob(1))
+ playsound(src, 'sound/items/pshoom/pshoom_2.ogg', 40, TRUE)
+ return
+ playsound(src, 'sound/items/pshoom/pshoom.ogg', 40, TRUE)
+
+/**
+ * Signal handler for when a part has been inserted into the BRPED.
+ *
+ * If the inserted item is a rigged or corrupted cell, does some logging.
+ *
+ * If it has a reagent holder, clears the reagents and registers signals to prevent new
+ * reagents being added and registers clean up signals on inserted item's removal from
+ * the BRPED.
+ */
+/obj/item/storage/part_replacer/bluespace/proc/on_part_entered(datum/source, obj/item/inserted_component)
+ SIGNAL_HANDLER
+
+ if(istype(inserted_component, /obj/item/stock_parts/power_store))
+ var/obj/item/stock_parts/power_store/inserted_cell = inserted_component
+ if(inserted_cell.rigged || inserted_cell.corrupted)
+ message_admins("[ADMIN_LOOKUPFLW(usr)] has inserted rigged/corrupted [inserted_cell] into [src].")
+ usr.log_message("has inserted rigged/corrupted [inserted_cell] into [src].", LOG_GAME)
+ usr.log_message("inserted rigged/corrupted [inserted_cell] into [src]", LOG_ATTACK)
+ return
+
+ if(inserted_component.reagents)
+ if(length(inserted_component.reagents.reagent_list))
+ inserted_component.reagents.clear_reagents()
+ to_chat(usr, span_notice("[src] churns as [inserted_component] has its reagents emptied into bluespace."))
+ RegisterSignal(inserted_component.reagents, COMSIG_REAGENTS_PRE_ADD_REAGENT, PROC_REF(on_insered_component_reagent_pre_add))
+
+/**
+ * Signal handler for when the reagents datum of an inserted part has reagents added to it.
+ *
+ * Registers the PRE_ADD variant which allows the signal handler to stop reagents being
+ * added.
+ *
+ * Simply returns COMPONENT_CANCEL_REAGENT_ADD. We never want to allow people to add
+ * reagents to beakers in BRPEDs as they can then be used for spammable remote bombing.
+ */
+/obj/item/storage/part_replacer/bluespace/proc/on_insered_component_reagent_pre_add(datum/source, reagent, amount, reagtemp, data, no_react)
+ SIGNAL_HANDLER
+
+ return COMPONENT_CANCEL_REAGENT_ADD
+
+/**
+ * Signal handler for a part is removed from the BRPED.
+ *
+ * Does signal registration cleanup on its reagents, if it has any.
+ */
+/obj/item/storage/part_replacer/bluespace/proc/on_part_exited(datum/source, obj/item/removed_component)
+ SIGNAL_HANDLER
+
+ if(removed_component.reagents)
+ UnregisterSignal(removed_component.reagents, COMSIG_REAGENTS_PRE_ADD_REAGENT)
+
+//RPED with tiered contents
+/obj/item/storage/part_replacer/bluespace/tier1/PopulateContents()
+ for(var/i in 1 to 10)
+ new /obj/item/stock_parts/capacitor(src)
+ new /obj/item/stock_parts/scanning_module(src)
+ new /obj/item/stock_parts/servo(src)
+ new /obj/item/stock_parts/micro_laser(src)
+ new /obj/item/stock_parts/matter_bin(src)
+ new /obj/item/stock_parts/power_store/cell/high(src)
+
+/obj/item/storage/part_replacer/bluespace/tier2/PopulateContents()
+ for(var/i in 1 to 10)
+ new /obj/item/stock_parts/capacitor/adv(src)
+ new /obj/item/stock_parts/scanning_module/adv(src)
+ new /obj/item/stock_parts/servo/nano(src)
+ new /obj/item/stock_parts/micro_laser/high(src)
+ new /obj/item/stock_parts/matter_bin/adv(src)
+ new /obj/item/stock_parts/power_store/cell/super(src)
+
+/obj/item/storage/part_replacer/bluespace/tier3/PopulateContents()
+ for(var/i in 1 to 10)
+ new /obj/item/stock_parts/capacitor/super(src)
+ new /obj/item/stock_parts/scanning_module/phasic(src)
+ new /obj/item/stock_parts/servo/pico(src)
+ new /obj/item/stock_parts/micro_laser/ultra(src)
+ new /obj/item/stock_parts/matter_bin/super(src)
+ new /obj/item/stock_parts/power_store/cell/hyper(src)
+
+/obj/item/storage/part_replacer/bluespace/tier4/PopulateContents()
+ for(var/i in 1 to 10)
+ new /obj/item/stock_parts/capacitor/quadratic(src)
+ new /obj/item/stock_parts/scanning_module/triphasic(src)
+ new /obj/item/stock_parts/servo/femto(src)
+ new /obj/item/stock_parts/micro_laser/quadultra(src)
+ new /obj/item/stock_parts/matter_bin/bluespace(src)
+ new /obj/item/stock_parts/power_store/cell/bluespace(src)
+
+//used in a cargo crate
+/obj/item/storage/part_replacer/cargo/PopulateContents()
+ for(var/i in 1 to 10)
+ new /obj/item/stock_parts/capacitor(src)
+ new /obj/item/stock_parts/scanning_module(src)
+ new /obj/item/stock_parts/servo(src)
+ new /obj/item/stock_parts/micro_laser(src)
+ new /obj/item/stock_parts/matter_bin(src)
+
+///Cyborg variant
+/obj/item/storage/part_replacer/cyborg
+ name = "rapid part exchange device"
+ desc = "Special mechanical module made to store, sort, and apply standard machine parts. This one has an extra large compartment for more parts."
+ icon_state = "borgrped"
+ inhand_icon_state = "RPED"
+ lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
+ storage_type = /datum/storage/rped/bluespace
diff --git a/code/modules/research/stock_parts.dm b/code/modules/research/stock_parts.dm
index 95839344469..beafc7ba260 100644
--- a/code/modules/research/stock_parts.dm
+++ b/code/modules/research/stock_parts.dm
@@ -1,218 +1,6 @@
/*Power cells are in code\modules\power\cell.dm
If you create T5+ please take a pass at mech_fabricator.dm. The parts being good enough allows it to go into minus values and create materials out of thin air when printing stuff.*/
-/obj/item/storage/part_replacer
- name = "rapid part exchange device"
- desc = "Special mechanical module made to store, sort, and apply standard machine parts."
- icon_state = "RPED"
- inhand_icon_state = "RPED"
- worn_icon_state = "RPED"
- lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
- w_class = WEIGHT_CLASS_HUGE
- var/works_from_distance = FALSE
- var/pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/tools/rped.ogg'
- var/alt_sound = null
-
-/obj/item/storage/part_replacer/Initialize(mapload)
- . = ..()
- create_storage(storage_type = /datum/storage/rped)
-
-/obj/item/storage/part_replacer/proc/part_replace_action(obj/attacked_object, mob/living/user)
- if(!ismachinery(attacked_object) || istype(attacked_object, /obj/machinery/computer))
- return FALSE
-
- var/obj/machinery/attacked_machinery = attacked_object
- if(!LAZYLEN(attacked_machinery.component_parts))
- return FALSE
-
- if(attacked_machinery.exchange_parts(user, src) && works_from_distance)
- user.Beam(attacked_machinery, icon_state = "rped_upgrade", time = 0.5 SECONDS)
- return TRUE
-
-/obj/item/storage/part_replacer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- if(part_replace_action(interacting_with, user))
- return ITEM_INTERACT_SUCCESS
- return NONE
-
-/obj/item/storage/part_replacer/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- if(!works_from_distance)
- return NONE
- if(part_replace_action(interacting_with, user))
- user.Beam(interacting_with, icon_state = "rped_upgrade", time = 0.5 SECONDS)
- return ITEM_INTERACT_SUCCESS
- if(istype(interacting_with, /obj/structure/frame))
- // Cursed snowflake but we need to handle frame ranged interaction here
- // Likely no longer necessary with the new framework, revisit later
- interacting_with.item_interaction(user, src)
- user.Beam(interacting_with, icon_state = "rped_upgrade", time = 0.5 SECONDS)
- return ITEM_INTERACT_SUCCESS
- return NONE
-
-/obj/item/storage/part_replacer/proc/play_rped_sound()
- //Plays the sound for RPED exhanging or installing parts.
- if(alt_sound && prob(1))
- playsound(src, alt_sound, 40, TRUE)
- else
- playsound(src, pshoom_or_beepboopblorpzingshadashwoosh, 40, TRUE)
-
-/obj/item/storage/part_replacer/bluespace
- name = "bluespace rapid part exchange device"
- desc = "A version of the RPED that allows for replacement of parts and scanning from a distance, along with higher capacity for parts."
- icon_state = "BS_RPED"
- inhand_icon_state = "BS_RPED"
- w_class = WEIGHT_CLASS_NORMAL
- works_from_distance = TRUE
- pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/pshoom/pshoom.ogg'
- alt_sound = 'sound/items/pshoom/pshoom_2.ogg'
-
-/obj/item/storage/part_replacer/bluespace/Initialize(mapload)
- . = ..()
-
- atom_storage.max_slots = 400
- atom_storage.max_total_storage = 800
- atom_storage.max_specific_storage = WEIGHT_CLASS_GIGANTIC
-
- RegisterSignal(src, COMSIG_ATOM_ENTERED, PROC_REF(on_part_entered))
- RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_part_exited))
-
-/**
- * Signal handler for when a part has been inserted into the BRPED.
- *
- * If the inserted item is a rigged or corrupted cell, does some logging.
- *
- * If it has a reagent holder, clears the reagents and registers signals to prevent new
- * reagents being added and registers clean up signals on inserted item's removal from
- * the BRPED.
- */
-/obj/item/storage/part_replacer/bluespace/proc/on_part_entered(datum/source, obj/item/inserted_component)
- SIGNAL_HANDLER
-
- if(istype(inserted_component, /obj/item/stock_parts/power_store))
- var/obj/item/stock_parts/power_store/inserted_cell = inserted_component
- if(inserted_cell.rigged || inserted_cell.corrupted)
- message_admins("[ADMIN_LOOKUPFLW(usr)] has inserted rigged/corrupted [inserted_cell] into [src].")
- usr.log_message("has inserted rigged/corrupted [inserted_cell] into [src].", LOG_GAME)
- usr.log_message("inserted rigged/corrupted [inserted_cell] into [src]", LOG_ATTACK)
- return
-
- if(inserted_component.reagents)
- if(length(inserted_component.reagents.reagent_list))
- inserted_component.reagents.clear_reagents()
- to_chat(usr, span_notice("[src] churns as [inserted_component] has its reagents emptied into bluespace."))
- RegisterSignal(inserted_component.reagents, COMSIG_REAGENTS_PRE_ADD_REAGENT, PROC_REF(on_insered_component_reagent_pre_add))
-
-/**
- * Signal handler for when the reagents datum of an inserted part has reagents added to it.
- *
- * Registers the PRE_ADD variant which allows the signal handler to stop reagents being
- * added.
- *
- * Simply returns COMPONENT_CANCEL_REAGENT_ADD. We never want to allow people to add
- * reagents to beakers in BRPEDs as they can then be used for spammable remote bombing.
- */
-/obj/item/storage/part_replacer/bluespace/proc/on_insered_component_reagent_pre_add(datum/source, reagent, amount, reagtemp, data, no_react)
- SIGNAL_HANDLER
-
- return COMPONENT_CANCEL_REAGENT_ADD
-
-/**
- * Signal handler for a part is removed from the BRPED.
- *
- * Does signal registration cleanup on its reagents, if it has any.
- */
-/obj/item/storage/part_replacer/bluespace/proc/on_part_exited(datum/source, obj/item/removed_component)
- SIGNAL_HANDLER
-
- if(removed_component.reagents)
- UnregisterSignal(removed_component.reagents, COMSIG_REAGENTS_PRE_ADD_REAGENT)
-
-
-/obj/item/storage/part_replacer/bluespace/tier1
-
-/obj/item/storage/part_replacer/bluespace/tier1/PopulateContents()
- for(var/i in 1 to 10)
- new /obj/item/stock_parts/capacitor(src)
- new /obj/item/stock_parts/scanning_module(src)
- new /obj/item/stock_parts/servo(src)
- new /obj/item/stock_parts/micro_laser(src)
- new /obj/item/stock_parts/matter_bin(src)
- new /obj/item/stock_parts/power_store/cell/high(src)
-
-/obj/item/storage/part_replacer/bluespace/tier2
-
-/obj/item/storage/part_replacer/bluespace/tier2/PopulateContents()
- for(var/i in 1 to 10)
- new /obj/item/stock_parts/capacitor/adv(src)
- new /obj/item/stock_parts/scanning_module/adv(src)
- new /obj/item/stock_parts/servo/nano(src)
- new /obj/item/stock_parts/micro_laser/high(src)
- new /obj/item/stock_parts/matter_bin/adv(src)
- new /obj/item/stock_parts/power_store/cell/super(src)
-
-/obj/item/storage/part_replacer/bluespace/tier3
-
-/obj/item/storage/part_replacer/bluespace/tier3/PopulateContents()
- for(var/i in 1 to 10)
- new /obj/item/stock_parts/capacitor/super(src)
- new /obj/item/stock_parts/scanning_module/phasic(src)
- new /obj/item/stock_parts/servo/pico(src)
- new /obj/item/stock_parts/micro_laser/ultra(src)
- new /obj/item/stock_parts/matter_bin/super(src)
- new /obj/item/stock_parts/power_store/cell/hyper(src)
-
-/obj/item/storage/part_replacer/bluespace/tier4
-
-/obj/item/storage/part_replacer/bluespace/tier4/PopulateContents()
- for(var/i in 1 to 10)
- new /obj/item/stock_parts/capacitor/quadratic(src)
- new /obj/item/stock_parts/scanning_module/triphasic(src)
- new /obj/item/stock_parts/servo/femto(src)
- new /obj/item/stock_parts/micro_laser/quadultra(src)
- new /obj/item/stock_parts/matter_bin/bluespace(src)
- new /obj/item/stock_parts/power_store/cell/bluespace(src)
-
-/obj/item/storage/part_replacer/cargo //used in a cargo crate
-
-/obj/item/storage/part_replacer/cargo/PopulateContents()
- for(var/i in 1 to 10)
- new /obj/item/stock_parts/capacitor(src)
- new /obj/item/stock_parts/scanning_module(src)
- new /obj/item/stock_parts/servo(src)
- new /obj/item/stock_parts/micro_laser(src)
- new /obj/item/stock_parts/matter_bin(src)
-
-/obj/item/storage/part_replacer/cyborg
- name = "rapid part exchange device"
- desc = "Special mechanical module made to store, sort, and apply standard machine parts. This one has an extra large compartment for more parts."
- icon_state = "borgrped"
- inhand_icon_state = "RPED"
- lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
-
-/obj/item/storage/part_replacer/cyborg/Initialize(mapload)
- . = ..()
- atom_storage.max_slots = 400
- atom_storage.max_total_storage = 800
- atom_storage.max_specific_storage = WEIGHT_CLASS_GIGANTIC
-
-/obj/item/storage/part_replacer/proc/get_sorted_parts(ignore_stacks = FALSE)
- var/list/part_list = list()
- //Assemble a list of current parts, then sort them by their rating!
- for(var/obj/item/component_part in contents)
- //No need to put circuit boards in this list or stacks when exchanging parts
- if(istype(component_part, /obj/item/circuitboard) || (ignore_stacks && istype(component_part, /obj/item/stack)))
- continue
- part_list += component_part
- //Sort the parts. This ensures that higher tier items are applied first.
- sortTim(part_list, GLOBAL_PROC_REF(cmp_rped_sort))
- return part_list
-
-/proc/cmp_rped_sort(obj/item/first_item, obj/item/second_item)
- /**
- * even though stacks aren't stock parts, get_part_rating() is defined on the item level (see /obj/item/proc/get_part_rating()) and defaults to returning 0.
- */
- return second_item.get_part_rating() - first_item.get_part_rating()
/obj/item/stock_parts
name = "stock part"
diff --git a/code/modules/research/techweb/nodes/cyborg_nodes.dm b/code/modules/research/techweb/nodes/cyborg_nodes.dm
index eda52defa5e..781ed343672 100644
--- a/code/modules/research/techweb/nodes/cyborg_nodes.dm
+++ b/code/modules/research/techweb/nodes/cyborg_nodes.dm
@@ -161,7 +161,6 @@
"implantcase",
"implanter",
"locator",
- "c38_trac",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
announce_channels = list(RADIO_CHANNEL_SECURITY, RADIO_CHANNEL_MEDICAL)
diff --git a/code/modules/research/techweb/nodes/security_nodes.dm b/code/modules/research/techweb/nodes/security_nodes.dm
index 97d2036207c..02679b4280e 100644
--- a/code/modules/research/techweb/nodes/security_nodes.dm
+++ b/code/modules/research/techweb/nodes/security_nodes.dm
@@ -7,7 +7,9 @@
"toy_armblade",
"toygun",
"c38_rubber",
- "sec_38",
+ "c38_rubber_mag",
+ "c38_sec",
+ "c38_mag",
"capbox",
"foam_dart",
"sec_beanbag_slug",
@@ -80,7 +82,13 @@
prereq_ids = list(TECHWEB_NODE_EXPLOSIVES)
design_ids = list(
"c38_hotshot",
+ "c38_hotshot_mag",
"c38_iceblox",
+ "c38_iceblox_mag",
+ "c38_trac",
+ "c38_trac_mag",
+ "c38_true_strike",
+ "c38_true_strike_mag",
"techshotshell",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_4_POINTS)
diff --git a/code/modules/research/xenobiology/crossbreeding/_weapons.dm b/code/modules/research/xenobiology/crossbreeding/_weapons.dm
index 3cc46b9be32..c73ef832c54 100644
--- a/code/modules/research/xenobiology/crossbreeding/_weapons.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_weapons.dm
@@ -87,6 +87,7 @@ Slimecrossing Weapons
icon = 'icons/obj/science/slimecrossing.dmi'
icon_state = "bloodgun"
inhand_icon_state = "bloodgun"
+ icon_angle = 180
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
item_flags = ABSTRACT | DROPDEL
diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm
index 2f14388fd49..04fa06dcfaa 100644
--- a/code/modules/surgery/tools.dm
+++ b/code/modules/surgery/tools.dm
@@ -4,6 +4,7 @@
icon = 'icons/obj/medical/surgery_tools.dmi'
icon_state = "retractor"
inhand_icon_state = "retractor"
+ icon_angle = 45
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*3, /datum/material/glass =SHEET_MATERIAL_AMOUNT * 1.5)
@@ -25,6 +26,7 @@
/obj/item/retractor/cyborg
icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "toolkit_medborg_retractor"
+ icon_angle = 45
/obj/item/hemostat
name = "hemostat"
@@ -32,6 +34,7 @@
icon = 'icons/obj/medical/surgery_tools.dmi'
icon_state = "hemostat"
inhand_icon_state = "hemostat"
+ icon_angle = 135
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
custom_materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/glass = SHEET_MATERIAL_AMOUNT*1.25)
@@ -55,6 +58,7 @@
/obj/item/hemostat/cyborg
icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "toolkit_medborg_hemostat"
+ icon_angle = 45
/obj/item/cautery
name = "cautery"
@@ -62,6 +66,7 @@
icon = 'icons/obj/medical/surgery_tools.dmi'
icon_state = "cautery"
inhand_icon_state = "cautery"
+ icon_angle = 135
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*1.25, /datum/material/glass = SMALL_MATERIAL_AMOUNT*7.5)
@@ -89,6 +94,7 @@
/obj/item/cautery/cyborg
icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "toolkit_medborg_cautery"
+ icon_angle = 45
/obj/item/cautery/advanced
name = "searing tool"
@@ -201,6 +207,7 @@
icon = 'icons/obj/medical/surgery_tools.dmi'
icon_state = "scalpel"
inhand_icon_state = "scalpel"
+ icon_angle = 180
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
obj_flags = CONDUCTS_ELECTRICITY
@@ -212,8 +219,8 @@
throw_speed = 3
throw_range = 5
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*2, /datum/material/glass =HALF_SHEET_MATERIAL_AMOUNT)
- attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
- attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
+ attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts")
+ attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut")
hitsound = 'sound/items/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
tool_behaviour = TOOL_SCALPEL
@@ -222,6 +229,8 @@
bare_wound_bonus = 15
/// How this looks when placed in a surgical tray
var/surgical_tray_overlay = "scalpel_normal"
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
/obj/item/scalpel/Initialize(mapload)
. = ..()
@@ -231,6 +240,9 @@
bonus_modifier = 0, \
)
AddElement(/datum/element/eyestab)
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple)
/obj/item/scalpel/get_surgery_tool_overlay(tray_extended)
return surgical_tray_overlay
@@ -242,6 +254,7 @@
/obj/item/scalpel/cyborg
icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "toolkit_medborg_scalpel"
+ icon_angle = 0
/obj/item/scalpel/augment
desc = "Ultra-sharp blade attached directly to your bone for extra-accuracy."
@@ -253,6 +266,7 @@
icon = 'icons/obj/medical/surgery_tools.dmi'
icon_state = "saw"
inhand_icon_state = "saw"
+ icon_angle = 180
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
hitsound = 'sound/items/weapons/circsawhit.ogg'
@@ -297,6 +311,7 @@
/obj/item/circular_saw/cyborg
icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "toolkit_medborg_saw"
+ icon_angle = 0
/obj/item/circular_saw/augment
desc = "A small but very fast spinning saw. It rips and tears until it is done."
@@ -461,12 +476,13 @@
name = "mechanical pinches"
desc = "An agglomerate of rods and gears."
icon = 'icons/obj/medical/surgery_tools.dmi'
- custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*6, /datum/material/glass = SHEET_MATERIAL_AMOUNT*2, /datum/material/silver = SHEET_MATERIAL_AMOUNT*2, /datum/material/titanium =SHEET_MATERIAL_AMOUNT * 2.5)
icon_state = "adv_retractor"
inhand_icon_state = "adv_retractor"
surgical_tray_overlay = "retractor_advanced"
+ icon_angle = 0
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*6, /datum/material/glass = SHEET_MATERIAL_AMOUNT*2, /datum/material/silver = SHEET_MATERIAL_AMOUNT*2, /datum/material/titanium =SHEET_MATERIAL_AMOUNT * 2.5)
w_class = WEIGHT_CLASS_NORMAL
toolspeed = 0.7
@@ -507,6 +523,7 @@
desc = "A type of heavy duty surgical shears used for achieving a clean separation between limb and patient. Keeping the patient still is imperative to be able to secure and align the shears."
icon = 'icons/obj/medical/surgery_tools.dmi'
icon_state = "shears"
+ icon_angle = 90
obj_flags = CONDUCTS_ELECTRICITY
item_flags = SURGICAL_TOOL
toolspeed = 1
@@ -592,6 +609,7 @@
desc = "For setting things right."
icon = 'icons/obj/medical/surgery_tools.dmi'
icon_state = "bonesetter"
+ icon_angle = 135
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
custom_materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT * 2.5, /datum/material/glass = SHEET_MATERIAL_AMOUNT*1.25, /datum/material/silver = SHEET_MATERIAL_AMOUNT*1.25)
@@ -609,6 +627,7 @@
/obj/item/bonesetter/cyborg
icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "toolkit_medborg_bonesetter"
+ icon_angle = 45
/obj/item/blood_filter
name = "blood filter"
diff --git a/code/modules/transport/tram/tram_doors.dm b/code/modules/transport/tram/tram_doors.dm
index 6e1680bcb4c..5eb3be234e3 100644
--- a/code/modules/transport/tram/tram_doors.dm
+++ b/code/modules/transport/tram/tram_doors.dm
@@ -211,7 +211,7 @@
if(!hasPower() && density)
balloon_alert(user, "pulling emergency exit...")
if(do_after(user, 4 SECONDS, target = src))
- try_to_crowbar(src, user, TRUE)
+ try_to_crowbar(null, user, TRUE)
return TRUE
/**
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 4d16d24e9e9..6e6045d675c 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -311,6 +311,7 @@
#include "unit_test.dm"
#include "verify_config_tags.dm"
#include "verify_emoji_names.dm"
+#include "washing.dm"
#include "weird_food.dm"
#include "wizard_loadout.dm"
#include "worn_icons.dm"
diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm
index 6a2bda4ee25..241d7b54c39 100644
--- a/code/modules/unit_tests/unit_test.dm
+++ b/code/modules/unit_tests/unit_test.dm
@@ -254,6 +254,8 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests())
//Yet more templates
/obj/machinery/restaurant_portal,
//Template type
+ /obj/machinery/power/turbine,
+ //Template type
/obj/effect/mob_spawn,
//Template type
/obj/structure/holosign/robot_seat,
diff --git a/code/modules/unit_tests/washing.dm b/code/modules/unit_tests/washing.dm
new file mode 100644
index 00000000000..c0239e9244f
--- /dev/null
+++ b/code/modules/unit_tests/washing.dm
@@ -0,0 +1,43 @@
+/datum/unit_test/washing
+ /// Stuff we want to test that isn't cleanables, just to make sure they are getting cleaned when they should
+ var/list/cleanable_bonus_list = list(
+ /obj/effect/rune,
+ /obj/item/clothing/gloves/color/black,
+ /mob/living/carbon/human/dummy/consistent,
+ )
+
+ /// Tracks if we caught the clean signal, to know we washed successfully
+ VAR_PRIVATE/clean_sig_caught = 0
+
+/datum/unit_test/washing/Run()
+ for(var/i in subtypesof(/obj/effect/decal/cleanable) + cleanable_bonus_list)
+ var/atom/movable/to_clean = allocate(i)
+ var/mopable = HAS_TRAIT(to_clean, TRAIT_MOPABLE)
+
+ clean_sig_caught = 0
+ RegisterSignal(to_clean, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean_caught))
+ run_loc_floor_bottom_left.wash(CLEAN_ALL)
+ // mopables are cleaned when their turf is cleaned
+ if(mopable)
+ if(clean_sig_caught == 0)
+ TEST_FAIL("[i] was not cleaned when its turf was cleaned (cleaning only mopables)!")
+ if(clean_sig_caught > 1)
+ TEST_FAIL("[i] was cleaned more than once when its turf was cleaned (cleaning only mopables)!")
+ // non-mopables require the all_contents = TRUE flag to be cleaned
+ else
+ if(clean_sig_caught != 0)
+ TEST_FAIL("[i] was cleaned when its turf was cleaned (cleaning only mopables)!")
+ run_loc_floor_bottom_left.wash(CLEAN_ALL, TRUE)
+ if(clean_sig_caught == 0)
+ TEST_FAIL("[i] was not cleaned when its turf was cleaned (cleaning all contents)!")
+ if(clean_sig_caught > 1)
+ TEST_FAIL("[i] was cleaned more than once when its turf was cleaned (cleaning all contents)!")
+
+ if(!QDELETED(to_clean))
+ if(istype(to_clean, /obj/effect/decal/cleanable))
+ TEST_FAIL("[i] was not deleted when its turf was cleaned!")
+ qdel(to_clean)
+
+/datum/unit_test/washing/proc/clean_caught(...)
+ SIGNAL_HANDLER
+ clean_sig_caught += 1
diff --git a/code/modules/uplink/uplink_items/job.dm b/code/modules/uplink/uplink_items/job.dm
index d9723f202f1..4923fd855e9 100644
--- a/code/modules/uplink/uplink_items/job.dm
+++ b/code/modules/uplink/uplink_items/job.dm
@@ -267,7 +267,7 @@
progression_minimum = 15 MINUTES
item = /obj/item/gun/chem
cost = 12
- restricted_roles = list(JOB_CHEMIST, JOB_CHIEF_MEDICAL_OFFICER, JOB_BOTANIST)
+ restricted_roles = list(JOB_CHEMIST, JOB_MEDICAL_DOCTOR, JOB_CHIEF_MEDICAL_OFFICER, JOB_BOTANIST)
/datum/uplink_item/role_restricted/pie_cannon
name = "Banana Cream Pie Cannon"
diff --git a/code/modules/vehicles/lavaboat.dm b/code/modules/vehicles/lavaboat.dm
index fbe130d9697..307c47ab9e2 100644
--- a/code/modules/vehicles/lavaboat.dm
+++ b/code/modules/vehicles/lavaboat.dm
@@ -23,6 +23,7 @@
icon = 'icons/mob/rideables/vehicles.dmi'
icon_state = "oar"
inhand_icon_state = "oar"
+ icon_angle = 45
lefthand_file = 'icons/mob/inhands/items/lavaland_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/lavaland_righthand.dmi'
force = 12
diff --git a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
index c62b2a0d9ce..c811e94c309 100644
--- a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
+++ b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm
@@ -68,6 +68,7 @@
var/obj/projectile/projectile_obj = new projectile(get_turf(src))
projectile_obj.log_override = TRUE //we log being fired ourselves a little further down.
projectile_obj.firer = chassis
+ projectile_obj.fired_from = src // mech = firer, equipment = fired from
projectile_obj.aim_projectile(target, source, modifiers, spread)
if(isliving(source) && source.client) //dont want it to happen from syndie mecha npc mobs, they do direct fire anyways
var/mob/living/shooter = source
@@ -193,6 +194,7 @@
icon_state = "mecha_honker"
energy_drain = 200
equip_cooldown = 150
+ projectiles_per_shot = 0
range = MECHA_MELEE|MECHA_RANGED
kickback = FALSE
mech_flags = EXOSUIT_MODULE_HONK
diff --git a/code/modules/vehicles/vehicle_key.dm b/code/modules/vehicles/vehicle_key.dm
index 2bcc17115b0..60b578d0962 100644
--- a/code/modules/vehicles/vehicle_key.dm
+++ b/code/modules/vehicles/vehicle_key.dm
@@ -26,6 +26,7 @@
/obj/item/key/janitor
desc = "A keyring with a small steel key, and a pink fob reading \"Pussy Wagon\"."
icon_state = "keyjanitor"
+ icon_angle = 90
force = 2
w_class = WEIGHT_CLASS_SMALL
throwforce = 9
diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm
index 779d253f29c..79e6b86f545 100644
--- a/code/modules/vending/_vending.dm
+++ b/code/modules/vending/_vending.dm
@@ -1161,15 +1161,15 @@ GLOBAL_LIST_EMPTY(vending_machines_to_restock)
return FALSE
/obj/machinery/vending/exchange_parts(mob/user, obj/item/storage/part_replacer/replacer)
- if(!istype(replacer))
- return FALSE
- if(!component_parts || !refill_canister)
+ if(!istype(replacer) || !component_parts || !refill_canister)
return FALSE
- if(!panel_open || replacer.works_from_distance)
+ var/works_from_distance = istype(replacer, /obj/item/storage/part_replacer/bluespace)
+
+ if(!panel_open || works_from_distance)
to_chat(user, display_parts(user))
- if(!panel_open && !replacer.works_from_distance)
+ if(!panel_open && !works_from_distance)
return FALSE
var/restocked = 0
diff --git a/html/changelogs/AutoChangeLog-pr-88574.yml b/html/changelogs/AutoChangeLog-pr-88574.yml
new file mode 100644
index 00000000000..7f7b8f9c8e7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-88574.yml
@@ -0,0 +1,4 @@
+author: "Melbert"
+delete-after: True
+changes:
+ - bugfix: "Lobby button sprites"
\ No newline at end of file
diff --git a/html/changelogs/archive/2024-12.yml b/html/changelogs/archive/2024-12.yml
index d1dbade2017..7fdad317573 100644
--- a/html/changelogs/archive/2024-12.yml
+++ b/html/changelogs/archive/2024-12.yml
@@ -333,8 +333,141 @@
grungussuss:
- bugfix: '*me emote works again'
2024-12-14:
+ EnterTheJake:
+ - bugfix: temporary blocks such as blade heretic orbiting knives properly stop body
+ throws.
+ Ghommie:
+ - bugfix: Actually fixed alt-clicking aquariums.
+ Majkl-J:
+ - bugfix: Aloe cream no longer catches fire seconds after finishing baking
+ - bugfix: Overcharged SMESes no longer spam runtime when timers chug up
+ NecromancerAnne (code), orcacora (sprites):
+ - rscadd: Adds NT BR-38 Battle Rifles. A hybrid weapon. Find it in your local armory
+ and cargo catalogue today. (Keep away from EMPs)
+ Runi-c:
+ - balance: medical doctors can buy Reagent Dartgun from traitor uplink
+ SmArtKar:
+ - bugfix: Fixes primed stingbangs being invisible
SmArtKar, LemonInTheDark:
- rscadd: Changed how spraycans color items - "old" mode is still availible via
right click.
- refactor: Refactored how some items and effects color things so that they look
prettier.
+ SyncIt21:
+ - code_imp: improved code for leaning
+ distributivgesetz:
+ - code_imp: Fixed rare cases where moving an object somewhere could silently fail,
+ but still run unintended code. Report any weird issues on Github
+ grungussuss:
+ - bugfix: fixed regal rat attack logic
+ - bugfix: fixed access on birdshot engi mulebot delivery window
+ mcbalaam:
+ - qol: Now all antagonists are visible to an admin in the orbit menu!
+ norsvenska:
+ - spellcheck: The Lance and Raven shuttle airlocks are now properly labelled emergency
+ airlocks, rather than emegency airlocks.
+ - spellcheck: The radio jammer now releases disruptor waves, rather than distruptor
+ waves.
+ timothymtorres:
+ - sound: Add water sound to sinks
+2024-12-15:
+ Ghommie:
+ - bugfix: Fixed the displayed stats when examining fishing rods twice.
+ LT3:
+ - bugfix: Fixed unconstructed solar panels on Nebulastation port aft solars
+ Melbert:
+ - rscadd: Adds Syndol to the chemical kit, an addictive hallucinogen that applies
+ bonus effects when security officers, assistants, or clowns are exposed.
+ - bugfix: Metalgen works as a lockpick; igniting a crate metalgen'd into plasma
+ will properly drop its contents.
+ - code_imp: Hiding stuff in food should generally work more consistently now.
+ - bugfix: Fixes players not doing the "searching for item" do-after for items hidden
+ in food.
+ - qol: When dragging an item (like, with your mouse cursor. not physically), your
+ cursor updates when hovering humans or cyborgs to indicate you're hovering over
+ a human or cyborg.
+ Paxilmaniac:
+ - bugfix: Fixes resin sprayers not working if the target is more than one tile away
+ from you
+ necromanceranne:
+ - code_imp: Various mob attack procs are treated as unarmed attacks as a baseline
+ assumption, rather than melee attacks.
+ timothymtorres:
+ - code_imp: Improve looping sounds to allow nested and non-associative lists
+2024-12-16:
+ JoshAdamPowell:
+ - map: In the new year's budget the syndicate have decided that chemists need beakers
+ to do their job properly.
+ Melbert:
+ - qol: Treatment message now better reflects what you're doing ("suturing", "applying",
+ etc)
+ - bugfix: Gauze is now stickier (and will actually apply to bodyparts)
+ OrionTheFox:
+ - bugfix: fixed the outdated N-Spect description falsely claiming it can scan people.
+ It can't. Nanotrasen denies all claims it ever broke sapient right to privacy
+ by giving crew full-body scanners in a handheld format.
+ SmArtKar:
+ - rscadd: Rave and plasma stabilizer MODules now utilize theme-specific visors
+ - qol: Jetpacks should ACTUALLY feel better now
+ SyncIt21:
+ - bugfix: Turbine converts energy to power correctly & shows correct reading with
+ multitool
+ - refactor: turbine code has been overall improved. report bugs on github
+ tontyGH:
+ - bugfix: Underlining your messages in loud mode shouldn't break anymore
+2024-12-17:
+ Melbert:
+ - balance: Tasers are now more realistic
+ - rscdel: Electrodes are no longer in the hallucination projectile pool
+ OrionTheFox:
+ - image: resprited the Blood Cult Archives/Altar (the two summoning tables)
+ SyncIt21:
+ - code_imp: improved code for RPED
+ - refactor: RPED attack chain has been refactored. Reports bugs on github
+2024-12-18:
+ SmArtKar:
+ - rscadd: Added new (purely visual) animations to sharp and pointy items.
+ - rscadd: Certain items, like knives and swords, now have a secondary stabbing attack.
+ - balance: Spears are now pointy and no longer act as oversized knives.
+ - balance: Structure damage is now affected by attacking item's AP.
+ - bugfix: You will now see the same attack verb in chat as everyone else.
+2024-12-19:
+ 00-Steven:
+ - bugfix: Custom emotes done via the custom emote keybind default to both audible
+ and visible again.
+ Darkened-Earth:
+ - bugfix: NanoTrasen has shown a xenobiology containment engineer for Delta class
+ stations a very important lesson between inlets and outlets
+ Hatterhat:
+ - qol: You can now pull singular trophies off kinetic crushers with RMB.
+ - qol: Kinetic crushers now have screentips informing people that they can remove
+ trophies via empty hand (RMB) or crowbar (LMB).
+ Melbert:
+ - bugfix: Gibs get bulk cleaned if you clean the turf again
+ - refactor: Changed how things determine "I can be bulk cleaned if I clean the turf
+ underneath me", let me know if you notice anything not getting bulk cleaned
+ or weird things getting bulk cleaned
+ SmArtKar:
+ - bugfix: Fixed a Honkerblast 5000 runtime
+ - bugfix: Fixed mech KA AOE dealing full damage in pressurized environments
+ - bugfix: Fixed c38 not misfiring when chambered in c357
+ - qol: Resist button now has visible feedback.
+ - qol: Readjusted UI layout.
+ - image: Completely redrawn Midnight and Midnight-derived UIs!
+ mikederkan:
+ - bugfix: fixes a minor spelling/grammatical error in the Funeral Supply and Religious
+ Supplies crates.
+ timothymtorres:
+ - sound: Add fishtank looping sounds to cryo cells when in use. Sound is from https://freesound.org/people/DudeAwesome/sounds/386023/
+ - sound: Add dice rolling sound
+ tontyGH:
+ - bugfix: /datum/gas_machine_connector will abstract_move() if their parent also
+ abstract_move()s, preventing a runtime
+2024-12-20:
+ Melbert:
+ - bugfix: Meshes should work better
+ - bugfix: Medical item usage is correctly blackbox logged
+ - bugfix: Double balloon alerts from medical item usage
+ - bugfix: Poultice works on dead people
+ SmArtKar:
+ - bugfix: Fixed an airlock unlocking runtime
diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi
index 41bdf992bbf..12bbd4788f0 100644
Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ
diff --git a/icons/hud/64x16_actions.dmi b/icons/hud/64x16_actions.dmi
index 23865a80f03..6a54c8e4bb3 100644
Binary files a/icons/hud/64x16_actions.dmi and b/icons/hud/64x16_actions.dmi differ
diff --git a/icons/hud/screen_alien.dmi b/icons/hud/screen_alien.dmi
index 5f3806dc7bb..e1f513f5b8f 100644
Binary files a/icons/hud/screen_alien.dmi and b/icons/hud/screen_alien.dmi differ
diff --git a/icons/hud/screen_clockwork.dmi b/icons/hud/screen_clockwork.dmi
index 809dfe8f1a8..d620da7e790 100644
Binary files a/icons/hud/screen_clockwork.dmi and b/icons/hud/screen_clockwork.dmi differ
diff --git a/icons/hud/screen_detective.dmi b/icons/hud/screen_detective.dmi
index 818bbcfc78b..0516ce6f079 100644
Binary files a/icons/hud/screen_detective.dmi and b/icons/hud/screen_detective.dmi differ
diff --git a/icons/hud/screen_glass.dmi b/icons/hud/screen_glass.dmi
index 3cf16106cd8..08db68e4d24 100644
Binary files a/icons/hud/screen_glass.dmi and b/icons/hud/screen_glass.dmi differ
diff --git a/icons/hud/screen_midnight.dmi b/icons/hud/screen_midnight.dmi
index 5de157be030..d748823d4ce 100644
Binary files a/icons/hud/screen_midnight.dmi and b/icons/hud/screen_midnight.dmi differ
diff --git a/icons/hud/screen_operative.dmi b/icons/hud/screen_operative.dmi
index 5fbaa7c5d5d..8719ef18d39 100644
Binary files a/icons/hud/screen_operative.dmi and b/icons/hud/screen_operative.dmi differ
diff --git a/icons/hud/screen_plasmafire.dmi b/icons/hud/screen_plasmafire.dmi
index d8b669c96c8..652beabadd8 100644
Binary files a/icons/hud/screen_plasmafire.dmi and b/icons/hud/screen_plasmafire.dmi differ
diff --git a/icons/hud/screen_retro.dmi b/icons/hud/screen_retro.dmi
index f3621569d76..21275e5320c 100644
Binary files a/icons/hud/screen_retro.dmi and b/icons/hud/screen_retro.dmi differ
diff --git a/icons/hud/screen_slimecore.dmi b/icons/hud/screen_slimecore.dmi
index a6160087ab8..790575deae7 100644
Binary files a/icons/hud/screen_slimecore.dmi and b/icons/hud/screen_slimecore.dmi differ
diff --git a/icons/hud/screen_trasenknox.dmi b/icons/hud/screen_trasenknox.dmi
index 4e17feb5211..bb6f82caafb 100644
Binary files a/icons/hud/screen_trasenknox.dmi and b/icons/hud/screen_trasenknox.dmi differ
diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi
index a67830ac5e9..9fd317494ad 100644
Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ
diff --git a/icons/mob/clothing/belt_mirror.dmi b/icons/mob/clothing/belt_mirror.dmi
index 95f7bc00ae9..0ffdb70219c 100644
Binary files a/icons/mob/clothing/belt_mirror.dmi and b/icons/mob/clothing/belt_mirror.dmi differ
diff --git a/icons/mob/clothing/modsuit/mod_clothing.dmi b/icons/mob/clothing/modsuit/mod_clothing.dmi
index 815983f7094..8a39f37bf0b 100644
Binary files a/icons/mob/clothing/modsuit/mod_clothing.dmi and b/icons/mob/clothing/modsuit/mod_clothing.dmi differ
diff --git a/icons/mob/clothing/modsuit/mod_modules.dmi b/icons/mob/clothing/modsuit/mod_modules.dmi
index 3c68a87d549..d6dfa4c50a2 100644
Binary files a/icons/mob/clothing/modsuit/mod_modules.dmi and b/icons/mob/clothing/modsuit/mod_modules.dmi differ
diff --git a/icons/mob/inhands/weapons/guns_lefthand.dmi b/icons/mob/inhands/weapons/guns_lefthand.dmi
index 90df2a892f9..369365131c6 100644
Binary files a/icons/mob/inhands/weapons/guns_lefthand.dmi and b/icons/mob/inhands/weapons/guns_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/guns_righthand.dmi b/icons/mob/inhands/weapons/guns_righthand.dmi
index eebed61656a..fb77baa515b 100644
Binary files a/icons/mob/inhands/weapons/guns_righthand.dmi and b/icons/mob/inhands/weapons/guns_righthand.dmi differ
diff --git a/icons/obj/antags/cult/structures.dmi b/icons/obj/antags/cult/structures.dmi
index 982742e8764..e42ce6c2202 100644
Binary files a/icons/obj/antags/cult/structures.dmi and b/icons/obj/antags/cult/structures.dmi differ
diff --git a/icons/obj/machines/engine/turbine.dmi b/icons/obj/machines/engine/turbine.dmi
index 1ae45eb2a1b..afd9839af13 100644
Binary files a/icons/obj/machines/engine/turbine.dmi and b/icons/obj/machines/engine/turbine.dmi differ
diff --git a/icons/obj/weapons/grenade.dmi b/icons/obj/weapons/grenade.dmi
index c65f6d0e9fb..628b271d423 100644
Binary files a/icons/obj/weapons/grenade.dmi and b/icons/obj/weapons/grenade.dmi differ
diff --git a/icons/obj/weapons/guns/ammo.dmi b/icons/obj/weapons/guns/ammo.dmi
index df6edd709f6..0f3b0620198 100644
Binary files a/icons/obj/weapons/guns/ammo.dmi and b/icons/obj/weapons/guns/ammo.dmi differ
diff --git a/icons/obj/weapons/guns/wide_guns.dmi b/icons/obj/weapons/guns/wide_guns.dmi
index 7e18f60eeb8..a193dcc5301 100644
Binary files a/icons/obj/weapons/guns/wide_guns.dmi and b/icons/obj/weapons/guns/wide_guns.dmi differ
diff --git a/icons/ui/inventory/back.png b/icons/ui/inventory/back.png
index 210045c1c66..e8226bc55e3 100644
Binary files a/icons/ui/inventory/back.png and b/icons/ui/inventory/back.png differ
diff --git a/icons/ui/inventory/belt.png b/icons/ui/inventory/belt.png
index 9bec60c7bb2..ef3e5586e47 100644
Binary files a/icons/ui/inventory/belt.png and b/icons/ui/inventory/belt.png differ
diff --git a/icons/ui/inventory/collar.png b/icons/ui/inventory/collar.png
index a7d1b527849..2e8c875851e 100644
Binary files a/icons/ui/inventory/collar.png and b/icons/ui/inventory/collar.png differ
diff --git a/icons/ui/inventory/ears.png b/icons/ui/inventory/ears.png
index 3c3ba349294..19752ed10d7 100644
Binary files a/icons/ui/inventory/ears.png and b/icons/ui/inventory/ears.png differ
diff --git a/icons/ui/inventory/glasses.png b/icons/ui/inventory/glasses.png
index 3dff0cd5992..9c8b0950abc 100644
Binary files a/icons/ui/inventory/glasses.png and b/icons/ui/inventory/glasses.png differ
diff --git a/icons/ui/inventory/gloves.png b/icons/ui/inventory/gloves.png
index 87c829eb626..f5c9de27b8e 100644
Binary files a/icons/ui/inventory/gloves.png and b/icons/ui/inventory/gloves.png differ
diff --git a/icons/ui/inventory/hand_l.png b/icons/ui/inventory/hand_l.png
index ab9535180d6..8923d81b5b7 100644
Binary files a/icons/ui/inventory/hand_l.png and b/icons/ui/inventory/hand_l.png differ
diff --git a/icons/ui/inventory/hand_r.png b/icons/ui/inventory/hand_r.png
index e6b0d3cb9da..b3b16b4e0cd 100644
Binary files a/icons/ui/inventory/hand_r.png and b/icons/ui/inventory/hand_r.png differ
diff --git a/icons/ui/inventory/head.png b/icons/ui/inventory/head.png
index 2d1b65159bb..650be20fe63 100644
Binary files a/icons/ui/inventory/head.png and b/icons/ui/inventory/head.png differ
diff --git a/icons/ui/inventory/id.png b/icons/ui/inventory/id.png
index 397b30ca42f..0505f38bc19 100644
Binary files a/icons/ui/inventory/id.png and b/icons/ui/inventory/id.png differ
diff --git a/icons/ui/inventory/mask.png b/icons/ui/inventory/mask.png
index dbaa80ec1ac..3690e6eca76 100644
Binary files a/icons/ui/inventory/mask.png and b/icons/ui/inventory/mask.png differ
diff --git a/icons/ui/inventory/neck.png b/icons/ui/inventory/neck.png
index 74cf1e33eaf..a6018e149c7 100644
Binary files a/icons/ui/inventory/neck.png and b/icons/ui/inventory/neck.png differ
diff --git a/icons/ui/inventory/pocket.png b/icons/ui/inventory/pocket.png
index 2e08ed41b05..0d31594a5a5 100644
Binary files a/icons/ui/inventory/pocket.png and b/icons/ui/inventory/pocket.png differ
diff --git a/icons/ui/inventory/shoes.png b/icons/ui/inventory/shoes.png
index b1b19c63397..170f81b5daa 100644
Binary files a/icons/ui/inventory/shoes.png and b/icons/ui/inventory/shoes.png differ
diff --git a/icons/ui/inventory/suit.png b/icons/ui/inventory/suit.png
index 71b877677fe..e3122ed47dc 100644
Binary files a/icons/ui/inventory/suit.png and b/icons/ui/inventory/suit.png differ
diff --git a/icons/ui/inventory/suit_storage.png b/icons/ui/inventory/suit_storage.png
index 0dd12ed4f8f..f35bd3783f7 100644
Binary files a/icons/ui/inventory/suit_storage.png and b/icons/ui/inventory/suit_storage.png differ
diff --git a/icons/ui/inventory/uniform.png b/icons/ui/inventory/uniform.png
index 56576429e8d..7f5951e3a0f 100644
Binary files a/icons/ui/inventory/uniform.png and b/icons/ui/inventory/uniform.png differ
diff --git a/sound/attributions.txt b/sound/attributions.txt
index a6cbf21f836..bd2e408c6c5 100644
--- a/sound/attributions.txt
+++ b/sound/attributions.txt
@@ -23,7 +23,6 @@ champagne_pop.ogg is credited to ultradust on freesound https://freesound.org/pe
can_open.ogg adapted from https://freesound.org/people/MaxDemianAGL/sounds/130031/
can_shake.ogg adapted from https://freesound.org/people/mcmast/sounds/456703/
-
splatter.ogg adapted from https://freesound.org/people/Rocktopus/sounds/233418/
hohoho.ogg and hehe.ogg are cut from a recording by Nanakisan on freesound: https://freesound.org/people/Nanakisan/sounds/253534/
mbox_full.ogg and mbox_end.ogg make use of The Ragtime Drummer by James Lent, in the public domain
@@ -181,8 +180,6 @@ https://freesound.org/people/shw489/sounds/234389/
soup_boil1.ogg through soup_boil5.ogg and soup_boil_end.ogg are taken from Boiling Soup from Freesoung.org (CC4) and converted to OGG / split apart (but is otherwise unchanged):
https://freesound.org/people/jorickhoofd/sounds/632783/
-
-
valve_opening.ogg was made by mixing water flowing samples from:
https://freesound.org/people/scriotxstudios/sounds/349111/?attribution=1 and squeaky scrape sound from:
https://freesound.org/people/Department64/sounds/669028/ which was modified with lower pitch
@@ -191,7 +188,6 @@ liquid_pour2.ogg and liquid_pour3.ogg were cut from
https://freesound.org/people/MattRuthSound/sounds/561896/
https://freesound.org/people/MattRuthSound/sounds/561895/
-
roaring_fire.ogg made from: 10835 big fire loop.wav by Robinhood76 -- https://freesound.org/s/612277/ -- License: Attribution NonCommercial 4.0
fire_puff made from: Bonfire Being Lit by samararaine -- https://freesound.org/s/186374/ -- License: Creative Commons 0
@@ -208,8 +204,6 @@ Bottle Tap.wav by alex_alexalex -- https://freesound.org/s/395492/ -- License: A
beaker_place.ogg was made by cutting and lowering pitch:
place glass object.wav by milpower -- https://freesound.org/s/353105/ -- License: Creative Commons 0
-
-
glass_reverse.ogg is adapted from a combination of:
https://freesound.org/people/C_Rogers/sounds/203368/ -- glass-shattering-hit_01.ogg by C_Rogers on freesound.org (CC0)
https://freesound.org/people/Czarcazas/sounds/330800/ -- Audio reversal/fading of Shattering Glass (Small) by Czarcazas -- https://freesound.org/s/330800/ -- License: Attribution 3.0
@@ -217,3 +211,10 @@ https://freesound.org/people/Czarcazas/sounds/330800/ -- Audio reversal/fading o
sound/effects/bonk.ogg - recorded by oranges on a coke zero bottle, edited by ninjanomnom, released to public domain
sound\items\weapons\hammer_death_scream.ogg - Undefeatablesos' scream recorded by Niron3206, edited by Niron3206, License: Creative Commons 0
+
+sound/machines/sink-faucet.ogg -- https://freesound.org/people/FOSSarts/sounds/740086/ -- by FOSSarts (CC0)
+
+cryo_1.ogg, cryo_2.ogg, cryo_3.ogg, cryo_4.ogg, cryo_5.ogg, cryo_6.ogg, cryo_7.ogg, cryo_8.ogg, cryo_9.ogg, cryo_10.ogg:
+converted to OGG / split apart (but is otherwise unchanged) -- original from https://freesound.org/people/DudeAwesome/sounds/386023/ by DudeAwesome -- License: CC BY 4.0
+
+sound/items/dice_roll.ogg -- https://freesound.org/people/Crovic/sounds/661935/ -- by Crovic (CC0)
diff --git a/sound/items/dice_roll.ogg b/sound/items/dice_roll.ogg
new file mode 100644
index 00000000000..71048df839b
Binary files /dev/null and b/sound/items/dice_roll.ogg differ
diff --git a/sound/machines/cryo/cryo_1.ogg b/sound/machines/cryo/cryo_1.ogg
new file mode 100644
index 00000000000..daad20b1f49
Binary files /dev/null and b/sound/machines/cryo/cryo_1.ogg differ
diff --git a/sound/machines/cryo/cryo_10.ogg b/sound/machines/cryo/cryo_10.ogg
new file mode 100644
index 00000000000..eedc518222f
Binary files /dev/null and b/sound/machines/cryo/cryo_10.ogg differ
diff --git a/sound/machines/cryo/cryo_2.ogg b/sound/machines/cryo/cryo_2.ogg
new file mode 100644
index 00000000000..7375eff09eb
Binary files /dev/null and b/sound/machines/cryo/cryo_2.ogg differ
diff --git a/sound/machines/cryo/cryo_3.ogg b/sound/machines/cryo/cryo_3.ogg
new file mode 100644
index 00000000000..4cbb033daff
Binary files /dev/null and b/sound/machines/cryo/cryo_3.ogg differ
diff --git a/sound/machines/cryo/cryo_4.ogg b/sound/machines/cryo/cryo_4.ogg
new file mode 100644
index 00000000000..6dff7c7a1c1
Binary files /dev/null and b/sound/machines/cryo/cryo_4.ogg differ
diff --git a/sound/machines/cryo/cryo_5.ogg b/sound/machines/cryo/cryo_5.ogg
new file mode 100644
index 00000000000..ff11f6d173d
Binary files /dev/null and b/sound/machines/cryo/cryo_5.ogg differ
diff --git a/sound/machines/cryo/cryo_6.ogg b/sound/machines/cryo/cryo_6.ogg
new file mode 100644
index 00000000000..575b727958b
Binary files /dev/null and b/sound/machines/cryo/cryo_6.ogg differ
diff --git a/sound/machines/cryo/cryo_7.ogg b/sound/machines/cryo/cryo_7.ogg
new file mode 100644
index 00000000000..f6fcb2bd0e3
Binary files /dev/null and b/sound/machines/cryo/cryo_7.ogg differ
diff --git a/sound/machines/cryo/cryo_8.ogg b/sound/machines/cryo/cryo_8.ogg
new file mode 100644
index 00000000000..2884ffa5bd2
Binary files /dev/null and b/sound/machines/cryo/cryo_8.ogg differ
diff --git a/sound/machines/cryo/cryo_9.ogg b/sound/machines/cryo/cryo_9.ogg
new file mode 100644
index 00000000000..c650837ed8b
Binary files /dev/null and b/sound/machines/cryo/cryo_9.ogg differ
diff --git a/sound/machines/sink-faucet.ogg b/sound/machines/sink-faucet.ogg
new file mode 100644
index 00000000000..7102a394030
Binary files /dev/null and b/sound/machines/sink-faucet.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 15b0963fa00..92521070815 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1052,6 +1052,7 @@
#include "code\datums\components\ai_has_target_timer.dm"
#include "code\datums\components\ai_listen_to_weather.dm"
#include "code\datums\components\ai_retaliate_advanced.dm"
+#include "code\datums\components\alternative_sharpness.dm"
#include "code\datums\components\amputating_limbs.dm"
#include "code\datums\components\anti_magic.dm"
#include "code\datums\components\appearance_on_aggro.dm"
@@ -5871,6 +5872,7 @@
#include "code\modules\research\designs.dm"
#include "code\modules\research\destructive_analyzer.dm"
#include "code\modules\research\experimentor.dm"
+#include "code\modules\research\part_replacer.dm"
#include "code\modules\research\rdconsole.dm"
#include "code\modules\research\rdmachines.dm"
#include "code\modules\research\research_disk.dm"
diff --git a/tgui/packages/tgui/interfaces/TurbineComputer.tsx b/tgui/packages/tgui/interfaces/TurbineComputer.tsx
index 4d1305b88b5..f67a09f2687 100644
--- a/tgui/packages/tgui/interfaces/TurbineComputer.tsx
+++ b/tgui/packages/tgui/interfaces/TurbineComputer.tsx
@@ -9,7 +9,9 @@ import {
NumberInput,
ProgressBar,
Section,
+ Stack,
} from '../components';
+import { formatPower } from '../format';
import { Window } from '../layouts';
type TurbineInfo = {
@@ -19,105 +21,99 @@ type TurbineInfo = {
power: number;
temp: number;
integrity: number;
- parts_linked: BooleanLike;
- parts_ready: BooleanLike;
max_rpm: number;
max_temperature: number;
regulator: number;
};
-export const TurbineComputer = (props) => {
+const TurbineDisplay = (props) => {
const { act, data } = useBackend();
- const parts_not_connected = !data.parts_linked && (
-
-
- {
- 'Parts not connected, use a multitool on the core rotor before trying again'
- }
-
-
+
+ return (
+ = 1000)}
+ onClick={() => act('toggle_power')}
+ >
+ {data.active ? 'Online' : 'Offline'}
+
+ }
+ >
+
+
+
+ act('regulate', {
+ regulate: value * 0.01,
+ })
+ }
+ />
+
+
+
+
+
+ {data.rpm} RPM
+
+
+ {data.max_rpm} RPM
+
+
+ {data.temp} K
+
+
+ {data.max_temperature} K
+
+
+ {formatPower(data.power)}
+
+
+
);
- const parts_not_ready = data.parts_linked && !data.parts_ready && (
+};
+
+const OutOfService = (props) => {
+ return (
-
- {
- 'Some parts have open maintenance hatchet, please close them before starting'
- }
-
+
+
+
+ {
+ 'Parts not connected, close all mantainence panels/use a multitool on the rotor before trying again'
+ }
+
+
+
);
+};
+
+export const TurbineComputer = (props) => {
+ const { data } = useBackend();
+
return (
- = 1000) || !data.parts_linked}
- onClick={() => act('toggle_power')}
- />
- }
- >
- {parts_not_connected}
- {parts_not_ready}
-
-
-
- act('regulate', {
- regulate: value * 0.01,
- })
- }
- />
-
-
-
-
-
- {data.rpm} RPM
-
-
- {data.max_rpm} RPM
-
-
- {data.temp} K
-
-
- {data.max_temperature} K
-
-
- {data.power * 4 * 0.001} kW
-
-
-
+ {data.connected ? : }
);
diff --git a/tools/UpdatePaths/Scripts/repaths_a357_to_c357.txt b/tools/UpdatePaths/Scripts/repaths_a357_to_c357.txt
new file mode 100644
index 00000000000..28ad97efff1
--- /dev/null
+++ b/tools/UpdatePaths/Scripts/repaths_a357_to_c357.txt
@@ -0,0 +1,3 @@
+#comment Repaths instances of a357 with c357, so as to acknowledge that it is consistent with other casings.
+
+/obj/item/ammo_casing/c357 : /obj/item/ammo_casing/a357{@OLD}