diff --git a/_maps/map_files/generic/centcomm.dmm b/_maps/map_files/generic/centcomm.dmm
index 43f777c72cc..5a1e80fe9dc 100644
--- a/_maps/map_files/generic/centcomm.dmm
+++ b/_maps/map_files/generic/centcomm.dmm
@@ -25,9 +25,6 @@
/obj/structure/window/reinforced,
/turf/simulated/wall/indestructible/riveted,
/area/space/centcomm)
-"ag" = (
-/turf/simulated/wall/indestructible/fakedoor,
-/area/ninja/holding)
"ah" = (
/obj/effect/mapping_helpers/airlock/access/all/centcomm/general,
/obj/machinery/door/airlock/centcom{
@@ -107,9 +104,6 @@
},
/turf/simulated/floor/holofloor,
/area/holodeck/source_knightarena)
-"at" = (
-/turf/simulated/wall/indestructible/fakedoor,
-/area/ninja/outpost)
"au" = (
/obj/machinery/abductor/experiment{
team = 1
@@ -399,11 +393,14 @@
/obj/item/hemostat/alien,
/turf/simulated/floor/plating/abductor,
/area/abductor_ship)
-"bz" = (
-/obj/structure/ninjatele{
- pixel_y = 25
+"bx" = (
+/obj/structure/table/glass/reinforced/plastitanium,
+/obj/machinery/kitchen_machine/microwave/upgraded{
+ desc = "Cooks and boils stuff, somehow.";
+ pixel_x = -3;
+ pixel_y = 5
},
-/turf/simulated/floor/plasteel/dark,
+/turf/simulated/floor/plasteel/full,
/area/ninja/outpost)
"bB" = (
/obj/effect/landmark/abductor/scientist,
@@ -473,13 +470,6 @@
"bM" = (
/turf/simulated/floor/holofloor,
/area/holodeck/source_emptycourt)
-"bN" = (
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
-"bO" = (
-/obj/structure/closet/secure_closet/bar,
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
"bP" = (
/obj/effect/landmark/abductor/scientist{
team = 4
@@ -491,9 +481,8 @@
/turf/simulated/floor/plasteel/dark,
/area/ghost_bar)
"bR" = (
-/obj/structure/reagent_dispensers/beerkeg,
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
+/turf/simulated/floor/plasteel/full,
+/area/ninja/outpost)
"bS" = (
/obj/structure/shuttle/engine/propulsion{
dir = 8;
@@ -525,10 +514,6 @@
/obj/effect/turf_decal/tiles/department/virology/side,
/turf/simulated/floor/holofloor,
/area/holodeck/source_emptycourt)
-"bY" = (
-/obj/machinery/economy/vending/boozeomat,
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
"bZ" = (
/obj/structure/chair/sofa/right,
/turf/simulated/floor/carpet/black,
@@ -581,26 +566,6 @@
"ch" = (
/turf/simulated/floor/holofloor/space,
/area/holodeck/source_space)
-"ci" = (
-/obj/structure/table,
-/obj/item/storage/box/donkpockets{
- pixel_x = 3;
- pixel_y = 3
- },
-/obj/item/storage/box/donkpockets{
- pixel_x = 3;
- pixel_y = 3
- },
-/obj/item/storage/box/donkpockets{
- pixel_x = 3;
- pixel_y = 3
- },
-/obj/item/storage/box/donkpockets{
- pixel_x = 3;
- pixel_y = 3
- },
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
"cj" = (
/obj/machinery/abductor/gland_dispenser,
/turf/simulated/floor/plating/abductor,
@@ -621,12 +586,6 @@
},
/turf/simulated/floor/holofloor,
/area/holodeck/source_basketball)
-"co" = (
-/obj/structure/rack,
-/obj/item/camera,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
"cp" = (
/obj/structure/table/abductor,
/obj/item/bonegel/alien,
@@ -646,29 +605,12 @@
/obj/structure/bed/abductor,
/turf/simulated/floor/plating/abductor,
/area/abductor_ship)
-"cs" = (
-/obj/structure/rack,
-/obj/item/toy/sword,
-/obj/item/gun/projectile/revolver/capgun,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
-"ct" = (
-/obj/machinery/prize_counter,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
"cu" = (
/obj/effect/turf_decal/tiles/department/security/side{
dir = 8
},
/turf/simulated/floor/holofloor,
/area/holodeck/source_thunderdomecourt)
-"cv" = (
-/obj/machinery/computer/arcade,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
"cw" = (
/obj/effect/turf_decal/tiles/department/security/side{
dir = 1
@@ -683,14 +625,6 @@
},
/turf/simulated/floor/holofloor,
/area/holodeck/source_boxingcourt)
-"cy" = (
-/turf/simulated/floor/beach/away/sand,
-/area/ninja/holding)
-"cz" = (
-/obj/effect/overlay/palmtree_r,
-/obj/effect/overlay/coconut,
-/turf/simulated/floor/beach/away/sand,
-/area/ninja/holding)
"cA" = (
/obj/structure/flora/grass/both,
/turf/simulated/floor/holofloor{
@@ -728,9 +662,9 @@
/turf/simulated/floor/holofloor,
/area/holodeck/source_basketball)
"cG" = (
-/obj/effect/overlay/palmtree_l,
-/turf/simulated/floor/beach/away/sand,
-/area/ninja/holding)
+/obj/structure/chair/stool/bar,
+/turf/simulated/floor/plasteel/dark,
+/area/ninja/outpost)
"cH" = (
/obj/structure/table/wood,
/obj/item/tank/internals/emergency_oxygen/double/vox,
@@ -822,32 +756,10 @@
},
/turf/simulated/floor/carpet/green,
/area/ghost_bar)
-"cY" = (
-/obj/structure/table,
-/obj/machinery/kitchen_machine/microwave/upgraded,
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
-"cZ" = (
-/obj/structure/table,
-/obj/item/clothing/head/that{
- pixel_x = 4;
- pixel_y = 6
- },
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
"da" = (
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
-"db" = (
-/obj/structure/chair/stool,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
-"dc" = (
-/obj/item/camera,
-/turf/simulated/floor/beach/away/sand,
-/area/ninja/holding)
+/obj/structure/showcase,
+/turf/simulated/floor/wood,
+/area/ninja/outpost)
"dd" = (
/obj/item/electropack{
pixel_x = -5
@@ -864,21 +776,6 @@
/obj/structure/chair/office/dark,
/turf/simulated/floor/plasteel/dark,
/area/ghost_bar)
-"df" = (
-/turf/simulated/wall/indestructible/fakeglass,
-/area/ninja/outpost)
-"dh" = (
-/obj/structure/window/reinforced{
- dir = 4
- },
-/obj/machinery/sleeper/upgraded,
-/turf/simulated/floor/carpet/grimey,
-/area/ninja/outpost)
-"di" = (
-/obj/structure/table,
-/obj/machinery/recharger,
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
"dj" = (
/obj/effect/turf_decal/tiles/department/security/side{
dir = 8
@@ -937,9 +834,7 @@
/turf/simulated/floor/plasteel/stairs/right,
/area/syndicate_mothership)
"du" = (
-/obj/structure/table,
-/obj/item/reagent_containers/drinks/shaker,
-/turf/simulated/floor/plasteel/freezer,
+/turf/simulated/floor/plasteel/dark,
/area/ninja/holding)
"dv" = (
/obj/machinery/atmospherics/unary/cryo_cell/upgraded,
@@ -948,25 +843,6 @@
},
/turf/simulated/floor/plasteel/white,
/area/centcom/control)
-"dw" = (
-/obj/structure/table,
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
-"dx" = (
-/obj/structure/table,
-/obj/item/lighter/zippo,
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
-"dy" = (
-/obj/structure/table,
-/obj/item/reagent_containers/drinks/cans/cola,
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
-"dz" = (
-/obj/structure/table,
-/obj/item/dice/d20,
-/turf/simulated/floor/plasteel/freezer,
-/area/ninja/holding)
"dA" = (
/obj/structure/flora/tree/dead,
/turf/simulated/floor/holofloor{
@@ -995,18 +871,6 @@
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/syndicate)
-"dE" = (
-/obj/structure/chair/stool,
-/obj/item/clothing/head/bandana{
- pixel_y = -10
- },
-/obj/item/clothing/glasses/sunglasses,
-/turf/simulated/floor/beach/away/sand,
-/area/ninja/holding)
-"dF" = (
-/obj/structure/chair/stool,
-/turf/simulated/floor/beach/away/sand,
-/area/ninja/holding)
"dG" = (
/obj/effect/turf_decal/tiles/department/virology/side{
dir = 1
@@ -1050,32 +914,10 @@
/obj/structure/closet/wardrobe/mixed,
/turf/simulated/floor/mineral/plastitanium/red,
/area/ghost_bar)
-"dT" = (
-/obj/effect/landmark/spawner/ninjastart,
-/turf/simulated/floor/carpet/grimey,
-/area/ninja/outpost)
-"dU" = (
-/turf/simulated/floor/carpet/grimey,
-/area/ninja/outpost)
-"dV" = (
-/obj/structure/rack,
-/obj/item/clothing/head/that{
- pixel_x = 4;
- pixel_y = 6
+"dS" = (
+/turf/simulated/wall/indestructible/fakedoor{
+ name = "Spider Clan Jail Cell"
},
-/obj/item/clothing/under/suit/black,
-/obj/item/clothing/suit/wcoat,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
-"dW" = (
-/obj/structure/chair/stool/bar,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
-"dX" = (
-/obj/item/beach_ball,
-/turf/simulated/floor/beach/away/sand,
/area/ninja/holding)
"dY" = (
/obj/effect/turf_decal/tiles/department/virology/side{
@@ -1101,12 +943,6 @@
"ed" = (
/turf/simulated/floor/wood,
/area/wizard_station)
-"ee" = (
-/obj/structure/rack,
-/obj/item/storage/fancy/crayons,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
"ef" = (
/obj/machinery/chem_dispenser/beer,
/obj/structure/table/reinforced,
@@ -1119,14 +955,6 @@
/obj/effect/landmark/spawner/prisonwarp,
/turf/simulated/floor/plating,
/area/centcom)
-"eh" = (
-/obj/machinery/economy/vending/coffee,
-/obj/structure/window/reinforced{
- dir = 4
- },
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
"ei" = (
/obj/effect/mapping_helpers/airlock/access/all/centcomm/general,
/obj/machinery/door/airlock/centcom{
@@ -1201,25 +1029,6 @@
/obj/structure/sign/public/cryo,
/turf/simulated/wall/indestructible/syndicate/no_diagonal,
/area/ghost_bar)
-"ey" = (
-/obj/structure/chair/stool,
-/obj/machinery/computer/security/telescreen{
- desc = "Damn, they better have /tg/thechannel on these things.";
- icon_state = "entertainment";
- name = "Entertainment monitor";
- pixel_y = -30
- },
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
-"ez" = (
-/obj/machinery/economy/vending/snack,
-/obj/structure/window/reinforced{
- dir = 4
- },
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
"eA" = (
/obj/structure/chair/stool{
dir = 1
@@ -1238,13 +1047,6 @@
},
/turf/simulated/floor/holofloor,
/area/holodeck/source_basketball)
-"eE" = (
-/turf/simulated/floor/beach/away/coastline,
-/area/ninja/holding)
-"eF" = (
-/obj/item/clothing/head/collectable/paper,
-/turf/simulated/floor/beach/away/coastline,
-/area/ninja/holding)
"eG" = (
/obj/effect/turf_decal/tiles/department/security/side{
dir = 8
@@ -1349,33 +1151,6 @@
/obj/effect/spawner/window/plastitanium,
/turf/simulated/floor/plating,
/area/shuttle/administration)
-"eY" = (
-/obj/machinery/economy/vending/cigarette,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
-"eZ" = (
-/obj/structure/table,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
-"fa" = (
-/obj/structure/chair/stool,
-/obj/machinery/light/spot,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
-"fb" = (
-/obj/machinery/economy/vending/cola,
-/obj/structure/window/reinforced{
- dir = 4
- },
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
-"fc" = (
-/turf/simulated/floor/beach/away/water,
-/area/ninja/holding)
"fd" = (
/obj/structure/table/wood,
/obj/item/paper_bin,
@@ -1386,22 +1161,9 @@
/obj/item/clipboard,
/turf/simulated/floor/plasteel/dark,
/area/centcom/specops)
-"fe" = (
-/obj/machinery/poolcontroller/invisible,
-/turf/simulated/floor/beach/away/water,
-/area/ninja/holding)
"fg" = (
/turf/simulated/wall/indestructible/wood,
/area/ninja/holding)
-"fi" = (
-/obj/effect/spawner/window/reinforced,
-/turf/simulated/floor/plating,
-/area/ninja/holding)
-"fj" = (
-/obj/effect/turf_decal/delivery,
-/obj/effect/turf_decal/tiles/dark/checker,
-/turf/simulated/floor/plasteel/white,
-/area/ninja/holding)
"fk" = (
/obj/machinery/door/airlock/hatch/syndicate{
autoclose = 0;
@@ -1454,10 +1216,6 @@
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/administration)
-"fu" = (
-/obj/effect/landmark/spawner/holding_facility,
-/turf/simulated/floor/engine,
-/area/ninja/holding)
"fv" = (
/obj/structure/table/wood,
/obj/item/reagent_containers/drinks/mug/novelty,
@@ -1490,6 +1248,10 @@
/obj/structure/sign/service/bar,
/turf/simulated/wall/indestructible/riveted,
/area/ghost_bar)
+"fD" = (
+/obj/machinery/economy/vending/coffee/free,
+/turf/simulated/floor/plasteel/dark,
+/area/ninja/outpost)
"fE" = (
/obj/structure/table,
/obj/item/storage/box/handcuffs,
@@ -1655,6 +1417,10 @@
/obj/effect/baseturf_helper/asteroid/snow,
/turf/simulated/wall/indestructible/rock/snow,
/area/syndicate_mothership)
+"gg" = (
+/obj/structure/showcase,
+/turf/simulated/floor/wood,
+/area/ninja/holding)
"gh" = (
/turf/simulated/wall/indestructible/opsglass,
/area/centcom)
@@ -2295,10 +2061,10 @@
/area/tdome/tdomeobserve)
"ib" = (
/obj/structure/table/wood,
+/obj/effect/turf_decal/tiles/jobs/bar/checker,
/obj/machinery/chem_dispenser/soda{
dir = 8
},
-/obj/effect/turf_decal/tiles/jobs/bar/checker,
/turf/simulated/floor/plasteel,
/area/tdome/tdomeobserve)
"ic" = (
@@ -3552,6 +3318,10 @@
},
/turf/simulated/floor/plasteel,
/area/admin)
+"md" = (
+/obj/structure/chair/wood,
+/turf/simulated/floor/bamboo,
+/area/ninja/outpost)
"me" = (
/obj/structure/rack,
/obj/item/clothing/glasses/sunglasses{
@@ -3629,6 +3399,10 @@
/obj/mecha/combat/marauder/seraph/loaded,
/turf/simulated/floor/plasteel,
/area/admin)
+"mn" = (
+/obj/effect/landmark/spawner/ninja_prison_warp,
+/turf/simulated/floor/plasteel/dark,
+/area/ninja/holding)
"mo" = (
/obj/machinery/washing_machine,
/turf/simulated/floor/plasteel/freezer,
@@ -3730,6 +3504,12 @@
},
/turf/simulated/floor/plasteel,
/area/admin)
+"mE" = (
+/obj/structure/bed/wood,
+/obj/item/bedsheet/black,
+/obj/effect/landmark/spawner/ninja,
+/turf/simulated/floor/wood,
+/area/ninja/outpost)
"mF" = (
/obj/structure/table,
/obj/item/stack/sheet/glass{
@@ -4490,6 +4270,13 @@
},
/turf/simulated/floor/backrooms_carpet,
/area/backrooms)
+"pa" = (
+/obj/structure/table/glass/reinforced/plastitanium,
+/obj/machinery/chem_dispenser/soda{
+ dir = 1
+ },
+/turf/simulated/floor/plasteel/full,
+/area/ninja/outpost)
"pc" = (
/obj/effect/turf_decal/stripes/line{
dir = 1
@@ -4960,6 +4747,9 @@
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/assault_pod)
+"qQ" = (
+/turf/simulated/floor/bamboo/tatami/black,
+/area/ninja/outpost)
"qR" = (
/turf/simulated/wall/indestructible,
/area/ghost_bar)
@@ -5845,6 +5635,12 @@
/obj/structure/lattice,
/turf/space,
/area/space/nearstation/centcom)
+"tM" = (
+/obj/structure/chair/wood{
+ dir = 1
+ },
+/turf/simulated/floor/bamboo,
+/area/ninja/outpost)
"tN" = (
/obj/machinery/door/window/classic/normal{
dir = 1
@@ -6251,6 +6047,9 @@
},
/turf/simulated/floor/carpet,
/area/admin)
+"vo" = (
+/turf/simulated/floor/carpet/black,
+/area/ninja/outpost)
"vq" = (
/obj/structure/chair/sofa/corp,
/turf/simulated/floor/transparent/glass,
@@ -6464,6 +6263,11 @@
/obj/item/soap,
/turf/simulated/floor/plasteel/freezer,
/area/trader_station/sol)
+"we" = (
+/obj/structure/table/glass/reinforced/plastitanium,
+/obj/item/reagent_containers/cooking/sushimat,
+/turf/simulated/floor/plasteel/full,
+/area/ninja/outpost)
"wf" = (
/obj/structure/mirror{
pixel_x = 28
@@ -7059,6 +6863,9 @@
},
/turf/simulated/floor/transparent/glass,
/area/centcom/specops)
+"yi" = (
+/turf/simulated/wall/indestructible/syndicate,
+/area/ninja/holding)
"yj" = (
/obj/structure/table/wood/fancy/royalblack,
/obj/item/gun/projectile/automatic/pistol,
@@ -7455,6 +7262,9 @@
},
/turf/simulated/floor/wood,
/area/syndicate_mothership)
+"zX" = (
+/turf/simulated/wall/indestructible/syndicate,
+/area/ninja/outpost)
"zY" = (
/obj/structure/table/reinforced,
/obj/machinery/cooking/stovetop,
@@ -7893,12 +7703,6 @@
/obj/structure/stone_tile/slab/cracked,
/turf/simulated/floor/lava/lava_land_surface,
/area/ruin/space/bubblegum_arena)
-"Bw" = (
-/obj/structure/ninjatele{
- pixel_x = -28
- },
-/turf/simulated/floor/plasteel/dark,
-/area/admin)
"Bx" = (
/obj/machinery/light/small{
dir = 4
@@ -7906,11 +7710,10 @@
/turf/simulated/floor/carpet/arcade,
/area/ghost_bar)
"By" = (
-/obj/structure/ninjatele{
- pixel_x = 28
- },
-/turf/simulated/floor/plasteel/dark,
-/area/admin)
+/obj/structure/table/wood,
+/obj/item/reagent_containers/drinks/tea,
+/turf/simulated/floor/bamboo,
+/area/ninja/outpost)
"Bz" = (
/obj/machinery/door/airlock/external{
id_tag = "s_docking_airlock";
@@ -8339,7 +8142,7 @@
/area/space/centcomm)
"Dd" = (
/obj/structure/rack,
-/obj/item/clothing/suit/space/space_ninja,
+/obj/item/clothing/suit/space_ninja,
/turf/simulated/floor/engine,
/area/admin)
"De" = (
@@ -8398,7 +8201,7 @@
/area/ghost_bar)
"Dv" = (
/obj/structure/rack,
-/obj/item/clothing/head/helmet/space/space_ninja,
+/obj/item/clothing/head/helmet/space_ninja,
/turf/simulated/floor/engine,
/area/admin)
"Dw" = (
@@ -8647,6 +8450,11 @@
},
/turf/simulated/floor/wood,
/area/centcom/evac)
+"En" = (
+/obj/structure/closet/crate,
+/obj/item/storage/box/donkpockets,
+/turf/simulated/floor/plasteel/full,
+/area/ninja/outpost)
"Eo" = (
/turf/simulated/floor/holofloor{
icon_state = "sand";
@@ -8943,6 +8751,12 @@
/obj/machinery/shower/directional/south,
/turf/simulated/floor/plasteel/freezer,
/area/syndicate_mothership)
+"FB" = (
+/obj/structure/mineral_door/wood{
+ name = "Main chamber"
+ },
+/turf/simulated/floor/wood,
+/area/ninja/outpost)
"FC" = (
/obj/docking_port/stationary{
dir = 2;
@@ -9368,6 +9182,9 @@
},
/turf/simulated/floor/plating,
/area/centcom/suppy)
+"He" = (
+/turf/simulated/floor/bamboo,
+/area/ninja/outpost)
"Hg" = (
/turf/simulated/wall/indestructible/riveted,
/area/centcom/evac)
@@ -9496,6 +9313,10 @@
/obj/structure/sign/public/drop,
/turf/simulated/wall/indestructible/syndicate/no_diagonal,
/area/syndicate_mothership)
+"HM" = (
+/obj/machinery/economy/vending/boozeomat,
+/turf/simulated/floor/plasteel/full,
+/area/ninja/outpost)
"HN" = (
/turf/simulated/floor/indestructible/necropolis,
/area/ruin/space/bubblegum_arena)
@@ -9929,6 +9750,10 @@
/obj/effect/turf_decal/tiles/dark/checker,
/turf/simulated/floor/plasteel/white,
/area/tdome/tdomeobserve)
+"JC" = (
+/obj/structure/dresser,
+/turf/simulated/floor/wood,
+/area/ninja/outpost)
"JE" = (
/obj/item/stack/sheet/plasmaglass/fifty,
/obj/item/stack/sheet/plasmaglass/fifty,
@@ -9971,6 +9796,10 @@
},
/turf/simulated/floor/plasteel/dark,
/area/centcom/gamma)
+"JN" = (
+/obj/structure/bed,
+/turf/simulated/floor/plasteel/dark,
+/area/ninja/holding)
"JO" = (
/obj/structure/sign/cargo/dock,
/turf/simulated/wall/indestructible/riveted,
@@ -10544,6 +10373,10 @@
},
/turf/simulated/floor/plasteel,
/area/tdome/arena)
+"LR" = (
+/obj/structure/table/glass/reinforced/plastitanium,
+/turf/simulated/floor/plasteel/full,
+/area/ninja/outpost)
"LT" = (
/obj/structure/table/wood/fancy/royalblack,
/obj/item/storage/fancy/matches,
@@ -10575,6 +10408,10 @@
},
/turf/simulated/floor/wood,
/area/ghost_bar)
+"Mc" = (
+/obj/item/kirbyplants/large,
+/turf/simulated/floor/wood,
+/area/ninja/outpost)
"Md" = (
/obj/structure/sign/public/arcade,
/turf/simulated/wall/indestructible/syndicate/no_diagonal,
@@ -10603,6 +10440,10 @@
},
/turf/simulated/floor/plasteel/dark,
/area/centcom/gamma)
+"Mi" = (
+/obj/item/throwing_star,
+/turf/simulated/floor/plasteel/dark,
+/area/ninja/outpost)
"Mk" = (
/obj/machinery/door_control/no_emag{
id = "thunderdomegen";
@@ -10627,6 +10468,12 @@
/obj/item/storage/box/trackimp,
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/administration)
+"Mp" = (
+/obj/structure/mirror{
+ pixel_x = 32
+ },
+/turf/simulated/floor/wood,
+/area/ninja/outpost)
"Mq" = (
/obj/structure/table/glass/reinforced/plastitanium,
/obj/machinery/computer/security/telescreen,
@@ -10676,6 +10523,13 @@
},
/turf/simulated/floor/plasteel/dark,
/area/centcom/suppy)
+"MA" = (
+/obj/machinery/door/airlock/hatch{
+ desc = "Danger: May contain robustness";
+ name = "Dojo"
+ },
+/turf/simulated/floor/wood,
+/area/ninja/outpost)
"MD" = (
/obj/machinery/door/airlock/centcom{
hackProof = 1;
@@ -11028,6 +10882,10 @@
},
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/administration)
+"NL" = (
+/obj/structure/table/wood,
+/turf/simulated/floor/bamboo,
+/area/ninja/outpost)
"NN" = (
/obj/structure/chair/office/dark{
dir = 8
@@ -11292,6 +11150,9 @@
},
/turf/simulated/floor/plasteel/dark,
/area/centcom/specops)
+"OJ" = (
+/turf/simulated/floor/wood,
+/area/ninja/holding)
"OK" = (
/turf/simulated/floor/plating/asteroid/ancient/airless,
/area/space/nearstation/centcom)
@@ -11549,6 +11410,10 @@
/obj/structure/sign/service/bar,
/turf/simulated/wall/indestructible/syndicate/no_diagonal,
/area/ghost_bar)
+"PL" = (
+/obj/machinery/economy/vending/cigarette/free,
+/turf/simulated/floor/plasteel/dark,
+/area/ninja/outpost)
"PM" = (
/obj/item/stack/sheet/metal/fifty,
/obj/item/stack/sheet/metal/fifty,
@@ -12254,6 +12119,11 @@
},
/turf/simulated/floor/plasteel,
/area/centcom/evac)
+"Sg" = (
+/obj/structure/table/glass/reinforced/plastitanium,
+/obj/item/reagent_containers/cooking/board,
+/turf/simulated/floor/plasteel/full,
+/area/ninja/outpost)
"Sh" = (
/obj/structure/table/wood,
/obj/item/storage/bag/dice,
@@ -12388,6 +12258,24 @@
/obj/machinery/door/airlock/centcom,
/turf/simulated/floor/wood,
/area/ghost_bar)
+"SJ" = (
+/obj/structure/closet/crate/freezer,
+/obj/item/food/sliced/sushi_sake,
+/obj/item/food/sliced/sushi_sake,
+/obj/item/food/sliced/sushi_masago,
+/obj/item/food/sliced/sushi_masago,
+/obj/item/food/sliced/sushi_ikura,
+/obj/item/food/sliced/sushi_ikura,
+/obj/item/food/sliced/sushi_ebi,
+/obj/item/food/sliced/sushi_ebi,
+/obj/item/food/sliced/sushi_tobiko,
+/obj/item/food/sliced/sushi_tobiko,
+/obj/item/food/sliced/sushi_tai,
+/obj/item/food/sliced/sushi_tai,
+/obj/item/food/sushi_unagi,
+/obj/item/food/sushi_unagi,
+/turf/simulated/floor/plasteel/full,
+/area/ninja/outpost)
"SK" = (
/obj/structure/table/reinforced,
/obj/item/storage/toolbox/mechanical,
@@ -12574,6 +12462,9 @@
/obj/machinery/computer/camera_advanced/shuttle_docker/ert,
/turf/simulated/floor/mineral/plastitanium/red,
/area/shuttle/specops)
+"TE" = (
+/turf/simulated/floor/wood,
+/area/ninja/outpost)
"TF" = (
/obj/machinery/recharger/wallcharger/upgraded{
pixel_x = 4;
@@ -13145,6 +13036,10 @@
},
/turf/simulated/floor/plasteel,
/area/centcom/evac)
+"VM" = (
+/obj/structure/toilet/directional/north,
+/turf/simulated/floor/plasteel/dark,
+/area/ninja/holding)
"VN" = (
/obj/effect/spawner/window/reinforced,
/turf/simulated/floor/plating,
@@ -13499,6 +13394,10 @@
/obj/item/kirbyplants/large,
/turf/simulated/floor/transparent/glass/reinforced,
/area/centcom/control)
+"WY" = (
+/obj/structure/bookcase/random,
+/turf/simulated/floor/wood,
+/area/ninja/outpost)
"WZ" = (
/obj/structure/table/reinforced,
/obj/item/crowbar/red,
@@ -13572,6 +13471,11 @@
},
/turf/simulated/floor/plasteel,
/area/centcom/evac)
+"Xm" = (
+/obj/structure/target_stake,
+/obj/item/target,
+/turf/simulated/floor/plasteel/dark,
+/area/ninja/outpost)
"Xn" = (
/obj/effect/turf_decal/delivery,
/obj/machinery/door/poddoor/multi_tile/impassable/triple{
@@ -50033,10 +49937,10 @@ aN
aN
aN
aN
-ac
-df
-df
-ac
+aN
+aN
+aN
+aN
aN
aN
aN
@@ -50290,10 +50194,10 @@ aN
aN
aN
aN
-ac
-dU
-dT
-ac
+aN
+aN
+aN
+aN
aN
aN
aN
@@ -50545,12 +50449,12 @@ aN
aN
aN
aN
-ac
-at
-ac
-dh
-dU
-ac
+aN
+aN
+aN
+aN
+aN
+aN
aN
aN
aN
@@ -50802,12 +50706,12 @@ aN
aN
aN
aN
-ac
-aB
-aB
-aB
-aB
-ac
+aN
+aN
+aN
+aN
+aN
+aN
aN
aN
aN
@@ -51059,12 +50963,12 @@ aN
aN
aN
aN
-ac
-bz
-aB
-aB
-aB
-ac
+aN
+aN
+aN
+aN
+aN
+aN
aN
aN
aN
@@ -51316,12 +51220,12 @@ aN
aN
aN
aN
-ac
-aB
-aB
-aB
-aB
-ac
+aN
+aN
+aN
+aN
+aN
+aN
aN
aN
aN
@@ -51573,12 +51477,12 @@ aN
aN
aN
aN
-ac
-ac
-ac
-ac
-ac
-ac
+aN
+aN
+aN
+aN
+aN
+aN
aN
aN
aN
@@ -51830,15 +51734,15 @@ aN
aN
aN
aN
-fg
-fg
-fg
-fg
-fg
-fg
-fg
-fg
-fg
+aN
+aN
+aN
+aN
+aN
+aN
+aN
+aN
+aN
aN
aN
aN
@@ -52086,27 +51990,27 @@ aN
aN
aN
aN
-aN
-fg
-cY
-cY
-di
-dV
-ee
-ee
-eY
-fg
-fg
-fg
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
+yi
aN
aN
aN
@@ -52343,27 +52247,27 @@ aN
aN
aN
aN
-aN
-ae
-bN
-bN
-bN
-da
-da
-da
-da
-fi
-fu
-fu
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+yi
+VM
+mn
+JN
+yi
+VM
+mn
+JN
+yi
+VM
+mn
+JN
+yi
+VM
+mn
+JN
+yi
+VM
+mn
+JN
+yi
aN
aN
aN
@@ -52600,27 +52504,27 @@ aN
aN
aN
aN
-aN
-ae
-bO
-bN
+yi
du
-dW
-da
-da
-da
-fj
-fu
-fu
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+du
+du
+ae
+du
+du
+du
+ae
+du
+du
+du
+ae
+du
+du
+du
+ae
+du
+du
+du
+yi
aN
aN
aN
@@ -52857,27 +52761,27 @@ aN
aN
aN
aN
-aN
+yi
+dS
ae
-bR
-bN
-dw
-dW
-da
-da
-da
-fj
-fu
-fu
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+ae
+yi
+dS
+ae
+ae
+yi
+dS
+ae
+ae
+yi
+dS
+ae
+ae
+yi
+dS
+ae
+ae
+yi
aN
aN
aN
@@ -53113,29 +53017,29 @@ aN
aN
aN
aN
-aN
-aN
-ae
-bR
-bN
-dx
-dW
-da
-da
-da
-fi
-fu
-fu
+yi
fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+fg
+yi
aN
aN
aN
@@ -53370,29 +53274,29 @@ aN
aN
aN
aN
-aN
-aN
+yi
fg
-bY
-bN
-dy
-dW
-da
-da
-db
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+gg
+OJ
+gg
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
+OJ
fg
-fg
-fg
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+yi
aN
aN
aN
@@ -53627,29 +53531,29 @@ aN
aN
aN
aN
-aN
-aN
-fg
-ci
-cZ
-dz
-dW
-da
-ey
-eZ
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+MA
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+zX
aN
aN
aN
@@ -53884,29 +53788,29 @@ aN
aN
aN
aN
-aN
-aN
-fg
-co
+zX
+zX
+ac
da
-da
-da
-da
-da
-fa
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+PL
+fD
+aB
+aB
+cG
+we
+bR
+pa
+ac
+zX
aN
aN
aN
@@ -54142,28 +54046,28 @@ aN
aN
aN
aN
-aN
-fg
-cs
-da
-da
-da
-da
-da
-db
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+aB
+aB
+aB
+aB
+cG
+LR
+bR
+bx
+ac
+zX
aN
aN
aN
@@ -54399,28 +54303,28 @@ aN
aN
aN
aN
-aN
-fg
-ct
-da
-da
-da
-da
-ey
-eZ
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+Mc
+TE
+He
+He
+He
+He
+He
+He
+TE
+TE
+aB
+qQ
+qQ
+qQ
+cG
+Sg
+bR
+HM
+ac
+zX
aN
aN
aN
@@ -54656,28 +54560,28 @@ aN
aN
aN
aN
-aN
-fg
-cv
-db
-da
-da
-da
-da
-db
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+WY
+TE
+He
+md
+By
+NL
+tM
+He
+TE
+TE
+aB
+qQ
+qQ
+qQ
+cG
+LR
+bR
+En
+ac
+zX
aN
aN
aN
@@ -54913,28 +54817,28 @@ aN
aN
aN
aN
-aN
-fg
-cy
-cy
-cy
-cy
-eh
-ez
-fb
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+WY
+TE
+He
+md
+NL
+NL
+tM
+He
+TE
+TE
+aB
+qQ
+qQ
+qQ
+cG
+LR
+bR
+SJ
+ac
+zX
aN
aN
aN
@@ -55170,28 +55074,28 @@ aN
aN
aN
aN
-aN
-fg
-cz
-cy
-cy
-cy
-cy
-eE
-fc
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+WY
+TE
+He
+md
+NL
+By
+tM
+He
+TE
+TE
+aB
+aB
+aB
+aB
+aB
+bR
+bR
+bR
+ac
+zX
aN
aN
aN
@@ -55427,28 +55331,28 @@ aN
aN
aN
aN
-aN
-fg
-cy
-cy
-dE
-cy
-cy
-eE
-fc
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+WY
+TE
+He
+md
+By
+NL
+tM
+He
+TE
+TE
+aB
+aB
+aB
+ac
+ac
+ac
+ac
+ac
+ac
+zX
aN
aN
aN
@@ -55684,28 +55588,28 @@ aN
aN
aN
aN
-aN
-ag
-cy
-dc
-cy
-dX
-cy
-eE
-fc
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+Mc
+TE
+He
+He
+He
+He
+He
+He
+TE
+TE
+aB
+aB
+aB
+ac
+Mc
+TE
+TE
+TE
+ac
+zX
aN
aN
aN
@@ -55941,28 +55845,28 @@ aN
aN
aN
aN
-aN
-fg
-cy
-cy
-dF
-cy
-cy
-eF
-fc
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+aB
+aB
+aB
+FB
+TE
+vo
+vo
+mE
+ac
+zX
aN
aN
aN
@@ -56198,28 +56102,28 @@ aN
aN
aN
aN
-aN
-fg
-cG
-cy
-cz
-cy
-cy
-eE
-fe
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+da
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+TE
+aB
+aB
+aB
+ac
+JC
+Mp
+TE
+TE
+ac
+zX
aN
aN
aN
@@ -56455,28 +56359,28 @@ aN
aN
aN
aN
-aN
-fg
-fg
-fg
-fg
-fg
-fg
-fg
-fg
-fg
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+ac
+aB
+Mi
+aB
+ac
+ac
+ac
+ac
+ac
+ac
+zX
aN
aN
aN
@@ -56712,28 +56616,28 @@ aN
aN
aN
aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+zX
+zX
+zX
+zX
+zX
+zX
+zX
+zX
+zX
+zX
+ac
+Mi
+Xm
+Mi
+ac
+zX
+zX
+zX
+zX
+zX
+zX
aN
aN
aN
@@ -56979,13 +56883,13 @@ aN
aN
aN
aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+ac
+ac
+ac
+ac
+ac
+zX
aN
aN
aN
@@ -57236,13 +57140,13 @@ aN
aN
aN
aN
-aN
-aN
-aN
-aN
-aN
-aN
-aN
+zX
+zX
+zX
+zX
+zX
+zX
+zX
aN
aN
aN
@@ -72507,7 +72411,7 @@ wk
vK
Bf
Bp
-Bw
+vK
wk
wk
wk
@@ -74049,7 +73953,7 @@ wk
vK
Bg
Bq
-By
+vK
wk
wk
wk
diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm
index cf3fc5af889..d4c9eebed37 100644
--- a/code/__DEFINES/gamemode.dm
+++ b/code/__DEFINES/gamemode.dm
@@ -64,6 +64,7 @@
#define SPECIAL_ROLE_TOURIST "Tourist"
#define SPECIAL_ROLE_HERETIC "Heretic"
#define SPECIAL_ROLE_EVENTMISC "Event Role"
+#define SPECIAL_ROLE_NINJA "Space Ninja"
// Constants used by code which checks the status of nuclear blasts during a
// round, regardless of original game mode, e.g. setting the ending cinematic.
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index b73f39bf14a..8c86281e709 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -45,6 +45,7 @@
#define ROLE_EVENTMISC "eventmisc"
#define ROLE_GHOST "ghost role"
#define ROLE_ELITE "lavaland elite"
+#define ROLE_NINJA "ninja"
// Misc jobban categories
#define ROLEBAN_AHUD "AntagHUD"
@@ -71,6 +72,7 @@ GLOBAL_LIST_INIT(special_roles_antags, list(
ROLE_WIZARD = /datum/game_mode/wizard, // Wizard
ROLE_MIND_FLAYER,
ROLE_HERETIC,
+ ROLE_NINJA,
// UNUSED/BROKEN ANTAGS
// ROLE_HOG_GOD = /datum/game_mode/hand_of_god,
diff --git a/code/__DEFINES/rolebans.dm b/code/__DEFINES/rolebans.dm
index b4d3d033424..62f6b39b497 100644
--- a/code/__DEFINES/rolebans.dm
+++ b/code/__DEFINES/rolebans.dm
@@ -14,6 +14,7 @@ GLOBAL_LIST_INIT(antag_roles, list(
ROLE_GUARDIAN,
ROLE_MORPH,
ROLE_TSPIDER,
+ ROLE_NINJA,
))
// Bannable other roles
diff --git a/code/__DEFINES/uplinks_defines.dm b/code/__DEFINES/uplinks_defines.dm
index c8bdcd52ef7..beb98ecc593 100644
--- a/code/__DEFINES/uplinks_defines.dm
+++ b/code/__DEFINES/uplinks_defines.dm
@@ -1,5 +1,6 @@
#define UPLINK_TYPE_TRAITOR "traitor"
#define UPLINK_TYPE_NUCLEAR "nuclear"
+#define UPLINK_TYPE_NINJA "ninja"
#define UPLINK_TYPE_SST "sst"
#define UPLINK_TYPE_SIT "sit"
#define UPLINK_TYPE_ADMIN "admin"
diff --git a/code/_globalvars/mapping_vars.dm b/code/_globalvars/mapping_vars.dm
index 281d279806d..0e359da6558 100644
--- a/code/_globalvars/mapping_vars.dm
+++ b/code/_globalvars/mapping_vars.dm
@@ -22,6 +22,7 @@ GLOBAL_LIST_EMPTY(newplayer_start)
GLOBAL_LIST_EMPTY(latejoin)
GLOBAL_LIST_EMPTY(prisonwarp) //prisoners go to these
GLOBAL_LIST_EMPTY(syndieprisonwarp) //contractor targets go to these
+GLOBAL_LIST_EMPTY(ninjaprisonwarp) // ninja targets go to these
GLOBAL_LIST_EMPTY(antagextractwarp) // exfiltrating antags go here
GLOBAL_LIST_EMPTY(backroomswarp) //backrooms hallucinators go to these
GLOBAL_LIST_EMPTY(xeno_spawn)//Aliens spawn at these.
@@ -37,7 +38,6 @@ GLOBAL_LIST_EMPTY(aroomwarp)
GLOBAL_LIST_EMPTY(prisonsecuritywarp) //prison security goes to these
GLOBAL_LIST_EMPTY(prisonwarped) //list of players already warped
GLOBAL_LIST_EMPTY(nukedisc_respawn)
-GLOBAL_LIST_EMPTY(ninjastart)
GLOBAL_LIST_EMPTY(carplist) //list of all carp-spawn landmarks
GLOBAL_LIST_EMPTY(syndicateofficer)
GLOBAL_LIST_EMPTY(roundstart_observer_start)
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 11b09031fc9..21245dc3fde 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -408,6 +408,18 @@
else
. += "thrall|NO"
+/datum/mind/proc/memory_edit_space_ninja(mob/living/carbon/human/H)
+ . = _memory_edit_header("space_ninja")
+ var/datum/antagonist/space_ninja/ninja = has_antag_datum(/datum/antagonist/space_ninja)
+ if(ninja)
+ . += "SPACE NINJA|no"
+ if(!ninja.has_antag_objectives())
+ . += "
Objectives are empty! Randomize!"
+ else
+ . += "space_ninja|NO"
+
+ . += _memory_edit_role_enabled(ROLE_NINJA)
+
/datum/mind/proc/memory_edit_mind_flayer(mob/living/carbon/human/H)
. = _memory_edit_header("mind_flayer")
var/datum/antagonist/mindflayer/flayer = has_antag_datum(/datum/antagonist/mindflayer)
@@ -605,6 +617,8 @@
sections["changeling"] = memory_edit_changeling(H)
/** VAMPIRE ***/
sections["vampire"] = memory_edit_vampire(H)
+ /** SPACE NINJA */
+ sections["space_ninja"] = memory_edit_space_ninja(H)
/** MINDFLAYER ***/
sections["mind_flayer"] = memory_edit_mind_flayer(H)
/** HERETIC ***/
@@ -1169,6 +1183,19 @@
log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]")
message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]")
+ else if(href_list["space_ninja"])
+ switch(href_list["space_ninja"])
+ if("clear")
+ if(has_antag_datum(/datum/antagonist/space_ninja))
+ remove_antag_datum(/datum/antagonist/space_ninja)
+ log_admin("[key_name(usr)] has de-ninja'd [key_name(current)].")
+ message_admins("[key_name(usr)] has de-ninja'd [key_name(current)].")
+ if("space_ninja")
+ make_space_ninja()
+ log_admin("[key_name(usr)] has ninja'd [key_name(current)].")
+ to_chat(current, "Your training awakens, and a myserious set of gear teleports in around you... You are a Space Ninja!")
+ message_admins("[key_name(usr)] has ninja'd [key_name(current)].")
+
else if(href_list["vampthrall"])
switch(href_list["vampthrall"])
if("clear")
@@ -1826,6 +1853,11 @@
add_antag_datum(/datum/antagonist/mindflayer)
SSticker.mode.mindflayers |= src
+/datum/mind/proc/make_space_ninja()
+ if(!has_antag_datum(/datum/antagonist/space_ninja))
+ add_antag_datum(/datum/antagonist/space_ninja)
+ SSticker.mode.ninjas |= src
+
/datum/mind/proc/make_heretic()
if(!has_antag_datum(/datum/antagonist/heretic))
add_antag_datum(/datum/antagonist/heretic)
diff --git a/code/datums/uplink_items/uplink_general.dm b/code/datums/uplink_items/uplink_general.dm
index becc5a73129..a47c7130e33 100644
--- a/code/datums/uplink_items/uplink_general.dm
+++ b/code/datums/uplink_items/uplink_general.dm
@@ -182,6 +182,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/dangerous
category = "Highly Visible and Dangerous Weapons"
+ excludefrom = list(UPLINK_TYPE_NINJA)
/datum/uplink_item/dangerous/pistol
name = "FK-69 Stechkin Pistol"
@@ -243,7 +244,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/chainsaw/syndie
cost = 65
surplus = 0 // This has caused major problems with un-needed chainsaw massacres. Bwoink bait.
- excludefrom = list(UPLINK_TYPE_NUCLEAR)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_NINJA)
can_discount = FALSE // Too gamer.
/datum/uplink_item/dangerous/universal_gun_kit
@@ -275,6 +276,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
category = "Ammunition"
surplus = 0 // Getting these in a discount or surplus is not a good time.
can_discount = FALSE
+ excludefrom = list(UPLINK_TYPE_NINJA)
/datum/uplink_item/ammo/pistol
name = "Stechkin - 10mm Magazine"
@@ -317,6 +319,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/stealthy_weapons
category = "Stealthy and Inconspicuous Weapons"
+ excludefrom = list(UPLINK_TYPE_NINJA)
/datum/uplink_item/stealthy_weapons/garrote
name = "Fiber Wire Garrote"
@@ -325,6 +328,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/garrote
reference = "GAR"
cost = 30
+ excludefrom = list()
/datum/uplink_item/stealthy_weapons/cameraflash
name = "Camera Flash"
@@ -342,6 +346,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "STK"
item = /obj/item/storage/box/syndie_kit/throwing_weapons
cost = 15
+ excludefrom = list()
/datum/uplink_item/stealthy_weapons/edagger
name = "Energy Dagger"
@@ -349,6 +354,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "EDP"
item = /obj/item/pen/edagger
cost = 10
+ excludefrom = list()
/datum/uplink_item/stealthy_weapons/foampistol
name = "Toy Gun (with Stun Darts)"
@@ -387,6 +393,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/reagent_containers/glass/bottle/traitor
cost = 10
surplus = 0 // Requires another item to function.
+ excludefrom = list()
/datum/uplink_item/stealthy_weapons/silencer
name = "Universal Suppressor"
@@ -416,6 +423,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/explosives
category = "Grenades and Explosives"
+ excludefrom = list(UPLINK_TYPE_NINJA)
/datum/uplink_item/explosives/plastic_explosives
name = "Composition C-4"
@@ -487,6 +495,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "TARG"
item = /obj/item/grenade/chem_grenade/tar
cost = 7
+ excludefrom = list()
////////////////////////////////////////
// MARK: STEALTHY TOOLS
@@ -494,6 +503,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/stealthy_tools
category = "Stealth and Camouflage Items"
+ excludefrom = list(UPLINK_TYPE_NINJA)
/datum/uplink_item/stealthy_tools/forgers_kit
name = "Forger's Kit"
@@ -526,6 +536,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "THIG"
item = /obj/item/clothing/glasses/chameleon/thermal
cost = 15
+ excludefrom = list()
/datum/uplink_item/stealthy_tools/night
name = "Nightvision Chameleon Glasses"
@@ -533,6 +544,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "TNIG"
item = /obj/item/clothing/glasses/chameleon/night
cost = 5
+ excludefrom = list()
/datum/uplink_item/stealthy_tools/agent_card
name = "Agent ID Card"
@@ -547,6 +559,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "CP"
item = /obj/item/chameleon
cost = 25
+ excludefrom = list()
/datum/uplink_item/stealthy_tools/chameleon_counter
name = "Chameleon Counterfeiter"
@@ -562,6 +575,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/storage/box/syndie_kit/camera_bug
cost = 5
surplus = 90
+ excludefrom = list()
/datum/uplink_item/stealthy_tools/dnascrambler
name = "DNA Scrambler"
@@ -569,6 +583,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "DNAS"
item = /obj/item/dnascrambler
cost = 7
+ excludefrom = list()
/datum/uplink_item/stealthy_tools/smugglersatchel
name = "Smuggler's Satchel"
@@ -615,6 +630,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "HM"
item = /obj/item/handheld_mirror
cost = 5
+ excludefrom = list()
////////////////////////////////////////
// MARK: DEVICES AND TOOLS
@@ -623,6 +639,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/device_tools
category = "Devices and Tools"
abstract = 1
+ excludefrom = list(UPLINK_TYPE_NINJA)
/datum/uplink_item/device_tools/emag
name = "Cryptographic Sequencer"
@@ -658,6 +675,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "NCAI"
item = /obj/item/reagent_containers/hypospray/autoinjector/nanocalcium
cost = 10
+ excludefrom = list()
/datum/uplink_item/device_tools/syndicate_teleporter
name = "Experimental Syndicate Teleporter"
@@ -692,6 +710,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "TAR"
item = /obj/item/reagent_containers/spray/sticky_tar
cost = 10
+ excludefrom = list()
/datum/uplink_item/device_tools/binary
name = "Binary Translator Key"
@@ -739,6 +758,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
name = "Advanced Pinpointer"
desc = "A pinpointer that tracks any specified coordinates, DNA string, high value item or the nuclear authentication disk."
reference = "ADVP"
+ excludefrom = list()
item = /obj/item/pinpointer/advpinpointer
cost = 10
can_discount = FALSE
@@ -749,6 +769,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "AID"
item = /obj/item/multitool/ai_detect
cost = 5
+ excludefrom = list()
/datum/uplink_item/device_tools/jammer
name = "Radio Jammer"
@@ -756,6 +777,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "RJ"
item = /obj/item/jammer
cost = 20
+ excludefrom = list()
/datum/uplink_item/device_tools/decoy_nade
name = "Decoy Grenade Kit"
@@ -763,6 +785,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "DCY"
item = /obj/item/storage/box/syndie_kit/decoy
cost = 20
+ excludefrom = list()
////////////////////////////////////////
// MARK: SPACE SUITS AND HARDSUITS
@@ -771,6 +794,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/suits
category = "Space Suits and MODsuits"
surplus = 10 //I am setting this to 10 as there are a bunch of modsuit parts in here that should be weighted to 10. Suits and modsuits adjusted below.
+ excludefrom = list(UPLINK_TYPE_NINJA)
/datum/uplink_item/suits/space_suit
name = "Syndicate Space Suit"
@@ -787,6 +811,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "MSTV"
item = /obj/item/mod/module/visor/thermal
cost = 15 // Don't forget, you need to get a modsuit to go with this
+ excludefrom = list()
/datum/uplink_item/suits/night
name = "MODsuit Night Visor Module"
@@ -794,6 +819,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "MSNV"
item = /obj/item/mod/module/visor/night
cost = 5 // It's night vision, rnd pumps out those goggles for anyone man.
+ excludefrom = list()
/datum/uplink_item/suits/plate_compression
name = "MODsuit Plate Compression Module"
@@ -809,6 +835,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "MSCM"
item = /obj/item/mod/module/chameleon
cost = 10
+ excludefrom = list()
/datum/uplink_item/suits/noslip
name = "MODsuit Anti-Slip Module"
@@ -816,6 +843,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "MSNS"
item = /obj/item/mod/module/noslip
cost = 5
+ excludefrom = list()
/datum/uplink_item/suits/springlock_module
name = "Heavily Modified Springlock MODsuit Module"
@@ -831,6 +859,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "FNAF"
item = /obj/item/mod/module/springlock/bite_of_87
cost = 5
+ excludefrom = list()
/datum/uplink_item/suits/hidden_holster
name = "Hidden Holster Module"
@@ -845,6 +874,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "SGM"
item = /obj/item/mod/module/dispenser/smoke
cost = 10
+ excludefrom = list()
////////////////////////////////////////
// MARK: IMPLANTS
@@ -852,6 +882,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/bio_chips
category = "Bio-chips"
+ excludefrom = list(UPLINK_TYPE_NINJA)
/datum/uplink_item/bio_chips/freedom
name = "Freedom Bio-chip"
@@ -873,6 +904,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "ESI"
item = /obj/item/bio_chip_implanter/storage
cost = 40
+ excludefrom = list()
/datum/uplink_item/bio_chips/mindslave
name = "Mindslave Bio-chip"
@@ -917,6 +949,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/cyber_implants
category = "Cybernetic Implants"
+ excludefrom = list(UPLINK_TYPE_NINJA)
/datum/uplink_item/cyber_implants/hackerman_deck
name = "Binyat Wireless Hacking System Autoimplanter"
@@ -935,6 +968,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "RZR"
item = /obj/item/autosurgeon/organ/syndicate/oneuse/razorwire
cost = 20
+ excludefrom = list()
/datum/uplink_item/cyber_implants/scope_eyes
name = "Hardened Kaleido Optics Eyes Autoimplanter"
@@ -944,6 +978,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "KOE"
item = /obj/item/autosurgeon/organ/syndicate/oneuse/scope_eyes
cost = 10
+ excludefrom = list()
/datum/uplink_item/cyber_implants/mantis_kit
name = "'Naginata' Mantis Blades Kit"
@@ -956,7 +991,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 50
surplus = 0
can_discount = FALSE
- excludefrom = list(UPLINK_TYPE_NUCLEAR)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_NINJA)
////////////////////////////////////////
// MARK: POINTLESS BADASSERY
@@ -965,6 +1000,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/badass
category = "(Pointless) Badassery"
surplus = 0
+ excludefrom = list(UPLINK_TYPE_NINJA)
/datum/uplink_item/badass/pen
name = "Syndicate Fountain Pen"
@@ -1025,6 +1061,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
category = "Bundles and Telecrystals"
surplus = 0
can_discount = FALSE
+ excludefrom = list(UPLINK_TYPE_NINJA)
/datum/uplink_item/bundles_tc/telecrystal
name = "Raw Telecrystal"
diff --git a/code/datums/uplink_items/uplink_nuclear.dm b/code/datums/uplink_items/uplink_nuclear.dm
index 2a88befdb8e..1b76874a77f 100644
--- a/code/datums/uplink_items/uplink_nuclear.dm
+++ b/code/datums/uplink_items/uplink_nuclear.dm
@@ -550,7 +550,7 @@
reference = "KMI"
item = /obj/item/bio_chip_implanter/krav_maga
cost = 25
- uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/datum/uplink_item/bio_chips/uplink/nuclear
name = "Nuclear Uplink Bio-chip"
@@ -661,34 +661,34 @@
/datum/uplink_item/stealthy_weapons/cqc/nuke
reference = "NCQC"
cost = 40
- excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
+ excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT, UPLINK_TYPE_NINJA)
/datum/uplink_item/explosives/syndicate_bomb/nuke
reference = "NSB"
cost = 55
- excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
+ excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT, UPLINK_TYPE_NINJA)
hijack_only = FALSE
/datum/uplink_item/explosives/emp_bomb/nuke
reference = "NSBEMP"
cost = 50
- excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
+ excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT, UPLINK_TYPE_NINJA)
/datum/uplink_item/explosives/atmosfiregrenades/nuke
reference = "NAPG"
cost = 60
- excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
+ excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT, UPLINK_TYPE_NINJA)
/datum/uplink_item/stealthy_tools/chameleon/nuke
reference = "NCHAM"
item = /obj/item/storage/box/syndie_kit/chameleon/nuke
cost = 30
- excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
+ excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT, UPLINK_TYPE_NINJA)
/datum/uplink_item/stealthy_tools/syndigaloshes/nuke
reference = "NNSSS"
cost = 20
- excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
+ excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT, UPLINK_TYPE_NINJA)
/datum/uplink_item/explosives/detomatix/nuclear
desc = "When inserted into a personal digital assistant, this cartridge gives you five opportunities to detonate PDAs of crew members who have their message feature enabled. The concussive effect from the explosion will knock the recipient out for a short period, and deafen them for longer. It has a chance to detonate your PDA. This version comes with a program to toggle your nuclear shuttle blast doors remotely."
diff --git a/code/datums/uplink_items/uplink_special.dm b/code/datums/uplink_items/uplink_special.dm
index c98f3a1ed6e..b48e2a393d8 100644
--- a/code/datums/uplink_items/uplink_special.dm
+++ b/code/datums/uplink_items/uplink_special.dm
@@ -3,7 +3,7 @@
category = "Special Offers"
can_discount = FALSE
surplus = 0
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT, UPLINK_TYPE_NINJA)
/datum/uplink_item/special/autosurgeon
name = "Syndicate Autosurgeon"
diff --git a/code/datums/uplink_items/uplink_traitor.dm b/code/datums/uplink_items/uplink_traitor.dm
index e2e8df793c6..f08211589fa 100644
--- a/code/datums/uplink_items/uplink_traitor.dm
+++ b/code/datums/uplink_items/uplink_traitor.dm
@@ -4,7 +4,7 @@
/datum/uplink_item/jobspecific
category = "Job Specific Tools"
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) // Stops the job specific category appearing for nukies
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA) // Stops the job specific category appearing for nukies
//Clown
/datum/uplink_item/jobspecific/clowngrenade
@@ -347,7 +347,7 @@
reference = "PP"
item = /obj/item/storage/box/syndie_kit/poisoner
cost = 10
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
job = list("Head of Personnel", "Quartermaster", "Cargo Technician", "Librarian", "Coroner", "Psychiatrist", "Virologist")
// Tarot card generator, librarian and Chaplain.
@@ -368,7 +368,7 @@
/datum/uplink_item/species_restricted
category = "Species Specific Gear"
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) // Stops the job specific category appearing for nukies
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA) // Stops the job specific category appearing for nukies
//skrell
/datum/uplink_item/species_restricted/lovepen
@@ -469,7 +469,7 @@
reference = "BLSG"
item = /obj/item/gun/projectile/automatic/shotgun/bulldog/traitor
cost = 50
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/datum/uplink_item/ammo/bull_rubbershot
name = "Bulldog - 12g Rubbershot Magazine"
@@ -477,7 +477,7 @@
reference = "12BRU"
item = /obj/item/ammo_box/magazine/m12g/rubbershot
cost = 10
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/datum/uplink_item/dangerous/guardian
name = "Holoparasites"
@@ -485,7 +485,7 @@
desc = "Though capable of near sorcerous feats via use of hardlight holograms and nanomachines, they require an organic host as a home base and source of fuel. \
The holoparasites are unable to incoporate themselves to changeling and vampire agents."
item = /obj/item/storage/box/syndie_kit/guardian/uplink
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
cost = 60
refund_path = /obj/item/guardiancreator/tech/choose
refundable = TRUE
@@ -506,7 +506,7 @@
reference = "SCS"
item = /obj/item/sleeping_carp_scroll
cost = 65
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
can_discount = FALSE
/datum/uplink_item/stealthy_weapons/bearserk
@@ -525,7 +525,7 @@
reference = "SBM"
item = /obj/item/storage/belt/military/traitor
cost = 10
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/datum/uplink_item/stealthy_tools/frame
name = "F.R.A.M.E. PDA Cartridge"
@@ -535,7 +535,7 @@
telecrystals normally."
reference = "FRAME"
item = /obj/item/cartridge/frame
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
cost = 20
/datum/uplink_item/stealthy_tools/voice_modulator
@@ -589,7 +589,7 @@
reference = "CGM"
item = /obj/item/clothing/gloves/color/black/krav_maga
cost = 50
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/datum/uplink_item/device_tools/extraction_beacon
name = "Extraction Flare"
@@ -597,7 +597,7 @@
reference = "EXTF"
item = /obj/item/wormhole_jaunter/extraction
limited_stock = 1
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/datum/uplink_item/device_tools/hyper_medipen
name = "Hyper-regenerative Medipen"
@@ -615,7 +615,7 @@
item = /obj/item/thermal_drill/syndicate
cost = 5
surplus = 0 // I feel like its amazing for one objective and one objective only. Far too specific.
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/datum/uplink_item/suits/modsuit
name = "Syndicate MODsuit"
@@ -628,7 +628,7 @@
item = /obj/item/mod/control/pre_equipped/traitor
cost = 30
surplus = 60 //I have upped the chance of modsuits from 40, as I do feel they are much more worthwhile with the base modsuit no longer being 8 tc, and the high armor values of the elite.
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/datum/uplink_item/suits/modsuit_elite
name = "Syndicate Elite MODsuit"
@@ -638,7 +638,7 @@
item = /obj/item/mod/control/pre_equipped/traitor_elite
cost = 45 //45 to start, no holopara / ebow.
surplus = 60
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/// Nukies get Nuclear Uplink Bio-chip instead
/datum/uplink_item/bio_chips/uplink
@@ -647,7 +647,7 @@
reference = "UI"
item = /obj/item/bio_chip_implanter/uplink
cost = 70
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
surplus = 0
can_discount = FALSE
@@ -661,7 +661,7 @@
reference = "QLSC"
item = /obj/item/autosurgeon/organ/syndicate/oneuse/sensory_enhancer
cost = 40
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) //No, nukies do not get to dodge bullets.
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA) //No, nukies do not get to dodge bullets.
/datum/uplink_item/species_restricted/skinmonger
name = "Skinmonger Autoimplanter"
@@ -680,7 +680,7 @@
reference = "SPC"
item = /obj/item/deck/cards/syndicate
cost = 2
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
surplus = 40
/datum/uplink_item/badass/plasticbag
@@ -689,7 +689,7 @@
reference = "PBAG"
item = /obj/item/storage/bag/plasticbag
cost = 1
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/datum/uplink_item/bundles_tc/contractor
name = "Syndicate Contractor Kit"
@@ -697,7 +697,7 @@
reference = "SCOK"
cost = 100
item = /obj/item/storage/box/syndie_kit/contractor
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/datum/uplink_item/bundles_tc/contractor/spawn_item(turf/loc, obj/item/uplink/U)
var/datum/mind/mind = usr.mind
@@ -729,7 +729,7 @@
reference = "SYB"
item = /obj/item/beacon/syndicate/bundle
cost = 100
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
/datum/uplink_item/bundles_tc/surplus_crate
name = "Syndicate Surplus Crate"
@@ -737,7 +737,7 @@
reference = "SYSC"
cost = 100
item = /obj/item/storage/box/syndie_kit/bundle
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
var/crate_value = 250
uses_special_spawn = TRUE
@@ -768,7 +768,7 @@
reference = "SB"
item = /obj/item/beacon/syndicate/bomb
cost = 40
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
surplus = 0
hijack_only = TRUE
@@ -779,7 +779,7 @@
reference = "SBEMP"
item = /obj/item/beacon/syndicate/bomb/emp
cost = 40
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
surplus = 0
can_discount = FALSE
@@ -794,7 +794,7 @@
reference = "APG"
item = /obj/item/storage/box/syndie_kit/atmosfiregrenades
cost = 50
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
surplus = 0
hijack_only = TRUE
@@ -822,4 +822,4 @@
reference = "DEPC"
item = /obj/item/cartridge/syndicate
cost = 30
- excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST)
+ excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_NINJA)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 9161902301e..56390217672 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -74,6 +74,8 @@
var/list/datum/mind/wizards = list()
/// A list of all minds that are wizard apprentices
var/list/datum/mind/apprentices = list()
+ /// A list of all minds that are ninjas
+ var/list/datum/mind/ninjas = list()
/// The cult team datum
var/datum/team/cult/cult_team
diff --git a/code/game/gamemodes/ninja_objectives.dm b/code/game/gamemodes/ninja_objectives.dm
new file mode 100644
index 00000000000..2bee7d95a1d
--- /dev/null
+++ b/code/game/gamemodes/ninja_objectives.dm
@@ -0,0 +1,352 @@
+#define NINJA_OBJECTIVE_EASY 10
+#define NINJA_OBJECTIVE_NORMAL 20
+#define NINJA_OBJECTIVE_HARD 40
+
+/datum/objective/ninja
+ /// Can you only roll this objective once?
+ var/onlyone = FALSE
+ /// Does this objective come with special gear
+ var/special_equipment_path
+ /// Rewarded currency for objective completion
+ var/reward_tc = NINJA_OBJECTIVE_EASY
+
+/datum/objective/ninja/New(text, datum/team/team_to_join, datum/mind/_owner)
+ . = ..()
+ if(special_equipment_path)
+ addtimer(CALLBACK(src, PROC_REF(hand_out_equipment)), 3 SECONDS, TIMER_DELETE_ME)
+
+/datum/objective/ninja/proc/hand_out_equipment()
+ give_kit(special_equipment_path)
+
+/datum/objective/ninja/is_invalid_target(datum/mind/possible_target)
+ . = ..()
+ if(possible_target.current)
+ var/turf/current_location = get_turf(possible_target.current)
+ if(current_location && !is_station_level(current_location.z))
+ return TARGET_INVALID_UNREACHABLE
+
+/datum/objective/ninja/proc/complete_objective()
+ for(var/datum/mind/M in get_owners())
+ var/mob/living/carbon/human/H = M.current
+ if(!ishuman(H))
+ continue
+ var/obj/item/bio_chip/uplink/ninja/nuplink = locate(/obj/item/bio_chip/uplink/ninja) in H
+ if(!nuplink)
+ continue
+ nuplink.hidden_uplink.uses += reward_tc
+ completed = TRUE
+
+/datum/objective/ninja/proc/check_objective_conditions()
+ return TRUE
+
+/datum/objective/ninja/kill
+ name = "Kill a Target"
+ reward_tc = NINJA_OBJECTIVE_NORMAL
+
+/datum/objective/ninja/kill/update_explanation_text()
+ if(target?.current)
+ explanation_text = "Kill [target.current.real_name], the [target.assigned_role]. Scan the corpse with your scanner to verify that the deed is done."
+ var/datum/job/target_job = SSjobs.GetJob(target.assigned_role)
+ if(target_job.job_department_flags & DEP_FLAG_COMMAND || target_job.job_department_flags & DEP_FLAG_SECURITY)
+ reward_tc = NINJA_OBJECTIVE_HARD
+
+/datum/objective/ninja/capture
+ name = "Capture a Target"
+ reward_tc = NINJA_OBJECTIVE_NORMAL
+ /// The kidnapee's belongings. Set upon capture.
+ var/list/obj/item/victim_belongings = null
+ /// Temporary objects that are available to the kidnapee during their time in jail. These are deleted when the victim is returned.
+ var/list/obj/temp_objs = null
+ /// Prisoner jail timer handle. On completion, returns the prisoner back to station.
+ var/prisoner_timer_handle = null
+
+/datum/objective/ninja/capture/update_explanation_text()
+ if(target?.current)
+ explanation_text = "Capture [target.current.real_name], the [target.assigned_role]. Use your energy net to capture them so that we can interrogate them at one of our many secret dojos."
+ var/datum/job/target_job = SSjobs.GetJob(target.assigned_role)
+ if(target_job.job_department_flags & DEP_FLAG_COMMAND || target_job.job_department_flags & DEP_FLAG_SECURITY)
+ reward_tc = NINJA_OBJECTIVE_HARD
+ if(target_job.job_department_flags & DEP_FLAG_SERVICE)
+ reward_tc = NINJA_OBJECTIVE_EASY
+
+/datum/objective/ninja/capture/proc/handle_capture(mob/living/sucker, turf/T)
+ var/mob/living/carbon/human/H = sucker
+
+ // Prepare their return
+ prisoner_timer_handle = addtimer(CALLBACK(src, PROC_REF(handle_target_return), sucker, T), rand(3 MINUTES, 5 MINUTES), TIMER_STOPPABLE)
+
+ LAZYSET(GLOB.prisoner_belongings.prisoners, sucker, src)
+
+ // Shove all of the victim's items in the secure locker.
+ victim_belongings = list()
+ var/list/obj/item/stuff_to_transfer = list()
+
+ // Cybernetic implants get removed first (to deal with NODROP stuff)
+ for(var/obj/item/organ/internal/cyberimp/I in H.internal_organs)
+ // Greys get to keep their implant
+ if(isgrey(H) && istype(I, /obj/item/organ/internal/cyberimp/brain/speech_translator))
+ continue
+ // IPCs keep this implant, free of charge!
+ if(ismachineperson(H) && istype(I, /obj/item/organ/internal/cyberimp/arm/power_cord))
+ continue
+ // Try removing it
+ I = I.remove(H)
+ if(I)
+ stuff_to_transfer += I
+
+ // Skrell headpocket. They already have a check in place to limit what's placed in them.
+ var/obj/item/organ/internal/headpocket/C = H.get_int_organ(/obj/item/organ/internal/headpocket)
+ if(C?.held_item)
+ GLOB.prisoner_belongings.give_item(C.held_item)
+ victim_belongings += C.held_item
+ C.held_item = null
+
+ if(sucker.back) // Lets not bork modsuits in funny ways.
+ var/obj/modsuit_safety = sucker.back
+ sucker.drop_item_to_ground(modsuit_safety)
+ stuff_to_transfer += modsuit_safety
+ // Regular items get removed in second
+ for(var/obj/item/I in sucker)
+ // Keep their uniform and shoes
+ if(I == H.w_uniform || I == H.shoes)
+ continue
+ // Plasmamen are no use if they're crispy
+ if(isplasmaman(H) && I == H.head)
+ continue
+
+ // Any kind of implant gets potentially removed (mindshield, freedoms, etc)
+ if(istype(I, /obj/item/bio_chip))
+ if(istype(I, /obj/item/bio_chip/storage)) // Storage items are removed and placed in the confiscation locker before the implant is taken.
+ var/obj/item/bio_chip/storage/storage_chip = I
+ for(var/it in storage_chip.storage)
+ storage_chip.storage.remove_from_storage(it)
+ stuff_to_transfer += it
+ qdel(I)
+ continue
+
+ if(sucker.drop_item_to_ground(I))
+ stuff_to_transfer += I
+
+ // Remove accessories from the suit if present
+ if(length(H.w_uniform?.accessories))
+ for(var/obj/item/clothing/accessory/A in H.w_uniform.accessories)
+ H.w_uniform.detach_accessory(A, null)
+ H.drop_item_to_ground(A)
+ stuff_to_transfer += A
+
+ // Transfer it all (or drop it if not possible)
+ for(var/obj/item/i as anything in stuff_to_transfer)
+ if(GLOB.prisoner_belongings.give_item(i))
+ victim_belongings += i
+ else if(!((ABSTRACT|NODROP) in i.flags)) // Anything that can't be put on hold, just drop it on the ground
+ i.forceMove(T)
+
+ // Give some species the necessary to survive. Courtesy of the Syndicate.
+ if(istype(H))
+ var/obj/item/tank/internals/emergency_oxygen/tank
+ var/obj/item/clothing/mask/breath/mask
+ if(isvox(H))
+ tank = new /obj/item/tank/internals/emergency_oxygen/nitrogen(H)
+ mask = new /obj/item/clothing/mask/breath/vox(H)
+ else if(isplasmaman(H))
+ tank = new /obj/item/tank/internals/emergency_oxygen/plasma(H)
+ mask = new /obj/item/clothing/mask/breath(H)
+
+ if(tank)
+ H.equip_to_appropriate_slot(tank)
+ H.equip_to_appropriate_slot(mask)
+ tank.toggle_internals(H, TRUE)
+
+ sucker.reagents.add_reagent("mutadone", 1) // 1u of mutadone for the Hulk:tm: experience
+ sucker.update_icons()
+
+ // Supply them with some chow. How generous is the Syndicate?
+ var/obj/item/food/sliced/bread/food = new(get_turf(sucker))
+ food.name = "stale bread"
+ food.desc = "Looks like your captors care for their prisoners as much as their bread."
+ food.trash = null
+ if(prob(10))
+ // Mold adds a bit of spice to it
+ food.name = "moldy bread"
+ food.reagents.add_reagent("fungus", 1)
+
+ var/obj/item/reagent_containers/drinks/drinkingglass/drink = new(get_turf(sucker))
+ drink.reagents.add_reagent("tea", 25) // British coders beware, tea in glasses
+
+ var/obj/item/coin/antagtoken/passingtime = new(get_turf(sucker))
+
+ temp_objs = list(food, drink, passingtime)
+
+ // Narrate their kidnapping and torturing experience.
+ if(sucker.stat != DEAD)
+ // Heal them up - gets them out of crit/soft crit.
+ sucker.reagents.add_reagent("omnizine", 10)
+
+ to_chat(sucker, SPAN_WARNING("You feel strange..."))
+ sucker.Paralyse(30 SECONDS)
+ sucker.EyeBlind(35 SECONDS)
+ sucker.EyeBlurry(35 SECONDS)
+ sucker.AdjustConfused(35 SECONDS)
+
+ for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.alive_mob_list)
+ if(G.summoner == sucker)
+ sucker.remove_guardian_actions()
+ to_chat(G, SPAN_DANGER("You feel your body ripped to shreds as you're forcibly removed from your summoner!"))
+ to_chat(sucker, SPAN_WARNING("You feel some part of you missing, you're not who you used to be..."))
+ G.ghostize()
+ qdel(G)
+
+ sleep(6 SECONDS)
+ to_chat(sucker, SPAN_WARNING("That portal did something to you..."))
+
+ sleep(6.5 SECONDS)
+ to_chat(sucker, SPAN_WARNING("Your head pounds... It feels like it's going to burst out your skull!"))
+
+ sleep(3 SECONDS)
+ to_chat(sucker, SPAN_WARNING("Your head pounds..."))
+
+ sleep(10 SECONDS)
+ to_chat(sucker, "A million voices echo in your head... \"Your mind held many valuable secrets - \
+ we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \
+ so it's only a matter of time before we send you back...\"")
+
+ to_chat(sucker, SPAN_DANGER("You have been kidnapped and interrogated for valuable information! You will be sent back to the station in a few minutes..."))
+
+/datum/objective/ninja/capture/proc/handle_target_return(mob/living/M, turf/T)
+ // Make a closet to return the target and their items neatly
+ var/obj/structure/closet/closet = new(T)
+
+ // Return their items
+ for(var/i in victim_belongings)
+ var/obj/item/I = GLOB.prisoner_belongings.remove_item(i)
+ if(!I)
+ continue
+ I.forceMove(closet)
+
+ victim_belongings = list()
+
+ // Clean up
+ var/obj/item/bio_chip/uplink/uplink_implant = locate() in M
+ uplink_implant?.hidden_uplink?.is_jammed = FALSE
+
+ QDEL_LIST_CONTENTS(temp_objs)
+
+ // Injuries due to questioning
+ injure_target(M)
+
+ // Return them a bit confused.
+ M.visible_message(SPAN_NOTICE("[M] vanishes..."))
+ M.forceMove(closet)
+ M.Paralyse(3 SECONDS)
+ M.EyeBlurry(5 SECONDS)
+ M.AdjustConfused(5 SECONDS)
+ M.Dizzy(70 SECONDS)
+ do_sparks(4, FALSE, T)
+
+ prisoner_timer_handle = null
+ GLOB.prisoner_belongings.prisoners[M] = null
+
+/datum/objective/ninja/capture/proc/injure_target(mob/living/M)
+ var/obj/item/organ/external/injury_target
+ if(prob(20)) // See if they're !!!lucky!!! enough to just chop a hand or foot off first, or even !!LUCKIER!! that it chose an already amputated limb
+ injury_target = M.get_organ(pick(BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT))
+ if(!injury_target)
+ return
+ default_damage(M)
+ injury_target.droplimb()
+ to_chat(M, SPAN_WARNING("You were interrogated by your captors before being sent back! Oh god, something's missing!"))
+ return
+ // Species specific punishments first
+ if(ismachineperson(M))
+ M.emp_act(EMP_HEAVY)
+ M.adjustBrainLoss(30)
+ to_chat(M, SPAN_WARNING("You were interrogated by your captors before being sent back! You feel like some of your components are loose!"))
+ return
+ default_damage(M) // Now that we won't accidentally kill an IPC we can make everyone take damage
+ if(isslimeperson(M))
+ injury_target = M.get_organ(pick(BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT))
+ if(!injury_target)
+ return
+ injury_target.cause_internal_bleeding()
+ injury_target = M.get_organ(BODY_ZONE_CHEST)
+ injury_target.cause_internal_bleeding()
+ to_chat(M, SPAN_WARNING("You were interrogated by your captors before being sent back! You feel like your inner membrane has been punctured!"))
+ return
+ if(prob(25)) // You either get broken ribs, or a broken limb and IB if you made it this far
+ injury_target = M.get_organ(BODY_ZONE_CHEST)
+ injury_target.fracture()
+ else
+ injury_target = M.get_organ(pick(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_R_LEG))
+ if(!injury_target)
+ return
+ injury_target.fracture()
+ injury_target.cause_internal_bleeding()
+
+/datum/objective/ninja/capture/proc/default_damage(mob/living/M)
+ M.adjustBruteLoss(40)
+ M.adjustBrainLoss(25)
+
+/datum/objective/ninja/hack_rnd
+ name = "Hack RnD"
+ explanation_text = "A client wants access to Nanotrasen's research databank. Use your scanner on their servers to give them a way inside."
+ needs_target = FALSE
+ onlyone = TRUE
+
+/datum/objective/ninja/interrogate_ai
+ name = "Interrogate AI"
+ explanation_text = "We wish to expunge some data from their AI system. Use your scanner on an active AI core to wirelessly transfer it to us for interrogation."
+ needs_target = FALSE
+ onlyone = TRUE
+ reward_tc = NINJA_OBJECTIVE_HARD
+
+/datum/objective/ninja/interrogate_ai/check_objective_conditions() // If there is no AI, you don't get the objective.
+ if(!length(GLOB.ai_list))
+ return FALSE
+ return TRUE
+
+/datum/objective/ninja/steal_supermatter
+ name = "Steal Supermatter"
+ explanation_text = "Steal the supermatter crystal, using the net gun we have modified for you. The crystal will sell well to the highest bidder."
+ needs_target = FALSE
+ onlyone = TRUE
+ reward_tc = NINJA_OBJECTIVE_HARD
+
+/datum/objective/ninja/steal_supermatter/check_objective_conditions() // If there is no supermatter, you don't get the objective.
+ return !isnull(GLOB.main_supermatter_engine)
+
+/datum/objective/ninja/insert_spider_rod
+ name = "Sabotage Fission Reactor"
+ explanation_text = "Insert a spider-clan uranium 238 fuel rod into the station's fission reactor, then scan the reactor chamber while it is active."
+ needs_target = FALSE
+ onlyone = TRUE
+ reward_tc = NINJA_OBJECTIVE_HARD
+ special_equipment_path = /obj/item/beacon/ninja_rod_spawner
+
+/datum/objective/ninja/insert_spider_rod/check_objective_conditions() // If there is no reactor, you don't get the objective.
+ return !isnull(GLOB.main_fission_reactor)
+
+/datum/objective/ninja/bomb_department
+ name = "Bomb Department"
+ needs_target = FALSE
+ special_equipment_path = /obj/item/wormhole_jaunter/ninja_bomb
+ explanation_text = "Use the special flare provided to call down and arm a spider bomb. The target department is inscribed on the flare."
+ reward_tc = NINJA_OBJECTIVE_NORMAL
+
+/datum/objective/ninja/bomb_department/emp
+ name = "EMP Department"
+ explanation_text = "Use the special flare provided to call down and arm an EMP bomb. The target department is inscribed on the flare."
+ special_equipment_path = /obj/item/wormhole_jaunter/ninja_bomb/emp
+
+/datum/objective/ninja/bomb_department/spiders
+ name = "Spider Bomb Department"
+ explanation_text = "Use the special flare provided to call down and arm a Spider bomb. The target department is inscribed on the flare."
+ special_equipment_path = /obj/item/wormhole_jaunter/ninja_bomb/spiders
+
+/datum/objective/ninja_exfiltrate
+ name = "Exfiltrate"
+ explanation_text = "Use your exfiltration flare to escape the station when your work is done."
+ needs_target = FALSE
+
+#undef NINJA_OBJECTIVE_EASY
+#undef NINJA_OBJECTIVE_NORMAL
+#undef NINJA_OBJECTIVE_HARD
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 104acefda2a..ccae8956d5c 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1014,6 +1014,17 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays)
add_fingerprint(user)
if(!headbutt_shock_check(user))
return ITEM_INTERACT_COMPLETE
+ if(istype(used, /obj/item/katana/energy) && user.a_intent == INTENT_HELP)
+ if(locked)
+ if(!do_after_once(user, 5 SECONDS, TRUE, src, allow_moving = FALSE, must_be_held = FALSE))
+ return ITEM_INTERACT_COMPLETE
+ unlock()
+ return ITEM_INTERACT_COMPLETE
+ else
+ if(!do_after_once(user, 2.5 SECONDS, TRUE, src, allow_moving = FALSE, must_be_held = FALSE))
+ return ITEM_INTERACT_COMPLETE
+ open()
+ return ITEM_INTERACT_COMPLETE
if(panel_open)
if(istype(used, /obj/item/kitchen/utensil/fork))
return NONE
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 852fbc342bb..47b2e3869fa 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -317,7 +317,11 @@
//If it's in the process of opening/closing, ignore the click
if(operating)
return ITEM_INTERACT_COMPLETE
-
+ if(istype(used, /obj/item/katana/energy) && user.a_intent == INTENT_HELP)
+ if(!do_after_once(user, 2.5 SECONDS, TRUE, src, allow_moving = FALSE, must_be_held = FALSE))
+ return ITEM_INTERACT_COMPLETE
+ open()
+ return ITEM_INTERACT_COMPLETE
add_fingerprint(user)
return ..()
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index a6198fe72d4..57834061b4f 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -104,6 +104,9 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/newplayer_start) //Without this you sp
name = "revenantspawn"
icon_state = "Rev"
+/obj/effect/landmark/spawner/ninja
+ name = "ninjaspawn"
+
/obj/effect/landmark/spawner/bubblegum_arena
name = "bubblegum_arena_human"
icon_state = "Explorer"
@@ -142,13 +145,6 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/newplayer_start) //Without this you sp
spawner_list = GLOB.ertdirector
return ..()
-/obj/effect/landmark/spawner/ninjastart
- name = "ninjastart"
-
-/obj/effect/landmark/spawner/ninjastart/Initialize(mapload)
- spawner_list = GLOB.ninjastart
- return ..()
-
/obj/effect/landmark/spawner/aroomwarp
name = "aroomwarp"
@@ -198,6 +194,13 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/newplayer_start) //Without this you sp
spawner_list = GLOB.syndieprisonwarp
return ..()
+/obj/effect/landmark/spawner/ninja_prison_warp
+ name = "ninja prison warp"
+
+/obj/effect/landmark/spawner/ninja_prison_warp/Initialize(mapload)
+ spawner_list = GLOB.ninjaprisonwarp
+ return ..()
+
/obj/effect/landmark/spawner/antag_extract_warp
name = "antagextractwarp"
@@ -230,9 +233,6 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/newplayer_start) //Without this you sp
/obj/effect/landmark/spawner/commando_manual
name = "Deathsquad Commando Manual"
-/obj/effect/landmark/spawner/holding_facility
- name = "Holding Facility"
-
/obj/effect/landmark/spawner/holocarp
name = "Holocarp Spawn"
diff --git a/code/game/objects/items/devices/radio/beacon.dm b/code/game/objects/items/devices/radio/beacon.dm
index 7ced265c2f6..dfd2f60905c 100644
--- a/code/game/objects/items/devices/radio/beacon.dm
+++ b/code/game/objects/items/devices/radio/beacon.dm
@@ -44,6 +44,19 @@
syndicate = TRUE
emagged = TRUE
+// Spawns a spider fuel rod for ninja objectives
+/obj/item/beacon/ninja_rod_spawner
+ name = "spider clan beacon"
+ desc = "A label on it reads: Activate to have a spider clan brand fuel rod teleported to your location."
+ origin_tech = "bluespace=6;syndicate=3"
+
+/obj/item/beacon/ninja_rod_spawner/attack_self__legacy__attackchain(mob/user)
+ if(!user)
+ return
+ var/obj/item/nuclear_rod/fuel/uranium_238/spiders/new_rod = new(user.loc)
+ qdel(src)
+ user.put_in_hands(new_rod)
+
// SINGULO BEACON SPAWNER
/obj/item/beacon/syndicate
name = "suspicious beacon"
diff --git a/code/game/objects/items/weapons/melee/melee.dm b/code/game/objects/items/weapons/melee/melee.dm
index a7c589edece..2137c09cedc 100644
--- a/code/game/objects/items/weapons/melee/melee.dm
+++ b/code/game/objects/items/weapons/melee/melee.dm
@@ -594,3 +594,34 @@
playsound(src, pick('sound/weapons/bulletflyby.ogg','sound/weapons/bulletflyby2.ogg','sound/weapons/bulletflyby3.ogg'), 75, 1)
return TRUE
return FALSE
+
+// MARK: KATANA
+/obj/item/katana
+ name = "katana"
+ desc = "Woefully underpowered in D20."
+ icon = 'icons/obj/weapons/melee.dmi'
+ icon_state = "katana"
+ lefthand_file = 'icons/mob/inhands/weapons_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons_righthand.dmi'
+ flags = CONDUCT
+ slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK
+ flags_2 = ALLOW_BELT_NO_JUMPSUIT_2 // Look, you can strap it to your back. You can strap it to your waist too.
+ force = 40
+ throwforce = 10
+ sharp = TRUE
+ w_class = WEIGHT_CLASS_BULKY
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 100, ACID = 50)
+ resistance_flags = FIRE_PROOF
+ needs_permit = TRUE
+
+ new_attack_chain = TRUE
+
+/obj/item/katana/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.5, _parryable_attack_types = ALL_ATTACK_TYPES)
+
+/obj/item/katana/suicide_act(mob/user)
+ user.visible_message("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku!")
+ return BRUTELOSS
diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm
index 1102d284349..ff091d01491 100644
--- a/code/game/objects/items/weapons/weaponry.dm
+++ b/code/game/objects/items/weapons/weaponry.dm
@@ -78,34 +78,6 @@
desc = "An engraved and fancy version of the claymore. It appears to be less sharp than it's more functional cousin."
force = 20
-/obj/item/katana
- name = "katana"
- desc = "Woefully underpowered in D20."
- icon = 'icons/obj/weapons/melee.dmi'
- icon_state = "katana"
- lefthand_file = 'icons/mob/inhands/weapons_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons_righthand.dmi'
- flags = CONDUCT
- slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK
- flags_2 = ALLOW_BELT_NO_JUMPSUIT_2 //Look, you can strap it to your back. You can strap it to your waist too.
- force = 40
- throwforce = 10
- sharp = TRUE
- w_class = WEIGHT_CLASS_BULKY
- hitsound = 'sound/weapons/bladeslice.ogg'
- attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
- armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 100, ACID = 50)
- resistance_flags = FIRE_PROOF
- needs_permit = TRUE
-
-/obj/item/katana/Initialize(mapload)
- . = ..()
- AddComponent(/datum/component/parry, _stamina_constant = 2, _stamina_coefficient = 0.5, _parryable_attack_types = ALL_ATTACK_TYPES)
-
-/obj/item/katana/suicide_act(mob/user)
- user.visible_message(SPAN_SUICIDE("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku!"))
- return BRUTELOSS
-
/obj/item/harpoon
name = "harpoon"
desc = "Tharr she blows!"
diff --git a/code/game/objects/structures/misc_structures.dm b/code/game/objects/structures/misc_structures.dm
index dfa0b0b50d4..f02bdd9f248 100644
--- a/code/game/objects/structures/misc_structures.dm
+++ b/code/game/objects/structures/misc_structures.dm
@@ -76,35 +76,6 @@
else
icon_state = "signpost_wood"
-/obj/structure/ninjatele
- name = "Long-Distance Teleportation Console"
- desc = "A console used to send a Spider Clan operative long distances rapidly."
- icon = 'icons/obj/ninjaobjects.dmi'
- icon_state = "teleconsole"
- anchored = TRUE
-
-/obj/structure/ninjatele/attack_hand(mob/user as mob)
- if(user.mind.special_role=="Ninja")
- switch(tgui_alert(user, "Phase Jaunt relay primed, target locked as [station_name()], initiate VOID-shift translocation? (Warning! Internals required!)", "Void Shift", list("Yes", "No")))
- if("Yes")
- if(user.z != src.z)
- return
-
- user.loc.loc.Exited(user)
- user.loc = pick(GLOB.carplist) // In the future, possibly make specific NinjaTele landmarks, and give him an option to teleport to North/South/East/West of SS13 instead of just hijacking a carpspawn.
-
- playsound(user.loc, 'sound/effects/phasein.ogg', 25, 1)
- playsound(user.loc, 'sound/effects/sparks2.ogg', 50, 1)
- new /obj/effect/temp_visual/dir_setting/ninja/phase(get_turf(user), user.dir)
- to_chat(user, "[SPAN_BOLDNOTICE("VOID-Shift")] translocation successful")
-
- if("No")
- to_chat(user, SPAN_DANGER("Process aborted!"))
- return
-
- else
- to_chat(user, "[SPAN_DANGER("FĆAL �Rr�R")]: ŧer nt recgnized, c-cntr-r䣧-ç äcked.")
-
/obj/structure/respawner
name = "\improper Long-Distance Cloning Machine"
desc = "Top-of-the-line Nanotrasen technology allows for cloning of crew members from off-station upon bluespace request."
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index 99187e022e1..7791b69c376 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -433,8 +433,8 @@
if(length(SSticker.mode.apprentices))
dat += check_role_table("Apprentices", SSticker.mode.apprentices)
- /*if(length(ticker.mode.ninjas))
- dat += check_role_table("Ninjas", ticker.mode.ninjas)*/
+ if(length(SSticker.mode.ninjas))
+ dat += check_role_table("Ninjas", SSticker.mode.ninjas)
if(SSticker.mode.cult_team)
dat += check_role_table("Cultists", SSticker.mode.cult_team.members)
diff --git a/code/modules/antagonists/space_ninja/datum_space_ninja.dm b/code/modules/antagonists/space_ninja/datum_space_ninja.dm
new file mode 100644
index 00000000000..bcec786aec4
--- /dev/null
+++ b/code/modules/antagonists/space_ninja/datum_space_ninja.dm
@@ -0,0 +1,95 @@
+RESTRICT_TYPE(/datum/antagonist/space_ninja)
+
+// Spider Clan Space Ninja
+/datum/antagonist/space_ninja
+ name = "Space Ninja"
+ roundend_category = "ninjas"
+ job_rank = ROLE_NINJA
+ special_role = SPECIAL_ROLE_NINJA
+ antag_hud_name = "hud_ninja"
+ antag_hud_type = ANTAG_HUD_NINJA
+ clown_gain_text = "Your spider clan training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself."
+ clown_removal_text = "You lose your spider clan training and return to your own clumsy, clownish self."
+ wiki_page_name = "Space Ninja"
+ var/give_uplink = TRUE
+ blurb_r = 200
+ blurb_a = 0.75
+ boss_title = "Spider Clan Daimyo"
+ /// Number of missions needed to complete
+ var/mission_goal = 4
+
+/datum/antagonist/space_ninja/on_gain()
+ ..()
+ mission_goal = 4 + (length(GLOB.crew_list) / 10)
+ SEND_SOUND(owner.current, sound('sound/ambience/antag/ninjaalert.ogg'))
+
+/datum/antagonist/space_ninja/give_objectives()
+ forge_objectives()
+
+/datum/antagonist/space_ninja/exfiltrate(mob/living/carbon/human/extractor, obj/item/radio/radio)
+ extractor.equipOutfit(/datum/outfit/admin/ghostbar_antag/space_ninja)
+ radio.autosay("--ZZZT!- Domo Ar!g@to, [extractor.real_name]. Your training paid -^%&!-ZZT!-", "Spider Clan HQ", "Security")
+ SSblackbox.record_feedback("tally", "successful_extraction", 1, "Space Ninja")
+
+/datum/antagonist/space_ninja/finalize_antag()
+ . = ..()
+ equip_ninja()
+
+/datum/antagonist/space_ninja/add_owner_to_gamemode()
+ SSticker.mode.ninjas |= owner
+
+/datum/antagonist/space_ninja/remove_owner_from_gamemode()
+ SSticker.mode.ninjas -= owner
+
+/datum/antagonist/space_ninja/proc/equip_ninja()
+ if(!ishuman(owner.current))
+ return
+ var/mob/living/carbon/human/new_ninja = owner.current
+ new_ninja.delete_equipment()
+ if(isvox(new_ninja))
+ new_ninja.equipOutfit(/datum/outfit/space_ninja/vox)
+ else
+ new_ninja.equipOutfit(/datum/outfit/space_ninja)
+
+/datum/antagonist/space_ninja/proc/forge_objectives()
+ var/list/moderate_objectives = list(
+ /datum/objective/ninja/capture = 8,
+ /datum/objective/ninja/kill = 8,
+ /datum/objective/ninja/hack_rnd = 5,
+ /datum/objective/ninja/bomb_department/emp = 3,
+ /datum/objective/ninja/bomb_department/spiders = 3,
+ )
+ var/list/all_ninja_objectives = list(
+ /datum/objective/ninja/capture = 8,
+ /datum/objective/ninja/kill = 8,
+ /datum/objective/ninja/hack_rnd = 4,
+ /datum/objective/ninja/bomb_department = 3,
+ /datum/objective/ninja/bomb_department/emp = 3,
+ /datum/objective/ninja/bomb_department/spiders = 3,
+ /datum/objective/ninja/steal_supermatter = 3,
+ /datum/objective/ninja/insert_spider_rod = 3,
+ /datum/objective/ninja/interrogate_ai = 3,
+ )
+ for(var/i in 1 to mission_goal)
+ if(i <= mission_goal / 2) // This ensures some easier objectives are rolled to allow ninjas to work up to harder ones.
+ forge_objective(moderate_objectives)
+ continue
+ forge_objective(all_ninja_objectives)
+ add_antag_objective(/datum/objective/ninja_exfiltrate)
+
+/datum/antagonist/space_ninja/proc/forge_objective(list/objective_list)
+
+ var/datum/objective/ninja/new_objective = pickweight(objective_list)
+ var/datum/objective/ninja/obj_item = new new_objective() // Needed to do to check validity and reroll
+ if(!obj_item.check_objective_conditions())
+ forge_objective(objective_list)
+ qdel(obj_item)
+ return
+ qdel(obj_item)
+ if(new_objective.onlyone)
+ var/list/current_objectives = get_antag_objectives()
+ for(var/datum/objective/ninja/c_objective in current_objectives)
+ if(istype(c_objective, new_objective))
+ forge_objective(objective_list)
+ return
+ add_antag_objective(new_objective)
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index f8879f25944..d75ac947f49 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -7,6 +7,7 @@ GLOBAL_LIST_INIT(special_role_times, list(
ROLE_TRAITOR = 7,
ROLE_CHANGELING = 14,
ROLE_WIZARD = 14,
+ ROLE_NINJA = 14,
ROLE_REV = 14,
ROLE_VAMPIRE = 14,
ROLE_BLOB = 14,
diff --git a/code/modules/clothing/gloves/colored_gloves.dm b/code/modules/clothing/gloves/colored_gloves.dm
index db73cdafd91..fcdc4b4eff5 100644
--- a/code/modules/clothing/gloves/colored_gloves.dm
+++ b/code/modules/clothing/gloves/colored_gloves.dm
@@ -46,7 +46,7 @@
var/can_be_cut = 1
sprite_sheets = list(
"Vox" = 'icons/mob/clothing/species/vox/gloves.dmi',
- "Skkulakin" = 'icons/mob/clothing/species/skkulakin/gloves.dmi',
+ "Skkulakin" = 'icons/mob/clothing/species/vox/gloves.dmi',
"Drask" = 'icons/mob/clothing/species/drask/gloves.dmi',
"Grey" = 'icons/mob/clothing/species/grey/gloves.dmi',
"Kidan" = 'icons/mob/clothing/species/kidan/gloves.dmi',
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index 29a921e83c9..1601c7f9f63 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -177,7 +177,6 @@ GLOBAL_LIST_EMPTY(event_last_fired)
new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/radiation_storm, 10, TRUE),
new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/spider_infestation, 10, TRUE),
new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/ion_storm, 10),
- //new /datum/event_meta/ninja(EVENT_LEVEL_MODERATE, "Space Ninja", /datum/event/space_ninja, 0, list(ASSIGNMENT_SECURITY = 15), TRUE),
// NON-BAY EVENTS
new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/mass_hallucination, 10),
new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/brand_intelligence, 5, TRUE),
@@ -216,6 +215,7 @@ GLOBAL_LIST_EMPTY(event_last_fired)
new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/demon_incursion, 10, TRUE, _first_run_time = 35 MINUTES),
new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/disease_outbreak, 8, TRUE),
new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/revenant, 9),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/space_ninja, 9),
new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_morph, 16, is_one_shot = TRUE),
//new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_floor_cluwne, 15, is_one_shot = TRUE)
//new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_pulsedemon, 20, is_one_shot = TRUE)
diff --git a/code/modules/events/space_ninja.dm b/code/modules/events/space_ninja.dm
new file mode 100644
index 00000000000..ece87cb3e52
--- /dev/null
+++ b/code/modules/events/space_ninja.dm
@@ -0,0 +1,86 @@
+/datum/event/space_ninja
+ name = "Space Ninja"
+ role_weights = list(ASSIGNMENT_SECURITY = 2)
+ role_requirements = list(ASSIGNMENT_CREW = 20, ASSIGNMENT_SECURITY = 2)
+ nominal_severity = EVENT_LEVEL_MAJOR
+
+/datum/event/space_ninja/start()
+ INVOKE_ASYNC(src, PROC_REF(poll_ninja))
+
+/datum/event/space_ninja/proc/poll_ninja(list/potential_ninjas)
+ var/list/candidates = list()
+ if(!potential_ninjas)
+ candidates = SSghost_spawns.poll_candidates("Do you want to play as a space ninja?", ROLE_NINJA, TRUE)
+ else
+ candidates = potential_ninjas
+ if(!length(candidates))
+ kill()
+ return
+ var/mob/new_ninja
+ while(length(candidates))
+ new_ninja = pick_n_take(candidates)
+ if(jobban_isbanned(new_ninja, ROLE_TRAITOR))
+ continue
+ break
+ if(!new_ninja)
+ kill()
+ return
+
+ var/gender_pref = input_async(new_ninja, "Please select a gender (10 seconds):", list("Male", "Female"))
+ addtimer(CALLBACK(src, PROC_REF(get_species), candidates, new_ninja, gender_pref), 10 SECONDS)
+
+/datum/event/space_ninja/proc/get_species(list/potential_ninjas, mob/new_ninja, datum/async_input/gender_pref)
+ gender_pref.close()
+ var/sel_gender = gender_pref.result
+ if(!sel_gender) // Player was AFK and did not select a gender
+ poll_ninja(potential_ninjas)
+ return
+ var/species_pref = input_async(new_ninja, "Please select a species (10 seconds):", list("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin", "Nian", "Drask", "Kidan", "Grey", "Skkulakin", "Vox", "Slime", "Machine", "Random"))
+ addtimer(CALLBACK(src, PROC_REF(create_ninja), potential_ninjas, new_ninja, sel_gender, species_pref), 10 SECONDS)
+
+/datum/event/space_ninja/proc/create_ninja(list/potential_ninjas, mob/new_ninja, sel_gender, datum/async_input/species_pref)
+ species_pref.close()
+ var/sel_species = species_pref.result
+ if(!sel_species) // Player was AFK and did not select a species
+ poll_ninja(potential_ninjas)
+ return
+ if(!new_ninja || !new_ninja.client)
+ poll_ninja(potential_ninjas)
+ return
+
+ var/list/spawn_locs = list()
+ for(var/obj/effect/landmark/spawner/ninja/N in GLOB.landmarks_list)
+ spawn_locs += get_turf(N)
+ if(!length(spawn_locs))
+ message_admins("Unable to spawn ninja - no valid spawn locations.")
+ return
+
+ var/mob/living/carbon/human/M = new(pick(spawn_locs))
+
+ M.ckey = new_ninja.ckey
+ dust_if_respawnable(new_ninja)
+
+ if(sel_gender == "Male")
+ M.change_gender(MALE)
+ M.change_body_type(MALE)
+ else
+ M.change_gender(FEMALE)
+ M.change_body_type(FEMALE)
+
+ if(sel_species == "Random")
+ sel_species = pick("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin", "Nian", "Drask", "Kidan", "Grey", "Skkulakin", "Vox", "Slime", "Machine")
+ if(sel_species == "Slime")
+ sel_species = "Slime People"
+ var/datum/species/S = GLOB.all_species[sel_species]
+ var/species = S.type
+ M.set_species(species, TRUE)
+ M.dna.ready_dna(M)
+ M.cleanSE()
+ M.overeatduration = 0
+ M.age = rand(23, 35)
+ M.regenerate_icons()
+
+ M.mind = new
+ M.mind.bind_to(M)
+ M.mind.set_original_mob(M)
+ M.mind.make_space_ninja()
diff --git a/code/modules/mod/mod_clothes.dm b/code/modules/mod/mod_clothes.dm
index f81c47b420f..2dd5aa816b5 100644
--- a/code/modules/mod/mod_clothes.dm
+++ b/code/modules/mod/mod_clothes.dm
@@ -11,6 +11,11 @@
permeability_coefficient = 0.01
sprite_sheets = list(
"Grey" = 'icons/mob/clothing/modsuit/species/grey_helmets.dmi',
+ "Skrell" = 'icons/mob/clothing/modsuit/species/skrell_modsuits.dmi',
+ "Nian" = 'icons/mob/clothing/modsuit/species/nian_modsuits.dmi',
+ "Diona" = 'icons/mob/clothing/modsuit/species/diona_modsuits.dmi',
+ "Kidan" = 'icons/mob/clothing/modsuit/species/kidan_modsuits.dmi',
+ "Drask" = 'icons/mob/clothing/modsuit/species/drask_modsuits.dmi',
"Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
"Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
"Unathi" = 'icons/mob/clothing/modsuit/species/unathi_modsuits.dmi',
@@ -37,13 +42,17 @@
insert_max = 0
hide_tail_by_species = list("modsuit")
sprite_sheets = list(
+ "Skrell" = 'icons/mob/clothing/modsuit/species/skrell_modsuits.dmi',
+ "Nian" = 'icons/mob/clothing/modsuit/species/nian_modsuits.dmi',
+ "Diona" = 'icons/mob/clothing/modsuit/species/diona_modsuits.dmi',
+ "Kidan" = 'icons/mob/clothing/modsuit/species/kidan_modsuits.dmi',
+ "Drask" = 'icons/mob/clothing/modsuit/species/drask_modsuits.dmi',
"Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
"Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
"Unathi" = 'icons/mob/clothing/modsuit/species/unathi_modsuits.dmi',
"Vox" = 'icons/mob/clothing/modsuit/species/vox_modsuits.dmi'
)
-
/obj/item/clothing/gloves/mod
name = "MOD gauntlets"
desc = "A pair of gauntlets for a MODsuit."
@@ -56,13 +65,17 @@
cold_protection = HANDS|ARMS
permeability_coefficient = 0.01
sprite_sheets = list(
+ "Skrell" = 'icons/mob/clothing/modsuit/species/skrell_modsuits.dmi',
+ "Nian" = 'icons/mob/clothing/modsuit/species/nian_modsuits.dmi',
+ "Diona" = 'icons/mob/clothing/modsuit/species/diona_modsuits.dmi',
+ "Kidan" = 'icons/mob/clothing/modsuit/species/kidan_modsuits.dmi',
+ "Drask" = 'icons/mob/clothing/modsuit/species/drask_modsuits.dmi',
"Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
"Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
"Unathi" = 'icons/mob/clothing/modsuit/species/unathi_modsuits.dmi',
"Vox" = 'icons/mob/clothing/modsuit/species/vox_modsuits.dmi'
)
-
/obj/item/clothing/shoes/mod
name = "MOD boots"
desc = "A pair of boots for a MODsuit."
@@ -75,6 +88,11 @@
cold_protection = FEET|LEGS
permeability_coefficient = 0.01
sprite_sheets = list(
+ "Skrell" = 'icons/mob/clothing/modsuit/species/skrell_modsuits.dmi',
+ "Nian" = 'icons/mob/clothing/modsuit/species/nian_modsuits.dmi',
+ "Diona" = 'icons/mob/clothing/modsuit/species/diona_modsuits.dmi',
+ "Kidan" = 'icons/mob/clothing/modsuit/species/kidan_modsuits.dmi',
+ "Drask" = 'icons/mob/clothing/modsuit/species/drask_modsuits.dmi',
"Vulpkanin" = 'icons/mob/clothing/modsuit/species/vulp_modsuits.dmi',
"Tajaran" = 'icons/mob/clothing/modsuit/species/taj_modsuits.dmi',
"Unathi" = 'icons/mob/clothing/modsuit/species/unathi_modsuits.dmi',
diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm
index 664c102e262..9cb3665924c 100644
--- a/code/modules/mod/mod_theme.dm
+++ b/code/modules/mod/mod_theme.dm
@@ -1384,3 +1384,58 @@
/// considering this should not be used, it's getting just DS armor, not infinity in everything.
/obj/item/mod/armor/mod_theme_administrative
armor = list(MELEE = 200, BULLET = 200, LASER = 50, ENERGY = 50, BOMB = INFINITY, RAD = INFINITY, FIRE = INFINITY, ACID = INFINITY)
+
+/datum/mod_theme/ninja
+ name = "'Shinobi' stealthsuit"
+ desc = "A specialized modsuit worn by the spider clan's elite ninja operatives."
+ default_skin = "ninja"
+ armor_type_1 = /obj/item/mod/armor/mod_theme_ninja
+ resistance_flags = FIRE_PROOF | ACID_PROOF
+ flag_2_flags = RAD_PROTECT_CONTENTS_2
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
+ siemens_coefficient = 0
+ slowdown_inactive = 1
+ slowdown_active = 0.3
+ ui_theme = "syndicate"
+ allowed_suit_storage = list(
+ /obj/item/katana/energy,
+ /obj/item/wormhole_jaunter/ninja_bomb,
+ /obj/item/throwing_star,
+ /obj/item/restraints/legcuffs/bola,
+ /obj/item/gun,
+ /obj/item/ninja_scanner,
+ )
+ skins = list(
+ "ninja" = list(
+ HELMET_FLAGS = list(
+ UNSEALED_LAYER = null,
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE | BLOCKHAIR,
+ UNSEALED_INVISIBILITY = HIDEFACE,
+ SEALED_INVISIBILITY = HIDEMASK | HIDEEYES | HIDEEARS | HIDEFACE,
+ UNSEALED_COVER = HEADCOVERSMOUTH,
+ SEALED_COVER = HEADCOVERSEYES,
+ CAN_OVERSLOT = TRUE,
+ ),
+ CHESTPLATE_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ SEALED_INVISIBILITY = HIDEJUMPSUIT | HIDETAIL,
+ CAN_OVERSLOT = TRUE,
+ ),
+ GAUNTLETS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ BOOTS_FLAGS = list(
+ UNSEALED_CLOTHING = THICKMATERIAL,
+ SEALED_CLOTHING = STOPSPRESSUREDMAGE,
+ CAN_OVERSLOT = TRUE,
+ ),
+ ),
+ )
+
+/obj/item/mod/armor/mod_theme_ninja
+
+ armor = list(MELEE = 50, BULLET = 35, LASER = 35, ENERGY = 30, BOMB = 25, RAD = 200, FIRE = INFINITY, ACID = INFINITY)
diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm
index 52da3adcce3..5259c440c06 100644
--- a/code/modules/mod/mod_types.dm
+++ b/code/modules/mod/mod_types.dm
@@ -569,6 +569,25 @@
/obj/item/mod/module/jetpack/advanced,
)
+/obj/item/mod/control/pre_equipped/ninja
+ icon_state = "ninja-control"
+ theme = /datum/mod_theme/ninja
+ starting_frequency = MODLINK_FREQ_SYNDICATE
+ applied_cell = /obj/item/stock_parts/cell/bluespace
+ applied_modules = list(
+ /obj/item/mod/module/storage/syndicate,
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/dispenser/smoke,
+ /obj/item/mod/module/shinobi_stealth,
+ /obj/item/mod/module/shinobi_freedom,
+ /obj/item/mod/module/shinobi_stims,
+ /obj/item/mod/module/noslip,
+ )
+ default_pins = list(
+ /obj/item/mod/module/jetpack/advanced,
+ /obj/item/mod/module/dispenser/smoke,
+ )
+
//these exist for the prefs menu
/obj/item/mod/control/pre_equipped/empty
starting_frequency = null
diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm
index b4f2e47eabf..dacf8bfa6d4 100644
--- a/code/modules/mod/modules/modules_antag.dm
+++ b/code/modules/mod/modules/modules_antag.dm
@@ -641,3 +641,77 @@
/obj/item/mod/module/anomaly_locked/teslawall/prebuilt
prebuilt = TRUE
removable = FALSE // No switching it into another suit / no free anomaly core
+
+/obj/item/mod/module/shinobi_stealth
+ name = "MOD shinobi stealth module"
+ desc = "An advanced module, designed by the Spider Clan, that replicates the advanced stealth technology in the classic ninja scarf."
+ icon_state = "armor_booster"
+ origin_tech = "combat=5;bluespace=6;syndicate=4"
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
+ removable = FALSE
+ /// Linked sneaking action
+ var/datum/action/cooldown/ninja/ninja_cloak/cloak_action
+
+/obj/item/mod/module/shinobi_stealth/Initialize(mapload)
+ . = ..()
+ cloak_action = new(src)
+
+/obj/item/mod/module/shinobi_stealth/on_suit_activation()
+ . = ..()
+ cloak_action.Grant(mod.wearer)
+
+/obj/item/mod/module/shinobi_stealth/on_suit_deactivation(deleting = FALSE)
+ if(deleting)
+ return
+ cloak_action.stop_sneaking()
+ cloak_action.Remove(mod.wearer)
+
+/obj/item/mod/module/shinobi_freedom
+ name = "MOD shinobi escape module"
+ desc = "An advanced module, designed by the Spider Clan, that replicates the advanced escaping technology in the classic ninja boots."
+ icon_state = "armor_booster"
+ origin_tech = "combat=5;bluespace=6;syndicate=4"
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
+ removable = FALSE
+ /// Linked freedom action
+ var/datum/action/cooldown/ninja/freedom_shoes/freedoms_action
+
+/obj/item/mod/module/shinobi_freedom/Initialize(mapload)
+ . = ..()
+ freedoms_action = new(src)
+
+/obj/item/mod/module/shinobi_freedom/on_suit_activation()
+ . = ..()
+ ADD_TRAIT(mod.wearer, TRAIT_GOTTAGONOTSOFAST, src)
+ freedoms_action.Grant(mod.wearer)
+
+/obj/item/mod/module/shinobi_freedom/on_suit_deactivation(deleting = FALSE)
+ if(deleting)
+ return
+ REMOVE_TRAIT(mod.wearer, TRAIT_GOTTAGONOTSOFAST, src)
+ freedoms_action.Remove(mod.wearer)
+
+/obj/item/mod/module/shinobi_stims
+ name = "MOD shinobi stimulant module"
+ desc = "An advanced module, designed by the Spider Clan, that replicates the advanced stimulants technology in the classic ninja kabuto."
+ icon_state = "armor_booster"
+ origin_tech = "combat=5;bluespace=6;syndicate=4"
+ active_power_cost = DEFAULT_CHARGE_DRAIN * 0.2
+ removable = FALSE
+ /// Linked stims action
+ var/datum/action/cooldown/ninja/stim_suit/antistun_action
+
+/obj/item/mod/module/shinobi_stims/Initialize(mapload)
+ . = ..()
+ antistun_action = new(src)
+
+/obj/item/mod/module/shinobi_stims/on_suit_activation()
+ . = ..()
+ ADD_TRAIT(mod.wearer, TRAIT_GOTTAGONOTSOFAST, src)
+ antistun_action.Grant(mod.wearer)
+
+/obj/item/mod/module/shinobi_stims/on_suit_deactivation(deleting = FALSE)
+ if(deleting)
+ return
+ REMOVE_TRAIT(mod.wearer, TRAIT_GOTTAGONOTSOFAST, src)
+ antistun_action.Remove(mod.wearer)
diff --git a/code/modules/ninja/energy_katana.dm b/code/modules/ninja/energy_katana.dm
deleted file mode 100644
index dc6da8238a5..00000000000
--- a/code/modules/ninja/energy_katana.dm
+++ /dev/null
@@ -1,76 +0,0 @@
-/obj/item/katana/energy
- name = "energy katana"
- desc = "A katana infused with a strong energy."
- icon = 'icons/obj/weapons/energy_melee.dmi'
- icon_state = "energy_katana"
- throwforce = 20
- armor_penetration_percentage = 50
- armor_penetration_flat = 10
- var/cooldown = 0 // Because spam aint cool, yo.
- var/datum/effect_system/spark_spread/spark_system
-
-/obj/item/katana/energy/attack__legacy__attackchain(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
- playsound(user, 'sound/weapons/blade1.ogg', 50, TRUE, -1)
- return ..()
-
-/obj/item/katana/energy/afterattack__legacy__attackchain(atom/target, mob/user, proximity_flag, click_parameters)
- if(!user || !target)
- return
-
- if(proximity_flag && user.mind.special_role == "Ninja" && !cooldown && isobj(target))
- cooldown = 1
- spark_system.start()
- playsound(user, "sparks", 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- playsound(user, 'sound/weapons/blade1.ogg', 50, 1)
- user.visible_message(SPAN_DANGER("[user] masterfully slices [target]!"), SPAN_NOTICE("You masterfully slice [target]!"))
- target.emag_act(user)
- sleep(15)
- cooldown = 0
-
-/*/obj/item/katana/energy/throw_impact(atom/hit_atom)
- if(ishuman(hit_atom))
- var/mob/living/carbon/human/H = hit_atom
- if(istype(H.wear_suit, /obj/item/clothing/suit/space/space_ninja))
- var/obj/item/clothing/suit/space/space_ninja/SN = H.wear_suit
- if(SN.energyKatana && SN.energyKatana == src)
- returnToOwner(H, 0, 1)
- return
- ..()*/
-
-/obj/item/katana/energy/proc/returnToOwner(mob/living/carbon/human/user, doSpark = 1, caught = 0)
- if(!istype(user))
- return
- loc = get_turf(src)
-
- if(doSpark)
- spark_system.start()
- playsound(get_turf(src), "sparks", 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
-
- var/msg = ""
-
- if(user.put_in_hands(src))
- msg = "Your Energy Katana teleports into your hand!"
- else if(user.equip_to_slot_if_possible(src, ITEM_SLOT_BELT, FALSE, TRUE))
- msg = "Your Energy Katana teleports back to you, sheathing itself as it does so!"
- else
- loc = get_turf(user)
- msg = "Your Energy Katana teleports to your location!"
-
- if(caught)
- if(loc == user)
- msg = "You catch your Energy Katana!"
- else
- msg = "Your Energy Katana lands at your feet!"
-
- if(msg)
- to_chat(user, SPAN_NOTICE("[msg]"))
-
-/obj/item/katana/energy/Initialize(mapload)
- . = ..()
- spark_system = new /datum/effect_system/spark_spread()
- spark_system.set_up(5, 0, src)
- spark_system.attach(src)
-
-/obj/item/katana/energy/Destroy()
- QDEL_NULL(spark_system)
- return ..()
diff --git a/code/modules/ninja/ninja_actions.dm b/code/modules/ninja/ninja_actions.dm
new file mode 100644
index 00000000000..480eb1a176d
--- /dev/null
+++ b/code/modules/ninja/ninja_actions.dm
@@ -0,0 +1,101 @@
+/datum/action/cooldown/ninja
+ background_icon_state = "bg_ninja"
+ button_icon_state = "genetic_view"
+
+/datum/action/cooldown/ninja/ninja_cloak
+ name = "Ninja's Stealth"
+ desc = "Toggles whether you are currently sneaking, reducing your visibility and movement speed. Enhanced effects in darkness."
+ button_icon_state = "vampire_cloak"
+ cooldown_time = 2 SECONDS
+ var/sneaking = FALSE
+
+/datum/action/cooldown/ninja/ninja_cloak/proc/update_action_name()
+ name = "[initial(name)] ([sneaking ? "Deactivate" : "Activate"])"
+ SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_NAME)
+ build_all_button_icons()
+
+/datum/action/cooldown/ninja/ninja_cloak/proc/stop_sneaking()
+ if(!ishuman(owner))
+ return
+ var/mob/living/carbon/human/H = owner
+ sneaking = FALSE
+ STOP_PROCESSING(SSobj, src)
+ UnregisterSignal(H, COMSIG_ATTACK)
+ UnregisterSignal(H, COMSIG_ATTACK_BY)
+ H.set_alpha_tracking(ALPHA_VISIBLE, src)
+ REMOVE_TRAIT(owner, TRAIT_GOTTAGOSLOW, src)
+
+/datum/action/cooldown/ninja/ninja_cloak/process()
+ . = ..()
+ if(!ishuman(owner))
+ return
+ var/mob/living/carbon/human/H = owner
+ if(H.stat)
+ stop_sneaking()
+ return
+ if(!sneaking)
+ stop_sneaking()
+ return
+ var/turf/simulated/T = get_turf(H)
+ var/light_available = T.get_lumcount() * 10
+
+ if(light_available <= 2)
+ H.set_alpha_tracking(ALPHA_VISIBLE * 0.1, src)
+ return
+ H.set_alpha_tracking(ALPHA_VISIBLE * 0.5, src)
+
+/datum/action/cooldown/ninja/ninja_cloak/Activate(atom/target)
+ if(!ishuman(owner))
+ return
+
+ var/mob/living/carbon/human/H = owner
+
+ sneaking = !sneaking
+ update_action_name()
+ if(sneaking)
+ to_chat(H, "You are now sneaking around. Stick to the darkness to conceal yourself better.")
+ START_PROCESSING(SSobj, src)
+ RegisterSignal(owner, COMSIG_ATTACK, PROC_REF(stop_sneaking))
+ RegisterSignal(owner, COMSIG_ATTACK_BY, PROC_REF(stop_sneaking))
+ ADD_TRAIT(owner, TRAIT_GOTTAGOSLOW, src)
+ else
+ to_chat(H, "You are no longer sneaking around.")
+ stop_sneaking()
+ StartCooldown()
+
+/datum/action/cooldown/ninja/freedom_shoes
+ name = "Ninja's Mobility"
+ desc = "Breaks free of leg restraints. 2 Minute cooldown."
+ button_icon = 'icons/obj/bio_chips.dmi'
+ button_icon_state = "freedom"
+ cooldown_time = 2 MINUTES
+
+/datum/action/cooldown/ninja/freedom_shoes/Activate(atom/target)
+ if(!ishuman(owner))
+ return
+ var/mob/living/carbon/human/H = owner
+ H.clear_legcuffs(TRUE)
+ H.SetKnockDown(0)
+ H.stand_up(TRUE)
+ StartCooldown()
+
+/datum/action/cooldown/ninja/stim_suit
+ name = "Ninja's Tenacity"
+ desc = "Activates an injector within your suit, pumping you with a small hit of stimulants. 2 Minute cooldown."
+ button_icon = 'icons/obj/bio_chips.dmi'
+ button_icon_state = "adrenal"
+ cooldown_time = 2 MINUTES
+
+/datum/action/cooldown/ninja/stim_suit/Activate(atom/target)
+ if(!ishuman(owner))
+ return
+ var/mob/living/carbon/human/H = owner
+ H.SetStunned(0)
+ H.SetWeakened(0)
+ H.SetKnockDown(0)
+ H.SetParalysis(0)
+ H.adjustStaminaLoss(-75)
+ H.stand_up(TRUE)
+ SEND_SIGNAL(H, COMSIG_LIVING_CLEAR_STUNS)
+ H.reagents.add_reagent("stimulative_agent", 1) // 10 Seconds.
+ StartCooldown()
diff --git a/code/modules/ninja/ninja_clothes.dm b/code/modules/ninja/ninja_clothes.dm
new file mode 100644
index 00000000000..55fdd3a12af
--- /dev/null
+++ b/code/modules/ninja/ninja_clothes.dm
@@ -0,0 +1,195 @@
+/obj/item/clothing/head/helmet/space_ninja
+ desc = "What may appear to be a simple black garment is in fact a highly sophisticated nano-weave helmet. Standard issue ninja gear."
+ name = "ninja fukumen"
+ icon_state = "s-ninja"
+ inhand_icon_state = "s-ninja_hood"
+ flags = BLOCKHAIR
+ flags_inv = HIDEEARS|HIDEEYES|HIDEFACE
+ armor = list(MELEE = 30, BULLET = 35, LASER = 25, ENERGY = 25, BOMB = 20, RAD = 100, FIRE = INFINITY, ACID = INFINITY)
+ origin_tech = "combat=5;materials=5;syndicate=2"
+ blockTracking = 1
+ icon_monitor = 'icons/mob/clothing/species/machine/monitor/hood.dmi'
+ sprite_sheets = list(
+ "Unathi" = 'icons/mob/clothing/species/unathi/head.dmi',
+ "Tajaran" = 'icons/mob/clothing/species/tajaran/head.dmi',
+ "Skrell" = 'icons/mob/clothing/species/skrell/head.dmi',
+ "Vox" = 'icons/mob/clothing/species/vox/head.dmi',
+ "Vulpkanin" = 'icons/mob/clothing/species/vulpkanin/head.dmi',
+ "Drask" = 'icons/mob/clothing/species/drask/head.dmi',
+ "Grey" = 'icons/mob/clothing/species/grey/head.dmi',
+ "Nian" = 'icons/mob/clothing/species/nian/head.dmi',
+ "Diona" = 'icons/mob/clothing/species/diona/head.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/head.dmi',
+ "Skkulakin" = 'icons/mob/clothing/species/vox/head.dmi',
+ )
+ /// Tracking the cloak action
+ var/datum/action/cooldown/ninja/ninja_cloak/cloak_action
+
+/obj/item/clothing/head/helmet/space_ninja/Initialize(mapload)
+ . = ..()
+ cloak_action = new(src)
+
+/obj/item/clothing/head/helmet/space_ninja/equipped(mob/user, slot, initial)
+ . = ..()
+ if(!ishuman(user))
+ return
+ if(slot != ITEM_SLOT_HEAD)
+ return
+ cloak_action.Grant(user)
+
+/obj/item/clothing/head/helmet/space_ninja/dropped(mob/user, silent)
+ . = ..()
+ cloak_action.stop_sneaking()
+ cloak_action.Remove(user)
+
+/obj/item/clothing/mask/gas/space_ninja
+ name = "ninja mask"
+ desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement."
+ icon_state = "s-ninja"
+ inhand_icon_state = "s-ninja_mask"
+ flash_protect = FLASH_PROTECTION_FLASH
+ origin_tech = "combat=4;materials=4;syndicate=1"
+ icon_monitor = 'icons/mob/clothing/species/machine/monitor/mask.dmi'
+ sprite_sheets = list(
+ "Unathi" = 'icons/mob/clothing/species/unathi/mask.dmi',
+ "Tajaran" = 'icons/mob/clothing/species/tajaran/mask.dmi',
+ "Skrell" = 'icons/mob/clothing/species/skrell/mask.dmi',
+ "Vox" = 'icons/mob/clothing/species/vox/mask.dmi',
+ "Vulpkanin" = 'icons/mob/clothing/species/vulpkanin/mask.dmi',
+ "Drask" = 'icons/mob/clothing/species/drask/mask.dmi',
+ "Grey" = 'icons/mob/clothing/species/grey/mask.dmi',
+ "Nian" = 'icons/mob/clothing/species/nian/mask.dmi',
+ "Diona" = 'icons/mob/clothing/species/diona/mask.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/mask.dmi',
+ "Skkulakin" = 'icons/mob/clothing/species/skkulakin/mask.dmi',
+ )
+ var/obj/item/voice_changer/voice_changer
+
+/obj/item/clothing/mask/gas/space_ninja/Initialize(mapload)
+ . = ..()
+ voice_changer = new(src)
+
+/obj/item/clothing/mask/gas/space_ninja/Destroy()
+ QDEL_NULL(voice_changer)
+ return ..()
+
+/obj/item/clothing/gloves/space_ninja
+ desc = "These nano-enhanced gloves insulate from electricity and provide fire resistance."
+ name = "ninja tekko"
+ icon_state = "s-ninja"
+ siemens_coefficient = 0
+ pickpocket = TRUE // Master of stealth, you can pick a pocket
+ armor = list(MELEE = 30, BULLET = 35, LASER = 25, ENERGY = 25, BOMB = 20, RAD = 100, FIRE = INFINITY, ACID = INFINITY)
+ cold_protection = HANDS
+ min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
+ heat_protection = HANDS
+ max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
+ origin_tech = "combat=5;materials=5;syndicate=2"
+ sprite_sheets = list(
+ "Vox" = 'icons/mob/clothing/species/vox/gloves.dmi',
+ "Skkulakin" = 'icons/mob/clothing/species/vox/gloves.dmi',
+ "Drask" = 'icons/mob/clothing/species/drask/gloves.dmi',
+ "Diona" = 'icons/mob/clothing/species/diona/gloves.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/gloves.dmi',
+ )
+
+/obj/item/clothing/shoes/space_ninja
+ name = "ninja jaka-tabi"
+ desc = "A pair of split-toed rubber-soled shoes. Excellent for running and even better for smashing skulls."
+ icon_state = "s-ninja"
+ permeability_coefficient = 0.01
+ no_slip = TRUE
+ armor = list(MELEE = 30, BULLET = 35, LASER = 25, ENERGY = 25, BOMB = 20, RAD = 100, FIRE = INFINITY, ACID = INFINITY)
+ cold_protection = FEET
+ min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
+ heat_protection = FEET
+ max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT
+ origin_tech = "combat=5;materials=5;syndicate=2"
+ sprite_sheets = list(
+ "Vox" = 'icons/mob/clothing/species/vox/shoes.dmi',
+ "Drask" = 'icons/mob/clothing/species/drask/shoes.dmi',
+ "Diona" = 'icons/mob/clothing/species/diona/shoes.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/shoes.dmi',
+ "Skkulakin" = 'icons/mob/clothing/species/vox/shoes.dmi',
+ )
+ /// Linked freedoms action
+ var/datum/action/cooldown/ninja/freedom_shoes/freedoms_action
+
+/obj/item/clothing/shoes/space_ninja/Initialize(mapload)
+ . = ..()
+ freedoms_action = new(src)
+
+/obj/item/clothing/shoes/space_ninja/equipped(mob/user, slot, initial)
+ . = ..()
+ if(!ishuman(user))
+ return
+ if(slot != ITEM_SLOT_SHOES)
+ return
+ ADD_TRAIT(user, TRAIT_GOTTAGONOTSOFAST, src)
+ freedoms_action.Grant(user)
+
+/obj/item/clothing/shoes/space_ninja/dropped(mob/user, silent)
+ . = ..()
+ REMOVE_TRAIT(user, TRAIT_GOTTAGONOTSOFAST, src)
+ freedoms_action.Remove(user)
+
+/obj/item/clothing/suit/space_ninja
+ name = "ninja uwagi"
+ desc = "A unique suit of nano-enhanced armor designed specifically for Spider Clan assassins."
+ icon_state = "s-ninja"
+ inhand_icon_state = "s-ninja_suit"
+ allowed = list(
+ /obj/item/gun,
+ /obj/item/ammo_box,
+ /obj/item/melee/baton,
+ /obj/item/tank/internals,
+ /obj/item/katana/energy,
+ /obj/item/restraints/handcuffs,
+ /obj/item/ninja_scanner
+ )
+ flags_inv = HIDEJUMPSUIT
+ armor = list(MELEE = 30, BULLET = 35, LASER = 25, ENERGY = 25, BOMB = 20, RAD = 100, FIRE = INFINITY, ACID = INFINITY)
+ origin_tech = "combat=5;materials=5;syndicate=2"
+ sprite_sheets = list(
+ "Vox" = 'icons/mob/clothing/species/vox/suit.dmi',
+ "Drask" = 'icons/mob/clothing/species/drask/suit.dmi',
+ "Grey" = 'icons/mob/clothing/species/grey/suit.dmi',
+ "Nian" = 'icons/mob/clothing/species/nian/suit.dmi',
+ "Diona" = 'icons/mob/clothing/species/diona/suit.dmi',
+ "Kidan" = 'icons/mob/clothing/species/kidan/suit.dmi',
+ "Skkulakin" = 'icons/mob/clothing/species/vox/suit.dmi',
+ )
+ /// Linked stims action
+ var/datum/action/cooldown/ninja/stim_suit/antistun_action
+
+/obj/item/clothing/suit/space_ninja/Initialize(mapload)
+ . = ..()
+ antistun_action = new(src)
+
+/obj/item/clothing/suit/space_ninja/equipped(mob/user, slot, initial)
+ . = ..()
+ if(!ishuman(user))
+ return
+ if(slot != ITEM_SLOT_OUTER_SUIT)
+ return
+ antistun_action.Grant(user)
+
+/obj/item/clothing/suit/space_ninja/dropped(mob/user, silent)
+ . = ..()
+ antistun_action.Remove(user)
+
+/obj/item/storage/bag/garment/syndie/ninja_suit
+ name = "ninja suit garment bag"
+
+/obj/item/storage/bag/garment/syndie/ninja_suit/populate_contents()
+ new /obj/item/clothing/under/syndicate(src)
+ new /obj/item/clothing/shoes/space_ninja(src)
+ new /obj/item/clothing/gloves/space_ninja(src)
+ new /obj/item/clothing/head/helmet/space_ninja(src)
+ new /obj/item/clothing/mask/gas/space_ninja(src)
+ new /obj/item/clothing/suit/space_ninja(src)
+
+/obj/item/card/id/syndicate/ninja
+ name = "ninja card"
+ initial_access = list(ACCESS_MAINT_TUNNELS)
+ icon_state = "data"
diff --git a/code/modules/ninja/ninja_deployables.dm b/code/modules/ninja/ninja_deployables.dm
new file mode 100644
index 00000000000..d9cb45c1d61
--- /dev/null
+++ b/code/modules/ninja/ninja_deployables.dm
@@ -0,0 +1,181 @@
+/obj/machinery/syndicatebomb/ninja
+ name = "spider clan bomb"
+ payload = /obj/item/bombcore/ninja
+
+/obj/machinery/syndicatebomb/ninja/emp
+ name = "spider clan EMP bomb"
+ payload = /obj/item/bombcore/emp/ninja
+
+/obj/machinery/syndicatebomb/ninja/spiders
+ name = "spider bomb"
+ payload = /obj/item/bombcore/badmin/summon/ninja_spiders
+
+/obj/item/bombcore/ninja
+ desc = "A powerful secondary explosive of spider clan design and unknown composition, it should be stable under normal conditions..."
+ range_heavy = 3
+ range_medium = 8
+ range_light = 15
+ range_flame = 17
+
+/obj/item/bombcore/emp/ninja
+ light_emp = 24
+ heavy_emp = 12
+
+/obj/item/bombcore/badmin/summon/ninja_spiders
+ summon_path = /mob/living/basic/giant_spider
+ amt_summon = 12
+
+/obj/item/wormhole_jaunter/ninja_bomb
+ name = "spider clan bomb deployment flare"
+ icon = 'icons/obj/lighting.dmi'
+ desc = "A single-use flare that will bring a high-yield payload to the location it was lit at."
+ icon_state = "flare-contractor"
+ inhand_icon_state = "flare"
+ /// What area should be blow up?
+ var/area/bomb_target
+ /// What kind of bomb is it
+ var/obj/machinery/syndicatebomb/bomb_type = /obj/machinery/syndicatebomb/ninja
+
+/obj/item/wormhole_jaunter/ninja_bomb/Initialize(mapload)
+ . = ..()
+ var/list/possible_areas = list(
+ // Rooms
+ /area/station/hallway/primary/aft,
+ /area/station/engineering/atmos,
+ /area/station/public/arcade,
+ /area/station/maintenance/assembly_line,
+ /area/station/public/storage/tools/auxiliary,
+ /area/station/command/office/blueshield,
+ /area/station/supply/storage,
+ /area/station/service/chapel,
+ /area/station/service/chapel/office,
+ /area/station/service/clown,
+ /area/station/legal/courtroom,
+ /area/station/public/toilet,
+ /area/station/engineering/control,
+ /area/station/engineering/controlroom,
+ /area/station/hallway/secondary/exit,
+ /area/holodeck/alphadeck,
+ /area/station/service/hydroponics,
+ /area/station/service/library,
+ /area/station/service/mime,
+ /area/station/supply/miningdock,
+ /area/station/medical/morgue,
+ /area/station/public/storage/office,
+ /area/station/public/pet_store,
+ /area/station/public/storage/tools,
+ /area/station/public/mrchangs,
+ /area/station/science/research,
+ /area/station/security/checkpoint,
+ /area/station/engineering/tech_storage,
+ /area/station/command/teleporter,
+ /area/station/science/storage,
+ /area/station/science/misc_lab,
+ /area/station/science/xenobiology,
+ /area/station/turret_protected/aisat/interior,
+ /area/station/aisat/atmos,
+ /area/station/aisat/hall,
+ /area/station/aisat/service,
+ /area/station/service/bar,
+ /area/station/supply/office,
+ /area/station/medical/chemistry,
+ /area/station/command/office/ce,
+ /area/station/command/office/cmo,
+ /area/station/medical/cloning,
+ /area/station/medical/cryo,
+ /area/station/public/dorms,
+ /area/station/engineering/equipmentstorage,
+ /area/station/engineering/break_room,
+ /area/station/ai_monitored/storage/eva,
+ /area/station/supply/expedition,
+ /area/station/science/genetics,
+ /area/station/engineering/gravitygenerator,
+ /area/station/command/office/hop,
+ /area/station/command/meeting_room,
+ /area/station/service/kitchen,
+ /area/station/science/robotics/chargebay,
+ /area/station/medical/medbay,
+ /area/station/medical/medbay2,
+ /area/station/medical/medbay3,
+ /area/station/medical/reception,
+ /area/station/medical/storage,
+ /area/station/medical/sleeper,
+ /area/station/command/server,
+ /area/station/command/office/ntrep,
+ /area/station/medical/paramedic,
+ /area/station/hallway/primary/port,
+ /area/station/supply/qm,
+ /area/station/command/office/rd,
+ /area/station/science/rnd,
+ /area/station/science/robotics,
+ /area/station/medical/surgery/primary,
+ /area/station/medical/surgery/secondary,
+ /area/station/telecomms/chamber,
+ /area/station/engineering/secure_storage
+ )
+ while(!bomb_target)
+ var/area/selected_area = pick_n_take(possible_areas)
+ for(var/area/potential in SSmapping.existing_station_areas)
+ if(potential.type != selected_area)
+ continue
+ bomb_target = potential
+ break
+
+/obj/item/wormhole_jaunter/ninja_bomb/examine(mob/user)
+ . = ..()
+ if(isAntag(user))
+ . += SPAN_WARNING("The target bomb location is: [bomb_target.name]")
+
+/obj/item/wormhole_jaunter/ninja_bomb/activate(mob/user)
+ if(!turf_check(user))
+ return
+ var/obj/effect/temp_visual/ninja_bomb/F = new /obj/effect/temp_visual/ninja_bomb(get_turf(src))
+ user.drop_item()
+ forceMove(F)
+ log_and_message_admins("[user] activated a ninja bomb flare.")
+ addtimer(CALLBACK(src, PROC_REF(spawn_bomb), user), 5 SECONDS)
+
+/obj/item/wormhole_jaunter/ninja_bomb/proc/spawn_bomb(mob/user)
+ new /obj/effect/decal/cleanable/ash(get_turf(src))
+ var/obj/machinery/syndicatebomb/new_bomb = new bomb_type(get_turf(src))
+ new_bomb.anchored = TRUE
+ new_bomb.activate()
+ var/objectives = user.mind.get_all_objectives()
+ for(var/datum/objective/ninja/bomb_department/bomb_dep in objectives)
+ if(!bomb_dep.completed)
+ to_chat(user, SPAN_NOTICE("Contract complete. Good work - your cut of the pay has been forwarded to your uplink."))
+ bomb_dep.complete_objective()
+ qdel(src)
+
+/obj/item/wormhole_jaunter/ninja_bomb/turf_check(mob/user)
+ var/turf/device_turf = get_turf(user)
+ if(!device_turf)
+ to_chat(user, "You're having difficulties getting the [name] to work.")
+ return FALSE
+ var/area/our_area = get_area(device_turf)
+ if(!istype(our_area, bomb_target))
+ to_chat(user, "You're having difficulties getting the [name] to work.")
+ return FALSE
+ return TRUE
+
+/obj/item/wormhole_jaunter/ninja_bomb/emp
+ bomb_type = /obj/machinery/syndicatebomb/ninja/emp
+
+/obj/item/wormhole_jaunter/ninja_bomb/spiders
+ bomb_type = /obj/machinery/syndicatebomb/ninja/spiders
+
+/obj/effect/temp_visual/ninja_bomb
+ name = "spider clan bomb deployment flare"
+ layer = BELOW_MOB_LAYER
+ icon = 'icons/obj/lighting.dmi'
+ icon_state = "flare-contractor-on"
+ duration = 5.1 SECONDS
+ /// Sound that plays when activated
+ var/activation_sound = 'sound/goonstation/misc/matchstick_light.ogg'
+ /// Light emitted when activated
+ var/emitted_color = "#1faf2b"
+
+/obj/effect/temp_visual/ninja_bomb/Initialize(mapload)
+ . = ..()
+ playsound(loc, activation_sound, 50, TRUE)
+ set_light(8, l_color = emitted_color)
diff --git a/code/modules/ninja/ninja_outfits.dm b/code/modules/ninja/ninja_outfits.dm
new file mode 100644
index 00000000000..ed8ca10a200
--- /dev/null
+++ b/code/modules/ninja/ninja_outfits.dm
@@ -0,0 +1,54 @@
+/datum/outfit/space_ninja
+ name = "Space Ninja"
+ uniform = /obj/item/clothing/under/syndicate
+ shoes = /obj/item/clothing/shoes/space_ninja
+ gloves = /obj/item/clothing/gloves/space_ninja
+ head = /obj/item/clothing/head/helmet/space_ninja
+ mask = /obj/item/clothing/mask/gas/space_ninja
+ glasses = /obj/item/clothing/glasses/night
+ suit = /obj/item/clothing/suit/space_ninja
+ belt = /obj/item/katana/energy
+ id = /obj/item/card/id/syndicate/ninja
+ back = /obj/item/storage/backpack/satchel
+ l_ear = /obj/item/radio/headset
+ box = /obj/item/storage/box/survival_syndie
+ backpack_contents = list(
+ /obj/item/storage/box/syndie_kit/throwing_weapons,
+ /obj/item/ninja_scanner,
+ /obj/item/gun/energy/kinetic_accelerator/energy_net,
+ /obj/item/wormhole_jaunter/extraction,
+ /obj/item/teleportation_scroll/apprentice,
+ )
+ bio_chips = list(
+ /obj/item/bio_chip/dust,
+ /obj/item/bio_chip/uplink/ninja
+ )
+
+/datum/outfit/space_ninja/vox
+ name = "Space Ninja - Vox"
+
+/datum/outfit/space_ninja/vox/post_equip(mob/living/carbon/human/H, visualsOnly)
+ . = ..()
+ var/obj/item/tank/internal_tank = new /obj/item/tank/internals/emergency_oxygen/double/vox(H)
+ if(!H.equip_to_appropriate_slot(internal_tank))
+ if(!H.put_in_any_hand_if_possible(internal_tank))
+ H.drop_item_to_ground(H.l_hand)
+ H.equip_or_collect(internal_tank, ITEM_SLOT_LEFT_HAND)
+ to_chat(H, "Could not find an empty slot for internals! Please report this as a bug")
+ H.internal = internal_tank
+
+/datum/outfit/admin/ghostbar_antag/space_ninja
+ name = "Ghostbar Space Ninja"
+ uniform = /obj/item/clothing/under/syndicate
+ shoes = /obj/item/clothing/shoes/space_ninja
+ gloves = /obj/item/clothing/gloves/space_ninja
+ head = /obj/item/clothing/head/helmet/space_ninja
+ mask = /obj/item/clothing/mask/gas/space_ninja
+ suit = /obj/item/clothing/suit/space_ninja
+ belt = /obj/item/toy/katana
+ back = /obj/item/storage/backpack/satchel
+ backpack_contents = list(
+ /obj/item/storage/box/engineer = 1,
+ /obj/item/flashlight = 1,
+ /obj/item/food/syndidonkpocket = 1,
+ )
diff --git a/code/modules/ninja/ninja_scanner.dm b/code/modules/ninja/ninja_scanner.dm
new file mode 100644
index 00000000000..1fa9504d194
--- /dev/null
+++ b/code/modules/ninja/ninja_scanner.dm
@@ -0,0 +1,143 @@
+/obj/item/ninja_scanner
+ name = "spider scanner"
+ desc = "Advanced multi-use scanning device used by the spider clan to confirm bounty completion. Scan the corpses of dead or netted crew to redeem your bounties."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "ninja_scanner"
+ slot_flags = ITEM_SLOT_BELT
+ force = 9 // Can be used as a weapon in an emergency
+ hitsound = 'sound/weapons/genhit1.ogg'
+ new_attack_chain = TRUE
+
+/obj/item/ninja_scanner/attack(mob/living/target, mob/living/user, params)
+ if(user.a_intent == INTENT_HARM)
+ return ..()
+ if(!user.mind)
+ return ..()
+ var/list/obj_list = user.mind.get_all_objectives()
+ if(!length(obj_list))
+ to_chat(user, SPAN_WARNING("You have no objectives!"))
+ return TRUE
+ for(var/datum/objective/ninja/ninja_obj in obj_list)
+ if(ninja_obj.completed)
+ continue
+ if(istype(ninja_obj, /datum/objective/ninja/kill))
+ if(target.stat != DEAD)
+ continue
+ if(!ninja_obj.target.current || target != ninja_obj.target.current)
+ continue
+ user.visible_message(SPAN_DANGER("[user] begins to use [src] to scan [target]!"), \
+ SPAN_NOTICE("You begin to scan [target]!"))
+ if(!do_after_once(user, 8 SECONDS, target = target, allow_moving = FALSE, attempt_cancel_message = "You stop scanning [target] before completing the scan."))
+ return TRUE
+ ninja_obj.completed = TRUE
+ to_chat(user, SPAN_NOTICE("Contract complete. Good work - your cut of the pay has been forwarded to your uplink."))
+ ninja_obj.complete_objective()
+ return TRUE
+
+ if(istype(ninja_obj, /datum/objective/ninja/capture))
+ var/datum/objective/ninja/capture/cap_obj = ninja_obj
+ if(target != ninja_obj.target.current)
+ continue
+ if(target.stat == DEAD)
+ if(!target.ghost_can_reenter()) // Target is DNR. Reroll.
+ to_chat(user, SPAN_NOTICE("Target is unrecoverable. Finding a new target..."))
+ ninja_obj.on_target_cryo()
+ else
+ to_chat(user, SPAN_NOTICE("Target is dead. Dead people don't talk. Esnure they are revived, then send them to us."))
+ continue
+ if(!target.buckled)
+ continue
+ var/obj/structure/bed/energy_net/trapped_net
+ for(var/obj/structure/bed/energy_net/net in target.loc)
+ trapped_net = net
+ break
+ if(!trapped_net)
+ continue
+ user.visible_message(SPAN_DANGER("[user] begins to use [src] to scan [target]!"), \
+ SPAN_NOTICE("You begin to scan [target]!"))
+ if(!do_after_once(user, 5 SECONDS, target = target, allow_moving = FALSE, attempt_cancel_message = "You stop scanning [target] before completing the scan."))
+ return TRUE
+ target.unbuckle(TRUE)
+ trapped_net.Destroy()
+ var/target_loc = target.loc
+ do_teleport(target, pick(GLOB.ninjaprisonwarp), 0, TRUE, bypass_area_flag = TRUE)
+ var/mob/living/carbon/c_target = target
+ if(istype(c_target))
+ var/obj/item/restraints/handcuffs/cuffs = target.get_item_by_slot(ITEM_SLOT_HANDCUFFED)
+ qdel(cuffs)
+ c_target.handcuffed = FALSE
+ c_target.update_handcuffed()
+ cap_obj.handle_capture(target, target_loc)
+ ninja_obj.completed = TRUE
+ to_chat(user, SPAN_NOTICE("Contract complete. Good work - your cut of the pay has been forwarded to your uplink."))
+ ninja_obj.complete_objective()
+ return TRUE
+
+ if(istype(ninja_obj, /datum/objective/ninja/interrogate_ai))
+ if(!is_ai(target))
+ return TRUE
+ var/mob/living/silicon/ai/AI = target
+ user.visible_message(SPAN_DANGER("[user] begins to use [src] to upload [AI]!"), \
+ SPAN_NOTICE("You begin to upload [AI]!"))
+ if(!do_after_once(user, 8 SECONDS, target = AI, allow_moving = FALSE, attempt_cancel_message = "You stop uploading [AI] before completing the upload."))
+ return TRUE
+ var/obj/item/aicard/card = new()
+ AI.take_overall_damage(50)
+ AI.transfer_ai(AI_TRANS_TO_CARD, user, null, card)
+ var/list/possible_spawns = GLOB.maints_loot_spawns
+ while(length(possible_spawns))
+ var/turf/current_turf = pick_n_take(possible_spawns)
+ if(!is_station_level(current_turf))
+ continue
+ if(!current_turf.density)
+ do_teleport(card, current_turf)
+ break
+ // Someone built a wall over it, check the surroundings
+ var/list/open_turfs = current_turf.AdjacentTurfs(open_only = TRUE)
+ if(length(open_turfs))
+ do_teleport(card, pick(open_turfs))
+ break
+ ninja_obj.completed = TRUE
+ to_chat(user, SPAN_NOTICE("Contract complete. Good work - your cut of the pay has been forwarded to your uplink."))
+ ninja_obj.complete_objective()
+ return TRUE
+ return ..()
+
+/obj/item/ninja_scanner/attack_obj(obj/attacked_obj, mob/living/user, params)
+ if(user.a_intent == INTENT_HARM)
+ return ..()
+ if(!user.mind)
+ return ..()
+ var/list/obj_list = user.mind.get_all_objectives()
+ if(!length(obj_list))
+ to_chat(user, SPAN_WARNING("You have no objectives!"))
+ return TRUE
+ for(var/datum/objective/ninja/ninja_obj in obj_list)
+ if(ninja_obj.completed)
+ continue
+ if(istype(ninja_obj, /datum/objective/ninja/hack_rnd) && istype(attacked_obj, /obj/machinery/computer/rnd_network_controller))
+ if(!do_after_once(user, 12 SECONDS, target = attacked_obj, allow_moving = FALSE, attempt_cancel_message = "You stop hacking [attacked_obj] before completing the download."))
+ return TRUE
+ to_chat(user, SPAN_NOTICE("Contract complete. Good work - your cut of the pay has been forwarded to your uplink."))
+ var/obj/machinery/computer/rnd_network_controller/rnd = attacked_obj
+ rnd.research_files.known_tech = list()
+ ninja_obj.completed = TRUE
+ ninja_obj.complete_objective()
+ return TRUE
+ if(istype(ninja_obj, /datum/objective/ninja/insert_spider_rod) && istype(attacked_obj, /obj/machinery/atmospherics/reactor_chamber))
+ var/obj/machinery/atmospherics/reactor_chamber/r_chamber = attacked_obj
+ if(!r_chamber.held_rod)
+ to_chat(user, SPAN_NOTICE("There is no rod in [r_chamber]."))
+ return TRUE
+ if(!istype(r_chamber.held_rod, /obj/item/nuclear_rod/fuel/uranium_238/spiders))
+ to_chat(user, SPAN_NOTICE("The rod in [r_chamber] is not one of ours."))
+ return TRUE
+ if(r_chamber.linked_reactor.offline)
+ to_chat(user, SPAN_NOTICE("[r_chamber.linked_reactor] is not online."))
+ return TRUE
+ to_chat(user, SPAN_NOTICE("Contract complete. Good work - your cut of the pay has been forwarded to your uplink."))
+ ninja_obj.completed = TRUE
+ ninja_obj.complete_objective()
+ return TRUE
+
+ return ..()
diff --git a/code/modules/ninja/ninja_uplink.dm b/code/modules/ninja/ninja_uplink.dm
new file mode 100644
index 00000000000..f5bcd4ad287
--- /dev/null
+++ b/code/modules/ninja/ninja_uplink.dm
@@ -0,0 +1,91 @@
+/obj/item/bio_chip/uplink/ninja
+ name = "spider clan uplink bio-chip"
+ desc = "Purchase things from the spider clan with earned currency."
+
+/obj/item/bio_chip/uplink/ninja/Initialize(mapload)
+ . = ..()
+ if(hidden_uplink)
+ hidden_uplink.update_uplink_type(UPLINK_TYPE_NINJA)
+ hidden_uplink.uses = 0
+
+/datum/uplink_item/ninja
+ category = "Spider Clan Provisions"
+ surplus = 0
+ uplinktypes = list(UPLINK_TYPE_NINJA)
+
+/datum/uplink_item/ninja/shuriken_printer
+ name = "Shuriken Printer"
+ desc = "An advanced, tiny autofabricator that slowly creates and stores energy shurikens."
+ reference = "SPR"
+ item = /obj/item/shuriken_printer
+ cost = 25
+
+/datum/uplink_item/ninja/ninja_modsuit
+ name = "'Shinobi' Stealth Modsuit"
+ desc = "A specialized modsuit worn by the spider clan's elite ninja operatives."
+ reference = "SNBMOD"
+ item = /obj/item/mod/control/pre_equipped/ninja
+ cost = 45
+
+/datum/uplink_item/ninja/energy_katana
+ name = "Energy Katana"
+ desc = "A plastitanium katana with a reinforced emitter edge. A ninja's most reliable tool."
+ reference = "EKAT"
+ item = /obj/item/katana/energy
+ cost = 20
+
+/datum/uplink_item/ninja/energy_net
+ name = "Energy Net Projector"
+ desc = "A non-lethal weapon favored by the Spider Clan. Targets struck will find themselves trapped in an energy net."
+ reference = "ENET"
+ item = /obj/item/gun/energy/kinetic_accelerator/energy_net
+ cost = 30
+
+/datum/uplink_item/ninja/spider_bomb
+ name = "Spider Bomb"
+ desc = "An evil device fabricated by the Spider Clan. This bomb contains a large number of dehydrated spider-cubes that will all activate upon detonation. These spiders do not discern friend from foe - all non-spiders make for good food."
+ reference = "SPBM"
+ item = /obj/machinery/syndicatebomb/ninja/spiders
+ cost = 65
+
+/datum/uplink_item/ninja/hood
+ name = "Ninja Scarf"
+ desc = "What may appear to be a simple black garment is in fact a highly sophisticated nano-weave helmet. Standard issue ninja gear."
+ reference = "NJSC"
+ item = /obj/item/clothing/head/helmet/space_ninja
+ cost = 10
+
+/datum/uplink_item/ninja/mask
+ name = "Ninja Mask"
+ desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement."
+ reference = "NJMSK"
+ item = /obj/item/clothing/mask/gas/space_ninja
+ cost = 10
+
+/datum/uplink_item/ninja/gloves
+ name = "Ninja Gloves"
+ desc = "These nano-enhanced gloves insulate from electricity and provide fire resistance."
+ reference = "NJGL"
+ item = /obj/item/clothing/gloves/space_ninja
+ cost = 10
+
+/datum/uplink_item/ninja/shoes
+ name = "Ninja Shoes"
+ desc = "A pair of running shoes. Excellent for running and even better for smashing skulls."
+ reference = "NJSH"
+ item = /obj/item/clothing/shoes/space_ninja
+ cost = 10
+
+/datum/uplink_item/ninja/suit
+ name = "Ninja Kabuto"
+ desc = "A unique suit of nano-enhanced armor designed specifically for Spider Clan assassins."
+ reference = "NJKB"
+ item = /obj/item/clothing/suit/space_ninja
+ cost = 10
+
+/datum/uplink_item/ninja/suit_bundle
+ name = "Full Ninja Outfit"
+ desc = "A bulk purchase of a full ninja outfit. Good for if you somehow lost the other set of highly advanced Spider Clan equipment."
+ reference = "NJST"
+ item = /obj/item/clothing/suit/space_ninja
+ cost = 40
diff --git a/code/modules/ninja/ninja_weapons.dm b/code/modules/ninja/ninja_weapons.dm
new file mode 100644
index 00000000000..19510c93174
--- /dev/null
+++ b/code/modules/ninja/ninja_weapons.dm
@@ -0,0 +1,227 @@
+/obj/item/katana/energy
+ name = "energy katana"
+ desc = "A plastitanium katana with a reinforced emitter edge. A ninja's most reliable tool."
+ icon = 'icons/obj/weapons/energy_melee.dmi'
+ icon_state = "energy_katana"
+ force = 30
+ embed_chance = 75
+ throwforce = 20
+ armor_penetration_percentage = 50
+ armor_penetration_flat = 10
+ origin_tech = "combat=6;syndicate=4"
+ hitsound = 'sound/weapons/blade1.ogg'
+ /// Is the weapon gripped or not?
+ var/gripped = FALSE
+
+/obj/item/katana/energy/activate_self(mob/living/carbon/human/user)
+ . = ..()
+ if(!ishuman(user))
+ return
+ var/is_proper_modsuit = FALSE
+ if(istype(user.gloves, /obj/item/clothing/gloves/mod))
+ var/obj/item/mod/control/modsuit = user.get_item_by_slot(ITEM_SLOT_BACK)
+ if(istype(modsuit) && istype(modsuit.theme, /datum/mod_theme/ninja))
+ is_proper_modsuit = TRUE
+ if(!istype(user.gloves, /obj/item/clothing/gloves/space_ninja) && !is_proper_modsuit)
+ return
+ if(!gripped)
+ gripped = TRUE
+ to_chat(user, SPAN_NOTICE("You tighten your grip on [src], ensuring you won't drop it."))
+ set_nodrop(TRUE, user)
+ return
+ gripped = FALSE
+ to_chat(user, SPAN_NOTICE("You relax your grip on [src]."))
+ set_nodrop(FALSE, user)
+
+/obj/item/katana/energy/dropped(mob/user, silent)
+ . = ..()
+ gripped = FALSE
+ to_chat(user, SPAN_WARNING("Your grip on [src] is loosened!"))
+ set_nodrop(FALSE, user)
+
+/obj/item/katana/energy/equipped(mob/user, slot, initial)
+ . = ..()
+ if(gripped)
+ gripped = FALSE
+ to_chat(user, SPAN_NOTICE("You relax your grip on [src]."))
+ set_nodrop(FALSE, user)
+
+/obj/item/katana/energy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ . = ..()
+ if(!.) // they did not block the attack
+ return
+ if(isprojectile(hitby))
+ var/obj/projectile/P = hitby
+ if(P.reflectability == REFLECTABILITY_ENERGY)
+ owner.visible_message(SPAN_DANGER("[owner] parries [attack_text] with [src]!"))
+ add_attack_logs(P.firer, src, "hit by [P.type] but got parried by [src]")
+ return -1
+ owner.visible_message(SPAN_DANGER("[owner] blocks [attack_text] with [src]!"))
+ playsound(src, 'sound/weapons/effects/ric3.ogg', 100, TRUE)
+ return TRUE
+ return TRUE
+
+/obj/item/energy_shuriken
+ name = "energy shuriken"
+ desc = "An ancient weapon, redefined. The blade hums with an electric edge, using itself as fuel."
+ icon = 'icons/obj/weapons/melee.dmi'
+ icon_state = "energy-shuriken"
+ inhand_icon_state = "eshield0"
+ lefthand_file = 'icons/mob/inhands/weapons_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons_righthand.dmi'
+ force = 5
+ throwforce = 30 // This is never used on mobs since this has a 100% embed chance.
+ throw_speed = 4
+ embedded_pain_multiplier = 4
+ w_class = WEIGHT_CLASS_SMALL
+ embed_chance = 100
+ embedded_fall_chance = 0
+ sharp = TRUE
+ resistance_flags = FIRE_PROOF
+
+/obj/item/energy_shuriken/Initialize(mapload)
+ . = ..()
+ // Only lasts so long. Delete self after some time.
+ addtimer(CALLBACK(src, PROC_REF(qdel), src), 30 SECONDS)
+
+/obj/item/energy_shuriken/throw_impact(atom/target)
+ . = ..()
+ if(!ishuman(target))
+ return
+ var/mob/living/carbon/human/H = target
+ H.apply_damage(60, STAMINA)
+
+/obj/item/shuriken_printer
+ name = "shuriken printer"
+ desc = "An advanced, tiny autofabricator that slowly creates and stores energy shurikens."
+ icon = 'icons/obj/weapons/melee.dmi'
+ icon_state = "shuriken-pouch"
+ inhand_icon_state = "eshield0"
+ lefthand_file = 'icons/mob/inhands/weapons_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons_righthand.dmi'
+ origin_tech = "combat=6;syndicate=5"
+ force = 5
+ slot_flags = ITEM_SLOT_BELT
+ w_class = WEIGHT_CLASS_SMALL
+ resistance_flags = FIRE_PROOF
+
+ new_attack_chain = TRUE
+ /// What is the maximum number of energy shurikens the printer can hold?
+ var/maximum_stars = 4
+ /// What is the current number of energy shurikens the printer is holding?
+ var/current_stars = 2
+ /// How long the cooldown is to print more shurikens
+ var/printing_time = 30 SECONDS
+ /// Timer used to print stars
+ var/fabrication_timer
+
+/obj/item/shuriken_printer/Initialize(mapload)
+ . = ..()
+ fabrication_timer = addtimer(CALLBACK(src, PROC_REF(print_star)), 30 SECONDS, TIMER_LOOP | TIMER_STOPPABLE)
+
+/obj/item/shuriken_printer/examine(mob/user)
+ . = ..()
+ . += SPAN_NOTICE("Alt-click to draw an energy shuriken!")
+ . += SPAN_NOTICE("It has [current_stars] stored.")
+
+/obj/item/shuriken_printer/AltClick(mob/living/carbon/user, modifiers)
+ if(!iscarbon(user))
+ return ..()
+ if(current_stars < 1)
+ return ..()
+ current_stars--
+ var/obj/item/energy_shuriken/star = new /obj/item/energy_shuriken(get_turf(src), src)
+ user.put_in_hands(star)
+ to_chat(user, SPAN_NOTICE("You draw [star] from [src]."))
+ user.throw_mode_on()
+
+/obj/item/shuriken_printer/proc/print_star()
+ if(current_stars < maximum_stars)
+ current_stars++
+
+/obj/item/gun/energy/kinetic_accelerator/energy_net
+ name = "energy net projector"
+ desc = "A non-lethal weapon favored by the Spider Clan. Targets stunned by this weapon will be trapped in an energy net for extraction."
+ icon_state = "energy-net-gun"
+ inhand_icon_state = "energy-net-gun"
+ w_class = WEIGHT_CLASS_SMALL
+ materials = list(MAT_METAL=2000)
+ origin_tech = "combat=6;magnets=4;syndicate=4"
+ suppressed = TRUE
+ ammo_type = list(/obj/item/ammo_casing/energy/net)
+ unique_rename = FALSE
+ overheat_time = 3 SECONDS
+ holds_charge = TRUE
+ unique_frequency = TRUE
+ can_flashlight = FALSE
+ max_mod_capacity = 0
+ empty_state = "energy-net-gun_empty"
+ can_holster = TRUE
+
+/obj/item/gun/energy/kinetic_accelerator/energy_net/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_SILENT_INSERTION, ROUNDSTART_TRAIT)
+
+/obj/item/ammo_casing/energy/net
+ projectile_type = /obj/projectile/energy/net
+ muzzle_flash_color = null
+ muzzle_flash_effect = /obj/effect/temp_visual/target_angled/muzzle_flash
+ select_name = "energy net"
+ e_cost = 500
+ fire_sound = 'sound/weapons/bolathrow.ogg'
+
+/obj/projectile/energy/net
+ name = "energy net"
+ icon_state = "net-end"
+ nodamage = 1
+ stamina = 60
+ knockdown = 4 SECONDS
+ speed = 1.5
+
+/obj/projectile/energy/net/fire(setAngle)
+ if(firer)
+ firer.Beam(src, icon_state = "net-beam", time = INFINITY, maxdistance = INFINITY, beam_type = /obj/effect/ebeam/floor)
+ return ..()
+
+/obj/projectile/energy/net/on_hit(atom/target, blocked, hit_zone)
+ . = ..()
+ if(!firer)
+ return
+ if(!ishuman(target))
+ return
+ var/mob/living/carbon/human/H = target
+ if(!H.stam_paralyzed)
+ return
+ var/obj/item/restraints/handcuffs/cable/zipties/cuff = new()
+ cuff.apply_cuffs(H, firer)
+ var/obj/structure/bed/energy_net/net = new(H.loc)
+ net.buckle_mob(H, TRUE)
+
+/obj/structure/bed/energy_net
+ name = "energy net"
+ desc = "A pulsing array of green energy that can securely hold a victim in place."
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "e-net"
+
+/obj/structure/bed/energy_net/user_unbuckle_mob(mob/living/buckled_mob, mob/user)
+ if(!has_buckled_mobs())
+ Destroy() // We don't exist if nothing is being held
+ for(var/buck in buckled_mobs) // No net, no buckles
+ var/mob/living/M = buck
+ if(M != user)
+ if(!do_after(user, 10 SECONDS, target = src))
+ if(M && M.buckled)
+ to_chat(user, SPAN_WARNING("You fail to release [M] from \the [src]!"))
+ return
+ M.visible_message(SPAN_NOTICE("[user.name] pulls [M.name] free from the energy net!"),\
+ SPAN_NOTICE("[user.name] pulls you free from the energy net."),\
+ SPAN_ITALICS("You hear a small zap..."))
+ unbuckle_mob(M)
+ Destroy()
+ if(!M.buckled)
+ return
+ M.visible_message(SPAN_WARNING("[M.name] breaks free from the energy net!"),\
+ SPAN_NOTICE("You break free from the energy net!"),\
+ SPAN_ITALICS("You hear a small zap..."))
+ unbuckle_mob(M)
+ Destroy()
diff --git a/code/modules/ninja/suit/ninja_gloves.dm b/code/modules/ninja/suit/ninja_gloves.dm
deleted file mode 100644
index 8291930fdf2..00000000000
--- a/code/modules/ninja/suit/ninja_gloves.dm
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-/obj/item/clothing/gloves/space_ninja
- desc = "These nano-enhanced gloves insulate from electricity and provide fire resistance."
- name = "ninja gloves"
- icon_state = "s-ninja"
- siemens_coefficient = 0
- pickpocket = 1
- cold_protection = HANDS
- min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
- heat_protection = HANDS
- max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
diff --git a/code/modules/ninja/suit/ninja_head.dm b/code/modules/ninja/suit/ninja_head.dm
deleted file mode 100644
index 6b61ac93d42..00000000000
--- a/code/modules/ninja/suit/ninja_head.dm
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-/obj/item/clothing/head/helmet/space/space_ninja
- desc = "What may appear to be a simple black garment is in fact a highly sophisticated nano-weave helmet. Standard issue ninja gear."
- name = "ninja hood"
- icon_state = "s-ninja"
- inhand_icon_state = "s-ninja_hood"
- flags_inv = HIDEEARS|HIDEEYES|HIDEFACE
- armor = list(MELEE = 75, BULLET = 50, LASER = 20, ENERGY = 10, BOMB = 20, RAD = 15, FIRE = INFINITY, ACID = INFINITY)
- blockTracking = 1
diff --git a/code/modules/ninja/suit/ninja_mask.dm b/code/modules/ninja/suit/ninja_mask.dm
deleted file mode 100644
index 9ea3e36c524..00000000000
--- a/code/modules/ninja/suit/ninja_mask.dm
+++ /dev/null
@@ -1,28 +0,0 @@
-
-/*
-
-Contents:
-- The Ninja Space Mask
-
-*/
-
-/obj/item/clothing/mask/gas/space_ninja
- name = "ninja mask"
- desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement."
- icon_state = "s-ninja(norm)"
- inhand_icon_state = "s-ninja_mask"
- sprite_sheets = list(
- "Vox" = 'icons/mob/clothing/species/vox/mask.dmi',
- "Unathi" = 'icons/mob/clothing/species/unathi/mask.dmi',
- "Tajaran" = 'icons/mob/clothing/species/tajaran/mask.dmi',
- "Vulpkanin" = 'icons/mob/clothing/species/vulpkanin/mask.dmi'
- )
- var/obj/item/voice_changer/voice_changer
-
-/obj/item/clothing/mask/gas/space_ninja/Initialize(mapload)
- . = ..()
- voice_changer = new(src)
-
-/obj/item/clothing/mask/gas/space_ninja/Destroy()
- QDEL_NULL(voice_changer)
- return ..()
diff --git a/code/modules/ninja/suit/ninja_shoes.dm b/code/modules/ninja/suit/ninja_shoes.dm
deleted file mode 100644
index 5095058efc1..00000000000
--- a/code/modules/ninja/suit/ninja_shoes.dm
+++ /dev/null
@@ -1,12 +0,0 @@
-
-/obj/item/clothing/shoes/space_ninja
- name = "ninja shoes"
- desc = "A pair of running shoes. Excellent for running and even better for smashing skulls."
- icon_state = "s-ninja"
- permeability_coefficient = 0.01
- no_slip = TRUE
- armor = list(MELEE = 75, BULLET = 50, LASER = 20, ENERGY = 10, BOMB = 20, RAD = 15, FIRE = INFINITY, ACID = INFINITY)
- cold_protection = FEET
- min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
- heat_protection = FEET
- max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT
diff --git a/code/modules/ninja/suit/ninja_suit.dm b/code/modules/ninja/suit/ninja_suit.dm
deleted file mode 100644
index 8d5a1b2c5f3..00000000000
--- a/code/modules/ninja/suit/ninja_suit.dm
+++ /dev/null
@@ -1,87 +0,0 @@
-
-/*
-
-Contents:
-- The Ninja Space Suit
-- Ninja Space Suit Procs
-
-*/
-
-/obj/item/clothing/suit/space/space_ninja
- name = "ninja suit"
- desc = "A unique, vacuum-proof suit of nano-enhanced armor designed specifically for Spider Clan assassins."
- icon_state = "s-ninja"
- inhand_icon_state = "s-ninja_suit"
- allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/melee/baton, /obj/item/tank/internals, /obj/item/stock_parts/cell)
- slowdown = 0
- flags_inv = HIDEJUMPSUIT|HIDETAIL
- armor = list(MELEE = 75, BULLET = 50, LASER = 20, ENERGY = 10, BOMB = 20, RAD = 15, FIRE = INFINITY, ACID = INFINITY)
-
- var/suitActive = 0
- var/suitBusy = 0
-
- var/obj/item/stock_parts/cell/suitCell
- var/obj/item/clothing/head/helmet/space/space_ninja/suitHood
- var/obj/item/clothing/gloves/space_ninja/suitGloves
- var/obj/item/clothing/shoes/space_ninja/suitShoes
- var/obj/item/clothing/mask/gas/space_ninja/suitMask
- var/mob/living/carbon/human/suitOccupant
-
-/obj/item/clothing/suit/space/space_ninja/get_cell()
- return suitCell
-
-/obj/item/clothing/suit/space/space_ninja/proc/toggle_suit_lock(mob/living/carbon/human/user)
- if(!suitActive)
- if(!istype(user.wear_suit, /obj/item/clothing/suit/space/space_ninja))
- to_chat(user, "ERROR: Unable to locate user.\nABORTING...")
- return 0
- if(!istype(user.head, /obj/item/clothing/head/helmet/space/space_ninja))
- to_chat(user, "ERROR: Unable to locate hood.\nABORTING...")
- return 0
- if(!istype(user.gloves, /obj/item/clothing/gloves/space_ninja))
- to_chat(user, "ERROR: Unable to locate gloves.\nABORTING...")
- return 0
- if(!istype(user.shoes, /obj/item/clothing/shoes/space_ninja))
- to_chat(user, "ERROR: Unable to locate foot gear.\nABORTING...")
- return 0
- if(!istype(user.wear_mask, /obj/item/clothing/mask/gas/space_ninja))
- to_chat(user, "ERROR: Unable to locate mask.\nABORTING...")
- return 0
-
- suitHood = user.head
- suitMask = user.wear_mask
- suitGloves = user.gloves
- suitShoes = user.shoes
- suitOccupant = user
-
- set_nodrop(TRUE)
- suitHood.set_nodrop(TRUE)
- suitMask.set_nodrop(TRUE)
- suitGloves.set_nodrop(TRUE)
- suitGloves.pickpocket = 1
- suitShoes.set_nodrop(TRUE)
- suitShoes.slowdown = -2
-
- icon_state = (user.gender == MALE ? "s-ninjan" : "s-ninjanf")
- suitGloves.icon_state = "s-ninjan"
-
- return 1
-
- else
- set_nodrop(FALSE)
- suitHood.set_nodrop(FALSE)
- suitMask.set_nodrop(FALSE)
- suitGloves.set_nodrop(FALSE)
- suitGloves.pickpocket = 0
- suitShoes.set_nodrop(FALSE)
- suitShoes.slowdown = -1
- icon_state = "s-ninja"
- suitGloves.icon_state = "s-ninja"
-
- suitHood = null
- suitMask = null
- suitGloves = null
- suitShoes = null
- suitOccupant = null
-
- return 1
diff --git a/code/modules/ninja/suit/suit_initialisation.dm b/code/modules/ninja/suit/suit_initialisation.dm
deleted file mode 100644
index 94110b9dda5..00000000000
--- a/code/modules/ninja/suit/suit_initialisation.dm
+++ /dev/null
@@ -1,92 +0,0 @@
-
-//Verbs link to procs because verb-like procs have a bug which prevents their use if the arguments are not readily referenced.
-//^ Old coder words may be false these days, Not taking the risk for now.
-
-/obj/item/clothing/suit/space/space_ninja/verb/toggle_suit()
- set category = "Space Ninja - Equipment"
- set name = "Toggle Suit"
-
- if(usr.mind.special_role == "Ninja")
- if(suitBusy)
- to_chat(usr, "ERROR: Suit systems busy, cannot initiate [suitActive ? "de-activation" : "activation"] protocols at this time.")
- return
-
- suitBusy = 1
-
- if(suitActive && (tgui_alert(usr, "Confirm suit systems shutdown? This cannot be halted once it has started.", "Confirm Shutdown", list("Yes", "No")) == "Yes"))
- to_chat(usr, SPAN_DARKMBLUE("Now de-initializing..."))
-
- sleep(15)
- to_chat(usr, SPAN_DARKMBLUE("Logging off, [usr.real_name]. Shutting down SpiderOS."))
-
- sleep(10)
- to_chat(usr, SPAN_DARKMBLUE("Primary system status: OFFLINE.\nBackup system status: OFFLINE."))
-
- sleep(5)
- to_chat(usr, SPAN_DARKMBLUE("VOID-shift device status: OFFLINE.\nCLOAK-tech device status: OFFLINE."))
- //TODO: Shut down any active abilities
-
- sleep(10)
- to_chat(usr, SPAN_DARKMBLUE("Disconnecting neural-net interface... Success."))
-
- QDEL_NULL(usr.hud_used)
- usr.create_mob_hud()
- usr.regenerate_icons()
-
- sleep(5)
- to_chat(usr, SPAN_DARKMBLUE("Disengaging neural-net interface... Success."))
-
- sleep(10)
- to_chat(usr, SPAN_DARKMBLUE("Unsecuring external locking mechanism...\nNeural-net abolished.\nOperation status: FINISHED."))
- //TODO: Grant verbs
- toggle_suit_lock(usr)
- usr.regenerate_icons()
- suitBusy = 0
- suitActive = 0
-
- else if(!suitActive) // Activate the suit.
- to_chat(usr, SPAN_DARKMBLUE("Now initializing..."))
-
- sleep(15)
- to_chat(usr, "Now establishing neural-net interface...")
- if(usr.mind.special_role != "Ninja")
- to_chat(usr, "FĆAL �Rr�R: ŧer nt recgnized, c-cntr-r䣧-ç äcked.")
- return
-
- sleep(10)
- to_chat(usr, SPAN_DARKMBLUE("Neural-net established. Now monitoring brainwave pattern. \nBrainwave pattern GREEN, proceeding."))
-
- sleep(10)
- to_chat(usr, SPAN_DARKMBLUE("Securing external locking mechanism..."))
- if(!toggle_suit_lock(usr))
- return
-
- sleep(5)
- to_chat(usr, SPAN_DARKMBLUE("Suit secured, extending neural-net interface..."))
-
- QDEL_NULL(usr.hud_used)
- usr.hud_used = new /datum/hud/human(usr, 'icons/mob/screen_ninja.dmi', "#ffffff", 255)
- if(usr.hud_used)
- usr.hud_used.show_hud(usr.hud_used.hud_version)
- usr.regenerate_icons()
-
- sleep(10)
- to_chat(usr, SPAN_DARKMBLUE("VOID-shift device status: ONLINE.\nCLOAK-tech device status:ONLINE"))
-
- sleep(5)
- to_chat(usr, SPAN_DARKMBLUE("Primary system status: ONLINE.\nBackup system status: ONLINE."))
- if(suitCell)
- to_chat(usr, SPAN_DARKMBLUE("Current energy capacity: [suitCell.charge]/[suitCell.maxcharge]."))
-
- sleep(10)
- to_chat(usr, SPAN_DARKMBLUE("All systems operational. Welcome to SpiderOS, [usr.real_name]."))
- //TODO: Grant ninja verbs here.
- suitBusy = 0
- suitActive = 1
-
- else
- suitBusy = 0
- to_chat(usr, SPAN_DARKMBLUE("NOTICE: Suit de-activation protocols aborted."))
- else
- to_chat(usr, "FĆAL �Rr�R: ŧer nt recgnized, c-cntr-r䣧-ç äcked.")
- return
diff --git a/code/modules/power/engines/fission/nuclear_rods.dm b/code/modules/power/engines/fission/nuclear_rods.dm
index afadc317b30..781e69fa19f 100644
--- a/code/modules/power/engines/fission/nuclear_rods.dm
+++ b/code/modules/power/engines/fission/nuclear_rods.dm
@@ -105,6 +105,9 @@
else if(!rad_component)
start_rads()
+/obj/item/nuclear_rod/proc/do_special_effect()
+ return
+
/obj/item/nuclear_rod/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
. = ..()
check_rad_shield()
@@ -164,6 +167,17 @@
craftable = TRUE
materials = list(MAT_METAL = 2000, MAT_URANIUM = 1000)
+/obj/item/nuclear_rod/fuel/uranium_238/spiders
+ name = "spider-clan uranium 238 fuel rod"
+ desc = "A small fuel rod for most NGCR reactors. This one was green webs coating its surface, with tiny spiders skittering along them."
+ w_class = WEIGHT_CLASS_NORMAL
+ craftable = FALSE
+ adjacent_requirements = list()
+
+/obj/item/nuclear_rod/fuel/uranium_238/spiders/do_special_effect()
+ if(prob(5))
+ new /mob/living/basic/spiderling(get_turf(src))
+
/obj/item/nuclear_rod/fuel/weak_thorium
name = "weak thorium fuel rod"
desc = "A specialized fuel rod refined from uranium 238. This rod will last longer than normal, and won't generate as much heat."
diff --git a/code/modules/power/engines/fission/reactor.dm b/code/modules/power/engines/fission/reactor.dm
index 9af1ea11967..da3fd8f13b3 100644
--- a/code/modules/power/engines/fission/reactor.dm
+++ b/code/modules/power/engines/fission/reactor.dm
@@ -605,6 +605,7 @@
final_heat += heat_total
final_power += power_total
chamber.held_rod.durability -= durability_loss
+ chamber.held_rod.do_special_effect()
if(final_heat)
average_heatgen = final_heat / active_chambers
diff --git a/code/modules/power/engines/supermatter/supermatter.dm b/code/modules/power/engines/supermatter/supermatter.dm
index 36ec59663d5..adfacfcca23 100644
--- a/code/modules/power/engines/supermatter/supermatter.dm
+++ b/code/modules/power/engines/supermatter/supermatter.dm
@@ -245,6 +245,9 @@
/// How long has it been since we processed the crystal?
var/tick_counter = 0
+ /// Are we being stolen?
+ var/being_stolen = FALSE
+
/obj/machinery/atmospherics/supermatter_crystal/Initialize(mapload)
. = ..()
@@ -697,12 +700,31 @@
countdown()
return 1
+/obj/machinery/atmospherics/supermatter_crystal/update_overlays()
+ . = ..()
+ overlays.Cut()
+ if(being_stolen)
+ . += "causality_field"
+ color = COLOR_GREEN
+ else
+ color = COLOR_WHITE
+
/obj/machinery/atmospherics/supermatter_crystal/bullet_act(obj/projectile/proj)
var/turf/L = loc
if(!istype(L))
return FALSE
if(!istype(proj, /obj/projectile/beam/emitter/hitscan) && power_changes)
investigate_log("has been hit by [proj] fired by [key_name(proj.firer)]", INVESTIGATE_SUPERMATTER)
+ if(istype(proj, /obj/projectile/energy/net))
+ var/mob/living/carbon/user = proj.firer
+ var/list/obj_list = user.mind.get_all_objectives()
+ for(var/datum/objective/ninja/ninja_obj in obj_list)
+ if(ninja_obj.completed)
+ continue
+ if(!istype(ninja_obj, /datum/objective/ninja/steal_supermatter))
+ continue
+ begin_theft()
+ return FALSE
if(proj.flag != BULLET)
if(power_changes) //This needs to be here I swear
power += proj.damage * bullet_energy
@@ -712,6 +734,32 @@
damage += proj.damage * bullet_energy
return FALSE
+/obj/machinery/atmospherics/supermatter_crystal/proc/begin_theft(mob/living/carbon/thief)
+ if(being_stolen)
+ return
+ message_admins("Ninja [thief] is beginning to steal the Supermatter Crystal.")
+ addtimer(src, CALLBACK(PROC_REF(complete_theft), thief), 3 MINUTES)
+ being_stolen = TRUE
+ update_icon()
+
+/obj/machinery/atmospherics/supermatter_crystal/proc/complete_theft(mob/living/carbon/thief)
+ if(!being_stolen)
+ return
+ if(!thief.mind)
+ return
+ var/list/obj_list = thief.mind.get_all_objectives()
+ if(!length(obj_list))
+ return
+ for(var/datum/objective/ninja/ninja_obj in obj_list)
+ if(ninja_obj.completed)
+ continue
+ if(!istype(ninja_obj, /datum/objective/ninja/steal_supermatter))
+ continue
+ ninja_obj.complete_objective()
+ break
+ message_admins("Ninja [thief] has stolen the Supermatter Crystal.")
+ qdel(src)
+
/obj/machinery/atmospherics/supermatter_crystal/singularity_act()
var/gain = 100
investigate_log("Supermatter shard consumed by singularity.", INVESTIGATE_SINGULO)
@@ -796,6 +844,15 @@
/obj/machinery/atmospherics/supermatter_crystal/item_interaction(mob/living/user, obj/item/used, list/modifiers)
if(!istype(used) || (used.flags & ABSTRACT) || !istype(user))
return ITEM_INTERACT_COMPLETE
+
+ if(being_stolen)
+ to_chat(user, SPAN_WARNING("You begin to very carefully unfasten the net tangled around [src]!"))
+ if(!do_after_once(user, 5 SECONDS, TRUE, src))
+ return ITEM_INTERACT_COMPLETE
+ being_stolen = FALSE
+ update_icon()
+ return ITEM_INTERACT_COMPLETE
+
if(moveable && default_unfasten_wrench(user, used, time = 20))
return ITEM_INTERACT_COMPLETE
diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi
index 4d8a64ffaac..d3a8f599c2f 100644
Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index ef45d2122cc..aa4c1f85250 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi
index 74602be2cd9..8382b7c9150 100644
Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ
diff --git a/icons/mob/clothing/feet.dmi b/icons/mob/clothing/feet.dmi
index 79f3616db91..83673ef7c63 100644
Binary files a/icons/mob/clothing/feet.dmi and b/icons/mob/clothing/feet.dmi differ
diff --git a/icons/mob/clothing/hands.dmi b/icons/mob/clothing/hands.dmi
index 95a804fa631..2d0fa8ded2b 100644
Binary files a/icons/mob/clothing/hands.dmi and b/icons/mob/clothing/hands.dmi differ
diff --git a/icons/mob/clothing/head.dmi b/icons/mob/clothing/head.dmi
index a959bf06733..5d55cdd544a 100644
Binary files a/icons/mob/clothing/head.dmi and b/icons/mob/clothing/head.dmi differ
diff --git a/icons/mob/clothing/mask.dmi b/icons/mob/clothing/mask.dmi
index 02efa70efb0..273875290dd 100644
Binary files a/icons/mob/clothing/mask.dmi and b/icons/mob/clothing/mask.dmi differ
diff --git a/icons/mob/clothing/modsuit/mod_clothing.dmi b/icons/mob/clothing/modsuit/mod_clothing.dmi
index 767533a4faa..2b11edd7f54 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/species/diona_modsuits.dmi b/icons/mob/clothing/modsuit/species/diona_modsuits.dmi
new file mode 100644
index 00000000000..7d61dd7feda
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/diona_modsuits.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/drask_modsuits.dmi b/icons/mob/clothing/modsuit/species/drask_modsuits.dmi
new file mode 100644
index 00000000000..50b7dec3714
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/drask_modsuits.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/grey_helmets.dmi b/icons/mob/clothing/modsuit/species/grey_helmets.dmi
index c3d6d995264..d6c1c0eac3e 100644
Binary files a/icons/mob/clothing/modsuit/species/grey_helmets.dmi and b/icons/mob/clothing/modsuit/species/grey_helmets.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/kidan_modsuits.dmi b/icons/mob/clothing/modsuit/species/kidan_modsuits.dmi
new file mode 100644
index 00000000000..04905c00f7f
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/kidan_modsuits.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/nian_modsuits.dmi b/icons/mob/clothing/modsuit/species/nian_modsuits.dmi
new file mode 100644
index 00000000000..1851eb421a9
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/nian_modsuits.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/skrell_modsuits.dmi b/icons/mob/clothing/modsuit/species/skrell_modsuits.dmi
new file mode 100644
index 00000000000..f8fba2b4140
Binary files /dev/null and b/icons/mob/clothing/modsuit/species/skrell_modsuits.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/taj_modsuits.dmi b/icons/mob/clothing/modsuit/species/taj_modsuits.dmi
index 68504935d6a..9bb93090282 100644
Binary files a/icons/mob/clothing/modsuit/species/taj_modsuits.dmi and b/icons/mob/clothing/modsuit/species/taj_modsuits.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/unathi_modsuits.dmi b/icons/mob/clothing/modsuit/species/unathi_modsuits.dmi
index 0f6a9594e3e..b213f06a4fb 100644
Binary files a/icons/mob/clothing/modsuit/species/unathi_modsuits.dmi and b/icons/mob/clothing/modsuit/species/unathi_modsuits.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/vox_modsuits.dmi b/icons/mob/clothing/modsuit/species/vox_modsuits.dmi
index 8ac2a37e068..b04557af04e 100644
Binary files a/icons/mob/clothing/modsuit/species/vox_modsuits.dmi and b/icons/mob/clothing/modsuit/species/vox_modsuits.dmi differ
diff --git a/icons/mob/clothing/modsuit/species/vulp_modsuits.dmi b/icons/mob/clothing/modsuit/species/vulp_modsuits.dmi
index b6ba4ccc0a1..088ff2a75ee 100644
Binary files a/icons/mob/clothing/modsuit/species/vulp_modsuits.dmi and b/icons/mob/clothing/modsuit/species/vulp_modsuits.dmi differ
diff --git a/icons/mob/clothing/species/diona/gloves.dmi b/icons/mob/clothing/species/diona/gloves.dmi
new file mode 100644
index 00000000000..0f5476d079d
Binary files /dev/null and b/icons/mob/clothing/species/diona/gloves.dmi differ
diff --git a/icons/mob/clothing/species/diona/head.dmi b/icons/mob/clothing/species/diona/head.dmi
new file mode 100644
index 00000000000..6d0730c5b9c
Binary files /dev/null and b/icons/mob/clothing/species/diona/head.dmi differ
diff --git a/icons/mob/clothing/species/diona/mask.dmi b/icons/mob/clothing/species/diona/mask.dmi
new file mode 100644
index 00000000000..f894d3f576c
Binary files /dev/null and b/icons/mob/clothing/species/diona/mask.dmi differ
diff --git a/icons/mob/clothing/species/diona/shoes.dmi b/icons/mob/clothing/species/diona/shoes.dmi
new file mode 100644
index 00000000000..656fc62bb76
Binary files /dev/null and b/icons/mob/clothing/species/diona/shoes.dmi differ
diff --git a/icons/mob/clothing/species/diona/suit.dmi b/icons/mob/clothing/species/diona/suit.dmi
new file mode 100644
index 00000000000..bced7327cfe
Binary files /dev/null and b/icons/mob/clothing/species/diona/suit.dmi differ
diff --git a/icons/mob/clothing/species/drask/gloves.dmi b/icons/mob/clothing/species/drask/gloves.dmi
index 01e46d55f2b..ffb56b05b6e 100644
Binary files a/icons/mob/clothing/species/drask/gloves.dmi and b/icons/mob/clothing/species/drask/gloves.dmi differ
diff --git a/icons/mob/clothing/species/drask/head.dmi b/icons/mob/clothing/species/drask/head.dmi
index afa56c5e6b7..9c2da14f14c 100644
Binary files a/icons/mob/clothing/species/drask/head.dmi and b/icons/mob/clothing/species/drask/head.dmi differ
diff --git a/icons/mob/clothing/species/drask/mask.dmi b/icons/mob/clothing/species/drask/mask.dmi
index 44a7cad1aa0..63f80d8760b 100644
Binary files a/icons/mob/clothing/species/drask/mask.dmi and b/icons/mob/clothing/species/drask/mask.dmi differ
diff --git a/icons/mob/clothing/species/drask/shoes.dmi b/icons/mob/clothing/species/drask/shoes.dmi
index 7758a0b0e11..94c711f3411 100644
Binary files a/icons/mob/clothing/species/drask/shoes.dmi and b/icons/mob/clothing/species/drask/shoes.dmi differ
diff --git a/icons/mob/clothing/species/drask/suit.dmi b/icons/mob/clothing/species/drask/suit.dmi
index 33e4bb7160e..7254ebe88a2 100644
Binary files a/icons/mob/clothing/species/drask/suit.dmi and b/icons/mob/clothing/species/drask/suit.dmi differ
diff --git a/icons/mob/clothing/species/grey/head.dmi b/icons/mob/clothing/species/grey/head.dmi
index 89c99343ecf..08208d5d645 100644
Binary files a/icons/mob/clothing/species/grey/head.dmi and b/icons/mob/clothing/species/grey/head.dmi differ
diff --git a/icons/mob/clothing/species/grey/mask.dmi b/icons/mob/clothing/species/grey/mask.dmi
index a625cb7f24b..5d0d2bbcf3a 100644
Binary files a/icons/mob/clothing/species/grey/mask.dmi and b/icons/mob/clothing/species/grey/mask.dmi differ
diff --git a/icons/mob/clothing/species/grey/suit.dmi b/icons/mob/clothing/species/grey/suit.dmi
index f4ed7e88679..879c923253f 100644
Binary files a/icons/mob/clothing/species/grey/suit.dmi and b/icons/mob/clothing/species/grey/suit.dmi differ
diff --git a/icons/mob/clothing/species/kidan/gloves.dmi b/icons/mob/clothing/species/kidan/gloves.dmi
index 9e9963baf73..cc6ab285852 100644
Binary files a/icons/mob/clothing/species/kidan/gloves.dmi and b/icons/mob/clothing/species/kidan/gloves.dmi differ
diff --git a/icons/mob/clothing/species/kidan/head.dmi b/icons/mob/clothing/species/kidan/head.dmi
index 6e99f871aed..8904bdf9927 100644
Binary files a/icons/mob/clothing/species/kidan/head.dmi and b/icons/mob/clothing/species/kidan/head.dmi differ
diff --git a/icons/mob/clothing/species/kidan/mask.dmi b/icons/mob/clothing/species/kidan/mask.dmi
index 89d446b9b37..86e5dfcff4a 100644
Binary files a/icons/mob/clothing/species/kidan/mask.dmi and b/icons/mob/clothing/species/kidan/mask.dmi differ
diff --git a/icons/mob/clothing/species/kidan/shoes.dmi b/icons/mob/clothing/species/kidan/shoes.dmi
new file mode 100644
index 00000000000..78c3c7e0f55
Binary files /dev/null and b/icons/mob/clothing/species/kidan/shoes.dmi differ
diff --git a/icons/mob/clothing/species/kidan/suit.dmi b/icons/mob/clothing/species/kidan/suit.dmi
index 5bd7a3dd1dc..4c8e020dd07 100644
Binary files a/icons/mob/clothing/species/kidan/suit.dmi and b/icons/mob/clothing/species/kidan/suit.dmi differ
diff --git a/icons/mob/clothing/species/machine/monitor/hood.dmi b/icons/mob/clothing/species/machine/monitor/hood.dmi
index e96062f9507..cd0d52b7579 100644
Binary files a/icons/mob/clothing/species/machine/monitor/hood.dmi and b/icons/mob/clothing/species/machine/monitor/hood.dmi differ
diff --git a/icons/mob/clothing/species/machine/monitor/mask.dmi b/icons/mob/clothing/species/machine/monitor/mask.dmi
new file mode 100644
index 00000000000..6f369e3a169
Binary files /dev/null and b/icons/mob/clothing/species/machine/monitor/mask.dmi differ
diff --git a/icons/mob/clothing/species/nian/head.dmi b/icons/mob/clothing/species/nian/head.dmi
index 6d183b6ffac..1dc428c5a8e 100644
Binary files a/icons/mob/clothing/species/nian/head.dmi and b/icons/mob/clothing/species/nian/head.dmi differ
diff --git a/icons/mob/clothing/species/nian/mask.dmi b/icons/mob/clothing/species/nian/mask.dmi
new file mode 100644
index 00000000000..ba3820578f3
Binary files /dev/null and b/icons/mob/clothing/species/nian/mask.dmi differ
diff --git a/icons/mob/clothing/species/nian/suit.dmi b/icons/mob/clothing/species/nian/suit.dmi
new file mode 100644
index 00000000000..60ad5713e4e
Binary files /dev/null and b/icons/mob/clothing/species/nian/suit.dmi differ
diff --git a/icons/mob/clothing/species/skkulakin/head.dmi b/icons/mob/clothing/species/skkulakin/head.dmi
index 0342016f86c..0ee4a5edbec 100644
Binary files a/icons/mob/clothing/species/skkulakin/head.dmi and b/icons/mob/clothing/species/skkulakin/head.dmi differ
diff --git a/icons/mob/clothing/species/skkulakin/mask.dmi b/icons/mob/clothing/species/skkulakin/mask.dmi
index 129a9e4b309..124828021ce 100644
Binary files a/icons/mob/clothing/species/skkulakin/mask.dmi and b/icons/mob/clothing/species/skkulakin/mask.dmi differ
diff --git a/icons/mob/clothing/species/skrell/head.dmi b/icons/mob/clothing/species/skrell/head.dmi
index 9a97aa6605f..20f40eeaf39 100644
Binary files a/icons/mob/clothing/species/skrell/head.dmi and b/icons/mob/clothing/species/skrell/head.dmi differ
diff --git a/icons/mob/clothing/species/skrell/mask.dmi b/icons/mob/clothing/species/skrell/mask.dmi
index 65f617a604d..60a355b3a50 100644
Binary files a/icons/mob/clothing/species/skrell/mask.dmi and b/icons/mob/clothing/species/skrell/mask.dmi differ
diff --git a/icons/mob/clothing/species/tajaran/head.dmi b/icons/mob/clothing/species/tajaran/head.dmi
index e736cec01ce..9976107156a 100644
Binary files a/icons/mob/clothing/species/tajaran/head.dmi and b/icons/mob/clothing/species/tajaran/head.dmi differ
diff --git a/icons/mob/clothing/species/tajaran/mask.dmi b/icons/mob/clothing/species/tajaran/mask.dmi
index cbbc968481d..0532e2c0eab 100644
Binary files a/icons/mob/clothing/species/tajaran/mask.dmi and b/icons/mob/clothing/species/tajaran/mask.dmi differ
diff --git a/icons/mob/clothing/species/unathi/head.dmi b/icons/mob/clothing/species/unathi/head.dmi
index de9cf4a7713..43af3a3aeaf 100644
Binary files a/icons/mob/clothing/species/unathi/head.dmi and b/icons/mob/clothing/species/unathi/head.dmi differ
diff --git a/icons/mob/clothing/species/unathi/mask.dmi b/icons/mob/clothing/species/unathi/mask.dmi
index 54f560e7a63..377e06fe8de 100644
Binary files a/icons/mob/clothing/species/unathi/mask.dmi and b/icons/mob/clothing/species/unathi/mask.dmi differ
diff --git a/icons/mob/clothing/species/vox/gloves.dmi b/icons/mob/clothing/species/vox/gloves.dmi
index af691ab7a7e..cf8fb5e42df 100644
Binary files a/icons/mob/clothing/species/vox/gloves.dmi and b/icons/mob/clothing/species/vox/gloves.dmi differ
diff --git a/icons/mob/clothing/species/vox/head.dmi b/icons/mob/clothing/species/vox/head.dmi
index 0680c279d0a..73377359c54 100644
Binary files a/icons/mob/clothing/species/vox/head.dmi and b/icons/mob/clothing/species/vox/head.dmi differ
diff --git a/icons/mob/clothing/species/vox/mask.dmi b/icons/mob/clothing/species/vox/mask.dmi
index 26dbe766c39..f070896c8ae 100644
Binary files a/icons/mob/clothing/species/vox/mask.dmi and b/icons/mob/clothing/species/vox/mask.dmi differ
diff --git a/icons/mob/clothing/species/vox/shoes.dmi b/icons/mob/clothing/species/vox/shoes.dmi
index 2a76db03982..30bebe00bd1 100644
Binary files a/icons/mob/clothing/species/vox/shoes.dmi and b/icons/mob/clothing/species/vox/shoes.dmi differ
diff --git a/icons/mob/clothing/species/vox/suit.dmi b/icons/mob/clothing/species/vox/suit.dmi
index 2d3a3d78c9d..64d9b79b2f1 100644
Binary files a/icons/mob/clothing/species/vox/suit.dmi and b/icons/mob/clothing/species/vox/suit.dmi differ
diff --git a/icons/mob/clothing/species/vulpkanin/head.dmi b/icons/mob/clothing/species/vulpkanin/head.dmi
index 7a4a5cc9387..9e29ea702e0 100644
Binary files a/icons/mob/clothing/species/vulpkanin/head.dmi and b/icons/mob/clothing/species/vulpkanin/head.dmi differ
diff --git a/icons/mob/clothing/species/vulpkanin/mask.dmi b/icons/mob/clothing/species/vulpkanin/mask.dmi
index 435b372071a..66ec1f03575 100644
Binary files a/icons/mob/clothing/species/vulpkanin/mask.dmi and b/icons/mob/clothing/species/vulpkanin/mask.dmi differ
diff --git a/icons/mob/clothing/suit.dmi b/icons/mob/clothing/suit.dmi
index 19e5653262f..1cbd0762402 100644
Binary files a/icons/mob/clothing/suit.dmi and b/icons/mob/clothing/suit.dmi differ
diff --git a/icons/mob/hud/antaghud.dmi b/icons/mob/hud/antaghud.dmi
index 83e3258def6..5d522722709 100644
Binary files a/icons/mob/hud/antaghud.dmi and b/icons/mob/hud/antaghud.dmi differ
diff --git a/icons/mob/inhands/guns_lefthand.dmi b/icons/mob/inhands/guns_lefthand.dmi
index 61c5575e430..7a546b1255b 100644
Binary files a/icons/mob/inhands/guns_lefthand.dmi and b/icons/mob/inhands/guns_lefthand.dmi differ
diff --git a/icons/mob/inhands/guns_righthand.dmi b/icons/mob/inhands/guns_righthand.dmi
index b18e637f99a..2d27afcb895 100644
Binary files a/icons/mob/inhands/guns_righthand.dmi and b/icons/mob/inhands/guns_righthand.dmi differ
diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi
index 48b97e2763e..b4e812d2ea6 100644
Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ
diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi
index ebc2e68e7af..71537bfb99b 100644
Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ
diff --git a/icons/obj/clothing/masks.dmi b/icons/obj/clothing/masks.dmi
index 3b91d89b6ec..0ccf371080f 100644
Binary files a/icons/obj/clothing/masks.dmi and b/icons/obj/clothing/masks.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_clothing.dmi b/icons/obj/clothing/modsuit/mod_clothing.dmi
index 37d4316c7c0..70ab6906739 100644
Binary files a/icons/obj/clothing/modsuit/mod_clothing.dmi and b/icons/obj/clothing/modsuit/mod_clothing.dmi differ
diff --git a/icons/obj/clothing/shoes.dmi b/icons/obj/clothing/shoes.dmi
index c10b9396901..b45c8b27b0f 100644
Binary files a/icons/obj/clothing/shoes.dmi and b/icons/obj/clothing/shoes.dmi differ
diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi
index c0f587f592b..3cc01448303 100644
Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index 5e0e58f477e..1803eb32644 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/icons/obj/guns/energy.dmi b/icons/obj/guns/energy.dmi
index 84218f24808..6ca66c5d168 100644
Binary files a/icons/obj/guns/energy.dmi and b/icons/obj/guns/energy.dmi differ
diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi
index ef09725bc90..b0a9d8dd1e1 100644
Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ
diff --git a/icons/obj/weapons/energy_melee.dmi b/icons/obj/weapons/energy_melee.dmi
index e8dea9c0848..a2ea548314b 100644
Binary files a/icons/obj/weapons/energy_melee.dmi and b/icons/obj/weapons/energy_melee.dmi differ
diff --git a/icons/obj/weapons/melee.dmi b/icons/obj/weapons/melee.dmi
index 74dabd82e32..aedd92d5e42 100644
Binary files a/icons/obj/weapons/melee.dmi and b/icons/obj/weapons/melee.dmi differ
diff --git a/paradise.dme b/paradise.dme
index dd2e87f3f94..83c0017f2b2 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -932,6 +932,7 @@
#include "code\game\dna\mutations\mutation_powers.dm"
#include "code\game\gamemodes\game_mode.dm"
#include "code\game\gamemodes\intercept_report.dm"
+#include "code\game\gamemodes\ninja_objectives.dm"
#include "code\game\gamemodes\objective.dm"
#include "code\game\gamemodes\objective_holder.dm"
#include "code\game\gamemodes\objectives_subtypes.dm"
@@ -1875,6 +1876,7 @@
#include "code\modules\antagonists\revolutionary\datum_headrev.dm"
#include "code\modules\antagonists\revolutionary\datum_revolutionary.dm"
#include "code\modules\antagonists\revolutionary\team_revolution.dm"
+#include "code\modules\antagonists\space_ninja\datum_space_ninja.dm"
#include "code\modules\antagonists\survivalist\survivalist.dm"
#include "code\modules\antagonists\traitor\datum_mindslave.dm"
#include "code\modules\antagonists\traitor\datum_traitor.dm"
@@ -2283,6 +2285,7 @@
#include "code\modules\events\sentience.dm"
#include "code\modules\events\slaughterevent.dm"
#include "code\modules\events\solarflare.dm"
+#include "code\modules\events\space_ninja.dm"
#include "code\modules\events\spacevine.dm"
#include "code\modules\events\spider_infestation.dm"
#include "code\modules\events\spider_terror.dm"
@@ -3040,13 +3043,13 @@
#include "code\modules\newscaster\newsfeed_datums.dm"
#include "code\modules\newscaster\obj\newscaster.dm"
#include "code\modules\newscaster\obj\newspaper.dm"
-#include "code\modules\ninja\energy_katana.dm"
-#include "code\modules\ninja\suit\ninja_gloves.dm"
-#include "code\modules\ninja\suit\ninja_head.dm"
-#include "code\modules\ninja\suit\ninja_mask.dm"
-#include "code\modules\ninja\suit\ninja_shoes.dm"
-#include "code\modules\ninja\suit\ninja_suit.dm"
-#include "code\modules\ninja\suit\suit_initialisation.dm"
+#include "code\modules\ninja\ninja_actions.dm"
+#include "code\modules\ninja\ninja_clothes.dm"
+#include "code\modules\ninja\ninja_deployables.dm"
+#include "code\modules\ninja\ninja_outfits.dm"
+#include "code\modules\ninja\ninja_scanner.dm"
+#include "code\modules\ninja\ninja_uplink.dm"
+#include "code\modules\ninja\ninja_weapons.dm"
#include "code\modules\paperwork\carbonpaper.dm"
#include "code\modules\paperwork\clipboard.dm"
#include "code\modules\paperwork\desk_bell.dm"
diff --git a/sound/ambience/antag/ninjaalert.ogg b/sound/ambience/antag/ninjaalert.ogg
new file mode 100644
index 00000000000..2d6847400c0
Binary files /dev/null and b/sound/ambience/antag/ninjaalert.ogg differ