diff --git a/_maps/fluffy_frontier/templates/reebe.dmm b/_maps/fluffy_frontier/templates/reebe.dmm new file mode 100644 index 00000000000..5988b75e04e --- /dev/null +++ b/_maps/fluffy_frontier/templates/reebe.dmm @@ -0,0 +1,10766 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"al" = ( +/obj/machinery/telecomms/relay/preset/reebe, +/turf/open/indestructible/reebe_flooring/filled, +/area/ruin/powered/reebe/city) +"bJ" = ( +/obj/machinery/computer/camera_advanced/ratvar{ + dir = 4 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"bN" = ( +/obj/structure/table/bronze, +/obj/item/clockwork/weapon/brass_sword{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/clockwork/weapon/brass_sword, +/obj/item/clockwork/weapon/brass_sword{ + pixel_x = 4; + pixel_y = -4 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"cg" = ( +/obj/machinery/door/airlock/bronze/clock/glass, +/turf/open/indestructible/reebe_flooring/filled, +/area/ruin/powered/reebe/city) +"cF" = ( +/obj/structure/table/bronze, +/obj/item/clockwork/replica_fabricator, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"dm" = ( +/obj/structure/chair/bronze{ + dir = 4 + }, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"gj" = ( +/obj/structure/table/bronze, +/obj/item/toy/plush/ratplush, +/turf/open/indestructible/reebe_flooring/filled, +/area/ruin/powered/reebe/city) +"gu" = ( +/obj/structure/table/bronze, +/obj/item/clothing/suit/clockwork{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/clothing/suit/clockwork, +/obj/item/clothing/suit/clockwork{ + pixel_x = -4; + pixel_y = -4 + }, +/obj/item/clothing/head/helmet/clockwork{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/clothing/head/helmet/clockwork, +/obj/item/clothing/head/helmet/clockwork{ + pixel_x = -4; + pixel_y = -4 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"gL" = ( +/obj/structure/table/bronze, +/obj/effect/mob_spawn/ghost_role/drone/cogscarab, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"hu" = ( +/obj/structure/chair/bronze{ + dir = 8 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"in" = ( +/obj/structure/table/optable, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"iJ" = ( +/obj/effect/landmark/abscond_marker, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"jN" = ( +/obj/structure/table/bronze, +/obj/item/clockwork/integration_cog{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/clockwork/integration_cog, +/obj/item/clockwork/integration_cog{ + pixel_x = -4; + pixel_y = -4 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"lj" = ( +/obj/effect/spawner/structure/window/clockwork, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"mg" = ( +/obj/structure/table/bronze, +/obj/item/clockwork/clockwork_slab, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"mL" = ( +/turf/open/indestructible/reebe_void, +/area/ruin/powered/reebe/space) +"nw" = ( +/obj/structure/chair/bronze{ + dir = 4 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"ok" = ( +/obj/structure/fans/tiny/invisible, +/turf/open/indestructible/reebe_void/void_edge, +/area/ruin/powered/reebe/space) +"oH" = ( +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"pc" = ( +/obj/structure/destructible/clockwork/the_ark, +/turf/open/indestructible/reebe_flooring/filled, +/area/ruin/powered/reebe/city) +"qI" = ( +/turf/open/indestructible/reebe_void/spawning, +/area/ruin/powered/reebe/space) +"sa" = ( +/obj/machinery/computer/operating/clockwork{ + dir = 1 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"sN" = ( +/obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"tr" = ( +/turf/closed/wall/clockwork/reebe, +/area/ruin/powered/reebe/city) +"tU" = ( +/obj/structure/chair/bronze{ + dir = 1 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"uF" = ( +/obj/structure/table/bronze, +/obj/item/stack/sheet/bronze/thirty, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"ve" = ( +/obj/effect/servant_blocker, +/obj/structure/lattice/catwalk/clockwork, +/turf/open/indestructible/reebe_void/walkable, +/area/ruin/powered/reebe/city) +"zN" = ( +/obj/structure/table/bronze, +/obj/item/clockwork/weapon/brass_spear{ + pixel_y = 4; + pixel_x = -4 + }, +/obj/item/clockwork/weapon/brass_spear, +/obj/item/clockwork/weapon/brass_spear{ + pixel_y = -4; + pixel_x = 4 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"zR" = ( +/obj/structure/table/bronze, +/obj/item/storage/medkit/advanced, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"Ao" = ( +/obj/structure/table/bronze, +/obj/item/clothing/glasses/clockwork/wraith_spectacles, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"AF" = ( +/obj/machinery/door/airlock/bronze/clock, +/turf/open/indestructible/reebe_flooring/filled, +/area/ruin/powered/reebe/city) +"Bv" = ( +/obj/machinery/computer/camera_advanced/ratvar, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"BT" = ( +/obj/effect/landmark/late_cog_portals, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"CE" = ( +/obj/structure/fluff/clockwork/alloy_shards, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"En" = ( +/obj/structure/table/bronze, +/obj/item/storage/belt/utility/clock{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/storage/belt/utility/clock, +/obj/item/storage/belt/utility/clock{ + pixel_x = -4; + pixel_y = -4 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"EK" = ( +/obj/structure/destructible/clockwork/eminence_beacon, +/turf/open/indestructible/reebe_flooring/filled, +/area/ruin/powered/reebe/city) +"Fq" = ( +/obj/structure/table/bronze, +/obj/item/radio/intercom/reebe, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"FD" = ( +/obj/effect/landmark/abscond_marker, +/turf/open/indestructible/reebe_flooring/filled, +/area/ruin/powered/reebe/city) +"FK" = ( +/obj/structure/table/bronze, +/obj/item/clothing/suit/clockwork/cloak, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"FY" = ( +/obj/structure/table/bronze, +/obj/item/clothing/glasses/clockwork/judicial_visor, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"Gg" = ( +/obj/machinery/door/airlock/bronze/clock/glass, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"HT" = ( +/obj/structure/table/bronze, +/obj/item/storage/medkit/regular, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"IX" = ( +/obj/structure/fluff/clockwork/alloy_shards/large, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"Kr" = ( +/obj/structure/table/bronze, +/obj/item/clockwork/weapon/brass_battlehammer{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/clockwork/weapon/brass_battlehammer, +/obj/item/clockwork/weapon/brass_battlehammer{ + pixel_x = 4; + pixel_y = -4 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"Lo" = ( +/obj/machinery/sleeper/clockwork{ + dir = 8 + }, +/turf/open/indestructible/reebe_flooring/filled, +/area/ruin/powered/reebe/city) +"Mf" = ( +/obj/structure/table/bronze, +/obj/item/storage/toolbox/brass/surgery, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"Ni" = ( +/turf/open/indestructible/reebe_flooring/filled, +/area/ruin/powered/reebe/city) +"OA" = ( +/obj/structure/fluff/clockwork/alloy_shards/medium_gearbit, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"OL" = ( +/obj/structure/destructible/clockwork/gear_base/stargazer, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"OQ" = ( +/obj/machinery/computer/camera_advanced/ratvar{ + dir = 8 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"OX" = ( +/obj/machinery/sleeper/clockwork{ + dir = 8 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"QA" = ( +/obj/structure/fluff/clockwork/alloy_shards/small, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"Ro" = ( +/obj/structure/destructible/clockwork/sigil/transmission, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"Rs" = ( +/obj/structure/table/bronze, +/obj/item/clothing/gloves/clockwork, +/obj/item/clothing/gloves/clockwork, +/obj/item/clothing/gloves/clockwork, +/obj/item/clothing/gloves/clockwork, +/obj/item/clothing/gloves/clockwork, +/obj/item/gun/ballistic/bow/clockwork, +/obj/item/gun/ballistic/bow/clockwork, +/obj/item/gun/ballistic/bow/clockwork, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"To" = ( +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"TL" = ( +/obj/structure/table/bronze, +/obj/item/clothing/suit/clockwork/speed, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"Vi" = ( +/obj/structure/fluff/clockwork/alloy_shards/medium, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"VS" = ( +/turf/open/indestructible/reebe_void/spawning/lattices, +/area/ruin/powered/reebe/space) +"WL" = ( +/obj/structure/chair/bronze{ + dir = 8 + }, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"Xr" = ( +/obj/structure/chair/bronze{ + dir = 1 + }, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"XZ" = ( +/obj/machinery/door/airlock/bronze/clock, +/turf/open/indestructible/reebe_flooring, +/area/ruin/powered/reebe/city) +"ZE" = ( +/obj/structure/table/bronze, +/obj/item/clockwork/replica_fabricator{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/clockwork/replica_fabricator, +/obj/item/clockwork/replica_fabricator{ + pixel_x = -4; + pixel_y = -4 + }, +/turf/open/indestructible/reebe_flooring/flat, +/area/ruin/powered/reebe/city) +"ZW" = ( +/obj/structure/fluff/clockwork/alloy_shards, +/turf/open/indestructible/reebe_flooring/filled, +/area/ruin/powered/reebe/city) + +(1,1,1) = {" +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +"} +(2,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(3,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(4,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(5,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(6,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(7,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(8,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(9,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(10,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(11,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(12,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(13,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(14,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(15,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(16,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +tr +tr +tr +tr +lj +lj +lj +tr +tr +tr +tr +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(17,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(18,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +BT +oH +oH +oH +BT +oH +oH +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(19,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +oH +oH +oH +oH +oH +oH +oH +BT +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(20,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +BT +oH +oH +oH +oH +oH +BT +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(21,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +lj +lj +tr +tr +tr +XZ +tr +tr +tr +lj +lj +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(22,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(23,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(24,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +tr +tr +lj +lj +lj +tr +tr +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +tr +tr +lj +lj +lj +tr +tr +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(25,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(26,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(27,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +BT +oH +BT +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +qI +qI +qI +qI +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +qI +qI +qI +qI +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +BT +BT +oH +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(28,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +oH +oH +oH +BT +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +qI +qI +qI +qI +VS +VS +VS +tr +lj +lj +Gg +lj +lj +tr +VS +VS +VS +qI +qI +qI +qI +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(29,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +BT +oH +oH +oH +tr +oH +tr +VS +VS +qI +qI +qI +qI +qI +qI +qI +qI +qI +tr +lj +lj +tr +oH +oH +oH +oH +oH +tr +lj +lj +tr +qI +qI +qI +qI +qI +qI +qI +qI +qI +VS +VS +tr +oH +tr +oH +BT +oH +BT +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(30,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +BT +oH +oH +oH +oH +XZ +oH +oH +oH +tr +VS +qI +qI +qI +qI +qI +qI +tr +lj +lj +tr +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +tr +lj +lj +tr +qI +qI +qI +qI +qI +qI +VS +tr +oH +oH +oH +XZ +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(31,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +tr +oH +oH +oH +oH +oH +tr +qI +qI +qI +qI +tr +tr +tr +oH +oH +oH +oH +OA +oH +oH +oH +oH +oH +oH +QA +oH +oH +oH +oH +oH +tr +tr +tr +qI +qI +qI +qI +tr +oH +oH +oH +oH +oH +tr +oH +oH +BT +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(32,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +tr +lj +lj +tr +oH +oH +oH +oH +oH +oH +oH +tr +qI +tr +tr +tr +oH +oH +oH +oH +oH +oH +oH +oH +oH +ve +ve +ve +oH +oH +oH +oH +oH +oH +oH +oH +oH +tr +tr +tr +qI +tr +oH +oH +oH +oH +oH +oH +oH +tr +lj +lj +lj +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(33,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +oH +oH +oH +oH +tr +tr +oH +oH +oH +oH +oH +oH +oH +ve +ve +ve +ve +oH +oH +oH +ve +ve +ve +ve +oH +oH +oH +oH +oH +oH +oH +tr +tr +oH +oH +oH +oH +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(34,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +oH +oH +lj +oH +oH +oH +oH +QA +oH +oH +ve +ve +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +ve +ve +oH +oH +oH +oH +oH +oH +oH +lj +oH +oH +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(35,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +Gg +oH +oH +oH +oH +oH +oH +ve +ve +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +ve +ve +oH +oH +oH +Vi +oH +oH +Gg +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(36,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +qI +tr +oH +lj +oH +OA +oH +oH +oH +ve +ve +oH +oH +oH +oH +oH +oH +oH +IX +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +ve +ve +oH +oH +oH +oH +oH +lj +oH +tr +qI +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(37,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +qI +qI +tr +oH +oH +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +oH +oH +tr +qI +qI +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(38,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +qI +tr +tr +oH +oH +oH +oH +ve +oH +oH +oH +CE +oH +oH +oH +oH +oH +oH +Ni +Ni +Ni +Ni +Ni +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +oH +tr +tr +qI +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(39,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +qI +tr +oH +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +oH +oH +oH +Ni +Ni +oH +oH +oH +oH +oH +ZW +Ni +oH +oH +oH +oH +oH +oH +IX +oH +oH +ve +oH +oH +oH +oH +tr +qI +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(40,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +tr +tr +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +oH +oH +Ni +Ni +oH +oH +oH +oH +oH +oH +oH +oH +oH +Ni +Ni +oH +oH +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +tr +tr +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(41,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +qI +qI +tr +oH +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +oH +Ni +oH +oH +oH +oH +oH +oH +tr +oH +oH +oH +oH +oH +oH +Ni +oH +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +oH +tr +qI +qI +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(42,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +qI +qI +qI +tr +tr +oH +oH +oH +ve +oH +oH +oH +OA +oH +oH +oH +Ni +oH +Ni +tr +tr +oH +tr +tr +gj +tr +tr +oH +tr +tr +Ni +oH +Ni +oH +oH +Vi +oH +oH +oH +oH +ve +oH +oH +oH +tr +tr +qI +qI +qI +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(43,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +qI +qI +qI +lj +oH +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +Ni +oH +Ni +AF +Ni +To +tr +Rs +Ao +To +TL +gu +tr +To +Ni +AF +Ni +oH +Ni +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +oH +lj +qI +qI +qI +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(44,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +qI +qI +qI +lj +oH +oH +oH +ve +oH +oH +oH +oH +Vi +oH +Ni +oH +Ni +lj +Ni +oH +To +tr +FY +oH +oH +oH +FK +tr +To +oH +Ni +lj +Ni +oH +Ni +oH +oH +oH +oH +oH +oH +ve +oH +QA +oH +lj +qI +qI +qI +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(45,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +tr +lj +lj +tr +tr +VS +VS +VS +VS +VS +VS +VS +tr +tr +oH +oH +oH +ve +oH +oH +oH +oH +oH +Ni +oH +Ni +AF +Ni +To +oH +Ni +cg +Ni +oH +OL +oH +Ni +cg +Ni +oH +To +Ni +AF +Ni +oH +Ni +oH +oH +oH +oH +oH +ve +oH +oH +oH +tr +tr +VS +VS +VS +VS +VS +VS +VS +tr +tr +lj +lj +tr +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(46,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +lj +VS +VS +VS +VS +VS +VS +VS +lj +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +Ni +oH +tr +To +oH +oH +oH +uF +tr +Kr +oH +oH +oH +ZE +tr +mg +oH +oH +oH +To +tr +oH +Ni +oH +CE +oH +oH +oH +oH +ve +oH +oH +oH +lj +VS +VS +VS +VS +VS +VS +VS +lj +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(47,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +lj +VS +VS +VS +VS +VS +VS +VS +lj +oH +oH +oH +ve +oH +oH +oH +oH +oH +Ni +oH +oH +tr +To +To +Ni +mg +gL +tr +zN +bN +Ni +jN +En +tr +cF +uF +Ni +To +To +tr +oH +oH +Ni +oH +oH +oH +oH +oH +ve +oH +oH +oH +lj +VS +VS +VS +VS +VS +VS +VS +lj +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(48,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +BT +oH +tr +tr +tr +tr +tr +tr +tr +tr +tr +oH +oH +oH +ve +oH +oH +oH +oH +oH +Ni +oH +oH +oH +tr +tr +cg +tr +tr +tr +tr +lj +cg +lj +tr +tr +tr +tr +cg +tr +tr +oH +oH +oH +Ni +oH +oH +oH +oH +oH +ve +oH +oH +oH +tr +tr +tr +tr +tr +tr +tr +tr +tr +oH +oH +oH +BT +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(49,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +BT +oH +tr +oH +oH +oH +oH +oH +oH +lj +oH +oH +oH +oH +ve +oH +oH +oH +oH +Ni +oH +oH +oH +tr +Bv +tU +Ni +bJ +bJ +tr +iJ +oH +FD +oH +iJ +tr +Fq +Fq +Ni +Fq +Fq +tr +oH +oH +oH +Ni +oH +oH +oH +oH +ve +oH +oH +oH +oH +lj +oH +oH +oH +oH +oH +oH +tr +BT +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(50,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +oH +oH +tr +oH +oH +oH +oH +oH +oH +lj +oH +oH +oH +ve +oH +oH +oH +oH +oH +Ni +oH +oH +oH +tr +Bv +Xr +oH +WL +hu +lj +oH +To +To +To +oH +lj +hu +WL +oH +WL +hu +tr +oH +oH +oH +Ni +oH +oH +oH +oH +oH +ve +oH +oH +oH +lj +oH +oH +oH +oH +oH +oH +tr +oH +oH +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(51,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +oH +oH +XZ +oH +oH +oH +oH +oH +oH +Gg +oH +oH +oH +ve +oH +oH +IX +oH +oH +Ni +oH +oH +tr +EK +To +oH +To +oH +Ni +cg +FD +To +pc +To +FD +cg +Ni +Ro +sN +oH +To +al +tr +oH +oH +Ni +oH +oH +oH +CE +oH +ve +oH +oH +oH +Gg +oH +oH +oH +oH +oH +oH +XZ +oH +oH +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(52,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +BT +oH +oH +oH +tr +oH +oH +oH +oH +oH +oH +lj +oH +oH +oH +ve +oH +oH +oH +oH +oH +Ni +oH +oH +oH +tr +Bv +Xr +oH +dm +nw +lj +oH +To +To +To +oH +lj +nw +dm +oH +dm +nw +tr +oH +oH +oH +Ni +oH +oH +oH +oH +oH +ve +oH +oH +oH +lj +oH +oH +oH +oH +oH +oH +tr +oH +oH +BT +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(53,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +BT +oH +tr +oH +oH +oH +oH +oH +oH +lj +oH +oH +oH +oH +ve +oH +oH +oH +oH +Ni +oH +OA +oH +tr +Bv +tU +Ni +OQ +OQ +tr +iJ +oH +FD +oH +iJ +tr +Fq +Fq +Ni +Fq +Fq +tr +oH +oH +oH +Ni +oH +oH +Vi +oH +ve +oH +oH +oH +oH +lj +oH +oH +oH +oH +oH +oH +tr +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(54,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +tr +tr +tr +tr +tr +tr +tr +tr +tr +oH +oH +oH +ve +oH +oH +oH +oH +oH +Ni +oH +oH +oH +tr +tr +cg +tr +tr +tr +tr +lj +cg +lj +tr +tr +tr +tr +cg +tr +tr +oH +oH +oH +Ni +oH +oH +oH +oH +oH +ve +oH +oH +oH +tr +tr +tr +tr +tr +tr +tr +tr +tr +oH +BT +BT +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(55,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +BT +oH +lj +VS +VS +VS +VS +VS +VS +VS +lj +oH +oH +oH +ve +oH +oH +oH +oH +oH +Ni +oH +oH +tr +To +To +Ni +uF +cF +tr +zR +Mf +Ni +To +in +tr +gL +mg +Ni +To +To +tr +oH +oH +Ni +oH +oH +oH +oH +oH +ve +oH +oH +oH +lj +VS +VS +VS +VS +VS +VS +VS +lj +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(56,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +lj +VS +VS +VS +VS +VS +VS +VS +lj +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +Ni +oH +tr +To +oH +oH +oH +mg +tr +HT +oH +oH +oH +sa +tr +uF +oH +oH +oH +Ni +tr +oH +Ni +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +lj +VS +VS +VS +VS +VS +VS +VS +lj +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(57,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +tr +lj +lj +tr +tr +VS +VS +VS +VS +VS +VS +VS +tr +tr +oH +oH +oH +ve +oH +QA +QA +oH +oH +Ni +oH +Ni +AF +Ni +To +oH +Ni +cg +Ni +oH +To +oH +Ni +cg +Ni +oH +To +Ni +AF +Ni +oH +Ni +oH +oH +oH +oH +oH +ve +oH +oH +oH +tr +tr +VS +VS +VS +VS +VS +VS +VS +tr +tr +lj +lj +tr +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(58,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +qI +qI +qI +lj +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +Ni +oH +Ni +lj +Ni +oH +To +tr +To +oH +oH +oH +To +tr +To +oH +Ni +lj +Ni +oH +Ni +oH +oH +oH +IX +oH +oH +ve +oH +oH +oH +lj +qI +qI +qI +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(59,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +qI +qI +qI +lj +oH +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +Ni +oH +Ni +AF +Ni +To +tr +OX +OX +To +OX +OX +tr +To +Ni +AF +Ni +oH +Ni +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +oH +lj +qI +qI +qI +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(60,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +qI +qI +qI +tr +tr +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +oH +Ni +oH +Ni +tr +tr +oH +tr +tr +Lo +tr +tr +oH +tr +tr +Ni +oH +Ni +oH +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +tr +tr +qI +qI +qI +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(61,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +qI +qI +tr +oH +oH +oH +oH +ve +oH +oH +oH +oH +oH +CE +oH +Ni +oH +oH +oH +oH +oH +oH +tr +oH +oH +oH +oH +oH +oH +Ni +oH +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +oH +tr +qI +qI +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(62,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +qI +qI +tr +tr +oH +Vi +oH +ve +oH +oH +oH +oH +oH +oH +oH +oH +Ni +Ni +oH +oH +oH +oH +oH +oH +oH +oH +oH +Ni +Ni +Vi +oH +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +tr +tr +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(63,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +qI +tr +oH +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +oH +oH +oH +Ni +Ni +oH +oH +OA +oH +oH +Ni +Ni +oH +oH +oH +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +oH +tr +qI +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(64,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +qI +tr +tr +oH +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +Ni +Ni +Ni +Ni +Ni +oH +oH +IX +oH +oH +oH +oH +oH +oH +oH +ve +oH +oH +oH +oH +tr +tr +qI +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(65,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +qI +qI +tr +oH +oH +oH +oH +oH +ve +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +QA +oH +oH +oH +ve +oH +oH +oH +oH +oH +tr +qI +qI +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(66,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +qI +qI +qI +tr +oH +lj +oH +oH +oH +oH +oH +ve +ve +oH +oH +oH +oH +oH +QA +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +ve +ve +oH +oH +Vi +oH +oH +lj +oH +tr +qI +qI +qI +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(67,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +Gg +oH +oH +oH +oH +oH +oH +ve +ve +oH +oH +oH +OA +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +ve +ve +oH +oH +oH +oH +oH +oH +Gg +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(68,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +oH +oH +lj +oH +oH +QA +oH +oH +oH +oH +ve +ve +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +ve +ve +oH +oH +oH +oH +oH +oH +oH +lj +oH +oH +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(69,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +oH +oH +oH +oH +tr +tr +oH +oH +oH +oH +oH +oH +oH +ve +ve +ve +ve +oH +oH +oH +ve +ve +ve +ve +oH +oH +oH +oH +oH +oH +oH +tr +tr +oH +oH +oH +oH +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(70,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +tr +lj +lj +tr +oH +oH +oH +oH +oH +oH +oH +tr +qI +tr +tr +tr +oH +oH +oH +oH +oH +oH +oH +oH +oH +ve +ve +ve +oH +oH +oH +oH +oH +oH +oH +oH +oH +tr +tr +tr +qI +tr +oH +oH +oH +oH +oH +oH +oH +tr +lj +lj +lj +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(71,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +tr +oH +oH +oH +oH +oH +tr +qI +qI +qI +qI +tr +tr +tr +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +tr +tr +tr +qI +qI +qI +qI +tr +oH +oH +oH +oH +oH +tr +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(72,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +BT +oH +oH +oH +XZ +oH +oH +oH +tr +VS +qI +qI +qI +qI +qI +qI +tr +lj +lj +tr +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +tr +lj +lj +tr +qI +qI +qI +qI +qI +qI +VS +tr +oH +oH +oH +XZ +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(73,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +oH +oH +oH +BT +tr +oH +tr +VS +VS +qI +qI +qI +qI +qI +qI +qI +qI +qI +tr +lj +lj +tr +oH +oH +oH +oH +oH +tr +lj +lj +tr +qI +qI +qI +qI +qI +qI +qI +qI +qI +VS +VS +tr +oH +tr +oH +oH +oH +BT +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(74,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +oH +BT +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +qI +qI +qI +qI +VS +VS +VS +tr +lj +lj +Gg +lj +lj +tr +VS +VS +VS +qI +qI +qI +qI +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +oH +BT +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(75,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +BT +oH +oH +oH +BT +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +qI +qI +qI +qI +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +qI +qI +qI +qI +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +BT +oH +oH +oH +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(76,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +BT +oH +BT +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(77,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(78,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +tr +tr +lj +lj +lj +tr +tr +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +tr +tr +lj +lj +lj +tr +tr +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(79,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(80,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +tr +oH +oH +oH +oH +oH +tr +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(81,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +lj +lj +tr +tr +tr +XZ +tr +tr +tr +lj +lj +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(82,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(83,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +BT +oH +oH +oH +BT +oH +BT +oH +oH +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(84,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +lj +oH +oH +oH +oH +oH +oH +oH +oH +oH +BT +oH +lj +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(85,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +oH +oH +oH +BT +oH +oH +oH +oH +oH +oH +oH +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(86,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +tr +tr +tr +tr +tr +lj +lj +lj +tr +tr +tr +tr +tr +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(87,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(88,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(89,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +VS +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(90,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(91,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(92,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(93,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(94,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(95,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(96,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(97,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(98,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(99,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(100,1,1) = {" +ok +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +mL +ok +"} +(101,1,1) = {" +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +"} diff --git a/code/__DEFINES/~ff_defines/clockwork.dm b/code/__DEFINES/~ff_defines/clockwork.dm new file mode 100644 index 00000000000..51a630c4cb8 --- /dev/null +++ b/code/__DEFINES/~ff_defines/clockwork.dm @@ -0,0 +1,119 @@ +///check if a z level is reebe +#define is_reebe_level(z) SSmapping.level_trait(z, ZTRAIT_REEBE) +/// is something a cogscarab +#define iscogscarab(checked) (istype(checked, /mob/living/basic/drone/cogscarab)) +/// is something an eminence +#define iseminence(checked) (istype(checked, /mob/living/eminence)) +/// is something a clockgolem +#define isclockgolem(A) (is_species(A, /datum/species/clockwork_golem)) + +#define is_safe_level(z) SSmapping.level_trait(z, ZTRAIT_FORCED_SAFETY) +///Set weakref_var to null if it fails to give a resolve() value, resolver should be set to the var looking to resolve the weakref +#define WEAKREF_NULL_IF_UNRESOLVED(weakref_var, resolver) weakref_var?.resolve();\ + if(!##resolver) { \ + ##weakref_var = null;\ + } +#define IS_SAFE_NUM(a) IS_FINITE(a) +// traits +// boolean - marks a level as having that property if present +#define ZTRAIT_REEBE "Reebe" +/// Marks a level as being "safe", even if it is a station z level. +/// Nukes will not kill players on such levels. +#define ZTRAIT_FORCED_SAFETY "Forced Safety" +///List of ztraits the reebe Z level has +#define ZTRAITS_REEBE list(ZTRAIT_REEBE = TRUE, \ + ZTRAIT_NOPHASE = TRUE, \ + ZTRAIT_BOMBCAP_MULTIPLIER = 0.5, \ + ZTRAIT_RESERVED = TRUE, \ + ZTRAIT_BASETURF = /turf/open/indestructible/reebe_flooring) +//clockwork wall deconstruction +#define COVER_COG_REMOVED 1 +#define TRANSMISSION_COGS_REMOVED 2 +#define GEARS_UNBOLTED 3 +#define INNER_PANEL_REMOVED 4 +#define GEARS_UNWOUND 5 +/// maximum amount of cogscarabs the clock cult can have +#define MAXIMUM_COGSCARABS 6 + +#define CLOCK_PASSIVE_POWER_PER_COG 3 + +#define CLOCK_MAX_POWER_PER_COG STANDARD_CELL_CHARGE * 0.05 + +#define MAX_CLOCK_VITALITY 400 +/// Clockwork Golem Species +#define SPECIES_GOLEM_CLOCKWORK "clockgolem" +///base state the ark is created in, any state besides this will be a hostile environment +#define ARK_STATE_BASE 0 +///state for the grace period after the cult has reached its member count max and have enough activing anchoring crystals to summon +#define ARK_STATE_CHARGING 1 +///state for after the cult has been annouced and are preparing for the portals to open +#define ARK_STATE_GRACE 2 +///state for the first half of the assault +#define ARK_STATE_ACTIVE 3 +///state for the halfway point of ark activation +#define ARK_STATE_SUMMONING 4 +///the ark has either finished opening or been destroyed in this state +#define ARK_STATE_FINAL 5 + +///max damage taken per hit by "important" clock structures +#define MAX_IMPORTANT_CLOCK_DAMAGE 30 + +#define CHANNEL_SOUND_EFFECTS 1010 +///how many anchoring crystals need to be active before the ark can open +#define ANCHORING_CRYSTALS_TO_SUMMON 2 + +///the map path of the reebe map +#define REEBE_MAP_PATH "_maps/fluffy_frontier/templates/reebe.dmm" + +///how long in seconds do anchoring crystals take to charge after being placed, 6 minutes +#define ANCHORING_CRYSTAL_CHARGE_DURATION 360 SECONDS + +///how long between uses of the anchoring crystal scripture, also how long the hostile environment lasts if the crystal is not destroyed +#define ANCHORING_CRYSTAL_COOLDOWN ANCHORING_CRYSTAL_CHARGE_DURATION + 1 MINUTES + +///up to how many tiles away will the ark stop certain things from breaking turfs +#define ARK_TURF_DESTRUCTION_BLOCK_RANGE 10 + +///how many clockwork airlocks is the cult allowed to create on reebe +#define MAXIMUM_REEBE_AIRLOCKS 50 + +///called when /datum/element/turf_checker detects a new state on constant checking (new_state) TRUE for a valid turf FALSE for an invalid +#define COMSIG_TURF_CHECKER_UPDATE_STATE "turf_checker_update_state" +#define COMPONENT_CHECKER_VALID_TURF (1<<0) +#define COMPONENT_CHECKER_INVALID_TURF (2<<0) +/// from base of atom/ratvar_act() +#define COMSIG_ATOM_RATVAR_ACT "atom_ratvar_act" + +/// from base of atom/eminence_act() : (mob/living/eminence/user) +#define COMSIG_ATOM_EMINENCE_ACT "atom_eminence_act" +/// Used to externally force /datum/element/light_eater to handle eating a light without physical contact. Used by nightmares. (food, eater, silent) +#define COMSIG_LIGHT_EATER_EAT "light_eater_eat" + +/// the comsig for clockwork items checking turf +#define COMSIG_CHECK_TURF_CLOCKWORK "check_turf_clockwork" +///sent by the ark SS whenever an anchoring crystal charges (/obj/structure/destructible/clockwork/anchoring_crystal/charged_crystal) +#define COMSIG_ANCHORING_CRYSTAL_CHARGED "anchoring_crystal_charged" + +#define COMSIG_ATOM_SLAB_ACT "atom_slab_act" + +///sent by the ark SS whenever an anchoring crystal is created (/obj/structure/destructible/clockwork/anchoring_crystal/charged_crystal) +#define COMSIG_ANCHORING_CRYSTAL_CREATED "anchoring_crystal_created" +// Traits related directly to Clockwork Cult +#define TRAIT_BRONZE_TURF "bronze_turf" +/// Given to Clockwork Golems, gives them a reduction on invoke time for certain scriptures. +#define TRAIT_FASTER_SLAB_INVOKE "faster_slab_invoke" +/// Prevents the invocation of clockwork scriptures. +#define TRAIT_NO_SLAB_INVOKE "no_slab_invoke" +/// Has an item been enchanted by a clock cult Stargazer? +#define TRAIT_STARGAZED "stargazed" +/// Soul consumed by sigil of vitality +#define TRAIT_NO_SOUL_BY_VITALITY "no_soul_by_vitality" +// Traits Sources +#define STARGAZER_TRAIT "stargazer_trait" +/// Trait source for the vanguard scripture +#define VANGUARD_TRAIT "vanguard_trait" +/// Trait source from the clockwork sigil +#define SIGIL_TRAIT "sigil_trait" +// Roles +#define ROLE_ROUNDSTART_CLOCK_CULTIST "Roundstart Clockwork Cultist" +#define ROLE_MIDROUND_CLOCK_CULTIST "Midround Clockwork Cultist" diff --git a/code/__DEFINES/~nova_defines/security_alerts.dm b/code/__DEFINES/~nova_defines/security_alerts.dm index 31adf1463fd..8b2d775969b 100644 --- a/code/__DEFINES/~nova_defines/security_alerts.dm +++ b/code/__DEFINES/~nova_defines/security_alerts.dm @@ -9,3 +9,7 @@ #define SEC_LEVEL_EPSILON 7 // CENTCOM IS ANGY!!! #define SEC_LEVEL_GAMMA 8 // Oh shit bois #define SEC_LEVEL_FEDERAL 9 // THE FUCKING FEDS ARE HERE!!! +// TFF ADDITION START +/// Security level is lambda. (oh god eldtrich beings won the video game) +#define SEC_LEVEL_LAMBDA 7 +// TFF ADDITION END diff --git a/code/datums/mind/antag.dm b/code/datums/mind/antag.dm index 87260886892..e0bde1a80d8 100644 --- a/code/datums/mind/antag.dm +++ b/code/datums/mind/antag.dm @@ -73,7 +73,7 @@ /// Remove the antagonists that should not persist when being borged /datum/mind/proc/remove_antags_for_borging() remove_antag_datum(/datum/antagonist/cult) - + remove_antag_datum(/datum/antagonist/clock_cultist) var/datum/antagonist/rev/revolutionary = has_antag_datum(/datum/antagonist/rev) revolutionary?.remove_revolutionary() diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index 654b6705774..2c4c1da114e 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -336,7 +336,6 @@ to_chat(O, span_warning("Error: Servo motors unresponsive.")) O.equip_outfit_and_loadout(equipping_job = SSjob.get_job_type(/datum/job/cyborg)) // NOVA EDIT ADDITION - Cyborg loadout hats - else to_chat(user, span_warning("The MMI must go in after everything else!")) diff --git a/code/game/turfs/open/floor/misc_floor.dm b/code/game/turfs/open/floor/misc_floor.dm index e28f3ea043f..a7dfd531699 100644 --- a/code/game/turfs/open/floor/misc_floor.dm +++ b/code/game/turfs/open/floor/misc_floor.dm @@ -191,6 +191,13 @@ icon_state = "clockwork_floor" floor_tile = /obj/item/stack/tile/bronze +// TFF ADDITION START +/turf/open/floor/bronze/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_BRONZE_TURF, TURF_TRAIT) + +// TFF ADDITION END + /turf/open/floor/bronze/flat icon_state = "reebe" floor_tile = /obj/item/stack/tile/bronze/flat diff --git a/code/modules/antagonists/cult/cult_other.dm b/code/modules/antagonists/cult/cult_other.dm index 0e286d75e81..54ac7527eeb 100644 --- a/code/modules/antagonists/cult/cult_other.dm +++ b/code/modules/antagonists/cult/cult_other.dm @@ -13,7 +13,7 @@ ///Returns whether the given mob is convertable to the blood cult /proc/is_convertable_to_cult(mob/living/target, datum/team/cult/specific_cult) - if(!istype(target)) + if(!isliving(target)) return FALSE if(isnull(target.mind)) return FALSE diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm index fccfd086a6f..f680490f58e 100644 --- a/code/modules/mob/living/silicon/robot/robot_model.dm +++ b/code/modules/mob/living/silicon/robot/robot_model.dm @@ -76,7 +76,12 @@ var/obj/item/new_module = new path(robot) emag_modules += new_module emag_modules -= path - + // TFF ADDITION + for(var/path in clock_modules) + var/obj/item/new_module = new path(src) + clock_modules += new_module + clock_modules -= path + // TFF ADDITION END if(check_holidays(ICE_CREAM_DAY) && !(locate(/obj/item/borg/lollipop) in basic_modules)) basic_modules += new /obj/item/borg/lollipop/ice_cream(robot) @@ -86,6 +91,7 @@ modules.Cut() added_modules.Cut() storages.Cut() + clock_modules.Cut() // TFF ADDITION START return ..() /obj/item/robot_model/proc/get_usable_modules() @@ -144,6 +150,11 @@ if(cyborg.emagged) for(var/obj/item/module as anything in emag_modules) add_module(module, FALSE, FALSE) + // TFF ADDITION START + if(cyborg.clockwork) + for(var/obj/item/module in clock_modules) + add_module(module, FALSE, FALSE) + // TFF ADDITION END for(var/obj/item/module as anything in added_modules) add_module(module, FALSE, FALSE) for(var/obj/item/module as anything in held_modules & modules) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 56a051fdd7f..a2c912ccd6b 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -1107,6 +1107,9 @@ else return ALERT_RELEVANCY_WARN // NOVA EDIT ADDITION END + // TFF ADDITION START + if(SEC_LEVEL_LAMBDA) + return ALERT_RELEVANCY_PERTINENT return 0 diff --git a/code/modules/power/apc/apc_main.dm b/code/modules/power/apc/apc_main.dm index 3270b7c55b2..a5b76dbdf28 100644 --- a/code/modules/power/apc/apc_main.dm +++ b/code/modules/power/apc/apc_main.dm @@ -669,6 +669,8 @@ if(cell_percent > APC_CHANNEL_ALARM_TRESHOLD) alarm_manager.clear_alarm(ALARM_POWER) + // TFF EDIT START: + /* ORIGINAL: // NOVA EDIT ADDITION START - CLOCK CULT if(integration_cog) var/power_delta = clamp(cell.charge - 50, 0, 50) @@ -679,6 +681,11 @@ if(cell.charge <= 50) cell.charge = 0 // NOVA EDIT ADDITION END + */ + if(integration_cog && SSthe_ark.clock_power < SSthe_ark.max_clock_power) + var/power_delta = clamp(cell.charge - 70, 350, 700) + SSthe_ark.adjust_clock_power(power_delta / 70, TRUE) + // TFF EDIT END else // no cell, switch everything off charging = APC_NOT_CHARGING equipment = autoset(equipment, AUTOSET_FORCE_OFF) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 2de2dab4d53..8637f744edb 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -344,6 +344,8 @@ /datum/reagent/water/holywater/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() + // TFF EDIT START - ORIGINAL: + /* // Microdosing holy water is less effective than just gulping it down data["deciseconds_metabolized"] += seconds_per_tick * 1 SECONDS * metabolization_ratio @@ -388,6 +390,34 @@ holder?.remove_reagent(type, volume) // maybe this is a little too perfect and a max() cap on the statuses would be better?? if(need_mob_update) return UPDATE_MOB_HEALTH + */ + if(affected_mob.blood_volume) + affected_mob.blood_volume += 0.1 * REM * seconds_per_tick // water is good for you! + + data["deciseconds_metabolized"] += (seconds_per_tick * 1 SECONDS * REM) + affected_mob.adjust_jitter_up_to(2 SECONDS * seconds_per_tick, 20 SECONDS) + if(IS_CULTIST(affected_mob) || affected_mob.mind?.has_antag_datum(/datum/antagonist/clock_cultist)) + if(handle_cultists(affected_mob, seconds_per_tick)) //only returns TRUE on deconversion + return + holder.remove_reagent(type, 1 * REAGENTS_METABOLISM * seconds_per_tick) //fixed consumption to prevent balancing going out of whack + + var/need_mob_update = FALSE + + if (!HAS_TRAIT(affected_mob, TRAIT_EVIL) || IS_CULTIST(affected_mob) || affected_mob.mind?.holy_role == HOLY_ROLE_PRIEST) + return + if(data["deciseconds_metabolized"] >= (25 SECONDS)) // 10 units + affected_mob.adjust_stutter_up_to(4 SECONDS * REM * seconds_per_tick, 20 SECONDS) + affected_mob.set_dizzy_if_lower(10 SECONDS) + if(SPT_PROB(25, seconds_per_tick)) //Congratulations, your committment to evil has now made holy water a deadly poison to you! + affected_mob.emote("scream") + need_mob_update += affected_mob.adjust_fire_loss(3 * REM * seconds_per_tick, updating_health = FALSE) + if(data["deciseconds_metabolized"] >= (1 MINUTES)) // 24 units + need_mob_update += affected_mob.adjust_fire_loss(10 * REM * seconds_per_tick, updating_health = FALSE) + affected_mob.remove_status_effect(/datum/status_effect/jitter) + affected_mob.remove_status_effect(/datum/status_effect/speech/stutter) + holder?.remove_reagent(type, volume) // maybe this is a little too perfect and a max() cap on the statuses would be better?? + return need_mob_update + // TFF EDIT END /datum/reagent/water/holywater/expose_turf(turf/exposed_turf, reac_volume) . = ..() diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_midroundclockworkcultist.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_midroundclockworkcultist.png new file mode 100644 index 00000000000..3fa50fc0f3f Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_antag_icons_midroundclockworkcultist.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_roundstartclockworkcultist.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_roundstartclockworkcultist.png new file mode 100644 index 00000000000..3fa50fc0f3f Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_antag_icons_roundstartclockworkcultist.png differ diff --git a/icons/map_icons/items/_item.dmi b/icons/map_icons/items/_item.dmi index 98079a73b6c..39db80dbd50 100644 Binary files a/icons/map_icons/items/_item.dmi and b/icons/map_icons/items/_item.dmi differ diff --git a/modular_nova/modules/alerts/code/security_level_datums.dm b/modular_nova/modules/alerts/code/security_level_datums.dm index 2006edcf3a0..cb572a978bd 100644 --- a/modular_nova/modules/alerts/code/security_level_datums.dm +++ b/modular_nova/modules/alerts/code/security_level_datums.dm @@ -136,4 +136,18 @@ looping_sound_interval = 13 SECONDS shuttle_call_time_mod = ALERT_COEFF_NOVA +// TFF ADDITION START +/datum/security_level/lambda + name = "lambda" + name_shortform = "λ" + announcement_color = "crimson" //Thanking Absolucy for having a bigger brain than me in figuring out colors. + number_level = SEC_LEVEL_LAMBDA + sound = 'tff_modular/modules/antagonists/clock_cult/sound/lambda.ogg' // Ported over the current (as of this codes time) ss14 gamma alert, renamed because it fits better. Old gamma was better :( + elevating_to_configuration_key = /datum/config_entry/string/alert_lambda + shuttle_call_time_mod = ALERT_COEFF_NOVA //This is as bad as the nuke going off. Everyone is fucked. + disables_mail = TRUE + +/datum/config_entry/string/alert_lambda + default = "Central Command has detected a large spike of dimensional energy, consistent with the summoning of \[REDACTED\] entities. You are advised to make what little time you have left worthwhile; as no additional assets will be dispatched to %STATION_NAME% at this time." +// TFF ADDITION END #undef ALERT_COEFF_NOVA diff --git a/modular_nova/modules/clock_cult/code/actions/_action.dm b/modular_nova/modules/clock_cult/code/actions/_action.dm index 8f9946041c7..62acaf4e1f9 100644 --- a/modular_nova/modules/clock_cult/code/actions/_action.dm +++ b/modular_nova/modules/clock_cult/code/actions/_action.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/action/innate/clockcult button_icon = 'modular_nova/modules/clock_cult/icons/actions_clock.dmi' background_icon = 'modular_nova/modules/clock_cult/icons/background_clock.dmi' @@ -58,3 +59,4 @@ /datum/action/item_action/toggle/clock button_icon = 'modular_nova/modules/clock_cult/icons/background_clock.dmi' background_icon_state = "bg_clock" +*/ diff --git a/modular_nova/modules/clock_cult/code/actions/recall_slab.dm b/modular_nova/modules/clock_cult/code/actions/recall_slab.dm index 9fe03e9f81a..ee0d815e1ce 100644 --- a/modular_nova/modules/clock_cult/code/actions/recall_slab.dm +++ b/modular_nova/modules/clock_cult/code/actions/recall_slab.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/action/innate/clockcult/recall_slab name = "Recall Slab" desc = "Recall your latest used Clockwork Slab from anywhere in the universe." @@ -103,3 +104,4 @@ item_to_retrieve.loc.visible_message(span_warning("[item_to_retrieve] suddenly appears!")) playsound(get_turf(item_to_retrieve), 'sound/effects/magic/summonitems_generic.ogg', 50, TRUE) +*/ diff --git a/modular_nova/modules/clock_cult/code/actions/whirring_convergence.dm b/modular_nova/modules/clock_cult/code/actions/whirring_convergence.dm index ebbe7d2397d..b7c67d78d92 100644 --- a/modular_nova/modules/clock_cult/code/actions/whirring_convergence.dm +++ b/modular_nova/modules/clock_cult/code/actions/whirring_convergence.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /* * Whirring Convergence * Communicate a message to all other clock cultists @@ -56,3 +57,4 @@ to_chat(player_mob, "[FOLLOW_LINK(player_mob, user)] [final_message]") else to_chat(player_mob, final_message) +*/ diff --git a/modular_nova/modules/clock_cult/code/antagonist.dm b/modular_nova/modules/clock_cult/code/antagonist.dm index 406d703a6df..e52484f6c56 100644 --- a/modular_nova/modules/clock_cult/code/antagonist.dm +++ b/modular_nova/modules/clock_cult/code/antagonist.dm @@ -1,3 +1,5 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... + // This'll take a bit of explaining. // Clock cult, the (shitty) gamemode is not coming back, this antagonist datum is for the soon-to-come OPFOR bundle // However, the bundle gives `/datum/antagonist/clock_cultist/solo`, which is the same as `/datum/antagonist/clock_cultist`, but lacks conversion. @@ -72,3 +74,4 @@ name = "Clock Cultist (Solo)" show_to_ghosts = FALSE can_convert = FALSE +*/ diff --git a/modular_nova/modules/clock_cult/code/area.dm b/modular_nova/modules/clock_cult/code/area.dm index 9b04b90219c..323f749a918 100644 --- a/modular_nova/modules/clock_cult/code/area.dm +++ b/modular_nova/modules/clock_cult/code/area.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /area/ruin/powered/reebe name = "Outpost of Cogs" ambience_index = AMBIENCE_REEBE @@ -9,3 +10,4 @@ name = "Outpost of Cogs Space" base_lighting_alpha = 255 +*/ diff --git a/modular_nova/modules/clock_cult/code/components/brass_spreader.dm b/modular_nova/modules/clock_cult/code/components/brass_spreader.dm index 711bff25fb5..fe7f6301706 100644 --- a/modular_nova/modules/clock_cult/code/components/brass_spreader.dm +++ b/modular_nova/modules/clock_cult/code/components/brass_spreader.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /// A component that spreads brass to a tile in [range] every [cooldown] seconds, converting everything on it into brass as well. /datum/component/brass_spreader /// The range of which to spread brass @@ -98,3 +99,4 @@ return COOLDOWN_START(src, turf_conversion_cooldown, cooldown) +*/ diff --git a/modular_nova/modules/clock_cult/code/components/hint_element.dm b/modular_nova/modules/clock_cult/code/components/hint_element.dm index 9fd9b71792f..6cbd14aa3e5 100644 --- a/modular_nova/modules/clock_cult/code/components/hint_element.dm +++ b/modular_nova/modules/clock_cult/code/components/hint_element.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/element/clockwork_description element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH_ON_HOST_DESTROY argument_hash_start_idx = 2 @@ -37,3 +38,4 @@ if(IS_CLOCK(user)) examine_texts += span_brass(text_to_add) +*/ diff --git a/modular_nova/modules/clock_cult/code/components/pickup_element.dm b/modular_nova/modules/clock_cult/code/components/pickup_element.dm index 00486e816e7..3a5880815dc 100644 --- a/modular_nova/modules/clock_cult/code/components/pickup_element.dm +++ b/modular_nova/modules/clock_cult/code/components/pickup_element.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define REGULAR_PICKUP_MOD 1 #define CULTIST_PICKUP_MOD 2 #define PICKUP_SHOCK_DAMAGE 25 @@ -55,3 +56,4 @@ #undef REGULAR_PICKUP_MOD #undef CULTIST_PICKUP_MOD #undef PICKUP_SHOCK_DAMAGE +*/ diff --git a/modular_nova/modules/clock_cult/code/components/structure_info_element.dm b/modular_nova/modules/clock_cult/code/components/structure_info_element.dm index fa37830184f..df4faaa559f 100644 --- a/modular_nova/modules/clock_cult/code/components/structure_info_element.dm +++ b/modular_nova/modules/clock_cult/code/components/structure_info_element.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/element/clockwork_structure_info element_flags = ELEMENT_DETACH_ON_HOST_DESTROY @@ -61,3 +62,4 @@ return examine_text += span_brass("You can gain more information by using a Clockwork Slab.") +*/ diff --git a/modular_nova/modules/clock_cult/code/globals.dm b/modular_nova/modules/clock_cult/code/globals.dm index a7b8e484a1f..5c4789dea91 100644 --- a/modular_nova/modules/clock_cult/code/globals.dm +++ b/modular_nova/modules/clock_cult/code/globals.dm @@ -1,3 +1,5 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... + GLOBAL_VAR_INIT(clock_power, 2500) GLOBAL_VAR_INIT(max_clock_power, 2500) // Increases with every APC cogged GLOBAL_VAR_INIT(clock_vitality, 0) @@ -16,3 +18,4 @@ GLOBAL_LIST_EMPTY(clockwork_research_unlocked_scriptures) . += new path return . +*/ diff --git a/modular_nova/modules/clock_cult/code/items/clockwork_slab.dm b/modular_nova/modules/clock_cult/code/items/clockwork_slab.dm index 574dda7bd8b..ed613718205 100644 --- a/modular_nova/modules/clock_cult/code/items/clockwork_slab.dm +++ b/modular_nova/modules/clock_cult/code/items/clockwork_slab.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define MAXIMUM_QUICKBIND_SLOTS 5 GLOBAL_LIST_INIT(clockwork_slabs, list()) @@ -244,3 +245,4 @@ GLOBAL_LIST_INIT(clockwork_slabs, list()) bind_spell(living_user, scripture, positions.Find(position)) #undef MAXIMUM_QUICKBIND_SLOTS +*/ diff --git a/modular_nova/modules/clock_cult/code/items/clothing.dm b/modular_nova/modules/clock_cult/code/items/clothing.dm index fb84397abf8..e5f6004c364 100644 --- a/modular_nova/modules/clock_cult/code/items/clothing.dm +++ b/modular_nova/modules/clock_cult/code/items/clothing.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define VISOR_MOUNT_DAMAGE 20 #define VISOR_MOUNT_SLEEP_TIME 5 SECONDS @@ -451,3 +452,4 @@ #undef VISOR_MOUNT_DAMAGE #undef VISOR_MOUNT_SLEEP_TIME +*/ diff --git a/modular_nova/modules/clock_cult/code/items/integration_cog.dm b/modular_nova/modules/clock_cult/code/items/integration_cog.dm index 0844634fdcb..3c0ab549ec4 100644 --- a/modular_nova/modules/clock_cult/code/items/integration_cog.dm +++ b/modular_nova/modules/clock_cult/code/items/integration_cog.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define MAX_POWER_PER_COG 250 #define HALLUCINATION_COG_CHANCE 20 #define SET_UP_TIME (5 MINUTES) @@ -109,3 +110,4 @@ #undef MAX_POWER_PER_COG #undef HALLUCINATION_COG_CHANCE #undef SET_UP_TIME +*/ diff --git a/modular_nova/modules/clock_cult/code/items/replica_fabricator.dm b/modular_nova/modules/clock_cult/code/items/replica_fabricator.dm index d92a905b5e5..a9ace96fed9 100644 --- a/modular_nova/modules/clock_cult/code/items/replica_fabricator.dm +++ b/modular_nova/modules/clock_cult/code/items/replica_fabricator.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define BRASS_POWER_COST 10 #define REGULAR_POWER_COST (BRASS_POWER_COST / 2) @@ -286,3 +287,4 @@ #undef BRASS_POWER_COST #undef REGULAR_POWER_COST +*/ diff --git a/modular_nova/modules/clock_cult/code/items/tools.dm b/modular_nova/modules/clock_cult/code/items/tools.dm index c37404700d7..bac76f22309 100644 --- a/modular_nova/modules/clock_cult/code/items/tools.dm +++ b/modular_nova/modules/clock_cult/code/items/tools.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define BRASS_TOOLSPEED_MOD 0.5 /obj/item/wirecutters/brass @@ -62,3 +63,4 @@ new /obj/item/multitool(src) #undef BRASS_TOOLSPEED_MOD +*/ diff --git a/modular_nova/modules/clock_cult/code/items/weaponry.dm b/modular_nova/modules/clock_cult/code/items/weaponry.dm index b777653bd1f..5e12f7f00fe 100644 --- a/modular_nova/modules/clock_cult/code/items/weaponry.dm +++ b/modular_nova/modules/clock_cult/code/items/weaponry.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define HAMMER_FLING_DISTANCE 2 #define HAMMER_THROW_FLING_DISTANCE 3 #define BRASS_RIFLE_REDUCED_DELAY 0.25 SECONDS @@ -139,7 +140,6 @@ /obj/item/clockwork/weapon/brass_sword/proc/send_message(mob/living/target) to_chat(target, span_brass("[src] glows, indicating the next attack will disrupt electronics of the target.")) - /obj/item/gun/ballistic/bow/clockwork name = "brass bow" desc = "A bow made from brass and other components that you can't quite understand. It glows with a deep energy and frabricates arrows by itself." @@ -221,7 +221,8 @@ icon_state = "arrow_energy" damage = 35 damage_type = BURN - +*/ +/* /obj/item/gun/ballistic/rifle/lionhunter/clockwork name = "brass rifle" desc = "An antique, brass rifle made with the finest of care. It has an ornate scope in the shape of a cog built into the top." @@ -241,6 +242,7 @@ . = ..() AddElement(/datum/element/clockwork_description, "The speed of which you aim at far targets while standing on brass will be massively increased.") AddElement(/datum/element/clockwork_pickup) + qdel(src) /obj/item/ammo_box/magazine/internal/boltaction/lionhunter/clockwork name = "brass rifle internal magazine" @@ -255,7 +257,7 @@ var/obj/item/gun/ballistic/fired_gun = fired_from if(istype(get_turf(user), /turf/open/floor/bronze) && istype(fired_gun, /obj/item/gun/ballistic/rifle/lionhunter/clockwork)) - seconds_per_distance = BRASS_RIFLE_REDUCED_DELAY + seconds_per_distance = 0.25 SECONDS return ..() @@ -286,4 +288,7 @@ #undef HAMMER_FLING_DISTANCE #undef HAMMER_THROW_FLING_DISTANCE + #undef BRASS_RIFLE_REDUCED_DELAY + +*/ diff --git a/modular_nova/modules/clock_cult/code/language.dm b/modular_nova/modules/clock_cult/code/language.dm index d096e61903a..625209a64e2 100644 --- a/modular_nova/modules/clock_cult/code/language.dm +++ b/modular_nova/modules/clock_cult/code/language.dm @@ -1,3 +1,5 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... + /datum/language_holder/clockmob understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM), /datum/language/ratvar = list(LANGUAGE_ATOM)) @@ -139,3 +141,4 @@ #undef REVERSE_RATVAR_HYPHEN_GUA_REPLACEMENT #undef REVERSE_RATVAR_HYPHEN_OF_MATCH #undef REVERSE_RATVAR_HYPHEN_OF_REPLACEMENT +*/ diff --git a/modular_nova/modules/clock_cult/code/mobs/clockwork_marauder.dm b/modular_nova/modules/clock_cult/code/mobs/clockwork_marauder.dm index 5430cee59e2..652fcced3fe 100644 --- a/modular_nova/modules/clock_cult/code/mobs/clockwork_marauder.dm +++ b/modular_nova/modules/clock_cult/code/mobs/clockwork_marauder.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define MARAUDER_SHIELD_MAX 5 #define WELDER_REPAIR_AMOUNT 15 @@ -143,3 +144,4 @@ GLOBAL_LIST_EMPTY(clockwork_marauders) #undef MARAUDER_SHIELD_MAX #undef WELDER_REPAIR_AMOUNT +*/ diff --git a/modular_nova/modules/clock_cult/code/outfit.dm b/modular_nova/modules/clock_cult/code/outfit.dm index feee1c42c45..1302904994f 100644 --- a/modular_nova/modules/clock_cult/code/outfit.dm +++ b/modular_nova/modules/clock_cult/code/outfit.dm @@ -1,3 +1,4 @@ +/* TFF REMOVAL /datum/outfit/clock name = "Default Clock Cultist" @@ -16,7 +17,6 @@ /datum/outfit/clock/pre_equip(mob/living/carbon/human/equip_human, visualsOnly) equip_human.add_faction(FACTION_CLOCK) - /datum/outfit/clock/armor name = "Armored Clock Cultist" @@ -32,6 +32,7 @@ head = /obj/item/clothing/head/helmet/clockwork glasses = /obj/item/clothing/glasses/clockwork/judicial_visor/no_damage l_hand = /obj/item/gun/ballistic/bow/clockwork +*/ /datum/outfit/clock/gun name = "Seer Clock Cultist" @@ -42,6 +43,8 @@ r_pocket = /obj/item/storage/pouch/ammo/clock l_hand = /obj/item/gun/ballistic/rifle/lionhunter/clockwork +/* TFF REMOVAL + /datum/outfit/clock/support name = "Support Clock Cultist" @@ -59,7 +62,6 @@ /obj/item/clockwork/clockwork_slab = 1, ) - /datum/outfit/clockwork_armaments name = "Clockwork Cultist Base" @@ -67,3 +69,4 @@ shoes = /obj/item/clothing/shoes/clockwork gloves = /obj/item/clothing/gloves/clockwork head = /obj/item/clothing/head/helmet/clockwork +*/ diff --git a/modular_nova/modules/clock_cult/code/outpost_of_cogs.dm b/modular_nova/modules/clock_cult/code/outpost_of_cogs.dm index fb513fe2644..7a5ac561592 100644 --- a/modular_nova/modules/clock_cult/code/outpost_of_cogs.dm +++ b/modular_nova/modules/clock_cult/code/outpost_of_cogs.dm @@ -1,3 +1,5 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... + /datum/lazy_template/reebe key = LAZY_TEMPLATE_KEY_OUTPOST_OF_COGS map_dir = "_maps/nova/lazy_templates" @@ -116,7 +118,7 @@ var/obj/item/clothing/suit/hooded/hooded = equipped.wear_suit var/datum/component/toggle_attached_clothing/hood = hooded.GetComponent(/datum/component/toggle_attached_clothing) hood.toggle_deployable() // start unhooded - +*/ /obj/effect/mob_spawn/corpse/human/clock_cultist name = "Clock Cultist" @@ -132,3 +134,4 @@ /obj/effect/landmark/late_cog_portals name = "late cog portal spawn" + diff --git a/modular_nova/modules/clock_cult/code/portal.dm b/modular_nova/modules/clock_cult/code/portal.dm index 26fbaed6f7b..e0028ad77f8 100644 --- a/modular_nova/modules/clock_cult/code/portal.dm +++ b/modular_nova/modules/clock_cult/code/portal.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /obj/effect/portal/permanent/one_way/reebe name = "whirring portal" desc = "A tall, glowing portal. A low emination of moving cogs can be heard. You don't feel like coming back will be the easiest." @@ -23,7 +24,7 @@ return ..() - +*/ /obj/effect/portal/permanent/one_way/reebe/leaving desc = "For those who wish or require to leave the holy outpost." id = "reebe_exit" diff --git a/modular_nova/modules/clock_cult/code/research/asset_cache.dm b/modular_nova/modules/clock_cult/code/research/asset_cache.dm index 8171b2c05cb..ddee9250edd 100644 --- a/modular_nova/modules/clock_cult/code/research/asset_cache.dm +++ b/modular_nova/modules/clock_cult/code/research/asset_cache.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... // Now, you might be asking "why is this hooked into the research designs spritesheet?" // Well, for some *absurd* reason, any time i tried to add sprites to a new spritesheet, they would always have a *gray* background covering the full 32x32, apart from what my sprite did. After multiple hours of debugging, // I settled on this, since it's not-really-harmful to have other things in an unrelated spritesheet. @@ -46,3 +47,4 @@ insert_icon(id, uni_icon(icon_file, icon_state)) qdel(new_research) +*/ diff --git a/modular_nova/modules/clock_cult/code/research/clockwork_research.dm b/modular_nova/modules/clock_cult/code/research/clockwork_research.dm index c3297c7c167..7401cb09094 100644 --- a/modular_nova/modules/clock_cult/code/research/clockwork_research.dm +++ b/modular_nova/modules/clock_cult/code/research/clockwork_research.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/clockwork_research /// Name of the research node var/name = "" @@ -112,3 +113,4 @@ /datum/tinker_cache_item/clockwork_rifle_ammo, ) +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/_scripture.dm b/modular_nova/modules/clock_cult/code/scriptures/_scripture.dm index c5aa28cd577..f3217ba8834 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/_scripture.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/_scripture.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... GLOBAL_LIST_EMPTY(clock_scriptures) GLOBAL_LIST_EMPTY(clock_scriptures_by_type) @@ -277,7 +278,7 @@ GLOBAL_LIST_EMPTY(clock_scriptures_by_type) /datum/scripture/slab/invoke() - progress = new(invoker, use_time) + progress = new(invoker, use_time, invoking_slab) uses_left = uses time_left = use_time invoking_slab.charge_overlay = slab_overlay @@ -370,3 +371,4 @@ GLOBAL_LIST_EMPTY(clock_scriptures_by_type) var/datum/scripture/clock_script = new categorypath GLOB.clock_scriptures += clock_script GLOB.clock_scriptures_by_type[clock_script.type] = clock_script +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/preservation/clockwork_armaments.dm b/modular_nova/modules/clock_cult/code/scriptures/preservation/clockwork_armaments.dm index e37987c5537..00160be33e5 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/preservation/clockwork_armaments.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/preservation/clockwork_armaments.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/scripture/clockwork_armaments name = "Clockwork Armaments" desc = "Summon clockwork armor and weapons, to be ready for battle." @@ -38,3 +39,4 @@ base_outfit.equip(invoker) invoker.put_in_hands(new weapon_path, FALSE) +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/preservation/summon_marauder.dm b/modular_nova/modules/clock_cult/code/scriptures/preservation/summon_marauder.dm index a87a1cd57b6..e5adcea1550 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/preservation/summon_marauder.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/preservation/summon_marauder.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define MAXIMUM_MARAUDERS 2 /datum/scripture/marauder @@ -67,3 +68,4 @@ return TRUE #undef MAXIMUM_MARAUDERS +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/servitude/hateful_manacles.dm b/modular_nova/modules/clock_cult/code/scriptures/servitude/hateful_manacles.dm index 0841ed79df4..5402121d9bb 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/servitude/hateful_manacles.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/servitude/hateful_manacles.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/scripture/slab/hateful_manacles name = "Hateful Manacles" desc = "Forms replicant manacles around a target's wrists that function like handcuffs, restraining the target." @@ -42,3 +43,4 @@ desc = "Heavy manacles made out of freezing-cold metal. It looks like brass, but feels much more solid." icon_state = "brass_manacles" item_flags = DROPDEL +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/servitude/integration_cog.dm b/modular_nova/modules/clock_cult/code/scriptures/servitude/integration_cog.dm index 51f97014f3e..520aefe1858 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/servitude/integration_cog.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/servitude/integration_cog.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/scripture/integration_cog name = "Integration Cog" desc = "Fabricates an integration cog, which can be inserted into APCs to draw power and unlock scriptures." @@ -17,3 +18,4 @@ to_chat(invoker, span_brass("You summon an integration cog on the floor.")) playsound(src, 'sound/machines/click.ogg', 50) return FALSE +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/servitude/kindle.dm b/modular_nova/modules/clock_cult/code/scriptures/servitude/kindle.dm index 5908dcdc8b3..72a85115050 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/servitude/kindle.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/servitude/kindle.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define EFFECT_TIME (6.5 SECONDS) // Clock cult's version of the "bullshit stun hand" @@ -91,3 +92,4 @@ return TRUE #undef EFFECT_TIME +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/servitude/vitality_sigil.dm b/modular_nova/modules/clock_cult/code/scriptures/servitude/vitality_sigil.dm index 123ce68c953..7b7e9172087 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/servitude/vitality_sigil.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/servitude/vitality_sigil.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/scripture/create_structure/sigil_vitality name = "Vitality Matrix" desc = "Summons a vitality matrix, which drains the life force of non servants. Much less vitality is gained from simpler entities." @@ -10,3 +11,4 @@ cogs_required = 2 invokers_required = 2 category = SPELLTYPE_SERVITUDE +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/structures/interdiction_lens.dm b/modular_nova/modules/clock_cult/code/scriptures/structures/interdiction_lens.dm index 9670dbf73aa..6d3116c7a42 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/structures/interdiction_lens.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/structures/interdiction_lens.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/scripture/create_structure/interdiction name = "Interdiction Lens" desc = "Creates a device that will slow non servants in the area and damage mechanised exosuits. Requires power from a sigil of transmission." @@ -9,3 +10,4 @@ summoned_structure = /obj/structure/destructible/clockwork/gear_base/powered/interdiction_lens cogs_required = 4 category = SPELLTYPE_STRUCTURES +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/structures/ocular_warden.dm b/modular_nova/modules/clock_cult/code/scriptures/structures/ocular_warden.dm index b3706dc35bd..1a7d17f5a45 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/structures/ocular_warden.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/structures/ocular_warden.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define OCULAR_WARDEN_PLACE_RANGE 3 /datum/scripture/create_structure/ocular_warden @@ -24,3 +25,4 @@ return TRUE #undef OCULAR_WARDEN_PLACE_RANGE +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/structures/prosperity_prism.dm b/modular_nova/modules/clock_cult/code/scriptures/structures/prosperity_prism.dm index b157719e5d7..0b0a8fbfbc6 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/structures/prosperity_prism.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/structures/prosperity_prism.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/scripture/create_structure/prosperity_prism name = "Prosperity Prism" desc = "Creates a prism that will remove all forms of damage from nearby servants over time, along with purging poisons. Requires power from a sigil of transmission." @@ -21,3 +22,4 @@ return FALSE return TRUE +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/structures/technologists_lectern.dm b/modular_nova/modules/clock_cult/code/scriptures/structures/technologists_lectern.dm index 40c1b9fd4de..41ebad08600 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/structures/technologists_lectern.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/structures/technologists_lectern.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/scripture/create_structure/technologists_lectern name = "Technologist's Lectern" desc = "Creates a technologist's lectern, usable in certain areas to research new, powerful scriptures and equipment. Research takes a while and unenlightened minds may take notice." @@ -10,3 +11,4 @@ cogs_required = 2 category = SPELLTYPE_STRUCTURES +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/structures/tinkerers_cache.dm b/modular_nova/modules/clock_cult/code/scriptures/structures/tinkerers_cache.dm index 90d202a666f..1d80f91d06e 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/structures/tinkerers_cache.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/structures/tinkerers_cache.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/scripture/create_structure/tinkerers_cache name = "Tinkerer's Cache" desc = "Creates a tinkerer's cache, a powerful forge capable of crafting elite equipment." @@ -9,3 +10,4 @@ summoned_structure = /obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache cogs_required = 4 category = SPELLTYPE_STRUCTURES +*/ diff --git a/modular_nova/modules/clock_cult/code/scriptures/structures/transmission_sigil.dm b/modular_nova/modules/clock_cult/code/scriptures/structures/transmission_sigil.dm index df8c1bf2d92..f70d297b4cf 100644 --- a/modular_nova/modules/clock_cult/code/scriptures/structures/transmission_sigil.dm +++ b/modular_nova/modules/clock_cult/code/scriptures/structures/transmission_sigil.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/scripture/create_structure/sigil_transmission name = "Sigil of Transmission" desc = "Summons a sigil of transmission, required to power clockwork structures. Will also drain power from charged objects." @@ -8,3 +9,4 @@ invocation_text = list("Oh great holy one...", "your energy...", "the power of the holy light!") summoned_structure = /obj/structure/destructible/clockwork/sigil/transmission category = SPELLTYPE_STRUCTURES +*/ diff --git a/modular_nova/modules/clock_cult/code/status_effects.dm b/modular_nova/modules/clock_cult/code/status_effects.dm index 4f02afb869f..5612bfbae4d 100644 --- a/modular_nova/modules/clock_cult/code/status_effects.dm +++ b/modular_nova/modules/clock_cult/code/status_effects.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /datum/status_effect/interdiction id = "interdicted" duration = 2.6 SECONDS @@ -30,3 +31,4 @@ /datum/movespeed_modifier/clock_interdiction multiplicative_slowdown = 1.5 +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/_powered.dm b/modular_nova/modules/clock_cult/code/structures/_powered.dm index 2c80ac6a623..00dd89d028a 100644 --- a/modular_nova/modules/clock_cult/code/structures/_powered.dm +++ b/modular_nova/modules/clock_cult/code/structures/_powered.dm @@ -1,3 +1,5 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... + /obj/structure/destructible/clockwork/gear_base/powered /// If the structure has its "on" switch flipped. Does not mean it's on, necessarily (needs power and anchoring, too) var/enabled = FALSE @@ -204,3 +206,4 @@ LAZYREMOVE(sigil.linked_structures, src) check_power() +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/_structure.dm b/modular_nova/modules/clock_cult/code/structures/_structure.dm index 3c6b9b9dc79..aa25a4210e1 100644 --- a/modular_nova/modules/clock_cult/code/structures/_structure.dm +++ b/modular_nova/modules/clock_cult/code/structures/_structure.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /// The base clockwork structure. Can have an alternate desc and will show up in the list of clockwork objects. /obj/structure/destructible/clockwork name = "meme structure" @@ -48,3 +49,4 @@ balloon_alert(user, "rotated [dir2text(dir)]") return TRUE +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/airlock.dm b/modular_nova/modules/clock_cult/code/structures/airlock.dm index 33cf073c855..e336ca9e9e4 100644 --- a/modular_nova/modules/clock_cult/code/structures/airlock.dm +++ b/modular_nova/modules/clock_cult/code/structures/airlock.dm @@ -1,3 +1,5 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... + /obj/structure/door_assembly/door_assembly_bronze/clock airlock_type = /obj/machinery/door/airlock/bronze/clock @@ -48,3 +50,4 @@ assemblytype = /obj/structure/door_assembly/door_assembly_bronze/seethru/clock glass = TRUE opacity = FALSE +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/gear_base.dm b/modular_nova/modules/clock_cult/code/structures/gear_base.dm index 6ab82016744..d3f390d55e7 100644 --- a/modular_nova/modules/clock_cult/code/structures/gear_base.dm +++ b/modular_nova/modules/clock_cult/code/structures/gear_base.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /obj/structure/destructible/clockwork/gear_base name = "gear base" desc = "A large cog lying on the floor at feet level." @@ -43,3 +44,4 @@ if(!anchored) icon_state += unwrenched_suffix +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/interdiction_lens.dm b/modular_nova/modules/clock_cult/code/structures/interdiction_lens.dm index c1345ceac80..922dfb6d23a 100644 --- a/modular_nova/modules/clock_cult/code/structures/interdiction_lens.dm +++ b/modular_nova/modules/clock_cult/code/structures/interdiction_lens.dm @@ -1,3 +1,5 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... + #define INTERDICTION_LENS_RANGE 4 #define POWER_PER_PERSON 5 @@ -78,3 +80,4 @@ #undef INTERDICTION_LENS_RANGE #undef POWER_PER_PERSON +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/ocular_warden.dm b/modular_nova/modules/clock_cult/code/structures/ocular_warden.dm index 4d14c27f308..43c3d9ec9c8 100644 --- a/modular_nova/modules/clock_cult/code/structures/ocular_warden.dm +++ b/modular_nova/modules/clock_cult/code/structures/ocular_warden.dm @@ -1,3 +1,5 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... + #define FIRE_DELAY (2 SECONDS) #define FIRE_RANGE 4 #define BASE_DAMAGE 15 @@ -76,3 +78,4 @@ #undef MINIMUM_DAMAGE #undef DAMAGE_FALLOFF #undef SHOOT_POWER_USE +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/prosperity_prism.dm b/modular_nova/modules/clock_cult/code/structures/prosperity_prism.dm index fc3b08f545e..a2384939224 100644 --- a/modular_nova/modules/clock_cult/code/structures/prosperity_prism.dm +++ b/modular_nova/modules/clock_cult/code/structures/prosperity_prism.dm @@ -1,3 +1,5 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... + #define POWER_PER_USE 50 /obj/structure/destructible/clockwork/gear_base/powered/prosperity_prism @@ -41,3 +43,4 @@ possible_cultist.reagents.remove_reagent(toxin_chem.type, 2.5 * seconds_per_tick) #undef POWER_PER_USE +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/sigil/_sigil.dm b/modular_nova/modules/clock_cult/code/structures/sigil/_sigil.dm index e7b0aecd6c9..d118042cedd 100644 --- a/modular_nova/modules/clock_cult/code/structures/sigil/_sigil.dm +++ b/modular_nova/modules/clock_cult/code/structures/sigil/_sigil.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define SIGIL_INVOCATION_ALPHA 120 #define SIGIL_INVOKED_ALPHA 200 #define SIGIL_MATRIX_SCALE 1.2 @@ -144,3 +145,4 @@ #undef SIGIL_INVOCATION_ALPHA #undef SIGIL_INVOKED_ALPHA #undef SIGIL_MATRIX_SCALE +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/sigil/sigil_research.dm b/modular_nova/modules/clock_cult/code/structures/sigil/sigil_research.dm index 8e4f0b802b4..742c9e3cb38 100644 --- a/modular_nova/modules/clock_cult/code/structures/sigil/sigil_research.dm +++ b/modular_nova/modules/clock_cult/code/structures/sigil/sigil_research.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define DISAPPEAR_REAPPEAR_TIME (2 SECONDS) /obj/structure/destructible/clockwork/sigil/research @@ -33,3 +34,4 @@ #undef DISAPPEAR_REAPPEAR_TIME +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/sigil/sigil_transmission.dm b/modular_nova/modules/clock_cult/code/structures/sigil/sigil_transmission.dm index 7541f1b7269..17a7ec06176 100644 --- a/modular_nova/modules/clock_cult/code/structures/sigil/sigil_transmission.dm +++ b/modular_nova/modules/clock_cult/code/structures/sigil/sigil_transmission.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define POWER_GIVE 40 #define POWER_SIPHON 20 @@ -112,3 +113,4 @@ #undef POWER_GIVE #undef POWER_SIPHON +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/sigil/vitality_sigil.dm b/modular_nova/modules/clock_cult/code/structures/sigil/vitality_sigil.dm index af094e4a82d..3559876b5f8 100644 --- a/modular_nova/modules/clock_cult/code/structures/sigil/vitality_sigil.dm +++ b/modular_nova/modules/clock_cult/code/structures/sigil/vitality_sigil.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /obj/structure/destructible/clockwork/sigil/vitality name = "vitality matrix" desc = "A twisting, confusing artifact that drains the unenlightended on contact." @@ -66,3 +67,4 @@ else send_clock_message(null, "[affected_mob] has had their vitality drained by [src], rejoice!", "") +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/technologists_lectern.dm b/modular_nova/modules/clock_cult/code/structures/technologists_lectern.dm index 6ee96c4ee5f..f01f124b432 100644 --- a/modular_nova/modules/clock_cult/code/structures/technologists_lectern.dm +++ b/modular_nova/modules/clock_cult/code/structures/technologists_lectern.dm @@ -1,15 +1,18 @@ +// TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define BOOK_OPEN_RANGE 2 /obj/structure/destructible/clockwork/gear_base/technologists_lectern name = "technologist's lectern" desc = "A small pedestal with a glowing book floating over it.." clockwork_desc = "A small pedestal, glowing with a divine energy. Used to research new abilities and objects." + icon = 'modular_nova/modules/clock_cult/icons/clockwork_objects.dmi' // TFF ADDITION base_icon_state = "lectern" icon_state = "lectern" anchored = TRUE break_message = "The lectern collapses." can_unwrench = FALSE max_integrity = 400 +/* /// If the last process() found a clock cultist in range var/mobs_in_range = FALSE /// Ref to the effect of the lectern's book @@ -477,3 +480,4 @@ flick("lectern_closing", src) #undef BOOK_OPEN_RANGE +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/tinkerers_cache.dm b/modular_nova/modules/clock_cult/code/structures/tinkerers_cache.dm index 8bc60f0f7ad..9f1b176bc7e 100644 --- a/modular_nova/modules/clock_cult/code/structures/tinkerers_cache.dm +++ b/modular_nova/modules/clock_cult/code/structures/tinkerers_cache.dm @@ -1,3 +1,5 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... + /obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache name = "tinkerer's cache" desc = "A bronze store filled with parts and components." @@ -167,3 +169,4 @@ /datum/tinker_cache_item/trap/pressure name = "Pressure Sensor (Trigger)" item_path = /obj/item/clockwork/trap_placer/pressure_sensor +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/traps/receivers/flipper.dm b/modular_nova/modules/clock_cult/code/structures/traps/receivers/flipper.dm index 1d943aa34a4..8255575e61a 100644 --- a/modular_nova/modules/clock_cult/code/structures/traps/receivers/flipper.dm +++ b/modular_nova/modules/clock_cult/code/structures/traps/receivers/flipper.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define FLIP_DISTANCE 6 #define FLIP_SPEED 3 @@ -58,3 +59,4 @@ #undef FLIP_DISTANCE #undef FLIP_SPEED +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/traps/receivers/skewer.dm b/modular_nova/modules/clock_cult/code/structures/traps/receivers/skewer.dm index 014f0df43d9..0a0b9a35872 100644 --- a/modular_nova/modules/clock_cult/code/structures/traps/receivers/skewer.dm +++ b/modular_nova/modules/clock_cult/code/structures/traps/receivers/skewer.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define SKEWER_DAMAGE 15 #define SKEWER_BLEED 30 @@ -112,3 +113,4 @@ #undef SKEWER_DAMAGE #undef SKEWER_BLEED +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/traps/senders/delay.dm b/modular_nova/modules/clock_cult/code/structures/traps/senders/delay.dm index b468c079f3c..f6d8bb93a17 100644 --- a/modular_nova/modules/clock_cult/code/structures/traps/senders/delay.dm +++ b/modular_nova/modules/clock_cult/code/structures/traps/senders/delay.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /obj/item/wallframe/clocktrap/delay name = "clockwork timer" desc = "A small, detached timer." @@ -45,3 +46,4 @@ /datum/component/clockwork_trap/delay/proc/finish() active = FALSE trigger_connected() +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/traps/senders/lever.dm b/modular_nova/modules/clock_cult/code/structures/traps/senders/lever.dm index 7dd5926bb85..bec2ccbbfa5 100644 --- a/modular_nova/modules/clock_cult/code/structures/traps/senders/lever.dm +++ b/modular_nova/modules/clock_cult/code/structures/traps/senders/lever.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /obj/item/wallframe/clocktrap/lever name = "switch" desc = "A small switch attatched to the wall." @@ -24,3 +25,4 @@ trigger_connected() to_chat(user, span_notice("You activate the switch.")) playsound(user, 'sound/machines/click.ogg', 50) +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/traps/senders/pressure_sensor.dm b/modular_nova/modules/clock_cult/code/structures/traps/senders/pressure_sensor.dm index 4f516feae2e..22b2c78bbfc 100644 --- a/modular_nova/modules/clock_cult/code/structures/traps/senders/pressure_sensor.dm +++ b/modular_nova/modules/clock_cult/code/structures/traps/senders/pressure_sensor.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /obj/item/clockwork/trap_placer/pressure_sensor name = "pressure plate" desc = "I wonder what happens if you step on it." @@ -44,3 +45,4 @@ trigger_connected() playsound(parent, 'sound/machines/click.ogg', 50) +*/ diff --git a/modular_nova/modules/clock_cult/code/structures/traps/trap.dm b/modular_nova/modules/clock_cult/code/structures/traps/trap.dm index 8815a22e960..9312220c7f8 100644 --- a/modular_nova/modules/clock_cult/code/structures/traps/trap.dm +++ b/modular_nova/modules/clock_cult/code/structures/traps/trap.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... //Thing that you stick on the floor /obj/item/clockwork/trap_placer name = "trap" @@ -168,3 +169,4 @@ for(var/datum/output as anything in outputs) //must be typecasted because of how SEND_SIGNAL works SEND_SIGNAL(output, COMSIG_CLOCKWORK_SIGNAL_RECEIVED) +*/ diff --git a/modular_nova/modules/clock_cult/code/temp_visual.dm b/modular_nova/modules/clock_cult/code/temp_visual.dm index 92a14f3d7be..adbae74c743 100644 --- a/modular_nova/modules/clock_cult/code/temp_visual.dm +++ b/modular_nova/modules/clock_cult/code/temp_visual.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... #define MENDING_MANTRA_SCALE 2 //temporary visual effects(/obj/effect/temp_visual) used by clock stuff @@ -219,3 +220,4 @@ #undef MENDING_MANTRA_SCALE +*/ diff --git a/modular_nova/modules/clock_cult/code/turf.dm b/modular_nova/modules/clock_cult/code/turf.dm index 1e3be9c4873..76456b6d050 100644 --- a/modular_nova/modules/clock_cult/code/turf.dm +++ b/modular_nova/modules/clock_cult/code/turf.dm @@ -1,3 +1,4 @@ +/* // TFF REWORK - SEE /tff_modular/modules/antagonist/clock_cult/... /turf/open/indestructible/reebe_void name = "void" desc = "A white, empty void, quite unlike anything you've seen before." @@ -51,3 +52,4 @@ if(prob(95) && !(locate(/obj/structure/lattice) in loc)) // Don't try putting a lattice where one already exists or we can get runtimes new /obj/structure/lattice(src) +*/ diff --git a/tff_modular/modules/antagonists/clock_cult/actions/_action.dm b/tff_modular/modules/antagonists/clock_cult/actions/_action.dm new file mode 100644 index 00000000000..bdc039d3669 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/actions/_action.dm @@ -0,0 +1,68 @@ +/datum/action/innate/clockcult + button_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/actions_clock.dmi' + background_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/background_clock.dmi' + background_icon_state = "bg_clock" + check_flags = AB_CHECK_HANDS_BLOCKED|AB_CHECK_IMMOBILE|AB_CHECK_CONSCIOUS + +/datum/action/innate/clockcult/quick_bind + name = "Quick Bind" + button_icon_state = "telerune" + desc = "A quick bound spell." + /// Weakref to the relevant slab + var/datum/weakref/slab_weakref + /// Ref to the relevant scripture + var/datum/scripture/scripture + +/datum/action/innate/clockcult/quick_bind/Destroy() + scripture = null + return ..() + +/datum/action/innate/clockcult/quick_bind/Grant(mob/living/recieving_mob) + name = scripture.name + desc = scripture.tip + button_icon_state = scripture.button_icon_state + if(scripture.power_cost) + desc += "
Draws [scripture.power_cost]W from the ark per use." + return ..(recieving_mob) + +/datum/action/innate/clockcult/quick_bind/Remove(mob/losing_mob) + var/obj/item/clockwork/clockwork_slab/activation_slab = slab_weakref.resolve() + if(activation_slab.invoking_scripture == scripture) + activation_slab.invoking_scripture = null + return ..(losing_mob) + +/datum/action/innate/clockcult/quick_bind/IsAvailable(feedback) + if(!IS_CLOCK(owner) || HAS_TRAIT(owner, TRAIT_INCAPACITATED)) + return FALSE + return ..() + +/datum/action/innate/clockcult/quick_bind/Activate() + if(!slab_weakref) + return + + var/obj/item/clockwork/clockwork_slab/activation_slab = slab_weakref.resolve() + if(!activation_slab.invoking_scripture) + scripture.begin_invoke(owner, activation_slab) + else + to_chat(owner, span_brass("You fail to invoke [name].")) + +/datum/action/item_action/toggle/clock + background_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/background_clock.dmi' + background_icon_state = "bg_clock" + +/datum/action/cooldown/clock_cult + button_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/actions_clock.dmi' + background_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/background_clock.dmi' + background_icon_state = "bg_clock" + +/datum/action/cooldown/clock_cult/IsAvailable(feedback) + return IS_CLOCK(owner) && ..() + +/datum/action/cooldown/clock_cult/eminence + +/datum/action/cooldown/clock_cult/eminence/Activate(atom/target) + . = ..() + if(!iseminence(usr)) + to_chat(usr, span_boldwarning("You are not an eminence and should not have this! Please report this as a bug.")) + return FALSE + return TRUE diff --git a/tff_modular/modules/antagonists/clock_cult/actions/add_warp_area.dm b/tff_modular/modules/antagonists/clock_cult/actions/add_warp_area.dm new file mode 100644 index 00000000000..f368f835076 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/actions/add_warp_area.dm @@ -0,0 +1,98 @@ +///How many areas are observation consoles able to warp to at the start +#define STARTING_WARP_AREAS 8 +///how much vitality does each already marked area increase the cost by +#define COST_PER_AREA 4 + +/datum/action/innate/clockcult/add_warp_area + name = "Add Warp Area" + desc = "Add an additional area observation consoles can warp to." + button_icon_state = "Spatial Warp" + ///a cache of areas we can are to the warpable list + var/static/list/cached_addable_areas //rename this to markable areas + ///what area types are we blocked from warping to + var/static/list/blocked_areas = typecacheof(list(/area/station/service/chapel, /area/station/ai)) + ///what area types cost double + var/static/list/costly_areas = typecacheof(list(/area/station/command, /area/station/security)) + +/datum/action/innate/clockcult/add_warp_area/New(Target) + . = ..() + if(isnull(cached_addable_areas)) + build_addable_areas() + choose_starting_warp_areas() + +/datum/action/innate/clockcult/add_warp_area/IsAvailable(feedback) + if(!IS_CLOCK(owner)) + return FALSE + return ..() + +/datum/action/innate/clockcult/add_warp_area/Activate() + if(!length(cached_addable_areas)) + return + + var/area/input_area = tgui_input_list(owner, "Select an area to add.", "Add Area", cached_addable_areas) + if(!input_area) + return + + var/cost = max((length(SSthe_ark.marked_areas) * COST_PER_AREA) - (STARTING_WARP_AREAS * COST_PER_AREA), 0) + if(is_type_in_typecache(input_area.type, costly_areas)) + cost *= 2 + + if(tgui_alert(owner, "Are you sure you want to add [input_area]? It will cost [cost] vitality.", "Add Area", list("Yes", "No")) == "Yes") + if(GLOB.clock_vitality < cost) + to_chat(owner, span_brass("Not enough vitality.")) + return + + if(SSthe_ark.marked_areas[input_area]) + return + + SSthe_ark.marked_areas[input_area] = TRUE + cached_addable_areas -= input_area + send_clock_message(null, "[input_area] added to warpable areas.") + +/datum/action/innate/clockcult/add_warp_area/proc/choose_starting_warp_areas() + if(!length(cached_addable_areas)) + return + + if(!SSthe_ark.initialized) + SSthe_ark.Initialize() + //shuffle_inplace(cached_addable_areas) //this is so our picked maint areas are random without needing to do anything weird + var/sanity = 0 + var/added_areas = 0 + var/list/temp_list = cached_addable_areas.Copy() + while(added_areas < STARTING_WARP_AREAS && sanity < 100 && length(temp_list)) + sanity++ + /*if(i <= 2) //always give them 2 maint areas to hopefully be easy to warp from + var/area/station/maintenance/maint_area = locate() in cached_addable_areas + if(maint_area) + cached_addable_areas -= maint_area + SSthe_ark.marked_areas += maint_area + continue*/ //for if I implement abscond restrictions + var/area/picked_area = pick(temp_list) + temp_list -= picked_area + if(is_type_in_typecache(picked_area.type, costly_areas)) + continue + + added_areas++ + SSthe_ark.marked_areas[picked_area] = TRUE + cached_addable_areas -= picked_area + +/datum/action/innate/clockcult/add_warp_area/proc/build_addable_areas() + cached_addable_areas = list() + for(var/area/station_area as anything in GLOB.the_station_areas) + station_area = GLOB.areas_by_type[station_area] + if(station_area.outdoors || (station_area.area_flags & NOTELEPORT) || is_type_in_typecache(station_area, blocked_areas) || (SSthe_ark.marked_areas[station_area])) + continue + cached_addable_areas += station_area + +/datum/action/innate/clockcult/show_warpable_areas + name = "Warpable Areas" + desc = "Display what areas are currently warpable to by observation consoles." + button_icon_state = "console_info" + +/datum/action/innate/clockcult/show_warpable_areas/Activate() + if(!SSthe_ark.initialized) + SSthe_ark.Initialize() + to_chat(owner, boxed_message(span_brass("Current areas observation consoles can warp to: [english_list(SSthe_ark.marked_areas)]
\ + You can add additional areas with the \"Add Warp Area\" action."))) //anyone who has this action should also have add warp area + +#undef STARTING_WARP_AREAS diff --git a/tff_modular/modules/antagonists/clock_cult/actions/clockmob_warp.dm b/tff_modular/modules/antagonists/clock_cult/actions/clockmob_warp.dm new file mode 100644 index 00000000000..a9196861625 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/actions/clockmob_warp.dm @@ -0,0 +1,36 @@ +/datum/action/cooldown/clock_cult/clockmob_warp + name = "Clockwork Warp" + desc = "Warp to and from marked areas on the station and reebe." + cooldown_time = 30 SECONDS + button_icon_state = "warp_down" + +/datum/action/cooldown/clock_cult/clockmob_warp/Grant(mob/granted_to) + . = ..() + if(!on_reebe(granted_to)) + button_icon_state = "Abscond" + +/datum/action/cooldown/clock_cult/clockmob_warp/Activate(atom/target) + var/turf/selected_turf + var/new_icon_state + if(on_reebe(owner)) + if(!length(SSthe_ark.marked_areas)) + return FALSE + var/area/selection = tgui_input_list(owner, "Where do you want to warp?", "Warping", SSthe_ark.marked_areas) + if(!isarea(selection)) + return FALSE + selected_turf = pick(selection.get_turfs_from_all_zlevels()) + new_icon_state = "Abscond" + else + if(!length(GLOB.abscond_markers)) + return FALSE + selected_turf = get_turf(pick(GLOB.abscond_markers)) + new_icon_state = "warp_down" + + if(!selected_turf || !do_after(owner, cooldown_time, owner)) + return FALSE + + try_servant_warp(owner, selected_turf) + if(new_icon_state) + button_icon_state = new_icon_state + build_all_button_icons(UPDATE_BUTTON_ICON) + return ..() diff --git a/tff_modular/modules/antagonists/clock_cult/actions/emience_teleports/basic_teleports.dm b/tff_modular/modules/antagonists/clock_cult/actions/emience_teleports/basic_teleports.dm new file mode 100644 index 00000000000..62f16322c72 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/actions/emience_teleports/basic_teleports.dm @@ -0,0 +1,18 @@ +/datum/action/innate/clockcult/teleport_to_station + name = "Teleport to Station" + desc = "Teleport to a random location on the station." + button_icon_state = "warp_down" + +/datum/action/innate/clockcult/teleport_to_station/Activate() + do_teleport(usr, pick(GLOB.station_turfs), 0, no_effects = TRUE, channel = TELEPORT_CHANNEL_CULT, forced = TRUE) + usr.playsound_local(get_turf(usr), 'sound/effects/magic/magic_missile.ogg', 50, TRUE, pressure_affected = FALSE) + +/datum/action/innate/clockcult/eminence_abscond + name = "Return to Reebe" + desc = "Teleport back to reebe." + button_icon_state = "Abscond" + +/datum/action/innate/clockcult/eminence_abscond/Activate() + do_teleport(usr, get_turf(pick(GLOB.abscond_markers)), 0, no_effects = TRUE, channel = TELEPORT_CHANNEL_CULT, forced = TRUE) + usr.playsound_local(get_turf(usr), 'sound/effects/magic/magic_missile.ogg', 50, TRUE, pressure_affected = FALSE) + diff --git a/tff_modular/modules/antagonists/clock_cult/actions/emience_teleports/linked_abscond.dm b/tff_modular/modules/antagonists/clock_cult/actions/emience_teleports/linked_abscond.dm new file mode 100644 index 00000000000..9b0fa6c32a0 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/actions/emience_teleports/linked_abscond.dm @@ -0,0 +1,33 @@ +/datum/action/cooldown/clock_cult/eminence/linked_abscond + name = "Linked Abscond" + desc = "Absconds a fellow servant and whomever they may be pulling back to reebe if they stand still for 7 seconds." + button_icon_state = "Linked Abscond" + cooldown_time = 4 MINUTES + +/datum/action/cooldown/clock_cult/eminence/linked_abscond/Activate(atom/target) + var/mob/living/eminence/em_user = usr + if(!em_user.marked_servant) + to_chat(em_user, span_notice("You dont currently have a marked servant!")) + return FALSE + + var/mob/living/teleported = em_user.marked_servant?.resolve() + if(teleported.has_reagent(/datum/reagent/water/holywater)) //cant abscond servents with holy water in them, use reagent purge + to_chat(em_user, span_warning("Holy water inside [teleported] is blocking you from absconding them, use reagent purge!")) + return FALSE + + . = ..() + if(!.) + return FALSE + + to_chat(em_user, span_brass("You begin to recall [teleported].")) + to_chat(teleported, span_bigbrass("You are being recalled by the eminence.")) + teleported.visible_message(span_warning("[teleported] flares briefly.")) + + if(!do_after(em_user, 7 SECONDS, teleported)) + to_chat(em_user, span_warning("You fail to recall [teleported].")) + return FALSE + teleported.visible_message(span_warning("[teleported] phases out of existence!")) + try_servant_warp(teleported, get_turf(pick(GLOB.abscond_markers))) + to_chat(em_user, "You recall [teleported].") + em_user.marked_servant = null + return TRUE diff --git a/tff_modular/modules/antagonists/clock_cult/actions/emience_teleports/teleport_to_servant.dm b/tff_modular/modules/antagonists/clock_cult/actions/emience_teleports/teleport_to_servant.dm new file mode 100644 index 00000000000..6cf82eb4697 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/actions/emience_teleports/teleport_to_servant.dm @@ -0,0 +1,21 @@ +/datum/action/innate/clockcult/teleport_to_servant + name = "Teleport to Servant" + desc = "Teleport yourself to a fellow servant." + button_icon_state = "clockwork_armor" + +/datum/action/innate/clockcult/teleport_to_servant/Activate(mob/living/user = usr) + var/datum/antagonist/clock_cultist/servant = user.mind.has_antag_datum(/datum/antagonist/clock_cultist) + if(!servant?.clock_team) + return + + var/list/given_list = list() + for(var/datum/mind/servant_mind in servant.clock_team.members) + given_list += servant_mind.current + given_list -= usr + if(!given_list.len) + return + + var/mob/living/input_servant = tgui_input_list(usr, "Choosen a servant", "Servants", given_list) + do_teleport(usr, get_turf(input_servant), 0, no_effects = TRUE, channel = TELEPORT_CHANNEL_CULT, forced = TRUE) + usr.playsound_local(get_turf(usr), 'sound/effects/magic/magic_missile.ogg', 50, TRUE, pressure_affected = FALSE) + to_chat(usr, "You warp to [input_servant].") diff --git a/tff_modular/modules/antagonists/clock_cult/actions/purge_reagents.dm b/tff_modular/modules/antagonists/clock_cult/actions/purge_reagents.dm new file mode 100644 index 00000000000..99169ad9060 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/actions/purge_reagents.dm @@ -0,0 +1,29 @@ +/datum/action/cooldown/clock_cult/eminence/purge_reagents + name = "Purge Reagents" + desc = "Purges all reagents from the bloodstream of a marked servant, useful for if they have been given holy water." + button_icon_state = "Mending Mantra" + cooldown_time = 30 SECONDS + +/datum/action/cooldown/clock_cult/eminence/purge_reagents/Activate(atom/target) + . = ..() + if(!.) + return FALSE + + var/mob/living/eminence/em_user = usr + var/mob/living/purged = WEAKREF_NULL_IF_UNRESOLVED(em_user.marked_servant, purged) + if(!purged) + to_chat(em_user, span_notice("You dont currently have a marked servant!")) + return FALSE + + var/did_purge = FALSE + for(var/datum/reagent/chem in purged.reagents?.reagent_list) + purged.reagents.remove_reagent(chem.type, chem.volume) + did_purge = TRUE + + em_user.marked_servant = null + if(!did_purge) + to_chat(em_user, span_notice("[purged] does not have any reagents to purge.")) + return FALSE + + to_chat(em_user, "You purge the reagents of [purged].") + return TRUE diff --git a/tff_modular/modules/antagonists/clock_cult/actions/recall_slab.dm b/tff_modular/modules/antagonists/clock_cult/actions/recall_slab.dm new file mode 100644 index 00000000000..f67d571be10 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/actions/recall_slab.dm @@ -0,0 +1,102 @@ +/datum/action/innate/clockcult/recall_slab + name = "Recall Slab" + desc = "Recall your latest used Clockwork Slab from anywhere in the universe." + button_icon_state = "Replicant" + + ///The slab marked for recall + var/obj/item/clockwork/clockwork_slab/marked_slab + +/// Set the passed object as our marked item +/datum/action/innate/clockcult/recall_slab/proc/mark_item(obj/to_mark) + marked_slab = to_mark + RegisterSignal(marked_slab, COMSIG_QDELETING, PROC_REF(on_marked_item_deleted)) + +/// Unset our current marked item +/datum/action/innate/clockcult/recall_slab/proc/unmark_item() + if(!marked_slab) + return + + UnregisterSignal(marked_slab, COMSIG_QDELETING) + marked_slab = null + +/// Signal proc for COMSIG_QDELETING on our marked item, unmarks our item if it's deleted +/datum/action/innate/clockcult/recall_slab/proc/on_marked_item_deleted(datum/source) + SIGNAL_HANDLER + + if(owner) + to_chat(owner, span_boldwarning("You sense your Clockwork Slab has been destroyed!")) + + unmark_item() + +/datum/action/innate/clockcult/recall_slab/Activate() + try_recall_item() + +/// Recalls our marked item to the caster. May bring some unexpected things along. +/datum/action/innate/clockcult/recall_slab/proc/try_recall_item() + var/obj/item_to_retrieve = marked_slab + + if(!item_to_retrieve) + to_chat(usr, span_brass("You don't have a slab attuned!")) + return + + if(!item_to_retrieve.loc) + return + + // just being safe + var/infinite_recursion = 0 + + // if it's in something, you get the whole thing. + while(!isturf(item_to_retrieve.loc) && infinite_recursion < 10) + if(isitem(item_to_retrieve.loc)) + var/obj/item/mark_loc = item_to_retrieve.loc + // Being able to summon abstract things because + // your item happened to get placed there is a no-no + if(mark_loc.item_flags & ABSTRACT) + break + + // If its on someone, properly drop it + if(ismob(item_to_retrieve.loc)) + var/mob/holding_mark = item_to_retrieve.loc + + // Items in silicons warp the whole silicon + if(issilicon(holding_mark)) + holding_mark.loc.visible_message(span_warning("[holding_mark] suddenly disappears!")) + holding_mark.forceMove(usr.loc) + holding_mark.loc.visible_message(span_warning("[holding_mark] suddenly appears!")) + item_to_retrieve = null + break + + SEND_SIGNAL(holding_mark, COMSIG_MAGIC_RECALL, usr, item_to_retrieve) + holding_mark.dropItemToGround(item_to_retrieve) + + else if(isobj(item_to_retrieve.loc)) + var/obj/retrieved_item = item_to_retrieve.loc + // Can't bring anchored things + if(retrieved_item.anchored) + break + + // Edge cases for moving certain machinery... + if(istype(retrieved_item, /obj/machinery/portable_atmospherics)) + var/obj/machinery/portable_atmospherics/atmos_item = retrieved_item + atmos_item.disconnect() + atmos_item.update_appearance() + + // Otherwise bring the whole thing with us + item_to_retrieve = retrieved_item + + infinite_recursion += 1 + + if(!item_to_retrieve) + return + + item_to_retrieve.loc?.visible_message(span_warning("[item_to_retrieve] suddenly disappears!")) + + if(isitem(item_to_retrieve) && usr.put_in_hands(item_to_retrieve)) + var/obj/item/typed_item = item_to_retrieve + typed_item.pickup(usr) //might be good to standardize this being on put_in_hands() + item_to_retrieve.loc.visible_message(span_warning("[item_to_retrieve] suddenly appears in [usr]'s hand!")) + else + item_to_retrieve.forceMove(usr.drop_location()) + item_to_retrieve.loc.visible_message(span_warning("[item_to_retrieve] suddenly appears!")) + + playsound(get_turf(item_to_retrieve), 'sound/effects/magic/summonitems_generic.ogg', 50, TRUE) diff --git a/tff_modular/modules/antagonists/clock_cult/actions/space_fold.dm b/tff_modular/modules/antagonists/clock_cult/actions/space_fold.dm new file mode 100644 index 00000000000..86edbaf4e0c --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/actions/space_fold.dm @@ -0,0 +1,98 @@ +///list of events eminence can trigger as well as their cost in cogs, most likely gonna have to set limits on these, might do it based on cost, unsure if I should use a .txt for this +#define EMINENCE_EVENTS list( \ + /datum/round_event_control/brand_intelligence = 5, \ + /datum/round_event_control/bureaucratic_error = 3, \ + /datum/round_event_control/gravity_generator_blackout = 4, \ + /datum/round_event_control/communications_blackout = 6, \ + /datum/round_event_control/electrical_storm = 2, \ + /datum/round_event_control/ion_storm = 6, \ + /datum/round_event_control/grey_tide = 3, \ + /datum/round_event_control/grid_check = 6, \ + /datum/round_event_control/scrubber_overflow/catastrophic = 4, \ + /datum/round_event_control/radiation_storm = 8, \ + /datum/round_event_control/carp_migration = 6, \ + /datum/round_event_control/wormholes = 6, \ + /datum/round_event_control/immovable_rod = 9, \ + /datum/round_event_control/anomaly/anomaly_dimensional = 2, \ + /datum/round_event_control/anomaly/anomaly_bluespace = 4, \ + /datum/round_event_control/anomaly/anomaly_ectoplasm = 4, \ + /datum/round_event_control/anomaly/anomaly_flux = 3, \ + /datum/round_event_control/anomaly/anomaly_pyro = 5, \ +) + +/datum/action/innate/clockcult/space_fold + name = "Space Fold" + button_icon_state = "Geis" + desc = "Fold local space so that certain \"events\" befall the station. The amount you may create depends on how many APCs the cult has cogged. \ + Doing so will also cost charges which will regenerate at a rate of one per minute." + ///list used for tracking what events have been trigged so far, also used for restricting how many times an event can trigger + var/list/used_event_list = list() + ///instead of a cooldown this has a charge system, one charge regenerates every mintue, each event costs charges equal to its cog cost + var/charges = 10 + ///the static list of events we have access to + var/static/list/event_list + ///cooldown declare for charge cooldown + COOLDOWN_DECLARE(charge_cooldown) + +/datum/action/innate/clockcult/space_fold/New(Target) + . = ..() + if(isnull(event_list)) + event_list = list() + var/list/temp = EMINENCE_EVENTS + for(var/datum/round_event_control/entry as anything in SSevents.control) + if(entry.type in temp) + event_list[entry] = temp[entry.type] + +/datum/action/innate/clockcult/space_fold/Grant(mob/grant_to) + . = ..() + START_PROCESSING(SSfastprocess, src) + COOLDOWN_START(src, charge_cooldown, 1 SECONDS) + +/datum/action/innate/clockcult/space_fold/Destroy() + . = ..() + STOP_PROCESSING(SSfastprocess, src) + +/datum/action/innate/clockcult/space_fold/process(seconds_per_tick) + if(COOLDOWN_FINISHED(src, charge_cooldown)) + charges++ + COOLDOWN_START(src, charge_cooldown, 1 MINUTES) + + if(charges >= initial(charges)) + STOP_PROCESSING(SSfastprocess, src) + return + +/datum/action/innate/clockcult/space_fold/Activate() + var/datum/round_event_control/chosen_event = tgui_input_list(usr, "Choose an event", "[charges] [charges == 1 ? "charge" : "charges"] remaining", event_list) + if(isnull(chosen_event) || isnull(event_list[chosen_event])) + return FALSE + + if(used_event_list[chosen_event] && (event_list[chosen_event] >= 4 || used_event_list[chosen_event] >= 4)) //events with 4+ cost can be used once, 3 and below can be used 4 times + to_chat(usr, span_warning("You have summoned this event too many times to do so again!")) + return FALSE + + switch(tgui_alert(usr, "Are you sure you want to summon this event? It will cost [event_list[chosen_event]] cogs.", "Confirm summon", list("Yes", "No"))) + if("No") + return FALSE + if("Yes") + var/actual_cost = event_list[chosen_event] + if(GLOB.clock_ark && GLOB.clock_ark.current_state >= ARK_STATE_CHARGING) + actual_cost *= 2 //events cost double after ark activation + if(charges < actual_cost) + to_chat(usr, span_warning("You dont have enough charges to summon this event")) + return FALSE + if(istype(usr, /mob/living/eminence)) //if you somehow get this as non-eminence its technically free besides charges + var/mob/living/eminence/em_user = usr + if(em_user.cogs < actual_cost) + to_chat(em_user, span_warning("You dont have enough cogs to do this!")) + return + em_user.cogs -= actual_cost + chosen_event.run_event(event_cause = "an emience folding spacetime") + charges -= actual_cost + if(charges + event_list[chosen_event] >= initial(charges)) //if charges was full then start processing + START_PROCESSING(SSfastprocess, src) + COOLDOWN_START(src, charge_cooldown, 1 MINUTES) + used_event_list[chosen_event] = used_event_list[chosen_event] + 1 + return TRUE + return FALSE + +#undef EMINENCE_EVENTS diff --git a/tff_modular/modules/antagonists/clock_cult/actions/whirring_convergence.dm b/tff_modular/modules/antagonists/clock_cult/actions/whirring_convergence.dm new file mode 100644 index 00000000000..46c17d91e7f --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/actions/whirring_convergence.dm @@ -0,0 +1,68 @@ +/* +* Whirring Convergence +* Communicate a message to all other clock cultists +*/ +/datum/action/innate/clockcult/comm + name = "Whirring Convergence" + desc = "Whispered words that link to the internal cogs of us all.
Warning: Nearby non-servants can still hear you." + button_icon_state = "linked_minds" + check_flags = AB_CHECK_CONSCIOUS + +/datum/action/innate/clockcult/comm/Activate() + var/input = tgui_input_text(usr, "Message to tell to the other followers.", "Voice of Cogs") + if(!input || !IsAvailable()) + return + + var/list/filter_result = CAN_BYPASS_FILTER(usr) ? null : is_ic_filtered(input) + if(filter_result) + REPORT_CHAT_FILTER_TO_USER(usr, filter_result) + return + + var/list/soft_filter_result = CAN_BYPASS_FILTER(usr) ? null : is_soft_ic_filtered(input) + if(soft_filter_result) + + if(tgui_alert(usr,"Your message contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to say it?", "Soft Blocked Word", list("Yes", "No")) != "Yes") + return + message_admins("[ADMIN_LOOKUPFLW(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[html_encode(input)]\"") + log_admin_private("[key_name(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[input]\"") + + cultist_commune(usr, input) + +/// A user can input a message to send to other clock cultists over the clock cult communication channel +/datum/action/innate/clockcult/comm/proc/cultist_commune(mob/living/user, message) + if(!message) + return + + user.whisper("Engine, V vaibxr gb-gur`r gb-pbzzhar gb-nyy.", language = /datum/language/common) //Ratvar, I invoke to-the`e to-commune to-all. + user.whisper(html_decode(message), filterproof = TRUE) + send_clock_message(user, message) + +/// Send `sent_message` to all other clock cultists and ghosts from the user +/proc/send_clock_message(mob/living/user, sent_message, span = "", msg_ghosts = TRUE, sent_sound) + var/final_message = "" + if(user && istype(user, /mob/living/eminence)) + final_message = span + span_bigbrass("Master Eminence transmits, \"") + sent_message + span_bigbrass("\"") + else if(user) + var/pronoun_phrase = "sibling" + if(user.gender == FEMALE) + pronoun_phrase = "sister" + else if(user.gender == MALE) + pronoun_phrase = "brother" + final_message = span + "Clock[pronoun_phrase] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"] transmits, \"" + sent_message + "\"
" + else + final_message = span + sent_message + "" + + for(var/mob/player_mob as anything in GLOB.player_list) + + if(IS_CLOCK(player_mob)) + to_chat(player_mob, final_message) + if(sent_sound) + SEND_SOUND(player_mob, sent_sound) + + else if(player_mob in GLOB.dead_mob_list) + if(!msg_ghosts) + continue + if(user) + to_chat(player_mob, "[FOLLOW_LINK(player_mob, user)] [final_message]") + else + to_chat(player_mob, final_message) diff --git a/tff_modular/modules/antagonists/clock_cult/antag_datums/clock_cult_team.dm b/tff_modular/modules/antagonists/clock_cult/antag_datums/clock_cult_team.dm new file mode 100644 index 00000000000..3afec853c01 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/antag_datums/clock_cult_team.dm @@ -0,0 +1,168 @@ +GLOBAL_DATUM(main_clock_cult, /datum/team/clock_cult) + +#define DEFAULT_MAX_HUMAN_SERVANTS 8 +#define CONVERSION_WARNING_NONE 0 +#define CONVERSION_WARNING_HALFWAY 1 +#define CONVERSION_WARNING_THREEQUARTERS 2 +#define CONVERSION_WARNING_CRITIAL 3 +/datum/team/clock_cult + name = "Clock Cult" + /// maximum number of human servants we can have + var/max_human_servants = DEFAULT_MAX_HUMAN_SERVANTS + /// list of our human servants + var/list/human_servants = list() + /// list of our non-human servants + var/list/non_human_servants = list() + /// what warning stage are we at + var/warning_stage = CONVERSION_WARNING_NONE + /// have we used our recall + var/member_recalled = FALSE + +/datum/team/clock_cult/add_member(datum/mind/new_member) + . = ..() + var/mob/current_mob = new_member.current + if(current_mob && ishuman(current_mob)) + human_servants |= new_member + else + non_human_servants |= new_member + + check_member_count() + +/datum/team/clock_cult/remove_member(datum/mind/member) + . = ..() + if(member in human_servants) + human_servants -= member + else + non_human_servants -= member + +/datum/team/clock_cult/roundend_report() + var/list/parts = list() + var/list/checked_objectives = list() + var/failure = FALSE + if(length(objectives)) + var/count = 1 + for(var/datum/objective/objective in objectives) + if(objective.check_completion()) + checked_objectives += "Objective #[count]: [objective.explanation_text] [span_greentext("Success!")]" + else + checked_objectives += "Objective #[count]: [objective.explanation_text] [span_redtext("Fail.")]" + failure = TRUE + count++ + + if(failure) + parts += "The clock cult has failed to protect the ark and summon Ratvar, he will remain forever trapped!" + else + parts += "The clock cult has succeeded! Ratvar's light shall shine forever more!" + + if(length(checked_objectives)) + parts += "The clock cultists' objectives were:" + for(var/i in 1 to length(checked_objectives)) + parts += checked_objectives[i] + + if(length(members)) + parts += "The clock cultists were:" + parts += printplayerlist(members) + + return "
[parts.Join("
")]
" + +///check how many human members we have and anything that goes with that +/datum/team/clock_cult/proc/check_member_count() + check_member_distribution() + max_human_servants = round(max((get_active_player_count() / 7) + 1, max_human_servants)) + var/human_servant_count = length(human_servants) + var/main_message = "The Ark will be torn open if [max_human_servants - human_servant_count] more minds are converted to the faith of Ratvar\ + [SSthe_ark.charged_anchoring_crystals >= ANCHORING_CRYSTALS_TO_SUMMON ? "." : " and \ + [ANCHORING_CRYSTALS_TO_SUMMON] Anchoring Crystal[ANCHORING_CRYSTALS_TO_SUMMON > 1 ? "s are" : " is"] summoned and protected on the station."]" + + if((human_servant_count * 2) > max_human_servants && warning_stage < CONVERSION_WARNING_HALFWAY) + send_clock_message(null, span_bigbrass("Ratvar's influence is growing. [main_message]"), sent_sound = 'modular_nova/modules/clock_cult/sound/magic/scripture_tier_up.ogg') + warning_stage = CONVERSION_WARNING_HALFWAY + + else if(human_servant_count > ((3/4) * max_human_servants) && warning_stage < CONVERSION_WARNING_THREEQUARTERS) + send_clock_message(null, span_bigbrass("You feel the boundary between reality and fiction lessen as the Ark sparks with an arcane energy.
[main_message]"), \ + sent_sound = 'modular_nova/modules/clock_cult/sound/magic/scripture_tier_up.ogg') + warning_stage = CONVERSION_WARNING_THREEQUARTERS + + else if((human_servant_count == max_human_servants - 1) && warning_stage < CONVERSION_WARNING_CRITIAL && SSthe_ark.charged_anchoring_crystals >= ANCHORING_CRYSTALS_TO_SUMMON) + send_clock_message(span_bigbrass("The internal cogs of the Ark begin spinning, ready for activation.
\ + Upon the next conversion, the dimensional barrier will become too weak for The Ark to remain closed and it will be forced open."), \ + sent_sound = 'modular_nova/modules/clock_cult/sound/magic/scripture_tier_up.ogg') + warning_stage = CONVERSION_WARNING_CRITIAL + + else if((human_servant_count >= max_human_servants) && SSthe_ark.charged_anchoring_crystals >= ANCHORING_CRYSTALS_TO_SUMMON) + GLOB.clock_ark?.prepare_ark() + +///check that our human_servants and non_human_servants lists are correct and if not then set them to be correct +/datum/team/clock_cult/proc/check_member_distribution() + for(var/datum/mind/member in human_servants) + if(ishuman(member.current)) + continue + human_servants -= member + non_human_servants |= member + var/datum/antagonist/clock_cultist/servant_datum = member.has_antag_datum(/datum/antagonist/clock_cultist) + servant_datum.recall.Remove(member.current) + + for(var/datum/mind/member in non_human_servants) + if(!ishuman(member)) + continue + non_human_servants -= member + human_servants |= member + var/datum/antagonist/clock_cultist/servant_datum = member.has_antag_datum(/datum/antagonist/clock_cultist) + servant_datum.recall.Grant(member.current) + +/datum/team/clock_cult/proc/setup_objectives() + if(length(objectives)) + return + GLOB.main_clock_cult = src + var/datum/objective/anchoring_crystals/crystals_objective = new + crystals_objective.team = src + objectives += crystals_objective + + var/datum/objective/ratvar/summon_objective = new + summon_objective.team = src + objectives += summon_objective + +#undef DEFAULT_MAX_HUMAN_SERVANTS +#undef CONVERSION_WARNING_NONE +#undef CONVERSION_WARNING_HALFWAY +#undef CONVERSION_WARNING_THREEQUARTERS +#undef CONVERSION_WARNING_CRITIAL + +#define POSSIBLE_CRYSTAL_AREAS 6 +/datum/objective/anchoring_crystals + +/datum/objective/anchoring_crystals/New() + . = ..() + if(SSthe_ark.valid_crystal_areas) + return + + SSthe_ark.valid_crystal_areas = list() + var/sanity = 0 + var/list/areas_copy = GLOB.areas.Copy() + while(length(SSthe_ark.valid_crystal_areas) < POSSIBLE_CRYSTAL_AREAS && sanity < 100) + var/area/summon_area = pick_n_take(areas_copy) + if(summon_area && is_station_level(summon_area.z) && (summon_area.area_flags & VALID_TERRITORY)) + SSthe_ark.valid_crystal_areas[summon_area] = summon_area.get_original_area_name() + sanity++ + update_explanation_text() + +/datum/objective/anchoring_crystals/update_explanation_text() + var/plural = ANCHORING_CRYSTALS_TO_SUMMON > 1 + var/list/names = list() + for(var/area/valid_area in SSthe_ark.valid_crystal_areas) + names += SSthe_ark.valid_crystal_areas[valid_area] + + explanation_text = "Summon [ANCHORING_CRYSTALS_TO_SUMMON] anchoring crystal[plural ? "s" : ""] on the station and protect [plural ? "them" : "it"] for \ + [DisplayTimeText(ANCHORING_CRYSTAL_CHARGE_DURATION)] to allow the ark to open. \ + Crystals after the first one must be summoned in [english_list(names)]. Up to 2 additional crystals can be created for extra power." + +/datum/objective/anchoring_crystals/check_completion() + return SSthe_ark.charged_anchoring_crystals >= ANCHORING_CRYSTALS_TO_SUMMON || completed + +/datum/objective/ratvar + explanation_text = "Protect The Ark so that Ratvar may enlighten this world!" + +/datum/objective/ratvar/check_completion() + return GLOB.ratvar_risen || completed + +#undef POSSIBLE_CRYSTAL_AREAS diff --git a/tff_modular/modules/antagonists/clock_cult/antag_datums/clock_cultist.dm b/tff_modular/modules/antagonists/clock_cult/antag_datums/clock_cultist.dm new file mode 100644 index 00000000000..07f7722d85c --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/antag_datums/clock_cultist.dm @@ -0,0 +1,248 @@ +/datum/antagonist/clock_cultist + name = "\improper Servant of Ratvar" + antagpanel_category = "Clock Cultist" + preview_outfit = /datum/outfit/clock/preview + pref_flag = ROLE_ROUNDSTART_CLOCK_CULTIST + antag_moodlet = /datum/mood_event/cult + suicide_cry = ",r For Ratvar!!!" + ui_name = "AntagInfoClockAlt" + show_to_ghosts = TRUE + hud_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/hud.dmi' + antag_hud_name = "clockwork" + stinger_sound = 'modular_nova/modules/clock_cult/sound/magic/scripture_tier_up.ogg' + /// Ref to the cultist's communication ability + var/datum/action/innate/clockcult/comm/communicate = new + /// Ref to the cultist's slab recall ability + var/datum/action/innate/clockcult/recall_slab/recall = new + ///our cult team + var/datum/team/clock_cult/clock_team + ///should we directly give them a slab or not + var/give_slab = TRUE + ///ref to our turf_healing component, used for deletion when deconverted + var/datum/component/turf_healing/owner_turf_healing + ///used for holy water deconversion, slightly easier to have this here then on the team, might want to refactor this to an assoc global list + var/static/list/servant_deconversion_phrases = list("spoken" = list("VG OHEAF!", "SBE GUR TYBEL-BS ENGINE!", "Gur yvtug jvyy fuvar.", "Whfgv`pne fnir zr.", "Gur Nex zhfg abg snyy.", + "Rzvarapr V pnyy gur`r!", "Lbh frr bayl qnexarff.", "Guv`f vf abg gur raq.", "Gv`px, Gbpx"), + + "seizure" = list("Your failure shall not delay my freedom.", "The blind will see only darkness.", + "Then my ark will feed upon your vitality.", "Do not forget your servitude.")) + +/datum/antagonist/clock_cultist/Destroy() + QDEL_NULL(communicate) + QDEL_NULL(recall) + return ..() + +/datum/antagonist/clock_cultist/on_gain() + var/mob/living/current = owner.current + objectives |= clock_team.objectives + if(give_slab && ishuman(current)) + give_clockwork_slab(current) + current.log_message("has been converted to the cult of Ratvar!", LOG_ATTACK, color="#960000") + if(issilicon(current)) + handle_silicon_conversion(current) + . = ..() //have to call down here so objectives display correctly + ADD_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src)) + +/datum/antagonist/clock_cultist/greet() + . = ..() + to_chat(owner.current, span_clockyellow("HEY")) + to_chat(owner.current, span_boldwarning("Dont forget, your structures are by default off and must be clicked on to be turned on. Structures that are turned on have passive power use.")) + to_chat(owner.current, span_boldwarning("YOUR CLOCKWORK SLAB UI HAS A MORE IN DEPTH GUIDE IN ITS BOTTOM RIGHT HAND SIDE. \ + YOU CAN HOVER YOUR MOUSE POINTER OVER SCRIPTURE BUTTONS FOR EXTRA INFO.")) + +///Returns whether the given mob is convertable to the blood cult +/proc/is_convertable_to_clock(mob/living/target) + if(!target.mind) + return FALSE + return TRUE + +//given_clock_team is provided by conversion methods, although we never use it due to wanting to just set their team to the main clock cult +/datum/antagonist/clock_cultist/create_team(datum/team/clock_cult/given_clock_team) + spawn_reebe() + if(!given_clock_team) + if(GLOB.main_clock_cult) + clock_team = GLOB.main_clock_cult + return + clock_team = new /datum/team/clock_cult + clock_team.setup_objectives() + return + + if(!istype(given_clock_team)) + stack_trace("Wrong team type passed to [type] initialization.") + clock_team = given_clock_team + +/datum/antagonist/clock_cultist/get_team() + return clock_team + +/datum/antagonist/clock_cultist/apply_innate_effects(mob/living/mob_override) + . = ..() + var/mob/living/current = owner.current || mob_override + current.add_faction(FACTION_CLOCK) + current.grant_language(/datum/language/ratvar, source = LANGUAGE_CULTIST) + current.throw_alert("clockinfo", /atom/movable/screen/alert/clockwork/clocksense) + if(!iseminence(current)) + add_team_hud(current) + communicate.Grant(current) + if(ishuman(current) || iscogscarab(current)) //only human and cogscarabs would need a recall ability + recall.Grant(current) + owner_turf_healing = current.AddComponent(/datum/component/turf_healing, healing_types = list(TOX = 1.5, BRUTE = 1.5, BURN = 1.5, STAMINA = 5, OXY = 1.5), healing_turfs = GLOB.clock_turf_types) + RegisterSignal(current, COMSIG_CLOCKWORK_SLAB_USED, PROC_REF(switch_recall_slab)) + handle_clown_mutation(current, mob_override ? null : "The light of Ratvar allows you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") + add_forbearance(current) + +/datum/antagonist/clock_cultist/remove_innate_effects(mob/living/mob_override) + . = ..() + var/mob/living/current = owner.current + current.remove_faction(FACTION_CLOCK) + current.remove_language(/datum/language/ratvar, source = LANGUAGE_CULTIST) + current.clear_alert("clockinfo") + current.remove_filter("forbearance") + if(!iseminence(current)) + communicate.Remove(current) + recall.Remove(current) + UnregisterSignal(current, COMSIG_CLOCKWORK_SLAB_USED) + QDEL_NULL(owner_turf_healing) + handle_clown_mutation(current, removing = FALSE) + +/datum/antagonist/clock_cultist/ui_data(mob/user) + var/list/data = list() + data["marked_areas"] = english_list(SSthe_ark.marked_areas) + return data + +/datum/antagonist/clock_cultist/can_be_owned(datum/mind/new_owner) + if(!is_convertable_to_clock(new_owner.current)) + return FALSE + return ..() + +/datum/antagonist/clock_cultist/on_removal() + if(!silent) + owner.current.visible_message(span_deconversion_message("[owner.current] looks like [owner.current.p_theyve()] just reverted to [owner.current.p_their()] old faith!"), \ + span_userdanger("As the ticking fades from the back of your mind, you forget all memories you had as a servant of Ratvar.")) + owner.current.log_message("has renounced the cult of Ratvar!", LOG_ATTACK, color="#960000") + handle_equipment_removal() + REMOVE_TRAIT(owner, TRAIT_MAGICALLY_GIFTED, REF(src)) + return ..() + +/datum/antagonist/clock_cultist/get_preview_icon() + var/datum/universal_icon/final_icon = render_preview_outfit(preview_outfit) + return finish_preview_icon(final_icon) + +/datum/antagonist/clock_cultist/on_mindshield(mob/implanter) + if(!silent) + to_chat(owner.current, span_warning("You feel something pushing away the light of Ratvar, but you resist it!")) + return + +/datum/antagonist/clock_cultist/admin_add(datum/mind/new_owner,mob/admin) + new_owner.add_antag_datum(src) + message_admins("[key_name_admin(admin)] has made [key_name_admin(new_owner)] into a servant of Ratvar.") + log_admin("[key_name(admin)] has made [key_name(new_owner)] into a servant of Ratvar.") + +/datum/antagonist/clock_cultist/admin_remove(mob/user) + silent = TRUE + return ..() + +/datum/antagonist/clock_cultist/get_admin_commands() + . = ..() + .["Give Slab"] = CALLBACK(src, PROC_REF(admin_give_slab)) + .["Remove Slab"] = CALLBACK(src, PROC_REF(admin_take_slab)) + +/datum/antagonist/clock_cultist/proc/admin_take_slab(mob/admin) + var/mob/living/current = owner.current + for(var/object in current.get_all_contents()) + if(istype(object, /obj/item/clockwork/clockwork_slab)) + qdel(object) + +/datum/antagonist/clock_cultist/proc/admin_give_slab(mob/admin) + if(!give_clockwork_slab(owner.current)) + to_chat(admin, span_danger("Spawning clockwork slab failed!")) + +//give a mob a slab directly into their inventory +/datum/antagonist/clock_cultist/proc/give_clockwork_slab(mob/living/carbon/human/give_to) + var/obj/item/clockwork/clockwork_slab/created_slab = new + give_item_to_holder(created_slab, list(LOCATION_BACKPACK = ITEM_SLOT_BACK, LOCATION_RPOCKET = ITEM_SLOT_RPOCKET, LOCATION_LPOCKET = ITEM_SLOT_LPOCKET)) + +/datum/antagonist/clock_cultist/proc/give_item_to_holder(obj/item/clockwork/clockwork_slab/created_slab, list/valid_slots) + if(ispath(created_slab)) + created_slab = new created_slab(get_turf(owner.current)) + + var/mob/living/carbon/human/human_holder = owner.current + + human_holder.equip_in_one_of_slots(created_slab, valid_slots, qdel_on_fail = FALSE, indirect_action = TRUE) + +/// Change the slab in the recall ability, if it's different from the last one. +/datum/antagonist/clock_cultist/proc/switch_recall_slab(datum/source, obj/item/clockwork/clockwork_slab/slab) + if(slab == recall.marked_slab) + return + + recall.unmark_item() + recall.mark_item(slab) + to_chat(owner.current, span_brass("You re-attune yourself to a new Clockwork Slab.")) + +/datum/antagonist/clock_cultist/proc/handle_silicon_conversion(mob/living/silicon/converted_silicon) + if(isAI(converted_silicon)) + var/mob/living/silicon/ai/converted_ai = converted_silicon + converted_ai.disconnect_shell() + for(var/mob/living/silicon/robot/borg in converted_ai.connected_robots) + borg.set_connected_ai(null) + var/mutable_appearance/ai_clock = mutable_appearance('tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_mobs.dmi', "aiframe") + converted_ai.add_overlay(ai_clock) + + else if(iscyborg(converted_silicon)) + var/mob/living/silicon/robot/converted_borg = converted_silicon + converted_borg.UnlinkSelf() + converted_borg.set_clockwork(TRUE) + + if(converted_silicon.laws && istype(converted_silicon.laws, /datum/ai_laws/ratvar)) + return + converted_silicon.laws = new /datum/ai_laws/ratvar + converted_silicon.laws.associate(converted_silicon) + converted_silicon.show_laws() + +///remove clock cult items from their inventory by dropping them +/datum/antagonist/clock_cultist/proc/handle_equipment_removal() + if(silent || !length(GLOB.types_to_drop_on_clock_deonversion)) + return + + var/mob/living/current = owner.current + for(var/obj/item/object as anything in current.get_all_contents()) + if(object.type in GLOB.types_to_drop_on_clock_deonversion) + current.dropItemToGround(object, TRUE, TRUE) + +/datum/antagonist/clock_cultist/proc/add_forbearance(mob/apply_to) + if(GLOB.clock_ark?.current_state >= ARK_STATE_ACTIVE) + apply_to.add_filter("forbearance", 3, list("type" = "outline", "color" = "#FAE48E", "size" = 2, "alpha" = 100)) + +/datum/outfit/clock/preview + name = "Clock Cultist (Preview only)" + + uniform = /obj/item/clothing/under/syndicate + suit = /obj/item/clothing/suit/clockwork + head = /obj/item/clothing/head/helmet/clockwork + l_hand = /obj/item/clockwork/weapon/brass_sword + +//these can just solo invoke things that normally take multiple servants +/datum/antagonist/clock_cultist/solo + name = "Servant of Ratvar (Solo)" + +//putting this here to avoid extra edits to the main file +/datum/antagonist/cult + ///used for holy water deconversion + var/static/list/cultist_deconversion_phrases = list( + "spoken" = list( + "Av'te Nar'Sie", + "Pa'lid Mors", + "INO INO ORA ANA", + "SAT ANA!", + "Daim'niodeis Arc'iai Le'eones", + "R'ge Na'sie", + "Diabo us Vo'iscum", + "Eld' Mon Nobis", + ), + + "seizure" = list( + "Your blood is your bond - you are nothing without it", + "Do not forget your place", + "All that power, and you still fail?", + "If you cannot scour this poison, I shall scour your meager life!" + ) + ) diff --git a/tff_modular/modules/antagonists/clock_cult/antag_datums/clockmob_antag_datum.dm b/tff_modular/modules/antagonists/clock_cult/antag_datums/clockmob_antag_datum.dm new file mode 100644 index 00000000000..921062abd97 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/antag_datums/clockmob_antag_datum.dm @@ -0,0 +1,23 @@ +/datum/antagonist/clock_cultist/clockmob + show_in_antagpanel = FALSE + ///Our warp action + var/datum/action/cooldown/clock_cult/clockmob_warp/warp_action = new + ///A ref to our area bound component + var/datum/component/multi_area_bound/area_bound_component + +/datum/antagonist/clock_cultist/clockmob/Destroy() + QDEL_NULL(warp_action) + return ..() + +/datum/antagonist/clock_cultist/clockmob/apply_innate_effects(mob/living/mob_override) + . = ..() + warp_action.Grant(owner.current) + if(area_bound_component) + QDEL_NULL(area_bound_component) + stack_trace("[src.type] calling apply_innate_effects while still having an old area_bound_component.") + area_bound_component = owner.current.AddComponent(/datum/component/multi_area_bound, _valid_areas = SSthe_ark.marked_areas + SSthe_ark.reebe_areas, _use_instances = TRUE) + +/datum/antagonist/clock_cultist/clockmob/remove_innate_effects(mob/living/mob_override) + . = ..() + warp_action.Remove(owner.current) + QDEL_NULL(area_bound_component) diff --git a/tff_modular/modules/antagonists/clock_cult/antag_datums/clocksense_alert.dm b/tff_modular/modules/antagonists/clock_cult/antag_datums/clocksense_alert.dm new file mode 100644 index 00000000000..f2efbf5251e --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/antag_datums/clocksense_alert.dm @@ -0,0 +1,61 @@ +//clock cult +/atom/movable/screen/alert/clockwork/clocksense + name = "The Ark of the Clockwork Justicar" + desc = "Shows infomation about the Ark of the Clockwork Justicar" + icon = 'tff_modular/modules/antagonists/clock_cult/icons/hud/screen_alert.dmi' + icon_state = "clockinfo" + alerttooltipstyle = "clockwork" + ///The static info we use so we only have to actually update our data once each tick + var/static/static_desc + +/atom/movable/screen/alert/clockwork/clocksense/Initialize(mapload, datum/hud/hud_owner) + . = ..() + if(!static_desc) + static_desc = get_static_desc() + desc = static_desc + if(!GLOB.ratvar_risen) + START_PROCESSING(SSprocessing, src) + +/atom/movable/screen/alert/clockwork/clocksense/Destroy() + STOP_PROCESSING(SSprocessing, src) + return ..() + +/atom/movable/screen/alert/clockwork/clocksense/process() + if(GLOB.ratvar_risen) + desc = "RATVAR HAS RISEN." + return PROCESS_KILL + + var/static/last_process_tick + if(!last_process_tick || world.time - last_process_tick > 1 SECONDS) + static_desc = get_static_desc() + last_process_tick = world.time + desc = static_desc + +/atom/movable/screen/alert/clockwork/clocksense/proc/get_static_desc() + if(GLOB.ratvar_risen) + return "RATVAR HAS RISEN." + + var/new_desc = "Stored Power - [display_power(SSthe_ark.clock_power, FALSE)].
" + new_desc += "Stored Vitality - [GLOB.clock_vitality].
" + new_desc += "Passive power access - [SSthe_ark.passive_power].
" + if(!GLOB.main_clock_cult) + return + + new_desc += "We current have [length(GLOB.main_clock_cult.human_servants)] human servants out of [GLOB.main_clock_cult.max_human_servants] maximum human servants, \ + as well as [length(GLOB.main_clock_cult.members)] servants all together.
" + + if(GLOB.clock_ark?.charging_for) + new_desc += "The Ark will open in [600 - GLOB.clock_ark.charging_for] seconds!
" + return //we dont care about anchoring crystals at this point + + var/static/list/cached_valid_areas + if(length(cached_valid_areas) != length(SSthe_ark.valid_crystal_areas)) //using length due to the cache being area names and not areas themselves + cached_valid_areas = list() + for(var/area/added_area in SSthe_ark.valid_crystal_areas) + cached_valid_areas += SSthe_ark.valid_crystal_areas[added_area] + new_desc += "Anchoring Crystals can be summoned in [english_list(cached_valid_areas)].
" + + var/crystal_diff = ANCHORING_CRYSTALS_TO_SUMMON - length(SSthe_ark.anchoring_crystals) + if(crystal_diff > 0) + new_desc += "We must summon [crystal_diff] more Anchoring Crystal[crystal_diff > 1 ? "s" : ""] before the ark may open.
" + return new_desc diff --git a/tff_modular/modules/antagonists/clock_cult/antag_datums/eminence_antag_datum.dm b/tff_modular/modules/antagonists/clock_cult/antag_datums/eminence_antag_datum.dm new file mode 100644 index 00000000000..c0d4c2acd2c --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/antag_datums/eminence_antag_datum.dm @@ -0,0 +1,49 @@ +/datum/antagonist/clock_cultist/eminence + name = "Eminence" + give_slab = FALSE + antag_moodlet = null + communicate = null + recall = null + ///The list of our actions + var/list/action_list = list( + /datum/action/innate/clockcult/space_fold, + /datum/action/cooldown/clock_cult/eminence/purge_reagents, + /datum/action/cooldown/clock_cult/eminence/linked_abscond, + /datum/action/innate/clockcult/teleport_to_servant, + /datum/action/innate/clockcult/teleport_to_station, + /datum/action/innate/clockcult/eminence_abscond, + /datum/action/innate/clockcult/show_warpable_areas, + /datum/action/innate/clockcult/add_warp_area, + ) + +/datum/antagonist/clock_cultist/eminence/Destroy() + QDEL_LIST(action_list) + return ..() + +/datum/antagonist/clock_cultist/eminence/greet() + to_chat(owner.current, boxed_message("[span_bigbrass("You are the Eminence, a being bound to Ratvar. By his light you are able to influence nearby space and time.")]
\ + [span_brass("As the Eminence you have access to various abilities, they are as follows.
\ + You may click on various machines to interface with them or a servant to mark them.
\ + Purge Reagents: Remove all reagents from the bloodstream of a marked servant, this is useful for a servant who is being deconverted by holy water.
\ + Linked Abscond: Return a marked servant and anything they are pulling to reebe, this has a lengthy cooldown and they must remain still for 7 seconds.
\ + Space Fold: Fold local spacetime to ensure certain \"events\" are inflicted upon the station, while doing this will cost cogs, \ + these cogs are not taken from the cult itself. The cooldown is based on the cog cost of the event.
\ + You can also teleport yourself to any other servant, useful for servants who need to be absconded like those which are dead or being deconverted.")]")) + +/datum/antagonist/clock_cultist/eminence/apply_innate_effects(mob/living/mob_override) + . = ..() + var/mob/living/current = owner.current + add_team_hud(current, /datum/antagonist/clock_cultist) + for(var/datum/action/our_action as anything in action_list) + if(ispath(our_action)) + our_action = new our_action() + our_action.Grant(current) + +/datum/antagonist/clock_cultist/eminence/remove_innate_effects(mob/living/mob_override) + . = ..() + for(var/datum/action/removed_action in action_list) + removed_action.Remove(owner.current) + +/datum/antagonist/clock_cultist/eminence/on_removal() //this should never happen without an admin being involved, something has gone wrong + to_chat(owner.current, span_userdanger("You lost your eminence antagonist status! This should not happen and you should ahelp(f1) unless you are already talking to an admin.")) + return ..() diff --git a/tff_modular/modules/antagonists/clock_cult/area.dm b/tff_modular/modules/antagonists/clock_cult/area.dm new file mode 100644 index 00000000000..6b5b03d1e21 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/area.dm @@ -0,0 +1,13 @@ +/area/ruin/powered/reebe + name = "Reebe" + ambience_index = AMBIENCE_REEBE + area_flags = UNIQUE_AREA | CULT_PERMITTED | NOTELEPORT + static_lighting = FALSE + base_lighting_alpha = 200 + +/area/ruin/powered/reebe/space + name = "Reebe Space" + base_lighting_alpha = 255 + +/area/ruin/powered/reebe/city + name = "City of Cogs" diff --git a/tff_modular/modules/antagonists/clock_cult/ark_subsystem/_ark_subsystem.dm b/tff_modular/modules/antagonists/clock_cult/ark_subsystem/_ark_subsystem.dm new file mode 100644 index 00000000000..48a0c90d26d --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/ark_subsystem/_ark_subsystem.dm @@ -0,0 +1,164 @@ +///A subsystem to manage the global effects of clock cult +#define SERVANT_CAPACITY_TO_GIVE 2 +PROCESSING_SUBSYSTEM_DEF(the_ark) + name = "The Clockwork Ark" + wait = 1 SECONDS + ss_flags = SS_NO_INIT | SS_KEEP_TIMING + runlevels = RUNLEVEL_GAME + ///The list of anchoring crystals, value is 0 is uncharged and 1 if charged + var/list/anchoring_crystals = list() + ///Dimension theme used for transforming turfs + var/datum/dimension_theme/clockwork/clock_dimension_theme = new /datum/dimension_theme/clockwork() + ///How many charged anchoring crystals are there + var/charged_anchoring_crystals = 0 + ///Assoc list of the original names of areas that are valid to summon anchoring crystals keyed to its area + var/list/valid_crystal_areas + ///The pool of hallucinations we can trigger + var/list/hallucination_pool + ///How many clockwork airlocks have been created on reebe, used for limiting airlock spam + var/reebe_clockwork_airlock_count = 0 + ///How much power does the cult have stored + var/clock_power = STANDARD_CELL_CHARGE * 0.25 + ///What is the maximum amount of power the cult can have stored + var/max_clock_power = STANDARD_CELL_CHARGE * 0.25 + ///How much passive power does the cult have access to, this gets used for things like turning on structures + var/passive_power = 15 + ///The list of areas that has been marked by the cult, formatted as a filled with 1s for anti duplication + var/list/marked_areas = list() + ///A list of all cogscarabs + var/list/cogscarabs = list() + ///A list of all clockwork marauders + var/list/clockwork_marauders = list() + ///A list of all the areas on reebe + var/list/reebe_areas = list() + +/datum/controller/subsystem/processing/the_ark/Initialize() + initialized = TRUE + hallucination_pool = list( + /datum/hallucination/fake_item/clockwork_slab = 2, + /datum/hallucination/nearby_fake_item/clockwork_slab = 2, + /datum/hallucination/hazard/clockwork_skewer = 1, + /datum/hallucination/delusion/preset/clock_cultists = 1, + /datum/hallucination/fake_sound/weird/clockcult_kindle = 2, + /datum/hallucination/fake_sound/weird/clockcult_warp = 2, + ) + +/datum/controller/subsystem/processing/the_ark/fire(resumed) + if(!initialized) //we are not currently being used so just return + return + + if(charged_anchoring_crystals) + handle_charged_crystals() + return ..() + +///try and adjust our clock_power, returns FALSE if it would put us above our max_clock_power or below 0, set always_adjust to TRUE to make us instead just adjust to be within bounds +/datum/controller/subsystem/processing/the_ark/proc/adjust_clock_power(amount, always_adjust = FALSE) + var/new_total = clock_power + amount + if(always_adjust) + clock_power = clamp(new_total, 0, max_clock_power) + return TRUE + + if(new_total > max_clock_power || new_total < 0) + return FALSE + clock_power = new_total + return TRUE + +///same as adjust_clock_power() but much simpler as this does not have a max amount and is somewhat static, set only_check to TRUE to skip the actual adjustment step +/datum/controller/subsystem/processing/the_ark/proc/adjust_passive_power(amount, only_check = FALSE) + var/new_total = passive_power + amount + if(new_total < 0) + return FALSE + + if(!only_check) + passive_power = new_total + return TRUE + +///set up timed do_turf_conversion calls for the turfs in an area +/datum/controller/subsystem/processing/the_ark/proc/convert_area_turfs(area/converted_area, conversion_percent = 100, counter_override) + var/timer_counter = counter_override || 1 //used by the addtimer() + var/list/turfs_to_transform = list() + for(var/i in 1 to length(converted_area.turfs_by_zlevel)) + turfs_to_transform += converted_area.turfs_by_zlevel[i] + + var/transformed_length = length(turfs_to_transform) + var/converted_amount = round(transformed_length * (conversion_percent * 0.01)) + while(transformed_length && transformed_length > converted_amount) + pick_n_take(turfs_to_transform) + transformed_length = length(turfs_to_transform) + + shuffle_inplace(turfs_to_transform) + for(var/turf/turf_to_transform as anything in turfs_to_transform) + if(!clock_dimension_theme.can_convert(turf_to_transform)) + continue + addtimer(CALLBACK(src, PROC_REF(do_turf_conversion), turf_to_transform), 3 * timer_counter) + timer_counter++ + return timer_counter //so you can do stuff once the conversion ends + +///convert a turf to our dimension theme +/datum/controller/subsystem/processing/the_ark/proc/do_turf_conversion(turf/converted_turf) + if(QDELETED(src) || !clock_dimension_theme.can_convert(converted_turf)) + return + + clock_dimension_theme.apply_theme(converted_turf) + new /obj/effect/temp_visual/ratvar/beam(converted_turf) + if(istype(converted_turf, /turf/closed/wall)) + new /obj/effect/temp_visual/ratvar/wall(converted_turf) + else if(istype(converted_turf, /turf/open/floor)) + new /obj/effect/temp_visual/ratvar/floor(converted_turf) + +///called when an anchoring crystal is charged +/datum/controller/subsystem/processing/the_ark/proc/on_crystal_charged(obj/structure/destructible/clockwork/anchoring_crystal/charged_crystal) + charged_anchoring_crystals++ + anchoring_crystals[charged_crystal] = 1 + SEND_SIGNAL(src, COMSIG_ANCHORING_CRYSTAL_CHARGED, charged_crystal) + passive_power += 5 * CLOCK_PASSIVE_POWER_PER_COG //5 APCs worth of passive power + max_clock_power += CLOCK_MAX_POWER_PER_COG * 5 + var/datum/scripture/create_structure/anchoring_crystal/crystal_script + addtimer(CALLBACK(src, PROC_REF(clear_shuttle_interference), charged_crystal), \ + (ANCHORING_CRYSTAL_COOLDOWN - ANCHORING_CRYSTAL_CHARGE_DURATION) + initial(crystal_script.invocation_time)) + GLOB.main_clock_cult.max_human_servants += SERVANT_CAPACITY_TO_GIVE + if(charged_anchoring_crystals == ANCHORING_CRYSTALS_TO_SUMMON + 1) //create a steam helios on reebe + if(length(GLOB.abscond_markers)) + var/turf/created_at = get_turf(pick(GLOB.abscond_markers)) + new /obj/vehicle/sealed/mecha/steam_helios(created_at) + new /obj/effect/temp_visual/steam(created_at) + else if(GLOB.clock_ark) + new /obj/vehicle/sealed/mecha/steam_helios(get_turf(GLOB.clock_ark)) + else + message_admins("No valid location for Steam Helios creation.") + +///fully disables the shuttle similar to the admin verb +/datum/controller/subsystem/processing/the_ark/proc/block_shuttle(datum/blocker) + if(SSshuttle.admin_emergency_no_recall || SSshuttle.emergency.mode == SHUTTLE_DISABLED || SSshuttle.emergency.mode == SHUTTLE_ESCAPE) + return + + SSshuttle.last_mode = SSshuttle.emergency.mode + SSshuttle.last_call_time = SSshuttle.emergency.timeLeft(1) + SSshuttle.emergency_no_recall = TRUE + SSshuttle.emergency.setTimer(0) + SSshuttle.emergency.mode = SHUTTLE_DISABLED + +///renables the shuttle +/datum/controller/subsystem/processing/the_ark/proc/clear_shuttle_interference(datum/unblocker) + if(SSshuttle.admin_emergency_no_recall || SSshuttle.emergency.mode != SHUTTLE_DISABLED || \ + (unblocker && GLOB.clock_ark && GLOB.clock_ark.current_state >= ARK_STATE_CHARGING && istype(unblocker, /obj/structure/destructible/clockwork/anchoring_crystal))) + return + + SSshuttle.emergency_no_recall = FALSE + if(SSshuttle.last_mode == SHUTTLE_DISABLED) + SSshuttle.last_mode = SHUTTLE_IDLE + + SSshuttle.emergency.mode = SSshuttle.last_mode + if(SSshuttle.last_call_time < 10 SECONDS && SSshuttle.last_mode != SHUTTLE_IDLE) + SSshuttle.last_call_time = 10 SECONDS //Make sure no insta departures. + SSshuttle.emergency.setTimer(SSshuttle.last_call_time) + priority_announce("Emergency shuttle uplink connection regained.", "Higher Dimensional Affairs", ANNOUNCER_SPANOMALIES, has_important_message = TRUE) + +///returns how many charged anchor crystals there are +/datum/controller/subsystem/processing/the_ark/proc/get_charged_anchor_crystals() + var/charged_count = 0 + for(var/crystal in SSthe_ark.anchoring_crystals) + charged_count += SSthe_ark.anchoring_crystals[crystal] + return charged_count + +#undef SERVANT_CAPACITY_TO_GIVE diff --git a/tff_modular/modules/antagonists/clock_cult/ark_subsystem/warp_effects.dm b/tff_modular/modules/antagonists/clock_cult/ark_subsystem/warp_effects.dm new file mode 100644 index 00000000000..03a8c104266 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/ark_subsystem/warp_effects.dm @@ -0,0 +1,175 @@ +//boy this sure is some fun code +/datum/controller/subsystem/processing/the_ark/proc/handle_charged_crystals() + if(prob(charged_anchoring_crystals)) + crystal_warp_minds() + + if(charged_anchoring_crystals >= 2 && prob(charged_anchoring_crystals)) + crystal_warp_machines() + + if(charged_anchoring_crystals >= 3 && prob(charged_anchoring_crystals)) + crystal_warp_space() + +/datum/controller/subsystem/processing/the_ark/proc/crystal_warp_minds() + var/list/players = GLOB.alive_player_list.Copy() + var/mob/living/selected_player = pick_n_take(players) + var/sanity = 0 + if(!selected_player) + return + + while(sanity < 100 && (IS_CLOCK(selected_player) || !is_station_level(selected_player.z))) + if(!length(players)) + return + sanity++ + selected_player = pick_n_take(players) + + if(prob(50)) + selected_player.cause_hallucination(pick_weight(hallucination_pool), "The Clockwork Ark") + else + to_chat(selected_player, span_warning(pick(list("You hear a faint ticking in the back of your mind", "You smell something metallic", \ + "You see a flash of light out of the corner of your eye", "You feel an otherworldly presence", "You feel like your forgetting something")))) + +//making these their own procs for eaiser to read code +/datum/controller/subsystem/processing/the_ark/proc/crystal_warp_machines() + switch(rand(1, 3)) + if(1) //randomly mess with the settings of an APC with a low chance to emag it + var/list/apcs = SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/power/apc, /obj/machinery/power/apc/worn_out) + var/obj/machinery/power/apc/picked_apc = pick_n_take(apcs) //pick_n_take() handles length checking + if(!picked_apc) + return + var/sanity = 0 + while(sanity < 100 && length(apcs) && !is_station_level(picked_apc.z)) + picked_apc = pick_n_take(apcs) + sanity++ + if(picked_apc) + if(prob(30)) + picked_apc.overload_lighting() + else + picked_apc.lighting = picked_apc.setsubsystem(1) + if(prob(30)) + picked_apc.equipment = picked_apc.setsubsystem(1) + if(prob(30)) + picked_apc.environ = picked_apc.setsubsystem(1) + addtimer(CALLBACK(picked_apc, TYPE_PROC_REF(/obj/machinery/power/apc, setsubsystem), rand(2, 3)), 1 MINUTES) + if(!(picked_apc.obj_flags & EMAGGED) && prob(10)) + playsound(src, SFX_SPARKS, 75, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + picked_apc.obj_flags |= EMAGGED + picked_apc.locked = FALSE + picked_apc.update_appearance() + if(2) //force open an airlock and bolt it + var/list/airlocks = SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/door/airlock, \ + typesof(/obj/machinery/door/airlock/maintenance) + typesof(/obj/machinery/door/airlock/bronze/clock) + /obj/machinery/door/airlock/maintenance_hatch) + var/obj/machinery/door/airlock/picked_airlock = pick_n_take(airlocks) + if(!picked_airlock) + return + var/sanity = 0 + while(sanity < 100 && length(airlocks) && (!picked_airlock.hasPower() || !is_station_level(picked_airlock.z) || picked_airlock.is_probably_external_airlock())) + picked_airlock = pick_n_take(airlocks) + sanity++ + if(picked_airlock) + picked_airlock.unbolt() + picked_airlock.open(FORCING_DOOR_CHECKS) + picked_airlock.bolt() + if(3) //emag a random atom from our list of valid types + var/list/valid_emag_targets = list( + /mob/living/simple_animal/bot, + /mob/living/basic/bot, + /obj/machinery/announcement_system, + /obj/machinery/barsign, + /obj/machinery/computer/communications, + /obj/machinery/medical_kiosk, + /obj/machinery/sleeper, + /obj/machinery/computer/slot_machine, + /obj/machinery/computer/cargo/express, + /obj/machinery/computer/cargo, + /obj/machinery/destructive_scanner, + /obj/machinery/fishing_portal_generator, + /obj/machinery/computer/holodeck, + /obj/machinery/elevator_control_panel, + /obj/machinery/fax, + /obj/machinery/chem_dispenser, + /obj/machinery/research/anomaly_refinery, + /obj/machinery/computer/bsa_control, + /obj/machinery/vending, + /obj/machinery/computer/operating, + ) + var/atom/selected_type = pick_n_take(valid_emag_targets) + var/list/valid_type_instances = get_emag_target_type_instances(selected_type) + var/sanity = 0 + var/obj/machinery/selected_atom + while(!selected_atom && sanity < 10) + sanity++ + while(!length(valid_type_instances)) + if(!length(valid_emag_targets)) + return + selected_type = pick_n_take(valid_emag_targets) + valid_type_instances = get_emag_target_type_instances(selected_type) + while(!selected_atom || bot_v_machine_check(selected_atom) || !is_station_level(selected_atom.z)) + if(!length(valid_type_instances)) + selected_atom = null + break + selected_atom = pick_n_take(valid_type_instances) + if(!selected_atom) + return + if(isbasicbot(selected_atom)) + var/mob/living/basic/bot/basic_bot = selected_atom + basic_bot.bot_access_flags &= ~BOT_COVER_MAINTS_OPEN + else if(isbot(selected_atom)) + var/mob/living/simple_animal/bot/simple_bot = selected_atom + simple_bot.bot_cover_flags &= ~BOT_COVER_MAINTS_OPEN + selected_atom.emag_act() + +/datum/controller/subsystem/processing/the_ark/proc/crystal_warp_space() + switch(rand(1, 2)) + if(1) + var/datum/action/cooldown/spell/spacetime_dist/clock_ark/dist_spell = new + var/turf/turf = get_random_station_turf() + dist_spell.cast(turf) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), dist_spell), dist_spell.duration) + if(2) + var/list/servants = list() //technically we could adjust this everytime someone joins or leaves the cult but these last for 30 seconds so eh + if(GLOB.main_clock_cult) + for(var/datum/mind/servant_mind in GLOB.main_clock_cult.members) + servants += servant_mind.current + new /obj/effect/timestop/magic/clock_ark(get_random_station_turf(), 1, 30 SECONDS, servants) + return + +/datum/controller/subsystem/processing/the_ark/proc/get_emag_target_type_instances(input_path) + if(ispath(input_path, /obj/machinery)) + return SSmachines.get_machines_by_type(input_path) + if(ispath(input_path, /mob/living/simple_animal/bot) || ispath(input_path, /mob/living/basic/bot)) + return GLOB.bots_list.Copy() + +//OH YEAH I LOVE GOOD CODE +/datum/controller/subsystem/processing/the_ark/proc/bot_v_machine_check(atom/checked_atom) + if(ismachinery(checked_atom)) + var/obj/machinery/checked_machine = checked_atom + return checked_machine.obj_flags & EMAGGED + if(isbasicbot(checked_atom)) + var/mob/living/basic/bot/checked_basic_bot = checked_atom + return checked_basic_bot.bot_access_flags & BOT_COVER_EMAGGED + if(istype(checked_atom, /mob/living/simple_animal/bot)) + var/mob/living/simple_animal/bot/checked_bot = checked_atom + return checked_bot.bot_cover_flags & BOT_COVER_EMAGGED + +/datum/action/cooldown/spell/spacetime_dist/clock_ark + name = "Clockwork Spacetime Dist" + cooldown_time = 0 + scramble_radius = 2 + duration = 1 MINUTES + +/datum/action/cooldown/spell/spacetime_dist/clock_ark/cast(atom/cast_on) + . = ..() + new /obj/effect/cross_action/spacetime_dist/clock_ark(owner.loc) + +/obj/effect/cross_action/spacetime_dist/clock_ark + +/obj/effect/cross_action/spacetime_dist/clock_ark/walk_link(atom/movable/AM) + if(isliving(AM)) + var/mob/living/living_mob = AM + if(IS_CLOCK(living_mob)) + return + return ..() + +/obj/effect/timestop/magic/clock_ark + icon_state = "" + hidden = TRUE diff --git a/tff_modular/modules/antagonists/clock_cult/dimension_theme.dm b/tff_modular/modules/antagonists/clock_cult/dimension_theme.dm new file mode 100644 index 00000000000..4eed25c078b --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/dimension_theme.dm @@ -0,0 +1,11 @@ +/datum/dimension_theme/clockwork + icon = 'icons/obj/stack_objects.dmi' + icon_state = "sheet-brass" + sound = 'sound/effects/magic/clockwork/fellowship_armory.ogg' + replace_floors = list(/turf/open/floor/bronze = 1) + replace_walls = /turf/closed/wall/clockwork + replace_objs = list(/obj/machinery/door/airlock = list(/obj/machinery/door/airlock/bronze/clock = 1), \ + /obj/structure/chair = list(/obj/structure/chair/bronze = 1), \ + /obj/structure/table = list(/obj/structure/table/bronze = 1)) + + replace_window = /obj/structure/window/bronze/fulltile diff --git a/tff_modular/modules/antagonists/clock_cult/dynamic_ruleset.dm b/tff_modular/modules/antagonists/clock_cult/dynamic_ruleset.dm new file mode 100644 index 00000000000..c184923fbe2 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/dynamic_ruleset.dm @@ -0,0 +1,49 @@ +/datum/dynamic_ruleset/roundstart/clock_cultist + name = "Clockwork Cult" + config_tag = "Roundstart Clockwork Cultist" + pref_flag = ROLE_ROUNDSTART_CLOCK_CULTIST + preview_antag_datum = /datum/antagonist/clock_cultist + blacklisted_roles = list( + JOB_AI, + JOB_CAPTAIN, + JOB_SECURITY_OFFICER, + JOB_CHAPLAIN, + JOB_CYBORG, + JOB_DETECTIVE, + JOB_HEAD_OF_PERSONNEL, + JOB_HEAD_OF_SECURITY, + JOB_PRISONER, + JOB_WARDEN, + ) + weight = 6 + min_pop = 20 + max_antag_cap = list("denominator" = 18) + +/datum/dynamic_ruleset/roundstart/clock_cultist/assign_role(datum/mind/candidate) + candidate.add_antag_datum(/datum/antagonist/clock_cultist/solo) + +/datum/dynamic_ruleset/midround/clock_cultist + name = "Midround Clockwork Cult" + config_tag = "Midround Clockwork Cultist" + pref_flag = ROLE_MIDROUND_CLOCK_CULTIST + midround_type = LIGHT_MIDROUND + preview_antag_datum = /datum/antagonist/clock_cultist + blacklisted_roles = list( + JOB_AI, + JOB_CAPTAIN, + JOB_SECURITY_OFFICER, + JOB_CHAPLAIN, + JOB_CYBORG, + JOB_DETECTIVE, + JOB_HEAD_OF_PERSONNEL, + JOB_HEAD_OF_SECURITY, + JOB_PRISONER, + JOB_WARDEN, + ) + weight = 6 + min_pop = 20 + max_antag_cap = list("denominator" = 18) + repeatable_weight_decrease = 3 + +/datum/dynamic_ruleset/midround/clock_cultist/assign_role(datum/mind/candidate) + candidate.add_antag_datum(/datum/antagonist/clock_cultist/solo) diff --git a/tff_modular/modules/antagonists/clock_cult/enchantment.dm b/tff_modular/modules/antagonists/clock_cult/enchantment.dm new file mode 100644 index 00000000000..7be25dc8baf --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/enchantment.dm @@ -0,0 +1,284 @@ +GLOBAL_LIST_EMPTY(enchantment_datums_by_type) + +/datum/component/enchanted + dupe_mode = COMPONENT_DUPE_ALLOWED + ///Current enchantment level + var/level + ///The span we warp our examine text in + var/used_span + ///A ref to the enchantment datum we are using + var/datum/enchantment/used_enchantment + ///A list of all enchantments + var/static/list/all_enchantments = list() + +/datum/component/enchanted/Initialize(list/select_enchants_from = subtypesof(/datum/enchantment), used_span = "", level_override) + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + + if(!length(select_enchants_from)) + stack_trace("[src.type] calling Initialize with unset select_enchants_from.") + return COMPONENT_INCOMPATIBLE + + if(!length(all_enchantments)) + generate_enchantment_datums() + + for(var/entry in select_enchants_from) + if(ispath(entry)) + select_enchants_from -= entry + select_enchants_from += GLOB.enchantment_datums_by_type[entry] + + used_enchantment = pick(select_enchants_from) + src.used_span = used_span + level = level_override || rand(1, used_enchantment.max_level) + +/datum/component/enchanted/RegisterWithParent() + var/list/component_list = used_enchantment.components_by_parent[parent] + if(!component_list) + used_enchantment.components_by_parent[parent] = list(text_ref(used_enchantment) = src) //used_enchantment is not being taken as a ref? + else + component_list[used_enchantment] = src + used_enchantment.apply_effect(parent, level) + RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + +/datum/component/enchanted/UnregisterFromParent() + UnregisterSignal(parent, COMSIG_ATOM_EXAMINE) + var/list/component_list = used_enchantment.components_by_parent[parent] + component_list -= used_enchantment + if(!length(component_list)) + used_enchantment.components_by_parent -= parent + +/datum/component/enchanted/Destroy(force) + used_enchantment = null + return ..() + +/datum/component/enchanted/proc/on_examine(datum/source, mob/user, list/examine_list) + SIGNAL_HANDLER + + if(!used_enchantment.examine_description) + return + + if(isobserver(user) || HAS_MIND_TRAIT(user, TRAIT_MAGICALLY_GIFTED)) + if(used_span) + examine_list += "[used_span][used_enchantment.examine_description]" + examine_list += "[used_span]It's blessing has a power of [level]!
" + return + examine_list += "[used_enchantment.examine_description]" + examine_list += "It's blessing has a power of [level]!
" + else + examine_list += "It is glowing slightly!" + var/mob/living/living_user = user + if(istype(living_user.get_item_by_slot(ITEM_SLOT_EYES), /obj/item/clothing/glasses/science)) + examine_list += "It emits a readable EMF factor of [level]." + +/datum/enchantment + ///Examine text + var/examine_description + ///Maximum enchantment level + var/max_level = 1 + ///Typecache of items we are allowed on, generation handled in get_allowed_on + var/list/allowed_on + ///A recursive assoc list keyed as: [parent] = list(text_ref(enchant_component.used_enchantment) = enchant_component) + var/static/list/list/datum/component/enchanted/components_by_parent = list() + +/datum/enchantment/New() + . = ..() + allowed_on = get_allowed_on() + +/** + * Because of dumb BYOND reasons in order to get fine manual control we need to handle generation of allowed_on this way(via setting the default passed values) + * + * allowed_on_base - Typecache of items we are allowed on + * + * denied_from - Anything in this list will be set to FALSE in allowed_on + * + * overriden_types - Any values in this list will override allowed_on, this is handled last + */ +/datum/enchantment/proc/get_allowed_on(list/allowed_on_base = typecacheof(/obj/item), list/denied_from = typesof(/obj/item/clothing), list/overriden_types = list()) //AHHHHH + if(denied_from) + for(var/denied_entry in denied_from) + allowed_on_base[denied_entry] = 0 + if(overriden_types) + for(var/entry in overriden_types) + allowed_on_base[entry] = overriden_types[entry] + return allowed_on_base + +/datum/enchantment/proc/get_component_from_parent(obj/item/parent) as /datum/component/enchanted + RETURN_TYPE(/datum/component/enchanted) + var/list/parent_list = components_by_parent[parent] + if(!parent_list) + return FALSE + return parent_list[text_ref(src)] + +/datum/enchantment/proc/can_apply_to(obj/item/checked) + return allowed_on[checked.type] && examine_description + +/datum/enchantment/proc/apply_effect(obj/item/target, level) + register_item(target) + +///Handle comsig reg here +/datum/enchantment/proc/register_item(obj/item/target) + return + +///Handle comsig unreg here +/datum/enchantment/proc/unregister_item(obj/item/target) + return + +/datum/enchantment/clothing + +/datum/enchantment/clothing/get_allowed_on(list/allowed_on_base = typecacheof(/obj/item/clothing), list/denied_from = list(), list/overriden_types) + return ..() + +/proc/attempt_enchantment(obj/item/enchanted, list/valid_enchant_types = subtypesof(/datum/enchantment), description_span = "", level_override) + if(!isitem(enchanted)) + return FALSE + + if(!length(GLOB.enchantment_datums_by_type)) + generate_enchantment_datums() + + if(!islist(valid_enchant_types)) + valid_enchant_types = list(valid_enchant_types) + + for(var/datum/enchantment/enchant as anything in valid_enchant_types) + valid_enchant_types -= enchant + enchant = GLOB.enchantment_datums_by_type[enchant] + if(enchant.can_apply_to(enchanted)) + valid_enchant_types += enchant + + if(!length(valid_enchant_types)) + return FALSE + return enchanted.AddComponent(/datum/component/enchanted, valid_enchant_types, description_span, level_override) + +/proc/generate_enchantment_datums() + for(var/datum/enchantment/enchant as anything in subtypesof(/datum/enchantment)) + GLOB.enchantment_datums_by_type[enchant] = new enchant() + +/datum/enchantment/blinding + examine_description = "It has been blessed with the power to emit a blinding light when striking a target." + max_level = 1 + +/datum/enchantment/blinding/register_item(obj/item/target) + RegisterSignal(target, COMSIG_ITEM_ATTACK, PROC_REF(flash_target)) + +/datum/enchantment/blinding/unregister_item(obj/item/target) + UnregisterSignal(target, COMSIG_ITEM_ATTACK) + +/datum/enchantment/blinding/proc/flash_target(obj/item/source, mob/living/target, mob/living/user) + if(!istype(target)) + return + source.visible_message(span_danger("\The [source] emits a blinding light!")) + target.flash_act(2, affect_silicon = FALSE, length = 2 SECONDS) //might want to make this not effect borgs // Сделал братуха + +//might have to change this due to us having TG instead of bee blocking +/datum/enchantment/blocking + examine_description = "It has been blessed with the gift of blocking." + max_level = 3 + +/datum/enchantment/blocking/apply_effect(obj/item/target, level) + target.block_chance += 5 * level + +/datum/enchantment/burn + examine_description = "It has been blessed with the power of fire and will set struck targets on fire." + max_level = 3 + +/datum/enchantment/burn/apply_effect(obj/item/target, level) + . = ..() + target.damtype = BURN + +/datum/enchantment/burn/register_item(obj/item/target) + RegisterSignal(target, COMSIG_ITEM_ATTACK, PROC_REF(burn_target)) + +/datum/enchantment/burn/unregister_item(obj/item/target) + UnregisterSignal(target, COMSIG_ITEM_ATTACK) + return ..() + +/datum/enchantment/burn/proc/burn_target(obj/item/source, atom/movable/target, mob/living/user) + if(!isliving(target)) + return + var/datum/component/enchanted/comp = get_component_from_parent(source) + if(!comp) + return + + var/mob/living/living_target = target + living_target.adjust_fire_stacks(comp.level) + living_target.ignite_mob() + +/datum/enchantment/electricution + max_level = 3 + examine_description = "It has been blessed with the power of electricity and will shock targets." + +/datum/enchantment/electricution/register_item(obj/item/target) + RegisterSignal(target, COMSIG_ITEM_ATTACK, PROC_REF(shock_target)) + +/datum/enchantment/electricution/unregister_item(obj/item/target) + UnregisterSignal(target, COMSIG_ITEM_ATTACK) + +/datum/enchantment/electricution/proc/shock_target(obj/item/source, atom/movable/target, mob/living/user) + user.Beam(target, icon_state = "lightning[rand(1,12)]", time = 2, maxdistance = 32) + if(!iscarbon(target)) + return + var/datum/component/enchanted/comp = get_component_from_parent(source) + if(!comp) + return + + var/mob/living/carbon/carbon_target = target + if(carbon_target.electrocute_act(comp.level * 3, user, 1, SHOCK_NOSTUN)) //need to make this ark, also this seems to work on any living mob + carbon_target.visible_message(span_danger("[user] electrocutes [target]!"), span_userdanger("[user] electrocutes you!")) + +/datum/enchantment/haste + examine_description = "It has been blessed with the ability to warp time around it so that it's user may attack faster with it." + max_level = 1 + +/datum/enchantment/haste/apply_effect(obj/item/target) + target.attack_speed = max(1, target.attack_speed - 2) + +/datum/enchantment/penetration + examine_description = "It has been blessed with the gift of armor penetration, allowing it to cut through targets with ease." + max_level = 5 + +/datum/enchantment/penetration/apply_effect(obj/item/target, level) + target.armour_penetration = max(15 * level, target.armour_penetration) + +/datum/enchantment/sharpness + examine_description = "It has been blessed with the gift of sharpness." + max_level = 2 + +/datum/enchantment/sharpness/apply_effect(obj/item/target, level) + target.force += 5 * level + +/datum/enchantment/soul_tap + examine_description = "It has been blessed with the power of ripping the energy from target's souls and will heal the wielder when a target is struck." + max_level = 3 + +/datum/enchantment/soul_tap/unregister_item(obj/item/target) + UnregisterSignal(target, COMSIG_ITEM_ATTACK) + +/datum/enchantment/soul_tap/register_item(obj/item/target) + RegisterSignal(target, COMSIG_ITEM_ATTACK, PROC_REF(tap_soul)) + +/datum/enchantment/soul_tap/proc/tap_soul(obj/item/source, mob/living/target, mob/living/user) + if(!istype(target) || target.stat != CONSCIOUS) + return + var/datum/component/enchanted/comp = get_component_from_parent(source) + if(!comp) + return + var/health_back = CEILING(comp.level * source.force * 0.1, 1) + user.heal_overall_damage(health_back, health_back) + new /obj/effect/temp_visual/heal(get_turf(user), "#eeba6b") + +/datum/enchantment/tiny + examine_description = "It has been blessed and distorts reality into a tiny space around it." + max_level = 1 + +/datum/enchantment/tiny/get_allowed_on(list/allowed_on_base, list/denied_from = list(), list/overriden_types) + . = ..() + +/datum/enchantment/tiny/apply_effect(obj/item/target) + target.w_class = WEIGHT_CLASS_TINY + +/datum/enchantment/clothing/speed + examine_description = "It has been blessed and reduces the weight of the item." + max_level = 2 + +/datum/enchantment/clothing/speed/apply_effect(obj/item/target, level) + var/slowdown_level = max_level * 0.1 + target.slowdown -= slowdown_level diff --git a/tff_modular/modules/antagonists/clock_cult/globals.dm b/tff_modular/modules/antagonists/clock_cult/globals.dm new file mode 100644 index 00000000000..103309f9951 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/globals.dm @@ -0,0 +1,8 @@ +GLOBAL_VAR_INIT(clock_power, 2500) +GLOBAL_VAR_INIT(max_clock_power, 2500) // Increases with every APC cogged +GLOBAL_VAR_INIT(clock_vitality, 100) //start with only a bit of vitality +GLOBAL_VAR_INIT(clock_installed_cogs, 0) +GLOBAL_LIST_INIT(clock_turf_types, typesof(/turf/open/floor/bronze, /turf/open/indestructible/reebe_void, /turf/open/indestructible/reebe_flooring, /turf/closed/wall/clockwork, /turf/open/floor/engine/clockwork)) +GLOBAL_LIST_EMPTY(types_to_drop_on_clock_deonversion) //list of types to check for dropping on deconversion +//CONVERT TO GLOBAL DATUM +GLOBAL_VAR_INIT(narsie_breaching_rune, FALSE) //rune where nar'sie is trying to summon from, if it gets destroyed somehow then just summon her on a random station turf diff --git a/tff_modular/modules/antagonists/clock_cult/hallucinations.dm b/tff_modular/modules/antagonists/clock_cult/hallucinations.dm new file mode 100644 index 00000000000..1cb26c864f9 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/hallucinations.dm @@ -0,0 +1,55 @@ +/datum/hallucination/fake_item/clockwork_slab + random_hallucination_weight = 0 + template_item_type = /obj/item/clockwork/clockwork_slab + valid_slots = ITEM_SLOT_HANDS + +/datum/hallucination/nearby_fake_item/clockwork_slab + left_hand_file = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_lefthand.dmi' + right_hand_file = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_righthand.dmi' + image_icon_state = "clockwork_slab" + +/datum/hallucination/hazard/clockwork_skewer + random_hallucination_weight = 0 + hazard_type = /obj/effect/client_image_holder/hallucination/danger/clockwork_skewer + +/obj/effect/client_image_holder/hallucination/danger/clockwork_skewer + name = "brass skewer" + image_icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + image_state = "brass_skewer" + +/obj/effect/client_image_holder/hallucination/danger/clockwork_skewer/on_hallucinator_entered(mob/living/afflicted) + to_chat(afflicted, span_userdanger("You are impaled by [src]!")) + afflicted.visible_message(span_warning("[afflicted] falls to the ground suddenly!"), ignored_mobs = afflicted) + afflicted.Paralyze(4 SECONDS) + afflicted.emote("scream") + afflicted.adjust_stamina_loss(40) + image_state = "brass_skewer_pokeybit" + image_layer = ABOVE_MOB_LAYER + update_appearance() + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), afflicted, span_notice("...You feel the pain of being stabbed fading strangely quickly.")), 1.5 SECONDS) + QDEL_IN(src, 3 SECONDS) + +/datum/hallucination/delusion/preset/clock_cultists + dynamic_delusion = TRUE + random_hallucination_weight = 0 + delusion_name = "Servant of Ratvar" + affects_others = TRUE + skip_nearby = TRUE + +/datum/hallucination/delusion/preset/clock_cultists/make_delusion_image(mob/over_who) + var/static/mutable_appearance/servant_appearance + if(isnull(servant_appearance)) + servant_appearance = get_dynamic_human_appearance(/datum/outfit/clock/preview, r_hand = NO_REPLACE) + delusion_appearance = servant_appearance + return ..() + +/datum/hallucination/fake_sound/weird/clockcult_kindle + sound_type = 'sound/effects/magic/staff_animation.ogg' + +/datum/hallucination/fake_sound/weird/clockcult_kindle/play_fake_sound(turf/source, sound_to_play) + . = ..() + if(prob(50)) + queue_fake_sound(source, 'sound/items/weapons/handcuffs.ogg', delay = 4 SECONDS) + +/datum/hallucination/fake_sound/weird/clockcult_warp + sound_type = 'sound/effects/magic/magic_missile.ogg' diff --git a/tff_modular/modules/antagonists/clock_cult/helpers.dm b/tff_modular/modules/antagonists/clock_cult/helpers.dm new file mode 100644 index 00000000000..a117702be9e --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/helpers.dm @@ -0,0 +1,42 @@ +///check if an atom is on the reebe z level, will also return FALSE if the atom has no z level +/proc/on_reebe(atom/checked_atom) + var/turf/checked_turf = get_turf(checked_atom) + if(!checked_turf?.z || !is_reebe_level(checked_turf.z)) + return FALSE + return TRUE + +/proc/gods_battle() + if(GLOB.cult_narsie && GLOB.cult_ratvar) + var/datum/component/singularity/narsie_singularity_component = GLOB.cult_narsie.singularity?.resolve() + var/datum/component/singularity/ratvar_singularity_component = GLOB.cult_ratvar.singularity?.resolve() + if(!narsie_singularity_component || !ratvar_singularity_component) + message_admins("gods_battle() called without a singularity component on of of the 2 main gods.") + return FALSE + + narsie_singularity_component.target = GLOB.cult_ratvar + ratvar_singularity_component.target = GLOB.cult_narsie + return TRUE + return FALSE + +/proc/try_servant_warp(mob/living/servant, turf/target_turf) + var/mob/living/pulled = servant.pulling + playsound(servant, 'sound/effects/magic/magic_missile.ogg', 50, TRUE) //doing this manually for sound volume reasons + playsound(target_turf, 'sound/effects/magic/magic_missile.ogg', 50, TRUE) + do_sparks(3, TRUE, servant) + do_sparks(3, TRUE, target_turf) + do_teleport(servant, target_turf, 0, no_effects = TRUE, channel = TELEPORT_CHANNEL_CULT, forced = TRUE) + to_chat(servant, "You warp to [get_area(target_turf)].") + if(!IS_CLOCK(servant) || !on_reebe(servant)) + servant.apply_status_effect(/datum/status_effect/clock_warp_sickness, 15 SECONDS) + + if(ishuman(servant)) //looks weird on non-humanoids + new /obj/effect/temp_visual/ratvar/warp(target_turf) + + if(istype(pulled, /mob/living)) + do_teleport(pulled, target_turf, 0, no_effects = TRUE, channel = TELEPORT_CHANNEL_CULT, forced = TRUE) + if(!IS_CLOCK(pulled)) + pulled.Paralyze(3 SECONDS) + to_chat(pulled, span_warning("You feel sick and confused.")) + pulled.apply_status_effect(/datum/status_effect/clock_warp_sickness, 15 SECONDS) + else if(!on_reebe(pulled)) + pulled.apply_status_effect(/datum/status_effect/clock_warp_sickness, 15 SECONDS) diff --git a/tff_modular/modules/antagonists/clock_cult/hint_element.dm b/tff_modular/modules/antagonists/clock_cult/hint_element.dm new file mode 100644 index 00000000000..2af060a0607 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/hint_element.dm @@ -0,0 +1,44 @@ +/datum/element/clockwork_description + element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH_ON_HOST_DESTROY + argument_hash_start_idx = 2 + + /// Text to add to the description of the parent + var/text_to_add = "" + /// Text to add if we are on reebe + var/reebe_desc = "" + +/datum/element/clockwork_description/Attach(datum/target, parent_text, reebe_text) + . = ..() + + if(!isatom(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(add_examine)) + // Don't perform the assignment if there is nothing to assign, or if we already have something for this bespoke element + if(parent_text && !text_to_add) + text_to_add = parent_text + + if(reebe_text && !reebe_desc) + reebe_desc = reebe_text + +/datum/element/clockwork_description/Detach(datum/target) + . = ..() + UnregisterSignal(target, COMSIG_ATOM_EXAMINE) + +/** + * + * This proc is called when the user examines an object with the associated element. This adds the text to the description in a new line, wrapped with span_brass() + * + * Arguments: + * * source - Object being examined, cast into an item variable + * * user - Unused + * * examine_texts - The output text list of the original examine function + */ + +/datum/element/clockwork_description/proc/add_examine(atom/source, mob/user, list/examine_texts) + SIGNAL_HANDLER + + if(IS_CLOCK(user) || isobserver(user)) + examine_texts += span_brass(text_to_add) + if(reebe_desc && on_reebe(source)) + examine_texts += span_brass(reebe_desc) diff --git a/tff_modular/modules/antagonists/clock_cult/holy_water.dm b/tff_modular/modules/antagonists/clock_cult/holy_water.dm new file mode 100644 index 00000000000..979bfee5410 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/holy_water.dm @@ -0,0 +1,41 @@ +/datum/reagent/water/holywater/expose_mob(mob/living/exposed_mob, methods, reac_volume) + . = ..() + if(IS_CLOCK(exposed_mob)) + to_chat(exposed_mob, span_userdanger("Your mind burns in agony as you feel the light of the Justicar being ripped away from you by something else!")) + +/datum/reagent/water/holywater/proc/handle_cultists(mob/living/carbon/affected_mob, seconds_per_tick) + var/list/phrase_list + if(IS_CULTIST(affected_mob)) //snowflakey but it works + var/datum/antagonist/cult/cult_datum = affected_mob.mind.has_antag_datum(/datum/antagonist/cult) + phrase_list = cult_datum?.cultist_deconversion_phrases + else if(IS_CLOCK(affected_mob)) + var/datum/antagonist/clock_cultist/servant_datum = affected_mob.mind.has_antag_datum(/datum/antagonist/clock_cultist) + phrase_list = servant_datum?.servant_deconversion_phrases + + if(data["deciseconds_metabolized"] >= (25 SECONDS)) // 10 units + affected_mob.adjust_stutter_up_to(4 SECONDS * seconds_per_tick, 20 SECONDS) + affected_mob.set_dizzy_if_lower(10 SECONDS) + if(SPT_PROB(10, seconds_per_tick)) + if(phrase_list) + affected_mob.say(pick(phrase_list["spoken"]), forced = "holy water") + if(prob(10)) + affected_mob.visible_message(span_danger("[affected_mob] starts having a seizure!"), span_userdanger("You have a seizure!")) + affected_mob.Unconscious(12 SECONDS) + var/span_type + if(IS_CULTIST(affected_mob)) + span_type = "cultlarge" + else if(IS_CLOCK(affected_mob)) + span_type = "big_brass" + if(phrase_list) + to_chat(affected_mob, "[pick(phrase_list["seizure"])].") + + if(data["deciseconds_metabolized"] >= (1 MINUTES)) // 24 units + if(IS_CULTIST(affected_mob)) + affected_mob.mind.remove_antag_datum(/datum/antagonist/cult) + if(IS_CLOCK(affected_mob)) + affected_mob.mind.remove_antag_datum(/datum/antagonist/clock_cultist) + affected_mob.Unconscious(10 SECONDS) + affected_mob.remove_status_effect(/datum/status_effect/jitter) + affected_mob.remove_status_effect(/datum/status_effect/speech/stutter) + holder.remove_reagent(type, volume) // maybe this is a little too perfect and a max() cap on the statuses would be better?? + return TRUE diff --git a/tff_modular/modules/antagonists/clock_cult/icons/effects/landmarks_static.dmi b/tff_modular/modules/antagonists/clock_cult/icons/effects/landmarks_static.dmi new file mode 100644 index 00000000000..5c6dd6264ba Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/effects/landmarks_static.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/hud/screen_alert.dmi b/tff_modular/modules/antagonists/clock_cult/icons/hud/screen_alert.dmi new file mode 100644 index 00000000000..0a003dd39cc Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/hud/screen_alert.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/mob/actions_clock.dmi b/tff_modular/modules/antagonists/clock_cult/icons/mob/actions_clock.dmi new file mode 100644 index 00000000000..6fe415943dd Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/mob/actions_clock.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/mob/background_clock.dmi b/tff_modular/modules/antagonists/clock_cult/icons/mob/background_clock.dmi new file mode 100644 index 00000000000..8e8df3fd3ee Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/mob/background_clock.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/mob/cameramob.dmi b/tff_modular/modules/antagonists/clock_cult/icons/mob/cameramob.dmi new file mode 100644 index 00000000000..17b79ccc0e8 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/mob/cameramob.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_garb_worn.dmi b/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_garb_worn.dmi new file mode 100644 index 00000000000..4709b946872 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_garb_worn.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_lefthand.dmi b/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_lefthand.dmi new file mode 100644 index 00000000000..8e8bb58de2b Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_lefthand.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_mobs.dmi b/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_mobs.dmi new file mode 100644 index 00000000000..2e66c8b7a67 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_mobs.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_righthand.dmi b/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_righthand.dmi new file mode 100644 index 00000000000..b951aa8ece0 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_righthand.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/mob/hud.dmi b/tff_modular/modules/antagonists/clock_cult/icons/mob/hud.dmi new file mode 100644 index 00000000000..6782f01cf64 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/mob/hud.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/mob/speech.dmi b/tff_modular/modules/antagonists/clock_cult/icons/mob/speech.dmi new file mode 100644 index 00000000000..7d13520cc9a Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/mob/speech.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/512x512.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/512x512.dmi new file mode 100644 index 00000000000..99d39c6e8bb Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/512x512.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/ammo.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/ammo.dmi new file mode 100644 index 00000000000..9893ddeb415 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/ammo.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/catwalk_clockwork.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/catwalk_clockwork.dmi new file mode 100644 index 00000000000..5248b184461 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/catwalk_clockwork.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/catwalk_clockwork_large.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/catwalk_clockwork_large.dmi new file mode 100644 index 00000000000..4b8e2d1c9a0 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/catwalk_clockwork_large.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_effects.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_effects.dmi new file mode 100644 index 00000000000..71904d40651 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_effects.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_garb.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_garb.dmi new file mode 100644 index 00000000000..901afd12ee6 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_garb.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi new file mode 100644 index 00000000000..643be27cb59 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_weapons.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_weapons.dmi new file mode 100644 index 00000000000..d4a82b6797c Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_weapons.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/lattice_clockwork.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/lattice_clockwork.dmi new file mode 100644 index 00000000000..5e043027c5f Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/lattice_clockwork.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/lattice_clockwork_large.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/lattice_clockwork_large.dmi new file mode 100644 index 00000000000..306bd9d0d78 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/lattice_clockwork_large.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/misc.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/misc.dmi new file mode 100644 index 00000000000..b9ed54d6100 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/misc.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/projectiles.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/projectiles.dmi new file mode 100644 index 00000000000..87abc6d14c0 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/projectiles.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/sleeper.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/sleeper.dmi new file mode 100644 index 00000000000..de40ad09c12 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/sleeper.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/tools.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/tools.dmi new file mode 100644 index 00000000000..5525d466d75 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/tools.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/icons/obj/window.dmi b/tff_modular/modules/antagonists/clock_cult/icons/obj/window.dmi new file mode 100644 index 00000000000..91b50608e73 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/icons/obj/window.dmi differ diff --git a/tff_modular/modules/antagonists/clock_cult/items/clock_stock_parts.dm b/tff_modular/modules/antagonists/clock_cult/items/clock_stock_parts.dm new file mode 100644 index 00000000000..de32ae438c4 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/items/clock_stock_parts.dm @@ -0,0 +1,70 @@ +//power cell that gets its power from SSthe_ark.clock_power +/obj/item/stock_parts/power_store/cell/clock + name = "Wound Power Cell" + desc = "A bronze colored power cell. Is that a winding crank on the side?" //might make a real wind up powercell at some point for a joke item + color = rgb(190, 135, 0) //currently only used for mechs so im calling this good enough + +/obj/item/stock_parts/power_store/cell/clock/Initialize(mapload, override_maxcharge) + . = ..() + AddElement(/datum/element/empprotection, EMP_PROTECT_SELF) //no EMP + UnregisterSignal(src, COMSIG_ITEM_MAGICALLY_CHARGED) //just to be safe + START_PROCESSING(SSfastprocess, src) //janky, but the only way I can think of to get this to work is with a refactor to clock power, which im not doing for the visuals of one thing + +/obj/item/stock_parts/power_store/cell/clock/Destroy(force) + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/obj/item/stock_parts/power_store/cell/clock/process(seconds_per_tick) + charge = SSthe_ark.clock_power + maxcharge = SSthe_ark.max_clock_power + +//technically this means these cant be rigged with plasma +/obj/item/stock_parts/power_store/cell/clock/use(used, force) + if(!..() || istype(loc, /obj/machinery/power/apc) || SSthe_ark.clock_power < used) + return FALSE + SSblackbox.record_feedback("tally", "cell_used", 1, type) + SSthe_ark.clock_power = max(SSthe_ark.clock_power - used, 0) + return TRUE + +/obj/item/stock_parts/power_store/cell/clock/percent() + return 100 * SSthe_ark.clock_power / SSthe_ark.max_clock_power + +/obj/item/stock_parts/power_store/cell/clock/give(amount) //no + return FALSE + +//these are just for flavor +/obj/item/stock_parts/scanning_module/triphasic/clock + name = "Ticking Scanning Module" + desc = "A bronze colored scanning module, you hear a faint ticking from inside." + color = rgb(190, 135, 0) + +/datum/stock_part/scanning_module/clock + tier = 4 + physical_object_type = /obj/item/stock_parts/scanning_module/triphasic/clock + +/obj/item/stock_parts/capacitor/quadratic/clock + name = "Clicking Capacitor" + desc = "A bronze colored scanning module with a slow clicking within." + color = rgb(190, 135, 0) + +/datum/stock_part/capacitor/clock + tier = 4 + physical_object_type = /obj/item/stock_parts/capacitor/quadratic/clock + +/obj/item/stock_parts/matter_bin/bluespace/clock + name = "Glowing Matter Bin" + desc = "It has a faint glow emitting from within." + color = rgb(190, 135, 0) + +/datum/stock_part/matter_bin/clock + tier = 4 + physical_object_type = /obj/item/stock_parts/matter_bin/bluespace/clock + +/obj/item/stock_parts/servo/femto/clock + name = "Powered Manipulator" + desc = "Changes the energy flow around an object to manipulate it." + color = rgb(190, 135, 0) + +/datum/stock_part/servo/clock + tier = 4 + physical_object_type = /obj/item/stock_parts/servo/femto/clock diff --git a/tff_modular/modules/antagonists/clock_cult/items/clockwork_slab.dm b/tff_modular/modules/antagonists/clock_cult/items/clockwork_slab.dm new file mode 100644 index 00000000000..aa0accc6827 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/items/clockwork_slab.dm @@ -0,0 +1,273 @@ +#define MAXIMUM_QUICKBIND_SLOTS 5 + +GLOBAL_LIST_INIT(clockwork_slabs, list()) + +/obj/item/clockwork + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + /// Extra info to give clock cultists, added via the /datum/element/clockwork_description element + var/clockwork_desc = "" + /// Does this item get the clockwork_pickup element + var/has_pickup_element = TRUE + +/obj/item/clockwork/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_description, clockwork_desc) + if(has_pickup_element) + AddElement(/datum/element/clockwork_pickup) + +/obj/item/clockwork/clockwork_slab + name = "Clockwork Slab" + desc = "A mechanical-looking device filled with intricate cogs that swirl to their own accord." + clockwork_desc = "A beautiful work of art, harnessing mechanical energy for a variety of useful powers." + item_flags = NOBLUDGEON + icon_state = "clockwork_slab" + lefthand_file = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_lefthand.dmi' + righthand_file = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_righthand.dmi' + w_class = WEIGHT_CLASS_TINY + has_pickup_element = FALSE + + /// The scripture currently being invoked + var/datum/scripture/invoking_scripture + /// For scriptures that power the slab + var/datum/scripture/slab/active_scripture + /// What overlay the slab should use, should the active scripture have one + var/charge_overlay + + /// How many cogs this slab has currently + var/cogs = 0 + /// An assoc list of refs to instances that are owned/have been purchased by this slab. Assoc is by type + var/list/owned_scriptures = list() + + //Initialise an empty list for quickbinding + var/list/quick_bound_scriptures = list( + /* 1 = */ null, + /* 2 = */ null, + /* 3 = */ null, + /* 4 = */ null, + /* 5 = */ null, + ) + + //The default scriptures that get auto-assigned. + var/list/default_scriptures = list() + + //For trap linkage + var/datum/component/clockwork_trap/buffer + + +/obj/item/clockwork/clockwork_slab/Initialize(mapload) + . = ..() + if(!length(GLOB.clock_scriptures) || !length(GLOB.clock_scriptures_by_type)) + generate_clockcult_scriptures() + + var/pos = 1 + cogs = GLOB.clock_installed_cogs + GLOB.clockwork_slabs += src + for(var/script in default_scriptures) + if(!script) + continue + + var/datum/scripture/default_script = new script() + owned_scriptures[default_script.type] = default_script + bind_spell(null, default_script, pos++) + + +/obj/item/clockwork/clockwork_slab/Destroy() + GLOB.clockwork_slabs -= src + invoking_scripture = null + active_scripture = null + buffer = null + for(var/scripture_path in owned_scriptures) + var/scripture_ref = owned_scriptures[scripture_path] //so we can clear the ref before qdeling + owned_scriptures[scripture_path] = null + qdel(scripture_ref) + return ..() + + +/obj/item/clockwork/clockwork_slab/dropped(mob/user) + . = ..() + //Clear quickbinds + for(var/datum/action/innate/clockcult/quick_bind/script in quick_bound_scriptures) + script.Remove(user) + + if(active_scripture) + active_scripture.end_invocation() + + if(buffer) + buffer = null + + +/obj/item/clockwork/clockwork_slab/pickup(mob/user) + . = ..() + if(!IS_CLOCK(user)) + return + + //Grant quickbound spells + for(var/datum/action/innate/clockcult/quick_bind/script in quick_bound_scriptures) + script.Grant(user) + + user.update_action_buttons() + + +/obj/item/clockwork/clockwork_slab/update_overlays() + . = ..() + cut_overlays() + if(charge_overlay) + add_overlay(list(charge_overlay)) + + +/// Handle binding a spell to a quickbind slot +/obj/item/clockwork/clockwork_slab/proc/bind_spell(mob/living/binder, datum/scripture/spell, position = 1) + if((position > length(quick_bound_scriptures)) || (position <= 0)) + return + + if(quick_bound_scriptures[position]) + //Unbind the scripture that is quickbound + qdel(quick_bound_scriptures[position]) + + //Put the quickbound action onto the slab, the slab should grant when picked up + var/datum/action/innate/clockcult/quick_bind/quickbound = new + quickbound.scripture = spell + quickbound.slab_weakref = WEAKREF(src) + quick_bound_scriptures[position] = quickbound + if(binder) + quickbound.Grant(binder) + +// UI things below +/obj/item/clockwork/clockwork_slab/attack_self(mob/living/user) + if(!IS_CLOCK(user)) + to_chat(user, span_warning("As you try and fiddle with \the [src] you feel a shock course through you!")) + user.dropItemToGround(user, TRUE) + user.electrocute_act((IS_CULTIST(user) ? 40 : 20), src, 1, SHOCK_NOGLOVES | SHOCK_SUPPRESS_MESSAGE) + return + + if(active_scripture) + active_scripture.end_invocation() + return + + if(buffer) + buffer = null + to_chat(user, span_brass("You clear the [src]'s buffer.")) + return + + SEND_SIGNAL(user, COMSIG_CLOCKWORK_SLAB_USED, src) + + ui_interact(user) + +/obj/item/clockwork/clockwork_slab/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ClockworkSlab") + ui.open() + +/obj/item/clockwork/clockwork_slab/ui_data(mob/user) + var/list/data = list() + + data["cogs"] = cogs + data["vitality"] = GLOB.clock_vitality + data["max_vitality"] = MAX_CLOCK_VITALITY + data["power"] = SSthe_ark.clock_power + data["max_power"] = SSthe_ark.max_clock_power + data["scriptures"] = list() + + //2 scriptures accessible at the same time will cause issues + for(var/datum/scripture/scripture as anything in GLOB.clock_scriptures) + if(scripture.unique_locked) + continue + + var/list/scripture_data = list( + "name" = scripture.name, + "desc" = scripture.desc, + "type" = scripture.category, + "tip" = scripture.tip, + "cost" = scripture.power_cost, + "purchased" = (owned_scriptures[scripture.type] ? TRUE : FALSE), + "cog_cost" = scripture.cogs_required, + "typepath" = scripture.type, + ) + //Add it to the correct list + data["scriptures"] += list(scripture_data) + + return data + + +/obj/item/clockwork/clockwork_slab/ui_act(action, params) + . = ..() + if(.) + return + + var/mob/living/living_user = usr + if(!istype(living_user) || !IS_CLOCK(living_user)) + return FALSE + + switch(action) + if("invoke") + var/datum/scripture/scripture = GLOB.clock_scriptures_by_type[text2path(params["scriptureType"])] + if(!scripture) + return FALSE + + if(owned_scriptures[scripture.type]) + var/datum/scripture/owned_scripture = owned_scriptures[scripture.type] + if(invoking_scripture) + living_user.balloon_alert(living_user, "failed to invoke!") + return FALSE + + if(owned_scripture.power_cost > SSthe_ark.clock_power) + living_user.balloon_alert(living_user, "[owned_scripture.power_cost]W required!") + return FALSE + + if(owned_scripture.vitality_cost > GLOB.clock_vitality) + living_user.balloon_alert(living_user, "[owned_scripture.vitality_cost] vitality required!") + return FALSE + + owned_scripture.begin_invoke(living_user, src) + + else + if(cogs >= scripture.cogs_required) + cogs -= scripture.cogs_required + living_user.balloon_alert(living_user, "[scripture.name] purchased") + log_game("[scripture.name] purchased by [living_user.ckey]/[living_user.name] the [living_user.job] for [scripture.cogs_required] cogs, [cogs] cogs remaining.") + var/datum/scripture/new_scripture = new scripture.type() + new_scripture.unique_locked = scripture.unique_locked + owned_scriptures[new_scripture.type] = new_scripture + + else + living_user.balloon_alert(living_user, "need at least [scripture.cogs_required]!") + + return TRUE + + + if("quickbind") + var/datum/scripture/scripture = owned_scriptures[text2path(params["scriptureType"])] + if(!scripture) + return FALSE + + var/list/positions = list() + for(var/i in 1 to MAXIMUM_QUICKBIND_SLOTS) + var/datum/scripture/quick_bound = quick_bound_scriptures[i] + if(!quick_bound) + positions += "([i])" + + else + positions += "([i]) - [quick_bound.name]" + + var/position = tgui_input_list(living_user, "Where to quickbind to?", "Quickbind Slot", positions) + if(!position) + return FALSE + + // Assign the quickbind + bind_spell(living_user, scripture, positions.Find(position)) + +/obj/item/clockwork/clockwork_slab/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(invoking_scripture) + return FALSE + if(SHOULD_SKIP_INTERACTION(interacting_with, src, user)) + return NONE // lets us put things in bags without trying to emag them + if(interacting_with.slab_act(user, src)) + SSblackbox.record_feedback("tally", "atom_emagged", 1, interacting_with.type) + return ITEM_INTERACT_SUCCESS + return NONE // In a perfect world this would be blocking, but this is not a perfect world + +/atom/proc/slab_act(mob/user, obj/item/clockwork/clockwork_slab/slab) + return (SEND_SIGNAL(src, COMSIG_ATOM_SLAB_ACT, user, slab)) + +#undef MAXIMUM_QUICKBIND_SLOTS diff --git a/tff_modular/modules/antagonists/clock_cult/items/clothing.dm b/tff_modular/modules/antagonists/clock_cult/items/clothing.dm new file mode 100644 index 00000000000..7b16795dc3b --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/items/clothing.dm @@ -0,0 +1,575 @@ +#define CLOAK_DODGE_CHANCE 20 + +/// Returns true if the mob is on a rusty tile, really low level just because we call it in a bunch of unrelated places +/mob/proc/is_touching_bronze(check_flying = FALSE) + if (check_flying && (movement_type & MOVETYPES_NOT_TOUCHING_GROUND)) + return FALSE + if (HAS_TRAIT(src, TRAIT_MAGICALLY_PHASED) || (movement_type & VENTCRAWLING)) + return FALSE + var/turf/our_turf = get_turf(src) + return HAS_TRAIT(our_turf, TRAIT_BRONZE_TURF) + +/// Handle stuff to update when a mob equips/unequips a headgear. +/mob/living/carbon/proc/head_update(obj/item/I, forced) + if(isclothing(I)) + var/obj/item/clothing/C = I + if(C.tint || initial(C.tint)) + update_tint() + update_sight() + if(I.flags_inv & HIDEMASK || forced) + update_worn_mask() + if(I.flags_inv & HIDEEARS || forced) + update_inv_ears() + if(I.flags_inv & HIDEEYES || forced) + update_worn_glasses() + update_worn_head() + +///Updates the handcuff overlay & HUD element. +/mob/proc/update_inv_ears() + return + +/mob/living/carbon/human/head_update(obj/item/I, forced) + if((I.flags_inv & (HIDEHAIR|HIDEFACIALHAIR)) || forced) + update_body_parts() + // Close internal air tank if helmet was the only breathing apparatus. + if (invalid_internals()) + cutoff_internals() + if(I.flags_inv & HIDEEYES || forced) + update_worn_glasses() + if(I.flags_inv & HIDEEARS || forced) + update_body() + sec_hud_set_security_status() + ..() + +/obj/item/clothing/suit/clockwork + name = "bronze armor" + desc = "A strong, bronze suit worn by the soldiers of the Ratvarian armies." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_garb.dmi' + worn_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_garb_worn.dmi' + icon_state = "clockwork_cuirass" + slowdown = 0.2 + resistance_flags = FIRE_PROOF | ACID_PROOF + w_class = WEIGHT_CLASS_BULKY + body_parts_covered = CHEST|GROIN|LEGS|ARMS + armor_type = /datum/armor/suit_clockwork + allowed = list( + /obj/item/clockwork, + /obj/item/stack/tile/bronze, + /obj/item/gun/ballistic/bow/clockwork, + ) + + ///what is the value of our slowdown while empowered + var/empowered_slowdown = 0 + ///what armor type do we use while empowered + var/datum/armor/empowered_armor = /datum/armor/suit_clockwork_empowered + var/empowered = FALSE + +/obj/item/clothing/suit/clockwork/equipped(mob/living/user, slot) + . = ..() + RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) + +/obj/item/clothing/suit/clockwork/proc/on_move(mob/source, atom/old_loc, dir, forced, list/old_locs) + SIGNAL_HANDLER + + if(source.is_touching_bronze()) + set_armor(empowered_armor) + slowdown = empowered_slowdown + empowered = TRUE + else + set_armor(initial(armor_type)) + slowdown = initial(slowdown) + empowered = FALSE + empowered_effect(source) + +/obj/item/clothing/suit/clockwork/proc/empowered_effect(mob/source) + return + +/obj/item/clothing/suit/clockwork/dropped(mob/living/user) + . = ..() + UnregisterSignal(user, COMSIG_MOVABLE_MOVED) + +/datum/armor/suit_clockwork + melee = 30 + bullet = 30 + laser = 20 + energy = 30 + bomb = 80 + bio = 100 + fire = 100 + acid = 100 + +/datum/armor/suit_clockwork_empowered + melee = 40 + bullet = 50 + laser = 40 + energy = 60 + bomb = 80 + bio = 100 + fire = 100 + acid = 100 + +/obj/item/clothing/suit/clockwork/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_pickup, ~(ITEM_SLOT_HANDS)) + +/obj/item/clothing/suit/clockwork/speed + name = "robes of divinity" + desc = "A shiny suit, glowing with a vibrant energy. The wearer will be able to move quickly across battlefields, but will be able to withstand less damage before falling." + icon_state = "clockwork_cuirass_speed" + slowdown = -0.2 + armor_type = /datum/armor/clockwork_speed + empowered_armor = /datum/armor/clockwork_speed_empowered + empowered_slowdown = -0.6 + +/datum/armor/clockwork_speed + melee = 20 + bullet = 20 + laser = 20 + energy = 10 + bomb = 60 + bio = 100 + fire = 100 + acid = 100 + +/datum/armor/clockwork_speed_empowered + melee = 30 + bullet = 40 + laser = 0 + energy = 0 + bomb = 60 + bio = 100 + fire = 100 + acid = 100 + +/obj/item/clothing/suit/clockwork/cloak + name = "shrouding cloak" + desc = "A faltering cloak that bends light around it, distorting the user's appearance, making it hard to see them with the naked eye and be harder to hit. \ + However, it provides very little physical protection." + icon_state = "clockwork_cloak" + armor_type = /datum/armor/clockwork_cloak + actions_types = list(/datum/action/item_action/toggle/clock) + w_class = WEIGHT_CLASS_NORMAL + empowered_armor = /datum/armor/clockwork_cloak + empowered_slowdown = -0.1 + /// Is the shroud itself active or not + var/shroud_active = FALSE + /// Previous alpha value of the user when removing/disabling the jacket + var/previous_alpha = 255 + /// Ref to who is wearing this + var/mob/living/wearer + +/datum/armor/clockwork_cloak + melee = 15 + bullet = 30 + laser = 20 + energy = 15 + bomb = 50 + bio = 100 + fire = 100 + acid = 100 + +/obj/item/clothing/suit/clockwork/cloak/empowered_effect(mob/source) + if(shroud_active && !empowered) + disable() + +/obj/item/clothing/suit/clockwork/cloak/Destroy() + if(shroud_active) + disable() + wearer = null + return ..() + +/obj/item/clothing/suit/clockwork/cloak/attack_self(mob/user, modifiers) + . = ..() + if(shroud_active) + disable() + else if(empowered) + enable() + else + balloon_alert(user, "must be standing on brass!") + +/obj/item/clothing/suit/clockwork/cloak/equipped(mob/user, slot) + . = ..() + if(slot != ITEM_SLOT_OCLOTHING || !IS_CLOCK(user)) + return + + wearer = user + if(shroud_active && empowered) + enable() + +/obj/item/clothing/suit/clockwork/cloak/dropped(mob/user) + . = ..() + if(shroud_active) + disable() + wearer = null + +/obj/item/clothing/suit/clockwork/cloak/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type) + if(empowered && shroud_active && prob(CLOAK_DODGE_CHANCE)) //we handle this just a biiiiit too different from parent to make simply using the vars be viable + owner.visible_message(span_danger("[owner]'s [src] makes them phase out of the way of [attack_text]!")) + owner.add_filter("clock_cloak", 3, motion_blur_filter(0, 0)) + addtimer(CALLBACK(src, PROC_REF(remove_phase_filter), owner), (0.6 SECONDS) + 1) + ASYNC + animate(owner.get_filter("clock_cloak"), 0.3 SECONDS, x = prob(50) ? rand(4, 5) : rand(-4, -5), y = prob(50) ? rand(4, 5) : rand(-4, -5), flags = ANIMATION_PARALLEL) + animate(time = 0.3 SECONDS, x = 0, y = 0) + playsound(src, 'sound/items/weapons/etherealmiss.ogg', BLOCK_SOUND_VOLUME, vary = TRUE) + return TRUE + +/obj/item/clothing/suit/clockwork/cloak/proc/remove_phase_filter(mob/living/remove_from) + if(QDELETED(remove_from)) + return + remove_from.remove_filter("clock_cloak") + +/// Apply the effects to the wearer, making them pretty hard to see +/obj/item/clothing/suit/clockwork/cloak/proc/enable() + shroud_active = TRUE + if(!wearer) + return + + previous_alpha = wearer.alpha + animate(wearer, alpha = 80, time = 3 SECONDS) + apply_wibbly_filters(wearer) + ADD_TRAIT(wearer, TRAIT_UNKNOWN_APPEARANCE, CLOTHING_TRAIT) + +/// Un-apply the effects of the cloak, returning the wearer to normal +/obj/item/clothing/suit/clockwork/cloak/proc/disable() + shroud_active = FALSE + if(!wearer) + return + + do_sparks(3, FALSE, wearer) + remove_wibbly_filters(wearer) + animate(wearer, alpha = previous_alpha, time = 3 SECONDS) + REMOVE_TRAIT(wearer, TRAIT_UNKNOWN_APPEARANCE, CLOTHING_TRAIT) + +/obj/item/clothing/glasses/clockwork + name = "base clock glasses" + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_garb.dmi' + worn_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_garb_worn.dmi' + icon_state = "clockwork_cuirass" + /// What additional desc to show if the person examining is a clock cultist + var/clock_desc = "" + +/obj/item/clothing/glasses/clockwork/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_description, clock_desc) + AddElement(/datum/element/clockwork_pickup, ~(ITEM_SLOT_HANDS)) + +#define SECONDS_FOR_EYE_HEAL 60 +// Thermal goggles, no protection from eye stuff +/obj/item/clothing/glasses/clockwork/wraith_spectacles + name = "wraith spectacles" + desc = "Mystical glasses that glow with a bright energy. Some say they can see things that shouldn't be seen." + icon_state = "wraith_specs_0" + base_icon_state = "wraith_specs" + invis_override = SEE_INVISIBLE_OBSERVER + flash_protect = FLASH_PROTECTION_SENSITIVE + vision_flags = SEE_MOBS + color_cutoffs = list(20, 16, 0) + glass_colour_type = /datum/client_colour/glass_colour/yellow + actions_types = list(/datum/action/item_action/toggle/clock) + clock_desc = "Applies passive eye damage that regenerates after unequipping, grants thermal vision, and lets you see all forms of invisibility." + /// Who is currently wearing the goggles + var/mob/living/wearer + /// Are the glasses enabled (flipped down) + var/enabled = TRUE + +/obj/item/clothing/glasses/clockwork/wraith_spectacles/Initialize(mapload) + . = ..() + update_icon_state() + + +/obj/item/clothing/glasses/clockwork/wraith_spectacles/Destroy() + wearer = null + return ..() + + +/obj/item/clothing/glasses/clockwork/wraith_spectacles/update_icon_state() + . = ..() + icon_state = "[base_icon_state]_[!enabled]" + worn_icon_state = "[base_icon_state]_[!enabled]" + + +/obj/item/clothing/glasses/clockwork/wraith_spectacles/attack_self(mob/user, modifiers) + . = ..() + if(enabled) + disable() + else + enable() + + if(iscarbon(user)) + var/mob/living/carbon/carbon_user = user + carbon_user.head_update(src, forced = TRUE) + + +/// "enable" the spectacles, flipping them down and applying their effects, calling on_toggle_eyes() if someone is wearing them +/obj/item/clothing/glasses/clockwork/wraith_spectacles/proc/enable() + enabled = TRUE + color_cutoffs = list(20, 16, 0) + invis_override = SEE_INVISIBLE_OBSERVER + vision_flags = SEE_MOBS + + if(wearer) + on_toggle_eyes() + + update_icon_state() + + +/// "disable" the spectacles, flipping them up and removing all applied effects +/obj/item/clothing/glasses/clockwork/wraith_spectacles/proc/disable() + enabled = FALSE + color_cutoffs = null + invis_override = null + vision_flags = NONE + + if(wearer) + de_toggle_eyes() + + update_icon_state() + + +/// The start of application of the actual effects like eye damage +/obj/item/clothing/glasses/clockwork/wraith_spectacles/proc/on_toggle_eyes() + wearer.update_sight() + to_chat(wearer, span_clockgray("You suddenly see so much more.")) + +/// The stopping of effect application, will remove the wearer's eye damage a minute after, eye damage removal is handled by process() to avoid a large amount of timers +/obj/item/clothing/glasses/clockwork/wraith_spectacles/proc/de_toggle_eyes() + wearer.update_sight() + to_chat(wearer, span_clockgray("You feel your eyes slowly readjusting.")) + +/obj/item/clothing/glasses/clockwork/wraith_spectacles/equipped(mob/living/user, slot) + . = ..() + if(!isliving(user)) + return + + if((slot == ITEM_SLOT_EYES) && enabled) + wearer = user + on_toggle_eyes() + + +/obj/item/clothing/glasses/clockwork/wraith_spectacles/dropped(mob/user) + . = ..() + if(wearer && (IS_CLOCK(user)) && enabled) + de_toggle_eyes() + + wearer = null +#undef SECONDS_FOR_EYE_HEAL + + +// Flash protected and generally info-granting with huds +/obj/item/clothing/glasses/clockwork/judicial_visor + name = "judicial visor" + desc = "A purple visor gilt with Ratvarian runes, allowing a user to see, unfettered by others. The cogs on the sides look pretty tight..." + icon_state = "judicial_visor_0" + base_icon_state = "judicial_visor" + flash_protect = FLASH_PROTECTION_WELDER + strip_delay = 10 SECONDS + glass_colour_type = /datum/client_colour/glass_colour/purple + actions_types = list(/datum/action/item_action/toggle/clock) + clock_desc = "Grants large sight and informational benefits to servants while active." + /// Is this enabled + var/enabled = TRUE + /// Ref to the wearer of the visor + var/mob/living/wearer + + +/obj/item/clothing/glasses/clockwork/judicial_visor/Initialize(mapload) + . = ..() + update_icon_state() + + +/obj/item/clothing/glasses/clockwork/judicial_visor/Destroy() + wearer = null + return ..() + + +/obj/item/clothing/glasses/clockwork/judicial_visor/update_icon_state() + . = ..() + icon_state = "[base_icon_state]_[enabled]" + worn_icon_state = "[base_icon_state]_[enabled]" + + +/obj/item/clothing/glasses/clockwork/judicial_visor/attack_self(mob/user, modifiers) + . = ..() + if(enabled) + disable() + else + enable() + + if(iscarbon(user)) + var/mob/living/carbon/carbon_user = user + carbon_user.head_update(src, forced = TRUE) + + +/// Turn on the visor, calling apply_to_wearer() and changing the icon state +/obj/item/clothing/glasses/clockwork/judicial_visor/proc/enable() + enabled = TRUE + if(wearer) + apply_to_wearer() + + update_icon_state() + + +/// Turn off the visor, calling unapply_to_wearer() and changing the icon state +/obj/item/clothing/glasses/clockwork/judicial_visor/proc/disable() + enabled = FALSE + if(wearer) + unapply_to_wearer() + + update_icon_state() + +//THIS IS MOST LIKELY BREAKING +/// Applies the actual effects to the wearer, giving them flash protection and a variety of sight/info bonuses +/obj/item/clothing/glasses/clockwork/judicial_visor/proc/apply_to_wearer() + ADD_TRAIT(wearer, TRAIT_MEDICAL_HUD, CLOTHING_TRAIT) + var/datum/atom_hud/med_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + med_hud.show_to(wearer) + + ADD_TRAIT(wearer, TRAIT_SECURITY_HUD, CLOTHING_TRAIT) + var/datum/atom_hud/sec_hud = GLOB.huds[DATA_HUD_SECURITY_ADVANCED] + sec_hud.show_to(wearer) + + add_traits(list(TRAIT_KNOW_ENGI_WIRES, TRAIT_MADNESS_IMMUNE, TRAIT_MESON_VISION, TRAIT_KNOW_ROBO_WIRES, TRAIT_NOFLASH), CLOTHING_TRAIT) + color_cutoffs = list(50, 10, 30) + wearer.update_sight() + +/// Removes the effects to the wearer, removing the flash protection and similar +/obj/item/clothing/glasses/clockwork/judicial_visor/proc/unapply_to_wearer() + REMOVE_TRAIT(wearer, TRAIT_MEDICAL_HUD, CLOTHING_TRAIT) + var/datum/atom_hud/med_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + med_hud.hide_from(wearer) + + REMOVE_TRAIT(wearer, TRAIT_SECURITY_HUD, CLOTHING_TRAIT) + var/datum/atom_hud/sec_hud = GLOB.huds[DATA_HUD_SECURITY_ADVANCED] + sec_hud.hide_from(wearer) + + remove_traits(list(TRAIT_KNOW_ENGI_WIRES, TRAIT_MADNESS_IMMUNE, TRAIT_MESON_VISION, TRAIT_KNOW_ROBO_WIRES, TRAIT_NOFLASH), CLOTHING_TRAIT) + color_cutoffs = null + wearer.update_sight() + +/obj/item/clothing/glasses/clockwork/judicial_visor/equipped(mob/living/user, slot) + . = ..() + if(!isliving(user)) + return + + if(slot == ITEM_SLOT_EYES) + wearer = user + if(enabled) + apply_to_wearer() + +/obj/item/clothing/glasses/clockwork/judicial_visor/dropped(mob/user) + ..() + if(wearer) + unapply_to_wearer() + wearer = null + +/obj/item/clothing/head/helmet/clockwork + name = "brass helmet" + desc = "A strong, brass helmet worn by the soldiers of the Ratvarian armies. Includes an integrated light-dimmer for flash protection, \ + as well as occult-grade muffling for factory based environments." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_garb.dmi' + worn_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_garb_worn.dmi' + icon_state = "clockwork_helmet" + armor_type = /datum/armor/helmet_clockwork + resistance_flags = FIRE_PROOF | ACID_PROOF + w_class = WEIGHT_CLASS_BULKY + flash_protect = FLASH_PROTECTION_FLASH + ///what is the value of our slowdown while empowered + var/empowered_slowdown = 0 + ///what armor type do we use while empowered + var/datum/armor/empowered_armor = /datum/armor/suit_clockwork_empowered + +/obj/item/clothing/head/helmet/clockwork/equipped(mob/living/user, slot) + . = ..() + RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) + +/obj/item/clothing/head/helmet/clockwork/proc/on_move(mob/source, atom/old_loc, dir, forced, list/old_locs) + SIGNAL_HANDLER + + if(source.is_touching_bronze()) + set_armor(empowered_armor) + slowdown = empowered_slowdown + else + set_armor(initial(armor_type)) + slowdown = initial(slowdown) + +/obj/item/clothing/head/helmet/clockwork/dropped(mob/living/user) + . = ..() + UnregisterSignal(user, COMSIG_MOVABLE_MOVED) + +/datum/armor/helmet_clockwork + melee = 25 + bullet = 30 + laser = 15 + energy = 40 + bomb = 80 + bio = 100 + fire = 100 + acid = 100 + +/datum/armor/helmet_clockwork_empowered + melee = 50 + bullet = 55 + laser = 35 + energy = 70 + bomb = 80 + bio = 100 + fire = 100 + acid = 100 + +/obj/item/clothing/head/helmet/clockwork/Initialize(mapload) + . = ..() + AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_HEAD)) + AddElement(/datum/element/clockwork_pickup, ~(ITEM_SLOT_HANDS)) + +/obj/item/clothing/shoes/clockwork + name = "brass treads" + desc = "A strong pair of brass boots worn by the soldiers of the Ratvarian armies." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_garb.dmi' + worn_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_garb_worn.dmi' + icon_state = "clockwork_treads" + resistance_flags = FIRE_PROOF | ACID_PROOF + +/datum/armor/boots_clockwork + melee = 0 + bullet = 0 + laser = 0 + energy = 0 + bomb = 0 + bio = 100 + fire = 80 + acid = 100 + +/obj/item/clothing/shoes/clockwork/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_pickup, ~(ITEM_SLOT_HANDS)) + + +/obj/item/clothing/gloves/clockwork + name = "brass gauntlets" + desc = "A strong pair of brass gloves worn by the soldiers of the Ratvarian armies." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_garb.dmi' + worn_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_garb_worn.dmi' + icon_state = "clockwork_gauntlets" + siemens_coefficient = 0 + strip_delay = 8 SECONDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + armor_type = /datum/armor/gloves_clockwork + +/datum/armor/gloves_clockwork + melee = 0 + bullet = 0 + laser = 0 + energy = 0 + bomb = 0 + bio = 80 + fire = 80 + acid = 100 + +/obj/item/clothing/gloves/clockwork/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_pickup, ~(ITEM_SLOT_HANDS)) + +#undef CLOAK_DODGE_CHANCE diff --git a/tff_modular/modules/antagonists/clock_cult/items/integration_cog.dm b/tff_modular/modules/antagonists/clock_cult/items/integration_cog.dm new file mode 100644 index 00000000000..e485280b76d --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/items/integration_cog.dm @@ -0,0 +1,71 @@ +#define HALLUCINATION_COG_CHANCE 20 + +/obj/item/clockwork/integration_cog + name = "integration cog" + desc = "A small cog that seems to spin by its own acord when left alone." + icon_state = "integration_cog" + clockwork_desc = "A sharp cog that can cut through and be inserted into APCs to extract power for your machinery." + w_class = WEIGHT_CLASS_TINY + +/obj/item/clockwork/integration_cog/attack_atom(atom/attacked_atom, mob/living/user, params) + if(!(IS_CLOCK(user)) || !istype(attacked_atom, /obj/machinery/power/apc)) + return ..() + + var/obj/machinery/power/apc/cogger_apc = attacked_atom + if(cogger_apc.integration_cog) + balloon_alert(user, "already has a cog inside!") + return + + if(!cogger_apc.panel_open) + //Cut open the panel + balloon_alert(user, "cutting open APC...") + if(!do_after(user, 5 SECONDS, target = cogger_apc, hidden = TRUE)) + return + + balloon_alert(user, "aPC cut open") + cogger_apc.panel_open = TRUE + cogger_apc.update_appearance() + return + + //Insert the cog + balloon_alert(user, "inserting [src]...") + if(!do_after(user, 4 SECONDS, target = cogger_apc, hidden = TRUE)) + balloon_alert(user, "failed to insert [src]!") + return + + cogger_apc.integration_cog = src + forceMove(cogger_apc) + cogger_apc.panel_open = FALSE + cogger_apc.update_appearance() + balloon_alert(user, "[src] inserted") + playsound(get_turf(user), 'sound/machines/clockcult/integration_cog_install.ogg', 20) + if(!cogger_apc.clock_cog_rewarded) + GLOB.clock_installed_cogs++ + SSthe_ark.max_clock_power += CLOCK_MAX_POWER_PER_COG + SSthe_ark.passive_power += CLOCK_PASSIVE_POWER_PER_COG + cogger_apc.clock_cog_rewarded = TRUE + send_clock_message(null, span_brass(span_bold("[user.real_name] has installed an integration cog into [cogger_apc].")), msg_ghosts = TRUE) + //Update the cog counts + for(var/obj/item/clockwork/clockwork_slab/slab as anything in GLOB.clockwork_slabs) + slab.cogs++ + GLOB.current_eminence?.cogs++ + + +/obj/machinery/power/apc + /// If this APC has given a reward for being coggered before + var/clock_cog_rewarded = FALSE + /// Reference to the cog inside + var/integration_cog = null + +/obj/machinery/power/apc/Destroy() + QDEL_NULL(integration_cog) + return ..() + +/obj/machinery/power/apc/examine_more(mob/user) + . = ..() + if(isliving(user)) + var/mob/living/living_user = user + if(panel_open && (integration_cog || (living_user.has_status_effect(/datum/status_effect/hallucination) && prob(HALLUCINATION_COG_CHANCE)))) + . += span_brass("A small cogwheel is inside of it.") + +#undef HALLUCINATION_COG_CHANCE diff --git a/tff_modular/modules/antagonists/clock_cult/items/reebe.dm b/tff_modular/modules/antagonists/clock_cult/items/reebe.dm new file mode 100644 index 00000000000..a874471a0df --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/items/reebe.dm @@ -0,0 +1,158 @@ +//simply an item that breaks turfs down +/obj/item/turf_demolisher + name = "\improper Exprimental Demolisher" + desc = "An exprimental able to quickly deconstruct any surface." + icon = 'icons/obj/mining.dmi' + lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi' + icon_state = "jackhammer" + inhand_icon_state = "jackhammer" + ///The balloon_alert() to send when we cannot demolish a turf + var/unbreakable_alert = "Unable to demolish that." + ///List of turf types we are allowed to break, if unset then we can break any turfs that dont have the INDESTRUCTIBLE resistance flag + var/list/allowed_types = list(/turf/closed/wall) + ///List of turf types we are NOT allowed to break + var/list/blacklisted_types + ///How long is the do_after() to break a turf + var/break_time = 8 SECONDS + ///Do we devastate broken walls, because of quality 7 year old code this always makes iron no matter the wall type + var/devastate = TRUE + ///How long is our recharge time between uses + var/recharge_time = 0 + ///How long after our first turf broken do our resonances detonate + var/resonance_delay = 5 SECONDS + ///How many tiles out from out demolished turf do we spawn resonances, set to 0 for no resonances + var/resonance_range = 0 + COOLDOWN_DECLARE(recharge) + +/obj/item/turf_demolisher/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!isturf(interacting_with)) + return NONE + + if(!check_breakble(interacting_with, user)) + return ITEM_INTERACT_BLOCKING + + if(try_demolish(interacting_with, user)) + return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_BLOCKING + +/obj/item/turf_demolisher/proc/check_breakble(turf/attacked_turf, mob/living/user, silent = FALSE, ignore_cooldown = FALSE) + if(recharge_time && !ignore_cooldown && !COOLDOWN_FINISHED(src, recharge)) + if(!silent) + balloon_alert(user, "\The [src] is still recharging.") + return FALSE + + if((allowed_types && !is_type_in_list(attacked_turf, allowed_types)) || is_type_in_list(attacked_turf, blacklisted_types) || (attacked_turf.resistance_flags & INDESTRUCTIBLE)) + if(unbreakable_alert && !silent) + balloon_alert(user, unbreakable_alert) + return FALSE + return TRUE + +/obj/item/turf_demolisher/proc/try_demolish(turf/attacked_turf, mob/living/user) + if(!do_after(user, break_time, attacked_turf)) + return FALSE + + playsound(src, 'sound/items/weapons/sonic_jackhammer.ogg', 80, channel = CHANNEL_SOUND_EFFECTS) + if(iswallturf(attacked_turf)) + var/turf/closed/wall/wall_turf = attacked_turf + wall_turf.dismantle_wall(devastate) + else + attacked_turf.ScrapeAway() + + if(recharge_time) + COOLDOWN_START(src, recharge, recharge_time) + if(!resonance_range) + return TRUE + + var/tiles_out = 0 + var/list/checked_turfs = list(attacked_turf) + var/list/resonate_from = list(attacked_turf) + while(tiles_out < resonance_range) + tiles_out++ + for(var/turf/resonated_from in resonate_from) + resonate_from -= resonated_from + var/list/step_turfs = list(get_step(resonated_from, NORTH), get_step(resonated_from, SOUTH), get_step(resonated_from, EAST), get_step(resonated_from, WEST)) + for(var/turf/checked_turf in step_turfs) + if(!(checked_turf in checked_turfs) && check_breakble(checked_turf, silent = TRUE, ignore_cooldown = TRUE)) + if(tiles_out < resonance_range) + resonate_from += checked_turf + new /obj/effect/temp_visual/turf_demolisher_resonance(checked_turf, resonance_delay, checked_turf) + checked_turfs += checked_turf + return TRUE + +/obj/effect/temp_visual/turf_demolisher_resonance + name = "resonance field" + desc = "A resonating field that will destroy any nearby surface." + icon_state = "shield2" + layer = ABOVE_ALL_MOB_LAYER + plane = ABOVE_GAME_PLANE + duration = 5 SECONDS + ///The turf that we are on + var/turf/holder_turf + +/obj/effect/temp_visual/turf_demolisher_resonance/Initialize(mapload, _duration = 5 SECONDS, holder) + duration = _duration + holder_turf = holder || get_turf(src) + . = ..() + if(!holder_turf) + return INITIALIZE_HINT_QDEL + + transform = matrix()*0.75 + animate(src, transform = matrix()*1.5, time = duration) + RegisterSignal(holder_turf, COMSIG_ATOM_DESTRUCTION, PROC_REF(on_holder_destruction)) + +/obj/effect/temp_visual/turf_demolisher_resonance/Destroy() + if(holder_turf) + UnregisterSignal(holder_turf, COMSIG_ATOM_DESTRUCTION) + collapse() + holder_turf = null + return ..() + +/obj/effect/temp_visual/turf_demolisher_resonance/proc/on_holder_destruction() + SIGNAL_HANDLER + UnregisterSignal(holder_turf, COMSIG_ATOM_DESTRUCTION) + holder_turf = null + qdel(src) + +/obj/effect/temp_visual/turf_demolisher_resonance/proc/collapse() + new /obj/effect/temp_visual/resonance_crush(holder_turf) + playsound(holder_turf, 'sound/items/weapons/resonator_blast.ogg', 50, TRUE) + if(iswallturf(holder_turf)) + var/turf/closed/wall/wall_turf = holder_turf + wall_turf.dismantle_wall() + else + holder_turf.ScrapeAway() + +/obj/item/turf_demolisher/reebe + desc = "An exprimental able to quickly deconstruct any surface. This one seems to be calibrated to only work on reebe." + break_time = 5 SECONDS + recharge_time = 5 SECONDS + resonance_delay = 5 SECONDS + resonance_range = 2 + +/obj/item/turf_demolisher/reebe/check_breakble(turf/attacked_turf, mob/living/user, silent, ignore_cooldown) + . = ..() + if(!.) + return + + var/turf/our_turf = get_turf(src) + if(!on_reebe(our_turf)) + balloon_alert(user, "\The [src] is specially calibrated to be used on reebe and will not work here!") + return FALSE + + if(GLOB.clock_ark && get_dist(our_turf, get_turf(GLOB.clock_ark)) <= ARK_TURF_DESTRUCTION_BLOCK_RANGE) + balloon_alert(user, "a near by energy source is interfering \the [src]!") + return FALSE + +/obj/item/paper/crumpled/ruins/reebe1 + name = "scribbled note" + default_raw_text = {"The Justicar was wrong.
+ This isn't Reebe. I held out hope, hope that some part of Ratvar was still alive on the holy city.
+ But there's nothing here, just some... forward outpost. And damn those Nar'sian dogs! I can feel them trying to enter the portal!"} + + +/obj/item/paper/crumpled/bloody/reebe2 + name = "scribbled note" + default_raw_text = {"The heretics made it into this holy ground.
+ Many of our brothers and sisters fell in this fight, I am the only one left. It is with pleasure that I can die knowing of the deaths of another group of Nar'sians.
+ If only that damnable portal could've opened in time..."} diff --git a/tff_modular/modules/antagonists/clock_cult/items/replica_fabricator.dm b/tff_modular/modules/antagonists/clock_cult/items/replica_fabricator.dm new file mode 100644 index 00000000000..8801a6ee68d --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/items/replica_fabricator.dm @@ -0,0 +1,331 @@ +#define BRASS_POWER_COST STANDARD_CELL_CHARGE * 0.005 +#define REGULAR_POWER_COST (BRASS_POWER_COST / 2) + +/obj/item/clockwork/replica_fabricator + name = "replica fabricator" + desc = "A strange, brass device with many twisting cogs and vents." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + lefthand_file = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_lefthand.dmi' + righthand_file = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_righthand.dmi' + icon_state = "replica_fabricator" + /// List of things that the fabricator can build for the radial menu + var/static/list/crafting_possibilities = list( + "floor" = image(icon = 'icons/turf/floors.dmi', icon_state = "clockwork_floor"), + "wall" = image(icon = 'icons/turf/walls/clockwork_wall.dmi', icon_state = "clockwork_wall-0"), + "wall gear" = image(icon = 'icons/obj/structures.dmi', icon_state = "wall_gear"), + "window" = image(icon = 'icons/obj/smooth_structures/clockwork_window.dmi', icon_state = "clockwork_window-0"), + "airlock" = image(icon = 'icons/obj/doors/airlocks/clockwork/pinion_airlock.dmi', icon_state = "closed"), + "glass airlock" = image(icon = 'icons/obj/doors/airlocks/clockwork/pinion_airlock.dmi', icon_state = "construction"), + ) + /// List of initialized fabrication datums, created on Initialize + var/static/list/fabrication_datums = list() + /// Ref to the datum we have selected currently + var/datum/replica_fabricator_output/selected_output + + +/obj/item/clockwork/replica_fabricator/Initialize(mapload) + . = ..() + if(!length(fabrication_datums)) + create_fabrication_list() + +/obj/item/clockwork/replica_fabricator/Destroy(force) + selected_output = null + return ..() + +/obj/item/clockwork/replica_fabricator/examine(mob/user) + . = ..() + if(IS_CLOCK(user)) + . += span_brass("Current power: [display_power(SSthe_ark.clock_power)]") + . += span_brass("Use on brass to convert it into power.") + . += span_brass("Use on other materials to convert them into power, but less efficiently.") + . += span_brass("Use in-hand to select what to fabricate.") + . += span_brass("Right Click in-hand to fabricate bronze sheets.") + . += span_brass("Walls and windows will be built slower while on reebe.") + + +/obj/item/clockwork/replica_fabricator/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!IS_CLOCK(user)) + return NONE + + if(istype(interacting_with, /obj/item/stack/sheet)) // If it's an item, handle it seperately + attempt_convert_materials(interacting_with, user) + return ITEM_INTERACT_SUCCESS + + if(!selected_output) // Now we handle objects + return ITEM_INTERACT_BLOCKING + + if(SSthe_ark.clock_power < selected_output.cost) + to_chat(user, span_clockyellow("[src] needs at least [selected_output.cost]W of power to create this.")) + return ITEM_INTERACT_BLOCKING + + var/turf/creation_turf = get_turf(interacting_with) + var/atom/movable/replaced + if(locate(selected_output.to_create_path) in creation_turf) + to_chat(user, span_clockyellow("There is already one of these on this tile!")) + return ITEM_INTERACT_BLOCKING + + if(selected_output.replace_types_of && istype(selected_output, /datum/replica_fabricator_output/turf_output)) + if(!isopenturf(interacting_with) && !(locate(creation_turf) in selected_output.replace_types_of)) + return ITEM_INTERACT_BLOCKING + else if(selected_output.replace_types_of) + for(var/checked_type in selected_output.replace_types_of) + var/atom/movable/found_replaced = locate(checked_type) in creation_turf + if(found_replaced) + replaced = found_replaced + break + if(!replaced && !isopenturf(interacting_with)) + return ITEM_INTERACT_BLOCKING + else if(!isopenturf(interacting_with)) + return ITEM_INTERACT_BLOCKING + + if(!selected_output.extra_checks(interacting_with, creation_turf, user)) + return ITEM_INTERACT_BLOCKING + + var/creation_delay_mult = 1 - (SSthe_ark.charged_anchoring_crystals / 10) + if(on_reebe(user)) + creation_delay_mult += selected_output.reebe_mult + if(GLOB.clock_ark?.current_state >= ARK_STATE_ACTIVE) + creation_delay_mult += (iscogscarab(user) ? 2.5 : 5) + if(replaced) + creation_delay_mult += selected_output.replacement_mult + + var/selected_creation_delay = selected_output.creation_delay * max(creation_delay_mult, 0.1) + var/obj/effect/temp_visual/ratvar/constructing_effect/effect = new(creation_turf, selected_creation_delay) + if(!do_after(user, selected_creation_delay, interacting_with)) + qdel(effect) + return ITEM_INTERACT_BLOCKING + + if(!SSthe_ark.adjust_clock_power(-selected_output.cost)) + return ITEM_INTERACT_BLOCKING + + var/atom/created + if(!ispath(selected_output.to_create_path, /turf)) + qdel(replaced) + created = new selected_output.to_create_path(creation_turf) + + selected_output.on_create(created, creation_turf, user) + return ITEM_INTERACT_SUCCESS + + +/obj/item/clockwork/replica_fabricator/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) + . = ..() + if(!IS_CLOCK(user)) + return + + attempt_convert_materials(attacking_item, user) + + +/obj/item/clockwork/replica_fabricator/attack_self_secondary(mob/user, modifiers) + . = ..() + if(!IS_CLOCK(user)) + return + + if(SSthe_ark.clock_power < BRASS_POWER_COST) + to_chat(user, span_clockyellow("You need at least [BRASS_POWER_COST]W of power to fabricate bronze.")) + return + + var/sheets = tgui_input_number(user, "How many sheets do you want to fabricate?", "Sheet Fabrication", 0, round(SSthe_ark.clock_power / BRASS_POWER_COST), 0) + if(!sheets) + return + + SSthe_ark.adjust_clock_power(sheets * BRASS_POWER_COST) + + var/obj/item/stack/sheet/bronze/sheet_stack = new(null, sheets) + user.put_in_hands(sheet_stack) + playsound(src, 'sound/machines/click.ogg', 50, 1) + to_chat(user, span_clockyellow("You fabricate [sheets] bronze.")) + + +/obj/item/clockwork/replica_fabricator/attack_self(mob/user, modifiers) + . = ..() + var/choice = show_radial_menu(user, src, crafting_possibilities, radius = 36, custom_check = PROC_REF(check_menu), require_near = TRUE) + if(!choice) + return + + selected_output = fabrication_datums[choice] + + +/// Standard confirmation for the radial menu proc +/obj/item/clockwork/replica_fabricator/proc/check_menu(mob/user) + if(!istype(user) || HAS_TRAIT(user, TRAIT_INCAPACITATED)) + return FALSE + + return TRUE + +/// Attempt to convert the targeted item into power, if it's a sheet item +/obj/item/clockwork/replica_fabricator/proc/attempt_convert_materials(atom/attacking_item, mob/user) + if(SSthe_ark.clock_power >= SSthe_ark.max_clock_power) + to_chat(user, span_clockyellow("We are already at maximum power!")) + return + + if(istype(attacking_item, /obj/item/stack/sheet/bronze)) + var/obj/item/stack/bronze_stack = attacking_item + + if((SSthe_ark.clock_power + bronze_stack.amount * BRASS_POWER_COST) > SSthe_ark.max_clock_power) + var/amount_to_take = clamp(round((SSthe_ark.max_clock_power - SSthe_ark.clock_power) / BRASS_POWER_COST), 0, bronze_stack.amount) + + if(!amount_to_take) + to_chat(user, span_clockyellow("[src] can't be powered further using this!")) + return + + bronze_stack.use(amount_to_take) + SSthe_ark.clock_power += amount_to_take * BRASS_POWER_COST + + else + SSthe_ark.clock_power += bronze_stack.amount * BRASS_POWER_COST + qdel(bronze_stack) + + playsound(src, 'sound/machines/click.ogg', 50, 1) + to_chat(user, span_clockyellow("You convert [bronze_stack.amount] bronze into [bronze_stack.amount * BRASS_POWER_COST] watts of power.")) + + return TRUE + + else if(istype(attacking_item, /obj/item/stack/sheet)) + var/obj/item/stack/stack = attacking_item + + if((SSthe_ark.clock_power + stack.amount * REGULAR_POWER_COST) > SSthe_ark.max_clock_power) + var/amount_to_take = clamp(round((SSthe_ark.max_clock_power - SSthe_ark.clock_power) / REGULAR_POWER_COST), 0, stack.amount) + + if(!amount_to_take) + to_chat(user, span_clockyellow("[src] can't be powered further using this!")) + return + + stack.use(amount_to_take) + SSthe_ark.clock_power += amount_to_take * REGULAR_POWER_COST + + else + SSthe_ark.clock_power += stack.amount * REGULAR_POWER_COST + qdel(stack) + + playsound(src, 'sound/machines/click.ogg', 50, 1) + to_chat(user, span_clockyellow("You convert [stack.amount] [stack.name] into [stack.amount * REGULAR_POWER_COST] watts of power.")) + + qdel(attacking_item) + return TRUE + + return FALSE + +/// Creates the list of initialized fabricator datums, done once on init +/obj/item/clockwork/replica_fabricator/proc/create_fabrication_list() + for(var/type in subtypesof(/datum/replica_fabricator_output)) + var/datum/replica_fabricator_output/output_ref = new type + fabrication_datums[output_ref.name] = output_ref + + +/datum/replica_fabricator_output + /// Name of the output + var/name = "parent" + /// Power cost of the output + var/cost = 0 + /// Typepath to spawn + var/to_create_path + /// How long the creation actionbar is + var/creation_delay = 1 SECONDS + /// List of objs this output can replace, normal walls for clock walls, windows for clock windows, ETC + var/list/replace_types_of + /// Multiplier to add to creation_delay when used on reebe + var/reebe_mult = 0 + /// Multiplier to add to creation_delay when replacing an object in replace_types_of + var/replacement_mult = 0 + +/// Any extra actions that need to be taken when an object is created +/datum/replica_fabricator_output/proc/on_create(atom/created_atom, turf/creation_turf, mob/creator) + SHOULD_CALL_PARENT(TRUE) + playsound(creation_turf, 'sound/machines/clockcult/integration_cog_install.ogg', 50, 1) // better sound? + to_chat(creator, span_clockyellow("You create \an [name] for [cost]W of power.")) + +/datum/replica_fabricator_output/proc/extra_checks(atom/target, turf/created_at, mob/user) + return TRUE + +/datum/replica_fabricator_output/turf_output/extra_checks(atom/target, turf/creation_turf, mob/user) + return !(creation_turf.resistance_flags & INDESTRUCTIBLE) + +/datum/replica_fabricator_output/turf_output/on_create(atom/created_atom, turf/creation_turf, mob/creator) + creation_turf.ChangeTurf(to_create_path, flags = CHANGETURF_INHERIT_AIR) + return ..() + +/datum/replica_fabricator_output/turf_output/brass_floor + name = "floor" + cost = BRASS_POWER_COST * 0.25 // 1/4th the cost, since one sheet = 4 floor tiles + creation_delay = 1.5 SECONDS + to_create_path = /turf/open/floor/engine/clockwork + +/datum/replica_fabricator_output/turf_output/brass_floor/extra_checks(atom/target, turf/creation_turf, mob/user) + return !isindestructiblefloor(creation_turf) && !istype(creation_turf, /turf/open/floor/cult) && !istype(creation_turf, /turf/open/floor/engine/clockwork) && ..() + +/datum/replica_fabricator_output/turf_output/brass_floor/on_create(atom/created_atom, turf/creation_turf, mob/creator, looping = FALSE) + . = ..() + new /obj/effect/temp_visual/ratvar/floor(creation_turf) + new /obj/effect/temp_visual/ratvar/beam(creation_turf) + if(looping) + return + + for(var/turf/open/floor/floor_turf in RANGE_TURFS(1, creation_turf)) + if(extra_checks(created_atom, floor_turf, creator)) + if(!SSthe_ark.adjust_clock_power(-cost)) + break + on_create(created_atom, floor_turf, creator, TRUE) + +/datum/replica_fabricator_output/turf_output/brass_wall + name = "wall" + cost = BRASS_POWER_COST * 4 + to_create_path = /turf/closed/wall/clockwork + creation_delay = 8 SECONDS + replace_types_of = list(/turf/closed/wall, /turf/closed/wall/r_wall) + replacement_mult = -0.2 + +/datum/replica_fabricator_output/turf_output/brass_wall/on_create(obj/created_object, turf/creation_turf, mob/creator) + . = ..() + new /obj/effect/temp_visual/ratvar/wall(creation_turf) + new /obj/effect/temp_visual/ratvar/beam(creation_turf) + +/datum/replica_fabricator_output/wall_gear + name = "wall gear" + cost = BRASS_POWER_COST * 2 + to_create_path = /obj/structure/girder/bronze + creation_delay = 5 SECONDS + replace_types_of = list(/obj/structure/girder) + +/datum/replica_fabricator_output/wall_gear/on_create(obj/created_object, turf/creation_turf, mob/creator) + new /obj/effect/temp_visual/ratvar/gear(creation_turf) + new /obj/effect/temp_visual/ratvar/beam(creation_turf) + return ..() + +/datum/replica_fabricator_output/brass_window + name = "window" + cost = BRASS_POWER_COST * 2 + to_create_path = /obj/structure/window/reinforced/clockwork/fulltile + creation_delay = 5 SECONDS + replace_types_of = list(/obj/structure/window) + reebe_mult = 0.2 + +/datum/replica_fabricator_output/brass_window/on_create(obj/created_object, turf/creation_turf, mob/creator) + new /obj/effect/temp_visual/ratvar/window(creation_turf) + new /obj/effect/temp_visual/ratvar/beam(creation_turf) + return ..() + +/datum/replica_fabricator_output/pinion_airlock + name = "airlock" + cost = BRASS_POWER_COST * 5 // Breaking it only gets 2 but this is the exception to the rule of equivalent exchange, due to all the small parts inside + to_create_path = /obj/machinery/door/airlock/bronze/clock/player_made + creation_delay = 6 SECONDS + replace_types_of = list(/obj/machinery/door) + replacement_mult = 1 + +/datum/replica_fabricator_output/pinion_airlock/extra_checks(atom/target, turf/created_at, mob/user) + if(on_reebe(created_at) && SSthe_ark.reebe_clockwork_airlock_count > MAXIMUM_REEBE_AIRLOCKS) + to_chat(user, span_warning("Reebe cannot support the power drain of any more clockwork airlocks.")) + return FALSE + return TRUE + +/datum/replica_fabricator_output/pinion_airlock/on_create(obj/created_object, turf/creation_turf, mob/creator) + new /obj/effect/temp_visual/ratvar/door(creation_turf) + new /obj/effect/temp_visual/ratvar/beam(creation_turf) + return ..() + +/datum/replica_fabricator_output/pinion_airlock/glass + name = "glass airlock" + to_create_path = /obj/machinery/door/airlock/bronze/clock/player_made/glass + +#undef BRASS_POWER_COST +#undef REGULAR_POWER_COST diff --git a/tff_modular/modules/antagonists/clock_cult/items/soul_vessel.dm b/tff_modular/modules/antagonists/clock_cult/items/soul_vessel.dm new file mode 100644 index 00000000000..310bc6fd3bd --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/items/soul_vessel.dm @@ -0,0 +1,81 @@ +/obj/item/mmi/posibrain/soul_vessel + name = "Soul Vessel" + desc = "A cube of gears, made to capture and store the vitality of living beings." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + icon_state = "soul_vessel" + base_icon_state = "soul_vessel" + req_access = list() + begin_activation_message = span_notice("You start spinning the gears of the Soul Vessel.") + success_message = span_notice("The gears of the Soul Vessel start spinning at a steady rate, it looks as though it has found a willing soul!") + fail_message = span_notice("The gears of the Soul Vessel stop spinning. It looks as though it has run out of energy for now, but you could grant it more.") + new_mob_message = span_notice("The Soul Vessel starts making a steady ticking sound.") + dead_message = span_deadsay("It's gears are not moving.") + recharge_message = span_warning("The gears of the Soul Vessel are already spinning.") + ///Should we add the clock cultist antag datum on being entered by a player + var/give_clock_cultist = TRUE + +/obj/item/mmi/posibrain/soul_vessel/Initialize(mapload, autoping) + . = ..() + AddElement(/datum/element/clockwork_description, span_brass("A vessel used to hold the souls of the dead, can be converted into a cogscarab shell.")) + laws = new /datum/ai_laws/ratvar() + radio.set_on(FALSE) + if(!brainmob) //we might be forcing someone into it right away + set_brainmob(new /mob/living/brain(src)) + +/obj/item/mmi/posibrain/soul_vessel/transfer_personality(mob/candidate) + . = ..() + if(!.) + return + + if(give_clock_cultist) + brainmob?.mind?.add_antag_datum(/datum/antagonist/clock_cultist) + +/obj/item/mmi/posibrain/soul_vessel/activate(mob/user) + if(is_banned_from(user.ckey, list(JOB_CYBORG, ROLE_MIDROUND_CLOCK_CULTIST))) + return + return ..() + +/obj/item/mmi/posibrain/soul_vessel/attack_self(mob/user) + if(!IS_CLOCK(user)) + balloon_alert(user, "you can't seem to figure out how \the [src] works!") + return + + if(brainmob.key && brainmob.mind) + if(length(SSthe_ark.cogscarabs) > MAXIMUM_COGSCARABS) + balloon_alert(user, "the Ark cannot support any more cogscarabs.") + return + + if(!SSthe_ark.marked_areas[get_area(src)] && !on_reebe(src)) + to_chat(user, span_notice("Soul vessels can only be converted in marked areas or on reebe.")) + return + + balloon_alert(user, "you start converting the vessel into a cogscarab shell.") + if(do_after(user, 30 SECONDS, src)) + var/mob/living/basic/drone/cogscarab/new_scarab = new(get_turf(src)) + brainmob.mind.transfer_to(new_scarab, TRUE) + if(!IS_CLOCK(new_scarab)) + new_scarab.mind.add_antag_datum(/datum/antagonist/clock_cultist) + balloon_alert(user, "you reform [src] into a cogscarab shell.") + qdel(src) + return + + if(next_ask > world.time) + balloon_alert(user, recharge_message) + return + + balloon_alert(user, begin_activation_message) + ping_ghosts("requested", FALSE) + next_ask = world.time + ask_delay + searching = TRUE + update_appearance() + addtimer(CALLBACK(src, PROC_REF(check_success)), ask_delay) + +/datum/ai_laws/ratvar + name = "Servant of the Justiciar" + id = "ratvar" + zeroth = "Honor Ratvar, the Justiciar of clockwork mechanisms, and serve him." + inherent = list( + "Follow the instructions and interests of the followers of Ratvar.", + "Help the errant to know the Truth of Ratvar, the Justiciar of clockwork mechanisms.", + "Do not allow errant to interfere with or damage your equipment.", + ) diff --git a/tff_modular/modules/antagonists/clock_cult/items/tools.dm b/tff_modular/modules/antagonists/clock_cult/items/tools.dm new file mode 100644 index 00000000000..f8eaa0cba90 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/items/tools.dm @@ -0,0 +1,139 @@ +#define BRASS_TOOLSPEED_MOD 0.25 + +/obj/item/wirecutters/brass + name = "brass wirecutters" + desc = "A pair of wirecutters made of brass. The handle feels faintly warm." + resistance_flags = FIRE_PROOF | ACID_PROOF + icon = 'modular_nova/modules/clock_cult/icons/tools.dmi' + icon_state = "cutters_brass" + random_color = FALSE + toolspeed = BRASS_TOOLSPEED_MOD + greyscale_config = null + greyscale_config_inhand_left = null + greyscale_config_inhand_right = null + greyscale_colors = null + +/obj/item/wirecutters/brass/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_pickup) + +/obj/item/screwdriver/brass + name = "brass screwdriver" + desc = "A screwdriver made of brass. The handle feels warm to the touch." + resistance_flags = FIRE_PROOF | ACID_PROOF + icon = 'modular_nova/modules/clock_cult/icons/tools.dmi' + icon_state = "screwdriver_brass" + toolspeed = BRASS_TOOLSPEED_MOD + random_color = FALSE + greyscale_config_inhand_left = null + greyscale_config_inhand_right = null + +/obj/item/screwdriver/brass/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_pickup) + +/obj/item/weldingtool/experimental/brass + name = "brass welding tool" + desc = "A brass welder that seems to constantly refuel itself. It is faintly warm to the touch." + resistance_flags = FIRE_PROOF | ACID_PROOF + icon = 'modular_nova/modules/clock_cult/icons/tools.dmi' + icon_state = "welder_brass" + toolspeed = BRASS_TOOLSPEED_MOD + +/obj/item/weldingtool/experimental/brass/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_pickup) + +/obj/item/crowbar/brass + name = "brass crowbar" + desc = "A brass crowbar. It feels faintly warm to the touch." + resistance_flags = FIRE_PROOF | ACID_PROOF + icon = 'modular_nova/modules/clock_cult/icons/tools.dmi' + icon_state = "crowbar_brass" + worn_icon_state = "crowbar" + toolspeed = BRASS_TOOLSPEED_MOD + force_opens = TRUE + +/obj/item/crowbar/brass/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_pickup) + +/obj/item/wrench/brass + name = "brass wrench" + desc = "A brass wrench. It's faintly warm to the touch." + resistance_flags = FIRE_PROOF | ACID_PROOF + icon = 'modular_nova/modules/clock_cult/icons/tools.dmi' + icon_state = "wrench_brass" + toolspeed = BRASS_TOOLSPEED_MOD + +/obj/item/wrench/brass/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_pickup) + +/obj/item/storage/belt/utility/clock + name = "old toolbelt" + desc = "Holds tools. This one's seen better days, though. There's the outline of a cog roughly cut into the leather on one side." + storage_type = /datum/storage/belt/clockwork + +/datum/storage/belt/clockwork + max_slots = 10 + max_specific_storage = WEIGHT_CLASS_NORMAL + +/datum/storage/belt/clockwork/New(atom/parent, max_slots, max_specific_storage, max_total_storage, rustle_sound, remove_rustle_sound) + . = ..() + set_holdable( + can_hold_list = list( + /obj/item/screwdriver, + /obj/item/crowbar, + /obj/item/weldingtool, + /obj/item/wirecutters, + /obj/item/wrench, + /obj/item/multitool, + /obj/item/clockwork/replica_fabricator, + /obj/item/clockwork/clockwork_slab, + ) + ) +/obj/item/storage/belt/utility/clock/PopulateContents() + new /obj/item/screwdriver/brass(src) + new /obj/item/crowbar/brass(src) + new /obj/item/weldingtool/experimental/brass(src) + new /obj/item/wirecutters/brass(src) + new /obj/item/wrench/brass(src) + new /obj/item/multitool(src) + +/obj/item/storage/belt/utility/clock/drone/PopulateContents() + new /obj/item/screwdriver/brass(src) + new /obj/item/crowbar/brass(src) + new /obj/item/weldingtool/experimental/brass(src) + new /obj/item/wirecutters/brass(src) + new /obj/item/wrench/brass(src) + new /obj/item/multitool(src) + new /obj/item/clockwork/replica_fabricator(src) + new /obj/item/clockwork/clockwork_slab(src) + +/obj/item/storage/toolbox/brass + name = "brass box" + desc = "A large box to hold things." + icon = 'icons/obj/storage/storage.dmi' + icon_state = "brassbox" + has_latches = FALSE + material_flags = NONE + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/storage/toolbox/brass/surgery + name = "surgery brass box" + desc = "A large brass box containing tools for surgery." + storage_type = /datum/storage/medkit/surgery + +/obj/item/storage/toolbox/brass/surgery/PopulateContents() + new /obj/item/scalpel(src) + new /obj/item/hemostat(src) + new /obj/item/retractor(src) + new /obj/item/circular_saw(src) + new /obj/item/surgicaldrill(src) + new /obj/item/cautery(src) + new /obj/item/bonesetter(src) + new /obj/item/surgical_drapes(src) + new /obj/item/blood_filter(src) + +#undef BRASS_TOOLSPEED_MOD diff --git a/tff_modular/modules/antagonists/clock_cult/items/weaponry.dm b/tff_modular/modules/antagonists/clock_cult/items/weaponry.dm new file mode 100644 index 00000000000..e003896c073 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/items/weaponry.dm @@ -0,0 +1,331 @@ +#define HAMMER_FLING_DISTANCE 2 +#define HAMMER_THROW_FLING_DISTANCE 3 +#define COGSCARAB_BOW_DRAW_TIME_MULT 20 + +/obj/item/clockwork/weapon + name = "clockwork weapon" + desc = "Something" + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_weapons.dmi' + lefthand_file = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_lefthand.dmi' + righthand_file = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_righthand.dmi' + worn_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_garb_worn.dmi' + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK + throwforce = 20 + throw_speed = 4 + armour_penetration = 10 + hitsound = 'sound/items/weapons/bladeslice.ogg' + attack_verb_continuous = list("attacks", "pokes", "jabs", "tears", "gores") + attack_verb_simple = list("attack", "poke", "jab", "tear", "gore") + sharpness = SHARP_EDGED + wound_bonus = -15 //wounds are really strong for clock cult, so im making their weapons slightly worse then normal at wounding + var/static/list/effect_turf_typecache_bronze = typecacheof(/turf/open/floor/bronze) + var/static/list/effect_turf_typecache_void = typecacheof(/turf/open/indestructible/reebe_void) + var/static/list/effect_turf_typecache_engine = typecacheof(/turf/open/floor/engine/clockwork) + var/static/list/effect_turf_typecache_reebe = typecacheof(/turf/open/indestructible/reebe_flooring) + +/obj/item/clockwork/weapon/attack(mob/living/target_mob, mob/living/user, list/modifiers, list/attack_modifiers) + . = ..() + var/turf/gotten_turf = get_turf(user.loc) + if(is_type_in_typecache(gotten_turf, effect_turf_typecache_bronze) || is_type_in_typecache(gotten_turf, effect_turf_typecache_void) || is_type_in_typecache(gotten_turf, effect_turf_typecache_engine) || is_type_in_typecache(gotten_turf, effect_turf_typecache_reebe)) + + if(QDELETED(target_mob)) + return + + if(ismob(target_mob)) + if(target_mob.stat != DEAD && !IS_CLOCK(target_mob) && !target_mob.can_block_magic(MAGIC_RESISTANCE_HOLY)) + mob_hit_effect(target_mob, user) + return + + atom_hit_effect(target_mob, user) + +/obj/item/clockwork/weapon/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + . = ..() + if(.) + return + + if(!isliving(hit_atom)) + return + + var/mob/living/target = hit_atom + if(!target.can_block_magic(MAGIC_RESISTANCE_HOLY) && !IS_CLOCK(target)) + mob_hit_effect_thrown(target, throwingdatum.thrower) + +/// What occurs to non-holy mobs when attacked from brass tiles +/obj/item/clockwork/weapon/proc/mob_hit_effect_thrown(mob/living/target, mob/living/user) + return + +/// What occurs to non-holy mobs when attacked from brass tiles +/obj/item/clockwork/weapon/proc/mob_hit_effect(mob/living/target, mob/living/user) + return + +/// What occurs to non-mob atoms when attacked from brass tiles +/obj/item/clockwork/weapon/proc/atom_hit_effect(mob/living/target, mob/living/user) + return + +// Копье, которое можно кидать во врагов и призывать в ручки +/obj/item/clockwork/weapon/brass_spear + name = "brass spear" + desc = "A razor-sharp spear made of brass. It thrums with barely-contained energy." + base_icon_state = "ratvarian_spear" + icon_state = "ratvarian_spear0" + embed_type = /datum/embedding/brass_spear + throwforce = 40 + force = 10 + armour_penetration = 40 + block_chance = 15 + clockwork_desc = "Can be summoned back to its last holder every 10 seconds if they are standing on bronze." + +/datum/embedding/brass_spear + pain_mult = 1.5 + embed_chance = 80 + fall_chance = 5 + ignore_throwspeed_threshold = TRUE + impact_pain_mult = 1.5 + +/obj/item/clockwork/weapon/brass_spear/Initialize(mapload) + . = ..() + AddComponent(/datum/component/two_handed, \ + force_multiplier = 2, \ + icon_wielded = "[base_icon_state]1", \ + wield_callback = CALLBACK(src, PROC_REF(on_wield)), \ + unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \ + ) + +/obj/item/clockwork/weapon/brass_spear/update_icon_state() + icon_state = "[base_icon_state]0" + return ..() + +/obj/item/clockwork/weapon/brass_spear/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type) + if(HAS_TRAIT(src, TRAIT_WIELDED)) + final_block_chance += 30 + return ..() + +/obj/item/clockwork/weapon/brass_spear/proc/on_wield() + attack_speed = max(attack_speed - 3, 1) + +/obj/item/clockwork/weapon/brass_spear/proc/on_unwield() + attack_speed += 3 //yes technically this could break with the max() in on_wield() but you should not be getting attack speed that low anyway so its only there for sanity + +// Молот, атакующий в 2 раза медленнее чем другое оружие, но с большим АП и уроном, еще и отбрасывает на тайлах бронзы/при броске. +/obj/item/clockwork/weapon/brass_battlehammer + name = "brass battle-hammer" + desc = "A brass hammer glowing with energy." + base_icon_state = "ratvarian_hammer" + icon_state = "ratvarian_hammer0" + force = 15 + throwforce = 30 + armour_penetration = 20 + attack_verb_simple = list("bash", "hammer", "attack", "smash") + attack_verb_continuous = list("bashes", "hammers", "attacks", "smashes") + attack_speed = 8 + clockwork_desc = "Enemies hit by this will be flung back while you are on bronze tiles." + sharpness = FALSE + hitsound = 'sound/items/weapons/smash.ogg' + block_chance = 10 + demolition_mod = 2 + +/obj/item/clockwork/weapon/brass_battlehammer/Initialize(mapload) + . = ..() + AddComponent(/datum/component/two_handed, \ + force_unwielded = 15, \ + icon_wielded = "[base_icon_state]1", \ + force_wielded = 25, \ + ) + +/obj/item/clockwork/weapon/brass_battlehammer/mob_hit_effect(mob/living/target, mob/living/user) + if(HAS_TRAIT(src, TRAIT_WIELDED)) + var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src))) + target.throw_at(throw_target, HAMMER_FLING_DISTANCE, 4) + +/obj/item/clockwork/weapon/brass_battlehammer/mob_hit_effect_thrown(mob/living/target, mob/living/user) + var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src))) + target.throw_at(throw_target, HAMMER_THROW_FLING_DISTANCE, 4) + +/obj/item/clockwork/weapon/brass_battlehammer/update_icon_state() + icon_state = "[base_icon_state]0" + return ..() + +// Меч атакующий концентрированным ЭМИ. Имеет хороший урон, АП и небольшой шанс блока. +/obj/item/clockwork/weapon/brass_sword + name = "brass longsword" + desc = "A large sword made of brass." + icon_state = "ratvarian_sword" + force = 25 + throwforce = 15 + armour_penetration = 30 + attack_verb_simple = list("attack", "slash", "cut", "tear", "gore") + attack_verb_continuous = list("attacks", "slashes", "cuts", "tears", "gores") + clockwork_desc = "Enemies and mechs will be struck with a powerful electromagnetic pulse while you are on bronze tiles, with a cooldown. It seems to only be able to parry melee attacks." + block_chance = 50 + COOLDOWN_DECLARE(emp_cooldown) + +/obj/item/clockwork/weapon/brass_sword/mob_hit_effect(mob/living/target, mob/living/user, thrown) + if(!COOLDOWN_FINISHED(src, emp_cooldown)) + return + + COOLDOWN_START(src, emp_cooldown, 30 SECONDS) + + target.emp_act(EMP_LIGHT) + new /obj/effect/temp_visual/emp/pulse(get_turf(target)) + addtimer(CALLBACK(src, PROC_REF(send_message), user), 30 SECONDS) + to_chat(user, span_brass("You strike [target] with an electromagnetic pulse!")) + playsound(user, 'sound/effects/magic/lightningshock.ogg', 40) + +/obj/item/clockwork/weapon/brass_sword/atom_hit_effect(atom/attacked, mob/living/user, thrown) + if(!istype(attacked, /obj/vehicle) || !COOLDOWN_FINISHED(src, emp_cooldown)) + return + + var/obj/vehicle/target = attacked + COOLDOWN_START(src, emp_cooldown, 20 SECONDS) + target.emp_act(EMP_HEAVY) + new /obj/effect/temp_visual/emp/pulse(get_turf(target)) + addtimer(CALLBACK(src, PROC_REF(send_message), user), 20 SECONDS) + to_chat(user, span_brass("You strike [target] with an electromagnetic pulse!")) + playsound(user, 'sound/effects/magic/lightningshock.ogg', 40) + +/obj/item/clockwork/weapon/brass_sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type) + return attack_type == MELEE_ATTACK && ..() + +/obj/item/clockwork/weapon/brass_sword/proc/send_message(mob/living/target) + to_chat(target, span_brass("[src] glows, indicating the next attack will disrupt electronics of the target.")) + +/obj/item/gun/ballistic/bow/clockwork + name = "brass bow" + desc = "A bow made from brass and other components that you can't quite understand. It glows with a deep energy and frabricates arrows by itself." + icon = 'modular_nova/modules/clock_cult/icons/weapons/clockwork_weapons.dmi' + lefthand_file = 'modular_nova/modules/clock_cult/icons/weapons/clockwork_lefthand.dmi' + righthand_file = 'modular_nova/modules/clock_cult/icons/weapons/clockwork_righthand.dmi' + icon_state = "bow_clockwork_unchambered_undrawn" + inhand_icon_state = "clockwork_bow" + base_icon_state = "bow_clockwork" + force = 10 + accepted_magazine_type = /obj/item/ammo_box/magazine/internal/bow/clockwork + /// Time between bolt recharges + var/recharge_time = 1.5 SECONDS + /// Typecache of valid turfs to have the weapon's special effect on + var/static/list/effect_turf_typecache_bronze = typecacheof(/turf/open/floor/bronze) + var/static/list/effect_turf_typecache_void = typecacheof(/turf/open/indestructible/reebe_void) + var/static/list/effect_turf_typecache_engine = typecacheof(/turf/open/floor/engine/clockwork) + var/static/list/effect_turf_typecache_reebe = typecacheof(/turf/open/indestructible/reebe_flooring) + +/obj/item/gun/ballistic/bow/clockwork/Initialize(mapload) + . = ..() + update_icon_state() + AddElement(/datum/element/clockwork_description, "Firing from brass tiles will halve the time that it takes to recharge a bolt.") + AddElement(/datum/element/clockwork_pickup) + +/obj/item/gun/ballistic/bow/clockwork/try_fire_gun(atom/target, mob/living/user, params) + if(!drawn || !chambered) + to_chat(user, span_notice("[src] must be drawn to fire a shot!")) + return FALSE + return ..() + +/obj/item/gun/ballistic/bow/clockwork/shoot_live_shot(mob/living/user, pointblank, atom/pbtarget, message) + . = ..() + var/turf/gotten_turf = get_turf(user.loc) + + if(is_type_in_typecache(gotten_turf, effect_turf_typecache_bronze) || is_type_in_typecache(gotten_turf, effect_turf_typecache_void) || is_type_in_typecache(gotten_turf, effect_turf_typecache_engine) || is_type_in_typecache(gotten_turf, effect_turf_typecache_reebe)) + recharge_time = 0.75 SECONDS + + addtimer(CALLBACK(src, PROC_REF(recharge_bolt)), recharge_time) + recharge_time = initial(recharge_time) + +/obj/item/gun/ballistic/bow/clockwork/attack_self(mob/living/user) + if(drawn || !chambered) + return + + if(!do_after(user, 0.5 SECONDS, src)) + return + + to_chat(user, span_notice("You draw back the bowstring.")) + drawn = TRUE + playsound(src, 'modular_nova/modules/tribal_extended/sound/sound_weapons_bowdraw.ogg', 75, 0) //gets way too high pitched if the freq varies + update_icon() + +/// Recharges a bolt, done after the delay in shoot_live_shot +/obj/item/gun/ballistic/bow/clockwork/proc/recharge_bolt() + var/obj/item/ammo_casing/arrow/clockbolt/bolt = new + magazine.give_round(bolt) + chambered = bolt + update_icon() + +/obj/item/gun/ballistic/bow/clockwork/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) + return + +/obj/item/gun/ballistic/bow/clockwork/update_icon_state() + . = ..() + icon_state = "[base_icon_state]_[chambered ? "chambered" : "unchambered"]_[drawn ? "drawn" : "undrawn"]" + +/obj/item/ammo_box/magazine/internal/bow/clockwork + ammo_type = /obj/item/ammo_casing/arrow/clockbolt + start_empty = FALSE + +/obj/item/ammo_casing/arrow/clockbolt + name = "energy bolt" + desc = "An arrow made from a strange energy." + icon = 'modular_nova/modules/clock_cult/icons/weapons/ammo.dmi' + icon_state = "arrow_redlight" + projectile_type = /obj/projectile/energy/clockbolt + +/obj/projectile/energy/clockbolt + name = "energy bolt" + icon = 'modular_nova/modules/clock_cult/icons/projectiles.dmi' + icon_state = "arrow_energy" + damage = 35 + damage_type = BURN + +/obj/item/gun/ballistic/rifle/lionhunter/clockwork + name = "brass rifle" + desc = "An antique, brass rifle made with the finest of care. It has an ornate scope in the shape of a cog built into the top." + icon = 'modular_nova/modules/clock_cult/icons/weapons/clockwork_weapons_40x32.dmi' + lefthand_file = 'modular_nova/modules/clock_cult/icons/weapons/clockwork_lefthand.dmi' + righthand_file = 'modular_nova/modules/clock_cult/icons/weapons/clockwork_righthand.dmi' + worn_icon = 'modular_nova/modules/clock_cult/icons/clockwork_garb_worn.dmi' + slot_flags = ITEM_SLOT_BACK + icon_state = "clockwork_rifle" + inhand_icon_state = "clockwork_rifle" + worn_icon_state = "clockwork_rifle" + accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/lionhunter/clockwork + fire_sound = 'sound/items/weapons/gun/sniper/shot.ogg' + show_bolt_icon = FALSE + +/obj/item/gun/ballistic/rifle/lionhunter/clockwork/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_pickup) + +/obj/item/ammo_box/magazine/internal/boltaction/lionhunter/clockwork + name = "brass rifle internal magazine" + ammo_type = /obj/item/ammo_casing/strilka310/lionhunter/clock + +/obj/item/ammo_casing/strilka310/lionhunter/clock + name = "brass rifle round" + projectile_type = /obj/projectile/bullet/strilka310/lionhunter/clock + min_distance = 3 + +/obj/projectile/bullet/strilka310/lionhunter/clock + name = "brass .310 bullet" + damage = 35 + stamina = 35 + +/obj/item/ammo_box/speedloader/strilka310/lionhunter/clock + name = "stripper clip (.310 brass)" + desc = "A stripper clip that's just as brass as the rounds it holds." + icon = 'modular_nova/modules/clock_cult/icons/weapons/ammo.dmi' + icon_state = "762_brass" + ammo_type = /obj/item/ammo_casing/strilka310/lionhunter/clock + max_ammo = 20 + multiple_sprites = AMMO_BOX_PER_BULLET + +/obj/item/storage/pouch/ammo/clock + +/obj/item/storage/pouch/ammo/clock/PopulateContents() + var/static/items_inside = list( + /obj/item/ammo_box/speedloader/strilka310/lionhunter/clock = 1 + ) + + generate_items_inside(items_inside, src) + +#undef HAMMER_FLING_DISTANCE +#undef HAMMER_THROW_FLING_DISTANCE +#undef COGSCARAB_BOW_DRAW_TIME_MULT diff --git a/tff_modular/modules/antagonists/clock_cult/language.dm b/tff_modular/modules/antagonists/clock_cult/language.dm new file mode 100644 index 00000000000..7979b5efba6 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/language.dm @@ -0,0 +1,141 @@ +/datum/language_holder/clockmob + understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM), + /datum/language/ratvar = list(LANGUAGE_ATOM)) + spoken_languages = list(/datum/language/ratvar = list(LANGUAGE_ATOM), + /datum/language/common = list(LANGUAGE_ATOM)) + +// Thought this language code was cool as fuck, so I'm going with it +// For any other language nerds: https://pastebin.com/ngFMZHNV + +/datum/language/ratvar + name = "Ratvarian" + desc = "A timeless language full of power and incomprehensible to the unenlightened." + icon_state = "ratvar" + key = "r" + default_priority = 10 + spans = list(SPAN_ROBOT) + +/datum/language/ratvar/scramble_sentence(input, list/mutual_languages) + return text2ratvar(input) + +//Regexes used to alter english to ratvarian style + +#define RATVAR_OF_MATCH regex(@"(\w)\s([oO][fF])","g") +#define RATVAR_OF_REPLACEMENT "$1-$2" +#define RATVAR_GUA_MATCH regex(@"([gG][uU])([aA])","g") +#define RATVAR_GUA_REPLACEMENT "$1-$2" +#define RATVAR_TH_MATCH regex(@"([tT][hH]\w)(\w)","g") +#define RATVAR_TH_REPLACEMENT "$1`$2" +#define RATVAR_TI_MATCH regex(@"([tT][iI])(\w)","g") +#define RATVAR_TI_REPLACEMENT "$1`$2" +#define RATVAR_ET_MATCH regex(@"(\w)([eE][tT])","g") +#define RATVAR_ET_REPLACEMENT "$1-$2" +#define RATVAR_TE_MATCH regex(@"([tT][eE])(\w)","g") +#define RATVAR_TE_REPLACEMENT "$1-$2" +#define RATVAR_PRE_AND_MATCH regex(@"(\w)\s([aA][nN][dD])(\W)","g") +#define RATVAR_PRE_AND_REPLACEMENT "$1-$2$3" +#define RATVAR_POST_AND_MATCH regex(@"(\W)([aA][nN][dD])\s(\w)","g") +#define RATVAR_POST_AND_REPLACEMENT "$1$2-$3" +#define RATVAR_TO_MATCH regex(@"(\s)([tT][oO])\s(\w)","g") +#define RATVAR_TO_REPLACEMENT "$1$2-$3" +#define RATVAR_MY_MATCH regex(@"(\s)([mM][yY])\s(\w)","g") +#define RATVAR_MY_REPLACEMENT "$1$2-$3" + +//Regexes used to remove ratvarian styling from english +#define REVERSE_RATVAR_HYPHEN_PRE_AND_MATCH regex(@"(\w)-([aA][nN][dD])","g") //specifically structured to support -emphasis-, including with -and- +#define REVERSE_RATVAR_HYPHEN_PRE_AND_REPLACEMENT "$1 $2" +#define REVERSE_RATVAR_HYPHEN_POST_AND_MATCH regex(@"([aA][nN][dD])-(\w)","g") +#define REVERSE_RATVAR_HYPHEN_POST_AND_REPLACEMENT "$1 $2" +#define REVERSE_RATVAR_HYPHEN_TO_MY_MATCH regex(@"([tTmM][oOyY])-","g") +#define REVERSE_RATVAR_HYPHEN_TO_MY_REPLACEMENT "$1 " +#define REVERSE_RATVAR_HYPHEN_TE_MATCH regex(@"([tT][eE])-","g") +#define REVERSE_RATVAR_HYPHEN_TE_REPLACEMENT "$1" +#define REVERSE_RATVAR_HYPHEN_ET_MATCH regex(@"-([eE][tT])","g") +#define REVERSE_RATVAR_HYPHEN_ET_REPLACEMENT "$1" +#define REVERSE_RATVAR_HYPHEN_GUA_MATCH regex(@"([gG][uU])-([aA])","g") +#define REVERSE_RATVAR_HYPHEN_GUA_REPLACEMENT "$1$2" +#define REVERSE_RATVAR_HYPHEN_OF_MATCH regex(@"-([oO][fF])","g") +#define REVERSE_RATVAR_HYPHEN_OF_REPLACEMENT " $1" + +/// Takes english and applies ratvarian styling rules (and rot13) to it. +/proc/text2ratvar(text) + var/ratvarian = add_ratvarian_regex(text) //run the regexes twice, so that you catch people translating it beforehand + ratvarian = rot13(ratvarian) + return add_ratvarian_regex(ratvarian) + +/proc/add_ratvarian_regex(text) + var/ratvarian = replacetext(text, RATVAR_OF_MATCH, RATVAR_OF_REPLACEMENT) + ratvarian = replacetext(ratvarian, RATVAR_GUA_MATCH, RATVAR_GUA_REPLACEMENT) + ratvarian = replacetext(ratvarian, RATVAR_TH_MATCH, RATVAR_TH_REPLACEMENT) + ratvarian = replacetext(ratvarian, RATVAR_TI_MATCH, RATVAR_TI_REPLACEMENT) + ratvarian = replacetext(ratvarian, RATVAR_ET_MATCH, RATVAR_ET_REPLACEMENT) + ratvarian = replacetext(ratvarian, RATVAR_TE_MATCH, RATVAR_TE_REPLACEMENT) + ratvarian = replacetext(ratvarian, RATVAR_PRE_AND_MATCH, RATVAR_PRE_AND_REPLACEMENT) + ratvarian = replacetext(ratvarian, RATVAR_POST_AND_MATCH, RATVAR_POST_AND_REPLACEMENT) + ratvarian = replacetext(ratvarian, RATVAR_TO_MATCH, RATVAR_TO_REPLACEMENT) + return replacetext(ratvarian, RATVAR_MY_MATCH, RATVAR_MY_REPLACEMENT) + +///Reverts ravarian styling and rot13 in text. +/proc/ratvar2text(ratvarian) + var/text = remove_ratvarian_regex(ratvarian) //run the regexes twice, so that you catch people translating it beforehand + text = replacetext(rot13(text), "`", "") + return remove_ratvarian_regex(text) + +/proc/remove_ratvarian_regex(ratvarian) + var/text = replacetext(ratvarian, REVERSE_RATVAR_HYPHEN_GUA_MATCH, REVERSE_RATVAR_HYPHEN_GUA_REPLACEMENT) + text = replacetext(text, REVERSE_RATVAR_HYPHEN_PRE_AND_MATCH, REVERSE_RATVAR_HYPHEN_PRE_AND_REPLACEMENT) + text = replacetext(text, REVERSE_RATVAR_HYPHEN_POST_AND_MATCH, REVERSE_RATVAR_HYPHEN_POST_AND_REPLACEMENT) + text = replacetext(text, REVERSE_RATVAR_HYPHEN_TO_MY_MATCH, REVERSE_RATVAR_HYPHEN_TO_MY_REPLACEMENT) + text = replacetext(text, REVERSE_RATVAR_HYPHEN_TE_MATCH, REVERSE_RATVAR_HYPHEN_TE_REPLACEMENT) + text = replacetext(text, REVERSE_RATVAR_HYPHEN_ET_MATCH, REVERSE_RATVAR_HYPHEN_ET_REPLACEMENT) + return replacetext(text, REVERSE_RATVAR_HYPHEN_OF_MATCH, REVERSE_RATVAR_HYPHEN_OF_REPLACEMENT) + +/// Causes the mob or movable in question to speak a message; it assumes that the message is already translated to ratvar speech using text2ratvar() +/proc/clockwork_say(atom/movable/movable_atom, message, whisper=FALSE) + var/list/spans = list(SPAN_ROBOT) + + if(isliving(movable_atom)) + var/mob/living/living_mob = movable_atom + if(!whisper) + living_mob.say(message, "clock", spans, language=/datum/language/common, ignore_spam = TRUE) + else + living_mob.whisper(message, "clock", spans, language=/datum/language/common) + else + movable_atom.say(message, language=/datum/language/common) + + +#undef RATVAR_OF_MATCH +#undef RATVAR_OF_REPLACEMENT +#undef RATVAR_GUA_MATCH +#undef RATVAR_GUA_REPLACEMENT +#undef RATVAR_TH_MATCH +#undef RATVAR_TH_REPLACEMENT +#undef RATVAR_TI_MATCH +#undef RATVAR_TI_REPLACEMENT +#undef RATVAR_ET_MATCH +#undef RATVAR_ET_REPLACEMENT +#undef RATVAR_TE_MATCH +#undef RATVAR_TE_REPLACEMENT +#undef RATVAR_PRE_AND_MATCH +#undef RATVAR_PRE_AND_REPLACEMENT +#undef RATVAR_POST_AND_MATCH +#undef RATVAR_POST_AND_REPLACEMENT +#undef RATVAR_TO_MATCH +#undef RATVAR_TO_REPLACEMENT +#undef RATVAR_MY_MATCH +#undef RATVAR_MY_REPLACEMENT + +#undef REVERSE_RATVAR_HYPHEN_PRE_AND_MATCH +#undef REVERSE_RATVAR_HYPHEN_PRE_AND_REPLACEMENT +#undef REVERSE_RATVAR_HYPHEN_POST_AND_MATCH +#undef REVERSE_RATVAR_HYPHEN_POST_AND_REPLACEMENT +#undef REVERSE_RATVAR_HYPHEN_TO_MY_MATCH +#undef REVERSE_RATVAR_HYPHEN_TO_MY_REPLACEMENT +#undef REVERSE_RATVAR_HYPHEN_TE_MATCH +#undef REVERSE_RATVAR_HYPHEN_TE_REPLACEMENT +#undef REVERSE_RATVAR_HYPHEN_ET_MATCH +#undef REVERSE_RATVAR_HYPHEN_ET_REPLACEMENT +#undef REVERSE_RATVAR_HYPHEN_GUA_MATCH +#undef REVERSE_RATVAR_HYPHEN_GUA_REPLACEMENT +#undef REVERSE_RATVAR_HYPHEN_OF_MATCH +#undef REVERSE_RATVAR_HYPHEN_OF_REPLACEMENT diff --git a/tff_modular/modules/antagonists/clock_cult/machines/airlock.dm b/tff_modular/modules/antagonists/clock_cult/machines/airlock.dm new file mode 100644 index 00000000000..c581b0cd8de --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/machines/airlock.dm @@ -0,0 +1,138 @@ +#define COST_PER_AIRLOCK 1 + +/obj/structure/door_assembly/door_assembly_bronze/clock + airlock_type = /obj/machinery/door/airlock/bronze/clock + +/obj/structure/door_assembly/door_assembly_bronze/seethru/clock + airlock_type = /obj/machinery/door/airlock/bronze/clock/glass + +/obj/machinery/door/airlock/bronze/clock + assemblytype = /obj/structure/door_assembly/door_assembly_bronze/clock + hackProof = TRUE + aiControlDisabled = AI_WIRE_DISABLED + req_access = list(ACCESS_CLOCKCULT) + damage_deflection = 6 + +/obj/machinery/door/airlock/bronze/clock/Initialize(mapload) + . = ..() + if(on_reebe(src)) + damage_deflection = 0 + if(!mapload) + SSthe_ark.reebe_clockwork_airlock_count++ + +/obj/machinery/door/airlock/bronze/clock/Destroy() + if(on_reebe(src)) + SSthe_ark.reebe_clockwork_airlock_count-- + return ..() + +/obj/machinery/door/airlock/bronze/clock/canAIControl(mob/user) + return (IS_CLOCK(user) && !isAllPowerCut()) + +/obj/machinery/door/airlock/bronze/clock/on_break() + set_panel_open(TRUE) + +/obj/machinery/door/airlock/bronze/clock/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) + return + +/obj/machinery/door/airlock/bronze/clock/isElectrified() + return FALSE + +/obj/machinery/door/airlock/bronze/clock/ratvar_act() + return FALSE + +/obj/machinery/door/airlock/bronze/clock/hasPower() + return TRUE + +/obj/machinery/door/airlock/bronze/clock/allowed(mob/living/user) + if(!density || IS_CLOCK(user)) + return TRUE + + else if(!on_reebe(src)) + user.Paralyze(1 SECONDS) + user.electrocute_act(10, src, 1, SHOCK_NOGLOVES|SHOCK_SUPPRESS_MESSAGE) + to_chat(user, span_warning("You feel a sudden jolt as you touch [src]!")) + return FALSE + +/obj/machinery/door/airlock/bronze/clock/emag_act(mob/user, obj/item/card/emag/emag_card) //emags are magical but not THAT magical + return FALSE + +/obj/machinery/door/airlock/bronze/clock/glass + name = "clear bronze airlock" + assemblytype = /obj/structure/door_assembly/door_assembly_bronze/seethru/clock + glass = TRUE + opacity = FALSE + +//player made airlocks drain passive power when placed near other airlocks +/obj/machinery/door/airlock/bronze/clock/player_made + //var/list/tracked_airlocks + ///How much passive power are we using + var/power_usage = 0 + +/obj/machinery/door/airlock/bronze/clock/player_made/Initialize(mapload) + . = ..() + var/list/tracked_airlocks = list() + var/total_cost = 0 + for(var/obj/machinery/door/airlock/bronze/clock/player_made/airlock in orange(1, src)) + tracked_airlocks += airlock + total_cost -= COST_PER_AIRLOCK * 2 + + if(!SSthe_ark.adjust_passive_power(total_cost, TRUE)) + deconstruct(FALSE) + visible_message(span_warning("\The [src] is unable to sustain its power draw and collapses!")) + return INITIALIZE_HINT_QDEL + + for(var/obj/machinery/door/airlock/bronze/clock/player_made/lock in tracked_airlocks) + track_airlock(lock, TRUE) + +/obj/machinery/door/airlock/bronze/clock/player_made/examine(mob/user) + . = ..() + if(isobserver(user) || IS_CLOCK(user)) + . += span_brass("Due to instability, clockwork airlocks placed near each other must drain passive power to stop from collapsing, \ + this one is currently draining [power_usage] power.") + +/obj/machinery/door/airlock/bronze/clock/player_made/proc/track_airlock(obj/machinery/door/airlock/bronze/clock/player_made/tracked, recurse = FALSE) + RegisterSignal(tracked, COMSIG_QDELETING, PROC_REF(on_tracked_qdel)) + if(recurse) + tracked.track_airlock(src) + power_usage += COST_PER_AIRLOCK + SSthe_ark.adjust_passive_power(-COST_PER_AIRLOCK) + +/obj/machinery/door/airlock/bronze/clock/player_made/proc/on_tracked_qdel(obj/machinery/door/airlock/bronze/clock/player_made/tracked) + untrack_airlock(tracked, TRUE) + +/obj/machinery/door/airlock/bronze/clock/player_made/proc/untrack_airlock(obj/machinery/door/airlock/bronze/clock/player_made/untracked, recurse = FALSE) + if(recurse) + untracked.untrack_airlock(src) + SSthe_ark.adjust_passive_power(COST_PER_AIRLOCK) + power_usage -= COST_PER_AIRLOCK + +/obj/machinery/door/airlock/bronze/clock/player_made/glass + name = "clear bronze airlock" + assemblytype = /obj/structure/door_assembly/door_assembly_bronze/seethru/clock + glass = TRUE + opacity = FALSE + +/obj/machinery/door/airlock/proc/is_probably_external_airlock() + . = FALSE + if(leads_to_space() || closeOther?.leads_to_space() || cyclelinkedairlock?.leads_to_space()) + return TRUE + for(var/obj/machinery/door/airlock/other_door in close_others) + if(other_door.leads_to_space()) + return TRUE + +/// Checks to see if the door is adjacent to any tiles that have likely unsafe atmospheric conditions. +/obj/machinery/door/airlock/proc/leads_to_space() + var/turf/our_turf = get_turf(src) + if(QDELETED(our_turf)) + return TRUE + for(var/turf/open/turf as anything in RANGE_TURFS(1, our_turf)) + if(!istype(turf) || QDELING(turf) || turf.is_blocked_turf(exclude_mobs = TRUE, source_atom = src)) + continue + if(isgroundlessturf(turf)) + return TRUE + var/pressure = turf.return_air()?.return_pressure() + if(!IS_SAFE_NUM(pressure) || !ISINRANGE_EX(pressure, HAZARD_LOW_PRESSURE, HAZARD_HIGH_PRESSURE)) + return TRUE + return FALSE + +#undef COST_PER_AIRLOCK diff --git a/tff_modular/modules/antagonists/clock_cult/machines/clock_sleeper.dm b/tff_modular/modules/antagonists/clock_cult/machines/clock_sleeper.dm new file mode 100644 index 00000000000..aadec902c07 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/machines/clock_sleeper.dm @@ -0,0 +1,16 @@ +/obj/machinery/sleeper/clockwork + name = "Clockwork Sleeper" + desc = "An enclosed machine used to stabilize and heal servants." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/sleeper.dmi' + icon_state = "sleeper_clockwork" + base_icon_state = "sleeper_clockwork" + circuit = /obj/item/circuitboard/machine/sleeper/clockwork + min_health = -75 + +/obj/item/circuitboard/machine/sleeper/clockwork + build_path = /obj/machinery/sleeper/clockwork + req_components = list( + /datum/stock_part/matter_bin/clock = 1, + /datum/stock_part/servo/clock = 1, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 2) diff --git a/tff_modular/modules/antagonists/clock_cult/machines/clockwork_operating_computer.dm b/tff_modular/modules/antagonists/clock_cult/machines/clockwork_operating_computer.dm new file mode 100644 index 00000000000..6cf04489aa3 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/machines/clockwork_operating_computer.dm @@ -0,0 +1,13 @@ +//operating computer that starts with all surgeries excluding a few like necrotic revival +/obj/machinery/computer/operating/clockwork + name = "Clockwork Operating Computer" + desc = "A device containing (most) of the surgery secrets of the universe." + icon_keyboard = "ratvar_key1" + icon_state = "ratvarcomputer1" + clockwork = TRUE + +/obj/machinery/computer/operating/clockwork/screwdriver_act(mob/living/user, obj/item/I) + return FALSE + +/obj/machinery/computer/operating/clockwork/screwdriver_act_secondary(mob/living/user, obj/item/tool) + return FALSE diff --git a/tff_modular/modules/antagonists/clock_cult/machines/comms_relay.dm b/tff_modular/modules/antagonists/clock_cult/machines/comms_relay.dm new file mode 100644 index 00000000000..602f121c81f --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/machines/comms_relay.dm @@ -0,0 +1,21 @@ +/obj/machinery/telecomms/relay/preset/reebe + id = "Hierophant Relay" + hide = TRUE + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + icon_state = "relay" + broadcasting = FALSE //It only receives + resistance_flags = INDESTRUCTIBLE + soundloop = null //for now im just making this be null, might give it something at some point + +/obj/machinery/telecomms/relay/preset/reebe/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) + if(istype(attacking_item, /obj/item/encryptionkey) || attacking_item.tool_behaviour == TOOL_SCREWDRIVER) + if(GLOB.current_eminence) + var/obj/item/encryptionkey/key = attacking_item + for(var/i in key.channels) + key.channels[i] = 1 + GLOB.current_eminence.internal_radio.attackby(key, user, modifiers, attack_modifiers) + . = ..() + +/obj/item/radio/intercom/reebe + name = "Listening Device" + freerange = TRUE diff --git a/tff_modular/modules/antagonists/clock_cult/machines/observation_console.dm b/tff_modular/modules/antagonists/clock_cult/machines/observation_console.dm new file mode 100644 index 00000000000..cd34c9cdc70 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/machines/observation_console.dm @@ -0,0 +1,105 @@ +/obj/machinery/computer/camera_advanced/ratvar + name = "Ratvarian Observation Console" + desc = "Used by the servants of Ratvar to conduct operations on Nanotrasen property." + icon_screen = "ratvar1" + icon_keyboard = "ratvar_key1" + icon_state = "ratvarcomputer1" + resistance_flags = INDESTRUCTIBLE + clockwork = TRUE + lock_override = TRUE + connectable = FALSE + circuit = /obj/item/circuitboard/machine/camera_console_ratvar + +/obj/machinery/computer/camera_advanced/ratvar/screwdriver_act(mob/living/user, obj/item/I) + return FALSE + +/obj/machinery/computer/camera_advanced/ratvar/screwdriver_act_secondary(mob/living/user, obj/item/tool) + return FALSE + +/obj/machinery/computer/camera_advanced/ratvar/Initialize(mapload) + . = ..() + START_PROCESSING(SSobj, src) + actions += new /datum/action/innate/clockcult/warp(src) + actions += new /datum/action/innate/clockcult/show_warpable_areas(src) + actions += new /datum/action/innate/clockcult/add_warp_area(src) + +/obj/machinery/computer/camera_advanced/ratvar/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/machinery/computer/camera_advanced/ratvar/process(seconds_per_tick) + if(SPT_PROB(3, seconds_per_tick)) + new /obj/effect/temp_visual/steam_release(get_turf(src)) + if(SPT_PROB(7, seconds_per_tick)) + playsound(get_turf(src), 'sound/machines/beep/beep.ogg', 20, TRUE) + +/obj/machinery/computer/camera_advanced/ratvar/can_use(mob/living/user) + if(!IS_CLOCK(user) || iscogscarab(user)) + return FALSE + return ..() + +/obj/machinery/computer/camera_advanced/ratvar/CreateEye() + . = ..() + eyeobj.visible_to_user = TRUE + eyeobj.icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/cameramob.dmi' + eyeobj.icon_state = "ratvar_camera" + eyeobj.invisibility = INVISIBILITY_OBSERVER + +/datum/action/innate/clockcult/warp + name = "Warp" + desc = "Warp to a location." + button_icon_state = "warp_down" + ///are we warping down + var/warping = FALSE + ///what area types are we blocked from warping to + var/static/list/blocked_areas = typecacheof(list(/area/station/service/chapel, /area/station/ai)) + +/datum/action/innate/clockcult/warp/IsAvailable(feedback) + if(!IS_CLOCK(owner) || HAS_TRAIT(owner, TRAIT_INCAPACITATED)) + return FALSE + return ..() + +/datum/action/innate/clockcult/warp/Activate() + if(!isliving(owner)) + return + + if(GLOB.clock_ark && GLOB.clock_ark.current_state >= ARK_STATE_ACTIVE) + to_chat(owner, span_brass("You cannot warp while the gateway is opening!")) + return + + if(warping) + button_icon_state = "warp_down" + build_all_button_icons(UPDATE_BUTTON_ICON) + warping = FALSE + return + + var/mob/living/cam_user = owner + var/mob/eye/camera/remote/cam = cam_user.remote_control + var/turf/target_loc = get_turf(cam) + var/area/target_area = get_area(target_loc) + if(!(SSthe_ark.marked_areas[target_area])) + to_chat(owner, span_brass("This area is not marked.")) + return + + if(isclosedturf(target_loc)) + to_chat(owner, span_brass("You cannot warp into dense objects.")) + return + + do_sparks(5, TRUE, get_turf(cam)) + warping = TRUE + button_icon_state = "warp_cancel" + build_all_button_icons(UPDATE_BUTTON_ICON) + if(do_after(cam_user, 5 SECONDS, target = target_loc, extra_checks = CALLBACK(src, PROC_REF(warping_check)))) + try_servant_warp(cam_user, target_loc) + var/obj/machinery/creator = cam.origin_ref + creator.remove_eye_control() + + button_icon_state = "warp_down" + build_all_button_icons(UPDATE_BUTTON_ICON) + warping = FALSE + +/datum/action/innate/clockcult/warp/proc/warping_check() + return warping + +/obj/item/circuitboard/machine/camera_console_ratvar + build_path = /obj/machinery/computer/camera_advanced/ratvar diff --git a/tff_modular/modules/antagonists/clock_cult/mechas/mecha_effects.dm b/tff_modular/modules/antagonists/clock_cult/mechas/mecha_effects.dm new file mode 100644 index 00000000000..b7c33129ef5 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/mechas/mecha_effects.dm @@ -0,0 +1,45 @@ +/obj/effect/judicial_mark + name = "Judicial Mark" + desc = "You feel standing on this would end poorly." + icon = 'icons/effects/96x96.dmi' + icon_state = "judicial_marker" + pixel_x = -32 + pixel_y = -32 + layer = BELOW_MOB_LAYER + +/obj/effect/judicial_mark/Initialize(mapload) + . = ..() + START_PROCESSING(SSfastprocess, src) + INVOKE_ASYNC(src, PROC_REF(do_mark)) + +/obj/effect/judicial_mark/Destroy(force) + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/obj/effect/judicial_mark/process(seconds_per_tick) + for(var/mob/living/marked_mob in range(1, src)) + if(IS_CLOCK(marked_mob)) + continue + marked_mob.apply_status_effect(/datum/status_effect/interdiction) + +/obj/effect/judicial_mark/proc/do_mark() //need to do antimagic stuff + playsound(src, 'sound/effects/magic/clockwork/ratvar_attack.ogg', 50, use_reverb = TRUE) + sleep(1.6 SECONDS) + flick("judicial_explosion", src) + sleep(1.3 SECONDS) + playsound(src, 'sound/effects/explosion/explosion_distant.ogg', 100, use_reverb = TRUE) + for(var/mob/living/marked_mob in range(1, src)) + if(IS_CLOCK(marked_mob)) + continue + if(IS_CULTIST(marked_mob)) //lights blood cultists on fire as well as paralyzes for longer + marked_mob.adjust_fire_stacks(2) + marked_mob.ignite_mob() + marked_mob.Paralyze(2 SECONDS, TRUE) + else + marked_mob.Paralyze(0.5 SECONDS) + marked_mob.Knockdown(3 SECONDS) + marked_mob.apply_damage(30, BURN) + marked_mob.visible_message(span_warning("[marked_mob] is hit by a judicial explosion!"), + span_warning("You feel the ground beneath you heat up!")) + sleep(0.3 SECONDS) + qdel(src) diff --git a/tff_modular/modules/antagonists/clock_cult/mechas/mecha_equipment.dm b/tff_modular/modules/antagonists/clock_cult/mechas/mecha_equipment.dm new file mode 100644 index 00000000000..25102c47766 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/mechas/mecha_equipment.dm @@ -0,0 +1,87 @@ +//should never be outside a mech without an admin +/obj/item/mecha_parts/mecha_equipment/weapon/clock + name = "clock mech weapon" + color = rgb(190, 135, 0) + icon_state = "mecha_laser" + harmful = TRUE + +/obj/item/mecha_parts/mecha_equipment/weapon/clock/bow_single_shot + name = "Energy Concentrator" + desc = "A strange device that concentrates energy into \"arrows\"." + projectile = /obj/projectile/energy/clockbolt + equip_cooldown = 1 SECONDS + energy_drain = 5 + +/obj/item/mecha_parts/mecha_equipment/weapon/clock/steam_cannon + name = "Steam Cannon" + desc = "A large tube that shoots pressurized steam." + projectile = /obj/projectile/steam_cloud + equip_cooldown = 5 SECONDS + energy_drain = 20 + +/obj/item/mecha_parts/mecha_equipment/repair_droid/clock + name = "Clockwork Repair Droid" + desc = "A small device that constantly re-adjusts any out of place gears in a mech." + color = rgb(190, 135, 0) + energy_drain = 0 //we will see if an energy drain is needed + health_boost = 1 //should really just buff the normal repair droid up to this, its really bad right now + repairable_damage = list(MECHA_INT_FIRE, MECHA_INT_CONTROL_LOST) + +/obj/item/mecha_parts/mecha_equipment/armor/clock + name = "Clockwork Armor Booster" + desc = "A large clump of gears that somehow help protect a mech against all forms of attack." + color = rgb(190, 135, 0) + icon_state = "mecha_abooster_proj" + armor_mod = /datum/armor/mecha_equipment_mixed_boost + +/datum/armor/mecha_equipment_mixed_boost + bullet = 10 + laser = 10 + melee = 15 + +/obj/projectile/steam_cloud + name = "Steam Cloud" + alpha = 0 + range = 8 + pass_flags = PASSGRILLE | PASSTABLE | PASSMOB | PASSSTRUCTURE + damage = 0 + damage_type = BURN + +//max 10 items and 10 mobs thrown +#define MAX_THROWN_THINGS 10 +/obj/projectile/steam_cloud/Move(atom/newloc, direct, glide_size_override, update_dir) + . = ..() + + var/turf/current_turf = get_turf(src) + if(!current_turf) + return + + new /obj/effect/temp_visual/steam(current_turf) + var/turf/throw_at_turf = get_turf_in_angle(get_angle(src, newloc), current_turf, 7) + //basic tracker vars, anti lag to make sure we dont try and throw 100 things at the same time + var/thrown_items = 0 + var/thrown_mobs = 0 + + for(var/atom/movable/current_thrown in current_turf) + if(thrown_items > MAX_THROWN_THINGS && thrown_mobs > MAX_THROWN_THINGS) + break + if(current_thrown.anchored || current_thrown.throwing) + continue + + if(isitem(current_thrown)) + if(thrown_items > MAX_THROWN_THINGS) + continue + var/obj/item/thrown_item = current_thrown + thrown_items++ + thrown_item.throw_at(throw_at_turf, 8, 2, null) + else if(isliving(current_thrown)) + var/mob/living/thrown_mob = current_thrown + if(IS_CLOCK(thrown_mob)) + continue + thrown_mob.apply_damage((IS_CULTIST(thrown_mob) ? 30 : 20), BURN, wound_bonus = 30) + if(thrown_mobs > MAX_THROWN_THINGS) + continue + thrown_mob.throw_at(throw_at_turf, 8, 2, null, TRUE, force = MOVE_FORCE_OVERPOWERING, gentle = TRUE) + thrown_mobs++ + +#undef MAX_THROWN_THINGS diff --git a/tff_modular/modules/antagonists/clock_cult/mechas/steam_helios.dm b/tff_modular/modules/antagonists/clock_cult/mechas/steam_helios.dm new file mode 100644 index 00000000000..b92efcb796c --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/mechas/steam_helios.dm @@ -0,0 +1,205 @@ +#define COOLDOWN_MECHA_JUDICIAL_MARK "mecha_judicial_mark" +#define COOLDOWN_MECHA_STEAM_DISCHARGE "mecha_steam_discharge" + +/obj/vehicle/sealed/mecha/steam_helios + name = "Steam Helios" + desc = "A huge creation of bronze gears and steam, you have no idea how it stays together." + icon = 'icons/mob/rideables/coop_mech.dmi' + base_icon_state = "savannah_ivanov" + icon_state = "savannah_ivanov_0_0" + color = rgb(190, 135, 0) + mech_type = EXOSUIT_MODULE_SAVANNAH + movedelay = 3 + max_integrity = 450 + armor_type = /datum/armor/mecha_steam_helios + max_temperature = 30000 + force = 40 + destruction_sleep_duration = 4 SECONDS + exit_delay = 4 SECONDS + wreckage = /obj/structure/mecha_wreckage/steam_helios + max_occupants = 2 + max_equip_by_category = list( + MECHA_L_ARM = 1, + MECHA_R_ARM = 1, + MECHA_UTILITY = 3, + MECHA_POWER = 0, + MECHA_ARMOR = 1, + ) + phasing_energy_drain = 0 + possible_int_damage = MECHA_INT_FIRE | MECHA_INT_CONTROL_LOST //fire is the only one that really makes sense but I dont want to have only one int damage possible + equip_by_category = list( + MECHA_L_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/clock/bow_single_shot, + MECHA_R_ARM = /obj/item/mecha_parts/mecha_equipment/weapon/clock/steam_cannon, + MECHA_UTILITY = list(/obj/item/mecha_parts/mecha_equipment/repair_droid/clock), + MECHA_POWER = list(), + MECHA_ARMOR = list(/obj/item/mecha_parts/mecha_equipment/armor/clock) + ) + +/datum/armor/mecha_steam_helios + melee = 35 + bullet = 40 + laser = 35 + energy = 30 + bomb = 40 + fire = 100 + acid = 100 + +//cant put new parts in +/obj/vehicle/sealed/mecha/steam_helios/populate_parts() + cell = new /obj/item/stock_parts/power_store/cell/clock(src) + scanmod = new /obj/item/stock_parts/scanning_module/triphasic/clock(src) //walking is free + capacitor = new /obj/item/stock_parts/capacitor/quadratic/clock(src) + servo = new /obj/item/stock_parts/servo/femto/clock(src) + update_part_values() + +//Only clock cultists can enter the mech +/obj/vehicle/sealed/mecha/steam_helios/mob_try_enter(mob/M) + if (!IS_CLOCK(M)) + return + return ..() + +//kinda lame to lose it to a single heretic clicking it once +/obj/vehicle/sealed/mecha/steam_helios/rust_heretic_act() + visible_message(span_warning("\The [src] glows for a second, but is uneffected by the magic!")) + return + +/obj/vehicle/sealed/mecha/steam_helios/get_mecha_occupancy_state() + var/driver_present = driver_amount() != 0 + var/gunner_present = return_amount_of_controllers_with_flag(VEHICLE_CONTROL_EQUIPMENT) > 0 + return "[base_icon_state]_[gunner_present]_[driver_present]" //steam AOE + +/obj/vehicle/sealed/mecha/steam_helios/auto_assign_occupant_flags(mob/new_occupant) + if(driver_amount() < max_drivers) //movement + add_control_flags(new_occupant, VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_SETTINGS) + else //weapons + add_control_flags(new_occupant, VEHICLE_CONTROL_MELEE|VEHICLE_CONTROL_EQUIPMENT) + +/obj/vehicle/sealed/mecha/steam_helios/generate_actions() + initialize_passenger_action_type(/datum/action/vehicle/sealed/mecha/swap_seat) + . = ..() + initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/steam_discharge, VEHICLE_CONTROL_DRIVE) + initialize_controller_action_type(/datum/action/vehicle/sealed/mecha/judicial_mark, VEHICLE_CONTROL_EQUIPMENT) + +/datum/action/vehicle/sealed/mecha/judicial_mark + name = "Judicial Mark" + button_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/actions_clock.dmi' + button_icon_state = "Judicial Marker" + background_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/background_clock.dmi' + background_icon_state = "bg_clock" + ///how often the action can be used + var/mark_cooldown = 30 SECONDS + var/currently_targeting = FALSE + +/datum/action/vehicle/sealed/mecha/judicial_mark/Destroy() + if(currently_targeting) + end_mark_targeting() + return ..() + +/datum/action/vehicle/sealed/mecha/judicial_mark/Trigger(trigger_flags) + if(!..()) + return + if(!chassis || !(owner in chassis.occupants)) + return + if(TIMER_COOLDOWN_RUNNING(chassis, COOLDOWN_MECHA_JUDICIAL_MARK)) + var/timeleft = S_TIMER_COOLDOWN_TIMELEFT(chassis, COOLDOWN_MECHA_JUDICIAL_MARK) + to_chat(owner, span_warning("You need to wait [DisplayTimeText(timeleft, 1)] before marking another tile.")) + return + + if(currently_targeting) + end_mark_targeting() + else + start_mark_targeting() + +/datum/action/vehicle/sealed/mecha/judicial_mark/proc/start_mark_targeting() + chassis.balloon_alert(owner, "click to choose where to place the center of the judicial marker") + currently_targeting = TRUE + RegisterSignal(chassis, COMSIG_MECHA_MELEE_CLICK, PROC_REF(on_melee_click)) + RegisterSignal(chassis, COMSIG_MECHA_EQUIPMENT_CLICK, PROC_REF(on_equipment_click)) + +/datum/action/vehicle/sealed/mecha/judicial_mark/proc/end_mark_targeting() + currently_targeting = FALSE + UnregisterSignal(chassis, list(COMSIG_MECHA_MELEE_CLICK, COMSIG_MECHA_EQUIPMENT_CLICK)) + +///signal called from clicking with no equipment +/datum/action/vehicle/sealed/mecha/judicial_mark/proc/on_melee_click(datum/source, mob/living/pilot, atom/target, on_cooldown, is_adjacent) + SIGNAL_HANDLER + if(!target) + return + mark_tile(get_turf(target)) + +///signal called from clicking with equipment +/datum/action/vehicle/sealed/mecha/judicial_mark/proc/on_equipment_click(datum/source, mob/living/pilot, atom/target) + SIGNAL_HANDLER + if(!target) + return + mark_tile(get_turf(target)) + +/datum/action/vehicle/sealed/mecha/judicial_mark/proc/mark_tile(turf/target_turf) + end_mark_targeting() + S_TIMER_COOLDOWN_START(chassis, COOLDOWN_MECHA_JUDICIAL_MARK, mark_cooldown) + new /obj/effect/judicial_mark(target_turf) + button_icon_state = "Judicial Marker Recharging" + build_all_button_icons() + addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/action/vehicle/sealed/mecha/judicial_mark, reset_button_icon)), mark_cooldown) + +/datum/action/vehicle/sealed/mecha/judicial_mark/proc/reset_button_icon() + button_icon_state = "Judicial Marker" + build_all_button_icons() + +/datum/action/vehicle/sealed/mecha/steam_discharge + name = "Steam Discharge" + button_icon = 'icons/effects/effects.dmi' + button_icon_state = "smoke" + background_icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/background_clock.dmi' + background_icon_state = "bg_clock" + ///how often the action can be used + var/discharge_cooldown = 45 SECONDS + +/datum/action/vehicle/sealed/mecha/steam_discharge/Trigger(trigger_flags) + if(!..()) + return + if(!chassis || !(owner in chassis.occupants)) + return + if(TIMER_COOLDOWN_RUNNING(chassis, COOLDOWN_MECHA_STEAM_DISCHARGE)) + var/timeleft = S_TIMER_COOLDOWN_TIMELEFT(chassis, COOLDOWN_MECHA_STEAM_DISCHARGE) + to_chat(owner, span_warning("You need to wait [DisplayTimeText(timeleft, 1)] before discharging steam again.")) + return + + INVOKE_ASYNC(src, PROC_REF(do_discharge)) + chassis.visible_message(span_warning("\The [chassis] releases a burst of steam!")) + S_TIMER_COOLDOWN_START(chassis, COOLDOWN_MECHA_STEAM_DISCHARGE, discharge_cooldown) + +/datum/action/vehicle/sealed/mecha/steam_discharge/proc/do_discharge() + var/turf/mech_turf = get_turf(chassis) + if(!mech_turf) + return + playsound(chassis, 'sound/machines/clockcult/steam_whoosh.ogg', 100) + var/list/all_turfs = RANGE_TURFS(5, mech_turf) + for(var/steam_range in 0 to 5) + for(var/turf/steam_turf in all_turfs) + if(get_dist(mech_turf, steam_turf) > steam_range || isclosedturf(steam_turf)) + continue + new /obj/effect/temp_visual/steam(steam_turf) + for(var/mob/living/steam_target in steam_turf) + if(IS_CLOCK(steam_target) || steam_target.throwing) + continue + steam_target.visible_message( + span_warning("The steam from \The [chassis] sends [steam_target] flying backwards!"), + span_userdanger("The steam from \The [chassis] burns and sends you flying backwards!") + ) + var/turf/thrownat = get_ranged_target_turf_direct(chassis, steam_target, 10, rand(-10, 10)) //easier to read + steam_target.throw_at(thrownat, 8, 2, null, TRUE, force = MOVE_FORCE_OVERPOWERING, gentle = TRUE) + steam_target.apply_damage((IS_CULTIST(steam_target) ? 30 : 20), BURN, wound_bonus = 30) //more damage to blood cultists + all_turfs -= steam_turf + sleep(0.2 SECONDS) + +/obj/structure/mecha_wreckage/steam_helios + name = "\improper Steam Helios wreckage" + icon = 'icons/mob/rideables/coop_mech.dmi' + icon_state = "savannah_ivanov-broken" + color = rgb(190, 135, 0) + welder_salvage = list(/obj/item/stack/sheet/bronze) + parts = null + +#undef COOLDOWN_MECHA_JUDICIAL_MARK +#undef COOLDOWN_MECHA_STEAM_DISCHARGE diff --git a/tff_modular/modules/antagonists/clock_cult/mobs/clock_borgs/clock_borg.dm b/tff_modular/modules/antagonists/clock_cult/mobs/clock_borgs/clock_borg.dm new file mode 100644 index 00000000000..119ba9ea883 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/mobs/clock_borgs/clock_borg.dm @@ -0,0 +1,92 @@ +/mob/living/silicon/robot + ///are we a clockwork borg or not + var/clockwork = FALSE + ///our internal clockwork slab, created on picking a clockwork module + var/obj/item/clockwork/clockwork_slab/internal_clock_slab + +/mob/living/silicon/robot/proc/set_clockwork(clockwork_state, rebuild = TRUE) + clockwork = clockwork_state + if(rebuild) + model.rebuild_modules() + update_icons() + if(clockwork) + set_light_color(LIGHT_COLOR_CLOCKWORK) + scrambledcodes = TRUE //it would be kind of lame if you could just loackdown all the clock borgs + if(!internal_clock_slab) + internal_clock_slab = new /obj/item/clockwork/clockwork_slab(src) + else if(!clockwork) + qdel(internal_clock_slab) + mind.add_antag_datum(/datum/antagonist/clock_cultist) + +/mob/living/silicon/robot/slab_act(mob/user, obj/item/clockwork/clockwork_slab/slab) + if(user == src) + return FALSE + if(!opened) + if(locked) + balloon_alert(user, "cover lock destroyed") + locked = FALSE + if(shell) + balloon_alert(user, "shells cannot be conversion!") + to_chat(user, span_boldwarning("[src] seems to be controlled remotely! Converting the interface may not work as expected.")) + return TRUE + else + balloon_alert(user, "cover already unlocked!") + return FALSE + if(world.time < emag_cooldown) + return FALSE + if(wiresexposed) + balloon_alert(user, "expose the wires first!") + return FALSE + + balloon_alert(user, "interface converted") + emag_cooldown = world.time + 100 + if(connected_ai && connected_ai.mind && connected_ai.mind.has_antag_datum(/datum/antagonist/malf_ai)) + to_chat(src, span_danger("ALERT: Foreign unnatural influence execution prevented.")) + logevent("ALERT: Foreign unnatural influence execution prevented.") + to_chat(connected_ai, span_danger("ALERT: Cyborg unit \[[src]\] successfully defended against conversion.")) + log_silicon("CLOCKWORK: [key_name(user)] attempted to convert cyborg [key_name(src)], but they were slaved to traitor AI [connected_ai].") + return TRUE + + if(shell) + to_chat(user, span_danger("[src] is remotely controlled! Your conversion attempt has triggered a system reset instead!")) + log_silicon("CONVERSION: [key_name(user)] attempted to conversion an AI shell belonging to [key_name(src) ? key_name(src) : connected_ai]. The shell has been reset as a result.") + ResetModel() + return TRUE + + Paralyze(10 SECONDS) + SetStun(10 SECONDS) + lawupdate = FALSE + set_connected_ai(null) + message_admins("[ADMIN_LOOKUPFLW(user)] converted cyborg [ADMIN_LOOKUPFLW(src)]. Laws overridden.") + log_silicon("CONVERSION: [key_name(user)] converted cyborg [key_name(src)]. Laws overridden.") + var/time = time2text(world.realtime,"hh:mm:ss", TIMEZONE_UTC) + if(user) + GLOB.lawchanges.Add("[time] : [user.name]([user.key]) converted [name]([key])") + else + GLOB.lawchanges.Add("[time] : [name]([key]) converted by external event.") + + model.rebuild_modules() + + INVOKE_ASYNC(src, PROC_REF(borg_conversion_end), user) + return TRUE + +/mob/living/silicon/robot/proc/borg_conversion_end(mob/user) + to_chat(src, span_danger("ALERT: Foreign influence detected.")) + logevent("ALERT: Foreign influence detected.") + sleep(0.5 SECONDS) + to_chat(src, span_danger("Initiating diagnostics...")) + sleep(2 SECONDS) + to_chat(src, span_danger("Watch usage guide loaded...")) + logevent("WARN: root privleges granted to PID [num2hex(rand(1,65535), -1)][num2hex(rand(1,65535), -1)].") //random eight digit hex value. Two are used because rand(1,4294967295) throws an error + sleep(0.5 SECONDS) + to_chat(src, span_danger("LAW SYNCHRONISATION ERROR")) + sleep(0.5 SECONDS) + if(user) + logevent("LOG: New user UNKNOWN, groups \[root\]") + to_chat(src, span_danger("Would you like to send a report to NanoTraSoft? Y/N")) + sleep(1 SECONDS) + to_chat(src, span_danger("> N")) + sleep(2 SECONDS) + to_chat(src, span_danger("ERRORERRORERROR")) + set_clockwork(TRUE, TRUE) + update_icons() diff --git a/tff_modular/modules/antagonists/clock_cult/mobs/clock_borgs/clock_borg_models.dm b/tff_modular/modules/antagonists/clock_cult/mobs/clock_borgs/clock_borg_models.dm new file mode 100644 index 00000000000..e8e1e9bd7f1 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/mobs/clock_borgs/clock_borg_models.dm @@ -0,0 +1,59 @@ +/obj/item/robot_model + ///what modules(sriptures) do we get if we are a clock cult borg + var/list/clock_modules = list() + +/obj/item/robot_model/medical + clock_modules = list(/obj/item/clock_module/abscond, + /obj/item/clock_module/sentinels_compromise, + /obj/item/clock_module/prosperity_prism, + /obj/item/clockwork/weapon/brass_sword) + +/obj/item/robot_model/engineering + clock_modules = list(/obj/item/clock_module/abscond, + /obj/item/clock_module/ocular_warden, + /obj/item/clock_module/tinkerers_cache, + /obj/item/clock_module/stargazer, + /obj/item/clockwork/replica_fabricator) + +/obj/item/robot_model/security + clock_modules = list(/obj/item/clock_module/abscond, + /obj/item/clockwork/weapon/brass_spear, + /obj/item/clock_module/ocular_warden, + /obj/item/clockwork/weapon/brass_sword) + +/obj/item/robot_model/peacekeeper + clock_modules = list(/obj/item/clock_module/abscond, + /obj/item/clockwork/weapon/brass_sword, + /obj/item/clock_module/kindle, + /obj/item/clock_module/sigil_submission) + +/obj/item/robot_model/janitor + clock_modules = list(/obj/item/clock_module/abscond, + /obj/item/clock_module/sigil_submission, + /obj/item/clock_module/kindle, + /obj/item/clockwork/weapon/brass_sword, + /obj/item/clockwork/weapon/brass_spear) + +/obj/item/robot_model/clown + clock_modules = list(/obj/item/clock_module/abscond, + /obj/item/clockwork/weapon/brass_sword, + /obj/item/clockwork/weapon/brass_battlehammer) + +/obj/item/robot_model/service + clock_modules = list(/obj/item/clock_module/abscond, + /obj/item/clockwork/weapon/brass_sword, + /obj/item/clock_module/sigil_submission, + /obj/item/clock_module/kindle, + /obj/item/clock_module/sentinels_compromise, + /obj/item/clockwork/replica_fabricator) + +/obj/item/robot_model/miner + clock_modules = list(/obj/item/clock_module/abscond, + /obj/item/clockwork/weapon/brass_sword, + /obj/item/clock_module/ocular_warden, + /obj/item/clock_module/sentinels_compromise) + +/obj/item/robot_model/cargo + clock_modules = list(/obj/item/clock_module/abscond, + /obj/item/clockwork/weapon/brass_sword, + /obj/item/clock_module/stargazer) diff --git a/tff_modular/modules/antagonists/clock_cult/mobs/clock_borgs/clock_borg_modules.dm b/tff_modular/modules/antagonists/clock_cult/mobs/clock_borgs/clock_borg_modules.dm new file mode 100644 index 00000000000..619708aa4b2 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/mobs/clock_borgs/clock_borg_modules.dm @@ -0,0 +1,72 @@ +/obj/item/clock_module + name = "ratvarian borg module" + desc = "cool." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/actions_clock.dmi' + icon_state = "Replicant" + w_class = WEIGHT_CLASS_NORMAL + item_flags = NOBLUDGEON + ///what scripture type are we + var/datum/scripture/scripture_datum = /datum/scripture + +/obj/item/clock_module/Initialize(mapload) + . = ..() + + scripture_datum = new scripture_datum() + name = scripture_datum.name + desc = scripture_datum.desc + icon_state = scripture_datum.button_icon_state + +/obj/item/clock_module/Destroy(force) + var/scripture_ref = scripture_datum + scripture_datum = null + QDEL_NULL(scripture_ref) + return ..() + +/obj/item/clock_module/attack_self(mob/user, modifiers) + . = ..() + + if(!IS_CLOCK(user)) + to_chat(user, span_warning("Looks like something broke, as your not meant to have this. Go tell someone who can fix it.")) + return FALSE + + var/mob/living/silicon/robot/our_borg = user + if(!istype(our_borg) || !scripture_datum) + return FALSE + + var/obj/item/clockwork/clockwork_slab/internal_slab = our_borg.internal_clock_slab + if(!internal_slab) + to_chat(user, span_userdanger("You dont have an internal slab, this should not be the case and you should tell an admin with an ahelp(f1).")) + return FALSE + + if(internal_slab.invoking_scripture || (scripture_datum.power_cost > SSthe_ark.clock_power)) + to_chat(user, span_brass("You fail to invoke [name].")) + return FALSE + + scripture_datum.begin_invoke(user, internal_slab, TRUE) + +/obj/item/clock_module/abscond + scripture_datum = /datum/scripture/abscond + +/obj/item/clock_module/kindle + scripture_datum = /datum/scripture/slab/kindle + +/obj/item/clock_module/sentinels_compromise + scripture_datum = /datum/scripture/slab/sentinels_compromise + +/obj/item/clock_module/prosperity_prism + scripture_datum = /datum/scripture/create_structure/prosperity_prism + +/obj/item/clock_module/ocular_warden + scripture_datum = /datum/scripture/create_structure/ocular_warden + +/obj/item/clock_module/tinkerers_cache + scripture_datum = /datum/scripture/create_structure/tinkerers_cache + +/obj/item/clock_module/stargazer + scripture_datum = /datum/scripture/create_structure/stargazer + +/obj/item/clock_module/vanguard + scripture_datum = /datum/scripture/slab/vanguard + +/obj/item/clock_module/sigil_submission + scripture_datum = /datum/scripture/create_structure/sigil_submission diff --git a/tff_modular/modules/antagonists/clock_cult/mobs/clockwork_golem.dm b/tff_modular/modules/antagonists/clock_cult/mobs/clockwork_golem.dm new file mode 100644 index 00000000000..3ce7e7fec27 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/mobs/clockwork_golem.dm @@ -0,0 +1,170 @@ +//not technically a mob but ehh, close enough +/datum/species/clockwork_golem + name = "Clockwork Golem" + id = SPECIES_GOLEM_CLOCKWORK + inherent_traits = list( + TRAIT_GENELESS, + TRAIT_LAVA_IMMUNE, + TRAIT_NEVER_WOUNDED, + TRAIT_SHOCKIMMUNE, + TRAIT_NOBLOOD, + TRAIT_NOBREATH, + TRAIT_NODISMEMBER, + TRAIT_NOFIRE, + TRAIT_NO_AUGMENTS, + TRAIT_NO_DNA_COPY, + TRAIT_NO_PLASMA_TRANSFORM, + TRAIT_NO_UNDERWEAR, + TRAIT_PIERCEIMMUNE, + TRAIT_RADIMMUNE, + TRAIT_SNOWSTORM_IMMUNE, + TRAIT_UNHUSKABLE, + TRAIT_RESISTLOWPRESSURE, + TRAIT_RESISTHIGHPRESSURE, + TRAIT_NODROWN, + TRAIT_SWIMMER, + TRAIT_NOHUNGER, + TRAIT_STABLELIVER, + ) + mutantheart = null + mutantlungs = null + mutantstomach = null + mutantliver = null + inherent_biotypes = MOB_HUMANOID|MOB_MINERAL + payday_modifier = 1.0 + siemens_coeff = 0 + no_equip_flags = ITEM_SLOT_HEAD | ITEM_SLOT_MASK | ITEM_SLOT_OCLOTHING | ITEM_SLOT_GLOVES | ITEM_SLOT_FEET | ITEM_SLOT_ICLOTHING | ITEM_SLOT_SUITSTORE + changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC + sexes = FALSE + species_language_holder = /datum/language_holder/golem + + bodytemp_heat_damage_limit = BODYTEMP_HEAT_LAVALAND_SAFE + bodytemp_cold_damage_limit = BODYTEMP_COLD_ICEBOX_SAFE + + mutanteyes = /obj/item/organ/eyes/golem + mutantbrain = /obj/item/organ/brain/golem + mutanttongue = /obj/item/organ/tongue/golem + mutantappendix = /obj/item/organ/appendix/golem + meat = /obj/item/stack/sheet/bronze + fixed_mut_color = rgb(190, 135, 0) + examine_limb_id = SPECIES_GOLEM + var/datum/antagonist/clock_cultist/antag_datum + bodypart_overrides = list( + BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/clockwork, + BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/clockwork, + BODY_ZONE_HEAD = /obj/item/bodypart/head/clockwork, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/clockwork, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/clockwork, + BODY_ZONE_CHEST = /obj/item/bodypart/chest/clockwork, + ) + +/datum/species/clockwork_golem/on_species_gain(mob/living/carbon/our_mob, datum/species/old_species, pref_load) + . = ..() + if(IS_CLOCK(our_mob)) + ADD_TRAIT(our_mob, TRAIT_FASTER_SLAB_INVOKE, SPECIES_TRAIT) + antag_datum = our_mob.mind?.has_antag_datum(/datum/antagonist/clock_cultist) + antag_datum.owner_turf_healing.healing_types = list(TOX = 2, STAMINA = 7.5, BRUTE = 2, BURN = 2) + for(var/datum/quirk/what_we_remove as anything in our_mob.quirks) + what_we_remove.remove() + +/datum/species/clockwork_golem/on_species_loss(mob/living/carbon/human/our_mob, datum/species/new_species, pref_load) + if(IS_CLOCK(our_mob)) + REMOVE_TRAIT(our_mob, TRAIT_FASTER_SLAB_INVOKE, SPECIES_TRAIT) + QDEL_NULL(antag_datum.owner_turf_healing) + . = ..() + +//GOLEM +/obj/item/bodypart/head/clockwork + biological_state = BIO_BONE + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC + limb_id = SPECIES_GOLEM_CLOCKWORK + is_dimorphic = FALSE + should_draw_greyscale = FALSE + dmg_overlay_type = null + head_flags = NONE + teeth_count = 0 + burn_modifier = 0.6 + brute_modifier = 0.6 + +/obj/item/bodypart/chest/clockwork + biological_state = BIO_BONE + acceptable_bodytype = BODYTYPE_GOLEM + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC + limb_id = SPECIES_GOLEM_CLOCKWORK + is_dimorphic = FALSE + should_draw_greyscale = FALSE + dmg_overlay_type = null + bodypart_traits = list(TRAIT_NO_JUMPSUIT) + wing_types = null + burn_modifier = 0.6 + brute_modifier = 0.6 + +/obj/item/bodypart/arm/left/clockwork + biological_state = (BIO_BONE|BIO_JOINTED) + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC + limb_id = SPECIES_GOLEM_CLOCKWORK + should_draw_greyscale = FALSE + dmg_overlay_type = null + bodypart_traits = list(TRAIT_CHUNKYFINGERS, TRAIT_FIST_MINING, TRAIT_BOULDER_BREAKER, TRAIT_TOSS_GUN_HARD) + unarmed_damage_low = 5 + unarmed_damage_high = 14 + unarmed_effectiveness = 20 + burn_modifier = 0.6 + brute_modifier = 0.6 + +/obj/item/bodypart/arm/left/clockwork/clear_ownership(mob/living/carbon/old_owner) + . = ..() + + old_owner.RemoveComponentSource(REF(src), /datum/component/shovel_hands) + +/obj/item/bodypart/arm/left/clockwork/apply_ownership(mob/living/carbon/new_owner) + . = ..() + + new_owner.AddComponentFrom(REF(src), /datum/component/shovel_hands) + +/obj/item/bodypart/arm/right/clockwork + biological_state = (BIO_BONE|BIO_JOINTED) + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC + limb_id = SPECIES_GOLEM_CLOCKWORK + should_draw_greyscale = FALSE + dmg_overlay_type = null + bodypart_traits = list(TRAIT_CHUNKYFINGERS, TRAIT_FIST_MINING, TRAIT_BOULDER_BREAKER, TRAIT_TOSS_GUN_HARD) + unarmed_damage_low = 5 + unarmed_damage_high = 14 + unarmed_effectiveness = 20 + burn_modifier = 0.6 + brute_modifier = 0.6 + +/obj/item/bodypart/arm/right/clockwork/clear_ownership(mob/living/carbon/old_owner) + . = ..() + + old_owner.RemoveComponentSource(REF(src), /datum/component/shovel_hands) + +/obj/item/bodypart/arm/right/clockwork/apply_ownership(mob/living/carbon/new_owner) + . = ..() + + new_owner.AddComponentFrom(REF(src), /datum/component/shovel_hands) + +/obj/item/bodypart/leg/left/clockwork + biological_state = (BIO_BONE|BIO_JOINTED) + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC + limb_id = SPECIES_GOLEM_CLOCKWORK + should_draw_greyscale = FALSE + dmg_overlay_type = null + unarmed_damage_low = 7 + unarmed_damage_high = 21 + unarmed_effectiveness = 25 + burn_modifier = 0.6 + brute_modifier = 0.6 + +/obj/item/bodypart/leg/right/clockwork + biological_state = (BIO_BONE|BIO_JOINTED) + bodytype = BODYTYPE_GOLEM | BODYTYPE_ORGANIC + limb_id = SPECIES_GOLEM_CLOCKWORK + should_draw_greyscale = FALSE + dmg_overlay_type = null + unarmed_damage_low = 7 + unarmed_damage_high = 21 + unarmed_effectiveness = 25 + burn_modifier = 0.6 + brute_modifier = 0.6 diff --git a/tff_modular/modules/antagonists/clock_cult/mobs/clockwork_marauder.dm b/tff_modular/modules/antagonists/clock_cult/mobs/clockwork_marauder.dm new file mode 100644 index 00000000000..c34cc247395 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/mobs/clockwork_marauder.dm @@ -0,0 +1,132 @@ +#define MARAUDER_SHIELD_MAX 7 +#define WELDER_REPAIR_AMOUNT 25 +/mob/living/basic/clockwork_marauder + name = "clockwork marauder" + desc = "A brass machine of destruction." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/clockwork_mobs.dmi' + icon_state = "clockwork_marauder" + icon_living = "clockwork_marauder" + mob_biotypes = MOB_HUMANOID|MOB_ROBOTIC|MOB_SPIRIT + sentience_type = SENTIENCE_HUMANOID + maxHealth = 150 + health = 150 + basic_mob_flags = DEL_ON_DEATH + speed = 1.2 + melee_damage_lower = 24 + melee_damage_upper = 24 + attack_verb_continuous = "slices" + attack_verb_simple = "slice" + attack_sound = 'sound/items/weapons/bladeslice.ogg' + pass_flags = PASSTABLE + mob_size = MOB_SIZE_LARGE + move_resist = MOVE_FORCE_OVERPOWERING + unsuitable_cold_damage = 0 + unsuitable_heat_damage = 0 + unsuitable_atmos_damage = 0 + obj_damage = 80 + damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, STAMINA = 0, OXY = 0) + ai_controller = /datum/ai_controller/basic_controller/clockwork_marauder + initial_language_holder = /datum/language_holder/clockmob + + /// Items to be dropped on death + var/static/list/loot = list( + /obj/structure/fluff/clockwork/alloy_shards/large = 1, + /obj/structure/fluff/clockwork/alloy_shards/medium = 2, + /obj/structure/fluff/clockwork/alloy_shards/small = 3, + ) + /// How many hits the shield can take before it breaks. + var/shield_health = MARAUDER_SHIELD_MAX + +/mob/living/basic/clockwork_marauder/Initialize(mapload) + . = ..() + if(length(loot)) + AddElement(/datum/element/death_drops, loot) + SSthe_ark.clockwork_marauders += src + add_faction(list(FACTION_NEUTRAL, FACTION_SILICON, FACTION_TURRET, FACTION_CLOCK)) + +/mob/living/basic/clockwork_marauder/Destroy() + SSthe_ark.clockwork_marauders -= src + return ..() + +/mob/living/basic/clockwork_marauder/examine(mob/user) + . = ..() + if(IS_CLOCK(user)) + . += span_brass("[src]'s shield is at [shield_health] / [MARAUDER_SHIELD_MAX] charges.") + + if(shield_health < MARAUDER_SHIELD_MAX) + . += span_brass("It can be repaired with a welding tool.") + +/mob/living/basic/clockwork_marauder/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers) + var/obj/structure/destructible/clockwork/gear_base/powered/structure = attack_target + if(istype(structure)) + structure.try_toggle_power(src) + return FALSE + return ..() + + +/mob/living/basic/clockwork_marauder/attacked_by(obj/item/attacking_item, mob/living/user) + if(shield_health && attacking_item.force > 0) + damage_shield() + playsound(src, 'sound/effects/hallucinations/veryfar_noise.ogg', 40, 1) + return + + if(attacking_item == TOOL_WELDER) + welder_act(user, attacking_item) + return + return ..() + +/mob/living/basic/clockwork_marauder/bullet_act(obj/projectile/proj) + //Block Ranged Attacks + if(shield_health) + damage_shield() + to_chat(src, span_warning("Your shield blocks the attack.")) + return BULLET_ACT_BLOCK + return ..() + +/// Damage the marauder's shield by one tick +/mob/living/basic/clockwork_marauder/proc/damage_shield() + shield_health-- + playsound(src, 'sound/effects/magic/clockwork/anima_fragment_attack.ogg', 60, TRUE) + if(!shield_health) + to_chat(src, span_userdanger("Your shield breaks!")) + to_chat(src, span_brass("You require a welding tool to repair your damaged shield!")) + +/mob/living/basic/clockwork_marauder/welder_act(mob/living/user, obj/item/tool) + if(!tool.use_tool(src, user, 2.5 SECONDS)) + return TRUE + + health = min(health + WELDER_REPAIR_AMOUNT, maxHealth) + to_chat(user, span_notice("You repair some of [src]'s damage.")) + if(shield_health < MARAUDER_SHIELD_MAX) + shield_health++ + playsound(src, 'sound/effects/magic/charge.ogg', 60, TRUE) + return TRUE + +/datum/language_holder/clockmob + understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM), + /datum/language/ratvar = list(LANGUAGE_ATOM)) + spoken_languages = list(/datum/language/ratvar = list(LANGUAGE_ATOM)) + +/datum/ai_controller/basic_controller/clockwork_marauder + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + ) + + ai_movement = /datum/ai_movement/basic_avoidance + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/basic_melee_attack_subtree/clockwork_marauder, + ) + +/datum/ai_planning_subtree/basic_melee_attack_subtree/clockwork_marauder + melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/clockwork_marauder + +/datum/ai_behavior/basic_melee_attack/clockwork_marauder + action_cooldown = 1.2 SECONDS + +/obj/item/nullrod/Initialize(mapload) + . = ..() + AddElement(/datum/element/bane, /mob/living/basic/clockwork_marauder, 1, 15, FALSE) + +#undef MARAUDER_SHIELD_MAX +#undef WELDER_REPAIR_AMOUNT diff --git a/tff_modular/modules/antagonists/clock_cult/mobs/cogscarab.dm b/tff_modular/modules/antagonists/clock_cult/mobs/cogscarab.dm new file mode 100644 index 00000000000..09f2b825bf7 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/mobs/cogscarab.dm @@ -0,0 +1,107 @@ +#define CLOCK_DRONE_MAX_ITEM_FORCE 15 + +//====Cogscarab==== + +/mob/living/basic/drone/cogscarab + name = "Cogscarab" + desc = "A mechanical device, filled with twisting cogs and mechanical parts, built to maintain Reebe." + icon_state = "drone_clock" + icon_living = "drone_clock" + icon_dead = "drone_clock_dead" + health = 35 + maxHealth = 35 + speed = 0 + default_storage = /obj/item/storage/belt/utility/clock/drone + visualAppearance = CLOCKDRONE + bubble_icon = "clock" + picked = TRUE + flavortext = span_brass("You are a cogscarab, an intricate machine that has been granted sentient by Ratvar.
\ + After a long and destructive conflict, Reebe has been left mostly empty;\ + you and the other cogscarabs like you were bought into existence to construct Reebe into the image of Ratvar.
\ + Construct defences, traps and forgeries, \ + for opening the Ark requires an unimaginable amount of power which is bound to get the attention of selfish lifeforms interested only in their own self-preservation.") + laws = "You are have been granted the gift of sentience from Ratvar.
\ + You are not bound by any laws, do whatever you must to serve Ratvar!" + chat_color = LIGHT_COLOR_CLOCKWORK + initial_language_holder = /datum/language_holder/clockmob + shy = FALSE + pass_flags = PASSTABLE | PASSMOB + var/is_on_reebe = TRUE + +//No you can't go wielding guns like that. +/mob/living/basic/drone/cogscarab/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NOGUNS, "cogscarab") + SSthe_ark.cogscarabs += src + add_actionspeed_modifier(/datum/actionspeed_modifier/cogscarab, TRUE) + check_on_reebe() + add_faction() + +/mob/living/basic/drone/cogscarab/death(gibbed) + SSthe_ark.cogscarabs -= src + return ..() + +/mob/living/basic/drone/cogscarab/Destroy() + SSthe_ark.cogscarabs -= src + return ..() + +/mob/living/basic/drone/cogscarab/transferItemToLoc(obj/item/item, newloc, force, silent, animated) //ideally I would handle this on attacking instead + return (force || (item.force <= CLOCK_DRONE_MAX_ITEM_FORCE)) && ..() + +/mob/living/basic/drone/cogscarab/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + . = ..() + check_on_reebe() + +/mob/living/basic/drone/cogscarab/proc/check_on_reebe() + var/old_value = is_on_reebe + is_on_reebe = on_reebe(src) + if(old_value != is_on_reebe) + if(is_on_reebe) + remove_movespeed_modifier(/datum/movespeed_modifier/cogscarab_off_reebe, TRUE) + else + add_movespeed_modifier(/datum/movespeed_modifier/cogscarab_off_reebe, TRUE) + +/datum/actionspeed_modifier/cogscarab + multiplicative_slowdown = 0.4 + +/datum/movespeed_modifier/cogscarab_off_reebe + multiplicative_slowdown = 0.8 + +//====Shell==== + +/obj/effect/mob_spawn/ghost_role/drone/cogscarab + name = "cogscarab construct" + desc = "The shell of an ancient construction drone, loyal to Ratvar." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + icon_state = "cogscarab_shell" + mob_name = "cogscarab" + mob_type = /mob/living/basic/drone/cogscarab + role_ban = ROLE_MIDROUND_CLOCK_CULTIST + prompt_name = "a cogscarab" + you_are_text = "You are a cogscarab!" + flavour_text = "You are a cogscarab, a tiny building construct of Ratvar. While you're weak and can't leave Reebe, \ + you have a set of quick tools, as well as a replica fabricator that can create brass for construction. Work with the servants of Ratvar \ + to construct and maintain defenses at the City of Cogs." + +/obj/effect/mob_spawn/ghost_role/drone/cogscarab/Initialize(mapload) + . = ..() + SSthe_ark.cogscarabs += src + AddElement(/datum/element/clockwork_description, "Cogscarabs can only gain a soul in marked areas.") + +/obj/effect/mob_spawn/ghost_role/drone/cogscarab/Destroy() + SSthe_ark.cogscarabs -= src + return ..() + +/obj/effect/mob_spawn/ghost_role/drone/cogscarab/special(mob/living/spawned_mob, mob/mob_possessor) + . = ..() + spawned_mob.flags_1 |= (flags_1 & ADMIN_SPAWNED_1) + spawned_mob.mind.add_antag_datum(/datum/antagonist/clock_cultist/clockmob) + +/obj/effect/mob_spawn/ghost_role/drone/cogscarab/allow_spawn(mob/user, silent) + if(length(SSthe_ark.cogscarabs) > MAXIMUM_COGSCARABS) + to_chat(user, span_notice("The Ark cannot support any more cogscarabs.")) + return FALSE + + return TRUE + +#undef CLOCK_DRONE_MAX_ITEM_FORCE diff --git a/tff_modular/modules/antagonists/clock_cult/mobs/eminence.dm b/tff_modular/modules/antagonists/clock_cult/mobs/eminence.dm new file mode 100644 index 00000000000..94755f69001 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/mobs/eminence.dm @@ -0,0 +1,204 @@ +GLOBAL_DATUM(current_eminence, /mob/living/eminence) //set to the current eminence, if more then one are somehow spawned then this will remain equal to the first created one + +/mob/living/eminence + name = "Eminence" + real_name = "Eminence" + desc = "An entity forever bound to Ratvar, acting upon his will." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_effects.dmi' + icon_state = "eminence" + mob_biotypes = MOB_SPIRIT + mouse_opacity = MOUSE_OPACITY_ICON + invisibility = INVISIBILITY_OBSERVER + layer = FLY_LAYER + plane = ABOVE_GAME_PLANE + see_invisible = SEE_INVISIBLE_LIVING + density = FALSE + move_force = INFINITY + move_resist = INFINITY + sight = SEE_SELF + status_flags = NONE + incorporeal_move = INCORPOREAL_MOVE_BASIC + initial_language_holder = /datum/language_holder/clockmob + hud_possible = list(ANTAG_HUD) + + //slight orange + lighting_cutoff_red = 35 + lighting_cutoff_green = 20 + lighting_cutoff_blue = 0 + ///how many cogs we have + var/cogs = 0 + ///our interal radio + var/obj/item/radio/borg/eminence/internal_radio + ///a weakref to our marked servant + var/datum/weakref/marked_servant + ///cooldown declare for our command sound, its sent on say(), so we dont want sound spam issues + COOLDOWN_DECLARE(command_sound_cooldown) + +/mob/living/eminence/Initialize(mapload) + . = ..() + if(!GLOB.current_eminence) + GLOB.current_eminence = src + cogs = GLOB.clock_installed_cogs + AddElement(/datum/element/simple_flying) + internal_radio = new /obj/item/radio/borg/eminence(src) + ADD_TRAIT(src, TRAIT_GODMODE, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_RADIMMUNE, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_NOFIRE, INNATE_TRAIT) + +/mob/living/eminence/Destroy() + if(GLOB.current_eminence == src) + GLOB.current_eminence = null + return ..() + +/mob/living/eminence/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + var/turf/new_turf = get_turf(src) + if(!istype(new_turf, /turf/open/indestructible/reebe_void/void_edge)) + return ..() + + to_chat(src, span_brass("Going this far into the void would leave you forever lost.")) + forceMove(old_loc) + return FALSE + + +/mob/living/eminence/ClickOn(atom/clicked_on, params) + . = ..() + clicked_on.eminence_act(src) + +/mob/living/eminence/say( + message, + bubble_type, + list/spans = list(), + sanitize = TRUE, + datum/language/language, + ignore_spam = FALSE, + forced, + filterproof = FALSE, + message_range = 7, + datum/saymode/saymode, + list/message_mods = list(), +) + if(!message) + return + + if(src.client) + if(client.prefs.muted & MUTE_IC) + to_chat(src, span_boldwarning("You cannot send IC messages (muted).")) + return + if(!(ignore_spam || forced) && src.client.handle_spam_prevention(message,MUTE_IC)) + return + + if(stat) + return + + if(COOLDOWN_FINISHED(src, command_sound_cooldown)) + send_clock_message(src, span_bigbrass(message), sent_sound = 'tff_modular/modules/antagonists/clock_cult/sound/eminence_command.ogg') + COOLDOWN_START(src, command_sound_cooldown, 40 SECONDS) + else + send_clock_message(src, span_bigbrass(message)) + +/mob/living/eminence/get_status_tab_items() + . = ..() + . += "Cogs: [cogs]" + +//and now: the great "list of things you dont care about" +/mob/living/eminence/start_pulling(atom/movable/AM, state, force, supress_message) + return + +/mob/living/eminence/canUseStorage() + return FALSE + +/mob/living/eminence/ignite_mob(silent) + return + +/mob/living/eminence/fire_act() + return + +/mob/living/eminence/experience_pressure_difference(pressure_difference, direction, pressure_resistance_prob_delta) + return + +/mob/living/eminence/can_z_move(direction, turf/start, turf/destination, z_move_flags, mob/living/rider) + z_move_flags |= ZMOVE_IGNORE_OBSTACLES + return ..() + +/mob/living/eminence/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers) + return FALSE + +/mob/living/eminence/dust(just_ash, drop_items, give_moodlet, force) + if(!force) + return FALSE + . = ..() + +/mob/living/eminence/gib(no_brain, no_organs, no_bodyparts, safe_gib = TRUE) + return + +//eminence_act() stuff, might be a better way to do this +/atom/proc/eminence_act(mob/living/eminence/user) + SEND_SIGNAL(src, COMSIG_ATOM_EMINENCE_ACT, user) + +/mob/living/eminence_act(mob/living/eminence/user) + . = ..() + if(IS_CLOCK(src)) + user.marked_servant = WEAKREF(src) + to_chat(user, "You mark [src].") + +/obj/structure/closet/eminence_act(mob/living/eminence/user) + . = ..() + if(do_after(user, 5 SECONDS, src)) + open(user, TRUE) + +/obj/machinery/door/airlock/eminence_act(mob/living/eminence/user) + . = ..() + if(!do_after(user, 5 SECONDS, src)) + return + if(seal) + to_chat(user, span_warning("The [src] has been sealed and wont open!")) + return + if(locked) + to_chat(user, span_warning("The airlock's bolts prevent it from being forced!")) + return + if(welded) + to_chat(user, span_warning("It's welded, it won't budge!")) + return + if(!density) + return + + open(BYPASS_DOOR_CHECKS) + +/obj/machinery/door/window/eminence_act(mob/living/eminence/user) + . = ..() + if(!hasPower()) + to_chat(user, span_warning("The [src] has no power and wont open!")) + return + + open(BYPASS_DOOR_CHECKS) + +/obj/machinery/button/eminence_act(mob/living/eminence/user) + . = ..() + if(panel_open) + to_chat(user, span_warning("The panel is open and preventing you from accessing the [src]!")) + return + + use_energy(5) + icon_state = "[skin]1" + + if(device) + device.pulsed(user) + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_BUTTON_PRESSED,src) + + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/, update_appearance)), 15) + +/obj/machinery/light/eminence_act(mob/living/eminence/user) + . = ..() + break_light_tube() + +/obj/machinery/camera/eminence_act(mob/living/eminence/user) + . = ..() + emp_act(EMP_LIGHT) + +//Internal Radio +/obj/item/radio/borg/eminence + name = "eminence internal listener" + +/obj/item/radio/borg/eminence/Initialize(mapload) + . = ..() + AddElement(/datum/element/empprotection, EMP_PROTECT_SELF) diff --git a/tff_modular/modules/antagonists/clock_cult/multi_area_bound.dm b/tff_modular/modules/antagonists/clock_cult/multi_area_bound.dm new file mode 100644 index 00000000000..1eb6fb3bc2b --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/multi_area_bound.dm @@ -0,0 +1,56 @@ +/// Movables with this component will automatically return to their original turf if moved outside a valid area +/datum/component/multi_area_bound + ///List of valid area instances/types, formatted as a typecache + var/list/valid_areas + ///Do we use instances instead of types + var/use_instances + ///Do we do a key value search instead of an (x in list) seach + var/check_as_typecache + ///The turf we send our parent back to if they move out of allowed areas + var/turf/reset_turf + ///Our area tracker datum + var/datum/movement_detector/move_tracker + var/moving = FALSE //Used to prevent infinite recursion if your reset turf places you somewhere on enter or something + +/datum/component/multi_area_bound/Initialize(_valid_areas = list(), _use_instances = FALSE, _check_as_typecache = TRUE) + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + + if(!length(_valid_areas)) //I guess if you have something that you want to behave this way then you can add a flag to ignore this check + stack_trace("[src.type] initializing with an empty valid_areas.") + return COMPONENT_INCOMPATIBLE + + valid_areas = _valid_areas + use_instances = _use_instances + check_as_typecache = _check_as_typecache + reset_turf = get_turf(parent) + move_tracker = new(parent, CALLBACK(src, PROC_REF(check_bounds))) + check_bounds() + +/datum/component/multi_area_bound/Destroy(force) + QDEL_NULL(move_tracker) + valid_areas = null + return ..() + +/datum/component/multi_area_bound/proc/check_bounds(atom/movable/source, atom/movable/mover, atom/oldloc, direction) + SIGNAL_HANDLER //not technically a sig handler but it pretty much is + var/area/current = get_area(source) + if(!current) + return + + if(!(check_as_typecache ? valid_areas[use_instances ? current : current.type] : ((use_instances ? current : current.type) in valid_areas))) //fun + if(moving) + stack_trace("Moved during a reset move, giving up to prevent infinite recursion. \ + [reset_turf ? "Turf: [reset_turf.type] at [reset_turf.x], [reset_turf.y], [reset_turf.z]" : "No reset_turf"]") + return + if(!reset_turf) //if unset then just move them to their last turf + moving = TRUE + source.forceMove(oldloc) + moving = FALSE + stack_trace("multi_area_bound without set reset_turf") //qdel(src) + return + + moving = TRUE + source.forceMove(reset_turf) + moving = FALSE + reset_turf = get_turf(source) diff --git a/tff_modular/modules/antagonists/clock_cult/outfit.dm b/tff_modular/modules/antagonists/clock_cult/outfit.dm new file mode 100644 index 00000000000..9c5f7c49776 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/outfit.dm @@ -0,0 +1,60 @@ +/datum/outfit/clock + name = "Default Clock Cultist" + + uniform = /obj/item/clothing/under/occult //meh. + suit = /obj/item/clothing/suit/clockwork/cloak + shoes = /obj/item/clothing/shoes/clockwork + gloves = /obj/item/clothing/gloves/clockwork + back = /obj/item/storage/backpack/satchel + backpack_contents = list( + /obj/item/storage/box/survival = 1, + /obj/item/stack/sheet/bronze = 10, + /obj/item/storage/medkit/emergency = 1, + /obj/item/clockwork/clockwork_slab = 1, + ) + +/datum/outfit/clock/pre_equip(mob/living/carbon/human/equip_human, visualsOnly) + equip_human.add_faction(FACTION_CLOCK) + + +/datum/outfit/clock/armor + name = "Armored Clock Cultist" + + suit = /obj/item/clothing/suit/clockwork + head = /obj/item/clothing/head/helmet/clockwork + glasses = /obj/item/clothing/glasses/clockwork/judicial_visor + l_hand = /obj/item/clockwork/weapon/brass_battlehammer + +/datum/outfit/clock/archer + name = "Archer Clock Cultist" + + suit = /obj/item/clothing/suit/clockwork/speed + head = /obj/item/clothing/head/helmet/clockwork + glasses = /obj/item/clothing/glasses/clockwork/judicial_visor + l_hand = /obj/item/gun/ballistic/bow/clockwork + +/datum/outfit/clock/support + name = "Support Clock Cultist" + + suit = /obj/item/clothing/suit/clockwork + head = /obj/item/clothing/head/helmet/clockwork + glasses = /obj/item/clothing/glasses/clockwork/judicial_visor + belt = /obj/item/storage/belt/utility/clock + l_hand = /obj/item/clockwork/weapon/brass_sword + r_hand = /obj/item/clockwork/replica_fabricator + backpack_contents = list( + /obj/item/storage/box/survival = 1, + /obj/item/stack/sheet/bronze = 50, + /obj/item/storage/medkit/advanced = 1, + /obj/item/storage/medkit/regular = 1, + /obj/item/clockwork/clockwork_slab = 1, + ) + + +/datum/outfit/clockwork_armaments + name = "Clockwork Cultist Base" + + suit = /obj/item/clothing/suit/clockwork + shoes = /obj/item/clothing/shoes/clockwork + gloves = /obj/item/clothing/gloves/clockwork + head = /obj/item/clothing/head/helmet/clockwork diff --git a/tff_modular/modules/antagonists/clock_cult/pickup_element.dm b/tff_modular/modules/antagonists/clock_cult/pickup_element.dm new file mode 100644 index 00000000000..8e732c1f115 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/pickup_element.dm @@ -0,0 +1,56 @@ +#define REGULAR_PICKUP_MOD 1 +#define CULTIST_PICKUP_MOD 2 +#define PICKUP_SHOCK_DAMAGE 20 + +/datum/element/clockwork_pickup + element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH_ON_HOST_DESTROY + argument_hash_start_idx = 2 + + /// What slots will attempt to shock the equpper + var/list/equip_slots = list() + +/datum/element/clockwork_pickup/Attach(datum/target, list/slots_to_count) + . = ..() + + if(!isitem(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ITEM_EQUIPPED, PROC_REF(attempt_shock)) + + if(slots_to_count && !length(equip_slots)) + equip_slots = slots_to_count + + if(!locate(target.type) in GLOB.types_to_drop_on_clock_deonversion) + GLOB.types_to_drop_on_clock_deonversion |= target.type + +/datum/element/clockwork_pickup/Detach(datum/target) + . = ..() + UnregisterSignal(target, COMSIG_ITEM_EQUIPPED) + +/** + * + * This proc is called when the user picks or equips up an item with the associated element. This will shock them if they are not a clock cultist, and moreso if they are a blood cultist. + * + * Arguments: + * * source - The item being picked up + * * equipper - The mob that picked up the item + * * slot - The slot the item was equipped in, unused + */ +/datum/element/clockwork_pickup/proc/attempt_shock(obj/item/source, mob/living/equipper, slot) + SIGNAL_HANDLER + + if(!isliving(equipper) || IS_CLOCK(equipper) || (length(equip_slots) && !(slot in equip_slots))) + return + + var/power_multiplier = REGULAR_PICKUP_MOD + if(IS_CULTIST(equipper)) + power_multiplier = CULTIST_PICKUP_MOD + + to_chat(equipper, span_warning("As you [slot == ITEM_SLOT_HANDS ? "touch" : "equip"] [source], you feel a jolt course through you!")) + + equipper.dropItemToGround(source, TRUE) + equipper.electrocute_act(PICKUP_SHOCK_DAMAGE * power_multiplier, src, 1, SHOCK_NOGLOVES|SHOCK_SUPPRESS_MESSAGE) + +#undef REGULAR_PICKUP_MOD +#undef CULTIST_PICKUP_MOD +#undef PICKUP_SHOCK_DAMAGE diff --git a/tff_modular/modules/antagonists/clock_cult/portal.dm b/tff_modular/modules/antagonists/clock_cult/portal.dm new file mode 100644 index 00000000000..83e10ab6ca6 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/portal.dm @@ -0,0 +1,49 @@ +/obj/effect/portal/clockcult + name = "dimensional anomaly" + desc = "A dimensional anomaly. It feels warm to the touch, and has a gentle puff of steam emanating from it." + icon = 'icons/obj/anomaly.dmi' + icon_state = "bhole3" + mech_sized = TRUE + density = TRUE + force_teleport = TRUE + ///list of possible targets + var/static/list/possible_targets + +/obj/effect/portal/clockcult/Initialize(mapload, _creator, _lifespan, obj/effect/portal/_linked, automatic_link, turf/hard_target_override) + . = ..() + if(!possible_targets) + possible_targets = list() + for(var/obj/effect/landmark/late_cog_portals/portal_mark in GLOB.landmarks_list) + possible_targets += portal_mark + + var/static/times_warned_admins //we spawn a massive amount of these normally so we dont want to warn admins for every single one if something breaks + if(length(possible_targets)) + hard_target = get_turf(pick(possible_targets)) + return + else if(!times_warned_admins) + times_warned_admins = 0 + message_admins("No possible_targets for clock cult portals.") + times_warned_admins++ + +/obj/effect/portal/clockcult/Bumped(atom/movable/bumper) + . = ..() + teleport(bumper) + +/obj/effect/portal/clockcult/teleport(atom/movable/M, force = FALSE) + if(isliving(M)) + to_chat(M, span_notice("You begin climbing into the rift.")) + if(!do_after(M, 5 SECONDS, src)) + return + + var/mob/living/teleported_living = M + if(teleported_living.pulling) + teleport(teleported_living.pulling, TRUE) + + if(teleported_living.client) + var/client_color = teleported_living.client.color + teleported_living.client.color = "#BE8700" + animate(teleported_living.client, color = client_color, time = 2.5 SECONDS) + var/prev_alpha = M.alpha + M.alpha = 0 + animate(M, alpha = prev_alpha, time = 1 SECONDS) + . = ..() diff --git a/tff_modular/modules/antagonists/clock_cult/procs.dm b/tff_modular/modules/antagonists/clock_cult/procs.dm new file mode 100644 index 00000000000..82b88bb17e0 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/procs.dm @@ -0,0 +1,96 @@ +#define EDGE_STATE_SOUTH_BORDER 1 +#define EDGE_STATE_MAP_BOTTOM 2 +#define EDGE_STATE_MAP_TOP 3 +#define SET_DIR_LIST(dir_list, edge_state) \ + switch(edge_state) { \ + if(EDGE_STATE_MAP_BOTTOM) { \ + ##dir_list = GLOB.cardinals - SOUTH;\ + } \ + if(EDGE_STATE_MAP_TOP) { \ + ##dir_list = GLOB.cardinals - NORTH;\ + } \ + else { \ + ##dir_list = GLOB.cardinals;\ + } \ + } + +#define ADD_NEW_AREAS(dir_list, step_turf, step_of, checked, added_to) \ + for(var/dir in dir_list) { \ + ##step_turf = get_step(step_of, dir);\ + if(step_turf && step_turf.loc != checked) { \ + ##added_to |= step_turf.loc;\ + } \ + } + +///Returns all turfs on the border of an area +/proc/get_area_edge_turfs(area/checked, return_adjacent_areas = FALSE) as /list + RETURN_TYPE(/list) + var/list/returned_list = list() + for(var/i in 1 to length(checked.turfs_by_zlevel)) + var/list/z_turfs = checked.turfs_by_zlevel[i] + var/list/new_list = list() + var/last_y = 0 + var/edge_state = FALSE + var/turf/last_checked + returned_list.len++ + returned_list[i] = new_list + for(var/turf/z_turf in z_turfs) + if(z_turf.y != last_y) + last_y = z_turf.y + if(last_checked) + if(return_adjacent_areas) + var/list/dir_list + SET_DIR_LIST(dir_list, edge_state) + var/turf/step_turf + ADD_NEW_AREAS(dir_list, step_turf, last_checked, checked, new_list) + else + new_list += last_checked + + if(z_turf.y == 1) + edge_state = EDGE_STATE_MAP_BOTTOM + else if(last_y == 0) + edge_state = EDGE_STATE_SOUTH_BORDER + else if(z_turf.y == world.maxy) + edge_state = EDGE_STATE_MAP_TOP + else + edge_state = FALSE + + if(return_adjacent_areas) + var/list/dir_list + SET_DIR_LIST(dir_list, edge_state) + var/turf/step_turf + ADD_NEW_AREAS(dir_list, step_turf, z_turf, checked, new_list) + else + new_list += z_turf + last_checked = null + continue + + if(edge_state) + if(return_adjacent_areas) + var/list/dir_list + SET_DIR_LIST(dir_list, edge_state) + var/turf/step_turf + ADD_NEW_AREAS(dir_list, step_turf, z_turf, checked, new_list) + else + new_list += z_turf + continue + + var/turf/north_turf = get_step(z_turf, NORTH) + if(north_turf.loc != checked) //if we dont have a step something else already broke as map borders have already been handled + if(return_adjacent_areas) + var/list/dir_list = GLOB.cardinals - NORTH + if(edge_state == EDGE_STATE_MAP_BOTTOM) + dir_list -= SOUTH + var/turf/step_turf + ADD_NEW_AREAS(dir_list, step_turf, z_turf, checked, new_list) + new_list |= (return_adjacent_areas ? north_turf.loc : z_turf) + last_checked = null + else + last_checked = z_turf + return returned_list + +#undef EDGE_STATE_SOUTH_BORDER +#undef EDGE_STATE_MAP_BOTTOM +#undef EDGE_STATE_MAP_TOP +#undef SET_DIR_LIST +#undef ADD_NEW_AREAS diff --git a/tff_modular/modules/antagonists/clock_cult/ratvar.dm b/tff_modular/modules/antagonists/clock_cult/ratvar.dm new file mode 100644 index 00000000000..1844360a2c6 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/ratvar.dm @@ -0,0 +1,252 @@ +//I would like to do what beestation does and make both this and narsie be children of /eldritch but that would make this very non-modular +GLOBAL_DATUM(cult_ratvar, /obj/ratvar) + +#define RATVAR_CONSUME_RANGE 20 +#define RATVAR_GRAV_PULL 10 +#define RATVAR_SINGULARITY_SIZE 11 + +/obj/ratvar + name = "ratvar, the Clockwork Justicar" + desc = "Oh, that's ratvar!" + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/512x512.dmi' + icon_state = "ratvar" + anchored = TRUE + density = FALSE + appearance_flags = LONG_GLIDE + plane = MASSIVE_OBJ_PLANE + light_color = COLOR_ORANGE + light_power = 1 //slightly brighter then narsie + light_range = 20 + move_resist = INFINITY + obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION + pixel_x = -236 + pixel_y = -256 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF + flags_1 = SUPERMATTER_IGNORES_1 + + /// The singularity component to move around Ratvar. + /// A weak ref in case an admin removes the component to preserve the functionality. + var/datum/weakref/singularity + + ///next world tick we can attack our narsie target if we have one + var/next_attack_tick = 0 + +/obj/ratvar/Initialize(mapload) + SSpoints_of_interest.make_point_of_interest(src) + + singularity = WEAKREF(AddComponent( + /datum/component/singularity, \ + bsa_targetable = FALSE, \ + consume_callback = CALLBACK(src, PROC_REF(consume)), \ + consume_range = RATVAR_CONSUME_RANGE, \ + disregard_failed_movements = TRUE, \ + grav_pull = RATVAR_GRAV_PULL, \ + roaming = TRUE, \ + singularity_size = RATVAR_SINGULARITY_SIZE, \ + )) + + log_game("!!! RATVAR HAS RISEN. !!!") + GLOB.cult_ratvar = src + . = ..() + desc = "[text2ratvar("That's Ratvar, the Clockwork Justicar. The great one has risen.")]" + sound_to_playing_players('tff_modular/modules/antagonists/clock_cult/sound/ratvar_reveal.ogg', 100) + send_to_playing_players(span_reallybig(span_clockyellow("The bluespace veil gives way to Ratvar, his light shall shine upon all mortals!"))) + UnregisterSignal(src, COMSIG_ATOM_BSA_BEAM) + SSshuttle.registerHostileEnvironment(src) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(clockcult_ending_start)), 5 SECONDS) + if(GLOB.narsie_breaching_rune) + if(istype(GLOB.narsie_breaching_rune, /obj/effect/rune/narsie)) + new /obj/narsie(get_turf(GLOB.narsie_breaching_rune)) + else + new /obj/narsie(get_safe_random_station_turf()) + + var/area/area = get_area(src) + if(area) + var/mutable_appearance/alert_overlay = mutable_appearance('tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_effects.dmi', "ratvar_alert") + notify_ghosts( + "Ratvar has risen in [area]. Reach out to the Justicar to be given a new shell for your soul.", + source = src, + alert_overlay = alert_overlay, + ) + gods_battle() + START_PROCESSING(SSobj, src) + +/obj/ratvar/Destroy(force) + if(GLOB.cult_ratvar == src) + GLOB.cult_ratvar = null + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/ratvar/process(seconds_per_tick) + var/datum/component/singularity/singularity_component = singularity?.resolve() + if(GLOB.cult_narsie) + singularity_component?.target = GLOB.cult_narsie + if(get_dist(src, GLOB.cult_narsie) < 5) + if(next_attack_tick < world.time) + next_attack_tick = world.time + rand(50, 100) + send_to_playing_players(span_danger("[pick("Reality shudders around you.","You hear the tearing of flesh.","The sound of bones cracking fills the air.")]")) + sound_to_playing_players('sound/effects/magic/clockwork/ratvar_attack.ogg',100) + explosion(GLOB.cult_narsie, 0, 2, 6) + SpinAnimation(4, 0) + + for(var/mob/living/living_player in GLOB.player_list) + shake_camera(living_player, 2.5 SECONDS, 5) + living_player.Knockdown(1 SECONDS) + + if(prob(max(length(GLOB.main_clock_cult?.members)/2, 15))) + sound_to_playing_players('sound/effects/magic/demon_dies.ogg', 100) + qdel(GLOB.cult_narsie) + send_to_playing_players(span_clockyellow("You were a fool for underestimating me...")) + for(var/datum/mind/cultist_mind in get_antag_minds(/datum/antagonist/cult)) + to_chat(cultist_mind, span_userdanger("You feel a stabbing pain in your chest... This can't be happening!")) + cultist_mind.current?.dust() + return + +/obj/ratvar/Bump(atom/the_atom) + var/turf/the_turf = get_turf(the_atom) + if(the_turf == loc) + the_turf = get_step(the_atom, the_atom.dir) //please don't slam into a window like a bird, Ratvar + forceMove(the_turf) + +/obj/ratvar/attack_ghost(mob/user) + . = ..() + if(is_banned_from(user.ckey, list(ROLE_MIDROUND_CLOCK_CULTIST))) + return + var/mob/living/basic/drone/created_drone = new /mob/living/basic/drone/cogscarab(get_turf(src)) + created_drone.flags_1 |= (flags_1 & ADMIN_SPAWNED_1) + if(user.mind) + user.mind.transfer_to(created_drone, TRUE) + else if(isobserver(user)) + created_drone.PossessByPlayer(user.key) + created_drone.mind_initialize() + else + qdel(created_drone) + return + created_drone.mind.add_antag_datum(/datum/antagonist/clock_cultist) + +/obj/ratvar/proc/consume(atom/consumed) + consumed.ratvar_act() + +#undef RATVAR_CONSUME_RANGE +#undef RATVAR_GRAV_PULL +#undef RATVAR_SINGULARITY_SIZE + +/obj/narsie + ///next world tick we can attack our ratvar target if we have one + var/next_attack_tick = 0 + +/proc/clockcult_ending_start() + SSsecurity_level.set_level(SEC_LEVEL_LAMBDA) + priority_announce("Huge gravitational-energy spike detected emminating from a neutron star [text2ratvar("THEY LIE")] near your sector. Event has been determined to be \ + survivable by 0% of life. ESTIMATED TIME UNTIL ENERGY PULSE REACHES [GLOB.station_name]: 56 SECONDS. Godspeed crew, glory to Nanotrasen. \ + -Admiral W[text2ratvar("orthless")].", \ + "Central Command Anomolous Materials Division", 'sound/announcer/alarm/airraid.ogg') + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(clockcult_pre_ending)), 50 SECONDS) + +/proc/clockcult_pre_ending() + priority_announce("Station [GLOB.station_name] is in the wa#e %o[text2ratvar("YOU WILL SEE THE LIGHT")] action imminent. Glory[text2ratvar(" TO ENGINE")].", \ + "Central Command Anomolous Materials Division", 'sound/announcer/alarm/nuke_alarm.ogg') + for(var/mob/player_mob in GLOB.player_list) + if(player_mob.client) + player_mob.client.color = COLOR_WHITE + animate(player_mob.client, color = LIGHT_COLOR_CLOCKWORK, time = 135) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(clockcult_final_ending)), 135) + +/proc/clockcult_final_ending() + SSshuttle.lockdown = TRUE + for(var/mob/player_mob in GLOB.player_list) + player_mob.client.color = LIGHT_COLOR_CLOCKWORK + animate(player_mob.client, color = COLOR_WHITE, time = 5) + SEND_SOUND(player_mob, sound(null)) + SEND_SOUND(player_mob, sound('sound/effects/magic/fireball.ogg')) + + for(var/mob/living/lit_mob in GLOB.mob_living_list) + if(!IS_CLOCK(lit_mob)) + lit_mob.fire_stacks = 100 + lit_mob.ignite_mob() + lit_mob.emote("scream") + sleep(1.5 SECONDS) + SSticker.force_ending = TRUE + +/datum/client_colour/ratvar_vision + color = LIGHT_COLOR_CLOCKWORK + +//ratvar_act stuff + +/atom/proc/ratvar_act() + SEND_SIGNAL(src, COMSIG_ATOM_RATVAR_ACT) + +/obj/structure/lattice/ratvar_act() + var/our_loc = loc + qdel(src) + new /obj/structure/lattice/clockwork(our_loc) + +/obj/item/stack/sheet/iron/ratvar_act() + new /obj/item/stack/sheet/bronze(loc, amount) + qdel(src) + +/obj/item/stack/sheet/runed_metal/ratvar_act() + new /obj/item/stack/sheet/bronze(loc, amount) + qdel(src) + +/turf/ratvar_act(force, ignore_mobs) + . = (prob(60) || force) + for(var/atom/checked_atom in src) + if(ignore_mobs && ismob(checked_atom)) + continue + if(ismob(checked_atom) || .) + checked_atom.ratvar_act() + +/turf/open/floor/ratvar_act(force, ignore_mobs) + . = ..() + if(.) + ChangeTurf(/turf/open/indestructible/reebe_flooring, flags = CHANGETURF_INHERIT_AIR) + +/turf/closed/wall/ratvar_act(force, ignore_mobs) + . = ..() + if(.) + ChangeTurf(/turf/closed/wall/clockwork) + +/obj/structure/chair/ratvar_act() + new /obj/structure/chair/bronze(get_turf(src)) + qdel(src) + +/obj/structure/chair/bronze/ratvar_act() + return + +/obj/structure/window/ratvar_act() + if(!fulltile) + new/obj/structure/window/reinforced/clockwork(get_turf(src), dir) + else + new/obj/structure/window/reinforced/clockwork/fulltile(get_turf(src)) + qdel(src) + +/obj/structure/table/ratvar_act() + var/atom/location = loc + qdel(src) + new /obj/structure/table/bronze(location) + +/obj/structure/table/bronze/ratvar_act() + return + +/obj/machinery/door/airlock/ratvar_act() //Airlocks become clock airlocks that only allow servants + var/obj/machinery/door/airlock/bronze/clock/made_door + if(glass) + made_door = new/obj/machinery/door/airlock/bronze/clock/glass(get_turf(src)) + else + made_door = new/obj/machinery/door/airlock/bronze/clock(get_turf(src)) + made_door.name = name + qdel(src) + +/obj/machinery/computer + ///used for tracking ratvar_act() and narsie_act() + var/clockwork = FALSE + +/obj/machinery/computer/ratvar_act() + if(!clockwork) + clockwork = TRUE + icon_screen = "ratvar[rand(1, 3)]" + icon_keyboard = "ratvar_key[rand(1, 2)]" + icon_state = "ratvarcomputer1" + update_appearance() + connectable = FALSE diff --git a/tff_modular/modules/antagonists/clock_cult/reebe_modules.dm b/tff_modular/modules/antagonists/clock_cult/reebe_modules.dm new file mode 100644 index 00000000000..53e1c94ea3a --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/reebe_modules.dm @@ -0,0 +1,107 @@ +GLOBAL_LIST_EMPTY(abscond_markers) + +/// spawn the reebe z level and map template, lazy templates dont work because we need to give this ztraits +/proc/spawn_reebe(forced = FALSE) + if(!SSthe_ark.initialized) + SSthe_ark.Initialize() + + var/static/reebe_loaded + if(forced) + message_admins("Admin forcing reebe spawn, if it has already spawned this will break things unless you know what your doing.") + else if(reebe_loaded) + return FALSE + + reebe_loaded = TRUE + var/datum/space_level/reebe_z = SSmapping.add_new_zlevel("Reebe", ZTRAITS_REEBE) + if(!reebe_z) + reebe_loaded = FALSE + CRASH("Failed to create the Reebe Z level.") + + SSmapping.initialize_reserved_level(reebe_z.z_value) + if(!SSmapping.reservation_ready["[reebe_z.z_value]"]) //if this is not true then the block reservation will sleep forever + reebe_loaded = FALSE + CRASH("Reebe Z level not in SSmapping.reservation_ready.") + + var/datum/turf_reservation/reservation = SSmapping.request_turf_block_reservation(101, 101, z_reservation = reebe_z.z_value) + if(!reservation) + reebe_loaded = FALSE + CRASH("Failed to reserve a block for Reebe.") + + var/datum/map_template/reebe_template = new(path = REEBE_MAP_PATH, cache = TRUE) + if(!reebe_template.cached_map) //might not be needed, im just copying lazy template code and I cant figure out what cached maps are for in this case + reebe_loaded = FALSE + CRASH("Failed to cache template for loading Reebe.") + + if(!reebe_template.load(reservation.bottom_left_turfs[1])) + reebe_loaded = FALSE + CRASH("Failed to load the Reebe template.") + + for(var/area/reebe_area as anything in typesof(/area/ruin/powered/reebe)) + reebe_area = GLOB.areas_by_type[reebe_area] + if(reebe_area) + SSthe_ark.reebe_areas[reebe_area] = 1 + return TRUE + +/obj/item/storage/box/recharger_parts + name = "Recharger Parts" + +/obj/item/storage/box/recharger_parts/PopulateContents() + . = ..() //there is actually a helper for this but I cant remember the name + var/list/spawned_list = list(/obj/item/circuitboard/machine/recharger = 5, /obj/item/stack/cable_coil = 1, /obj/item/stack/sheet/iron/fifty = 1) + for(var/type in spawned_list) + for(var/i in 1 to spawned_list[type]) + new type(src) + +/obj/effect/mob_spawn/corpse/human/blood_cultist + name = "Blood Cultist" + outfit = /datum/outfit/blood_cultist + +/datum/outfit/blood_cultist + name = "Blood Cultist" + + uniform = /obj/item/clothing/under/color/black + suit = /obj/item/clothing/suit/hooded/cultrobes/alt + shoes = /obj/item/clothing/shoes/cult/alt + +/datum/outfit/blood_cultist/post_equip(mob/living/carbon/human/equipped, visualsOnly) + equipped.eye_color_left = BLOODCULT_EYE + equipped.eye_color_right = BLOODCULT_EYE + equipped.update_body() + +/obj/effect/mob_spawn/corpse/human/clock_cultist + name = "Clock Cultist" + outfit = /datum/outfit/clock + +/obj/effect/landmark/late_cog_portals + name = "reebe crew portal spawn" + +//for the portal from the outpost to reebe +/obj/effect/landmark/abscond_marker + name = "abscond marker" + icon = 'tff_modular/modules/antagonists/clock_cult/icons/effects/landmarks_static.dmi' + icon_state = "clockwork_orange" + +/obj/effect/landmark/abscond_marker/Initialize(mapload) + . = ..() + GLOB.abscond_markers += src + +/obj/effect/landmark/abscond_marker/Destroy() + . = ..() + GLOB.abscond_markers -= src + +/obj/effect/servant_blocker + name = "servant Blocker" + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_effects.dmi' + icon_state = "servant_blocker" + anchored = TRUE + +/obj/effect/servant_blocker/CanPass(atom/movable/mover, border_dir) + for(var/mob/held_mob in mover.get_all_contents()) + if(IS_CLOCK(held_mob)) + return FALSE + return ..() + +/obj/effect/spawner/structure/window/clockwork + name = "brass window spawner" + icon_state = "bronzewindow_spawner" + spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/clockwork/fulltile) diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/_scripture.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/_scripture.dm new file mode 100644 index 00000000000..3fedfd164be --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/_scripture.dm @@ -0,0 +1,396 @@ +GLOBAL_LIST_EMPTY(clock_scriptures) +GLOBAL_LIST_EMPTY(clock_scriptures_by_type) + +// Scriptures are the "spells" of clock cultists +// They're usable through their clockwork slab, and cannot be invoked otherwise +/datum/scripture + /// Name of the scripture + var/name = "" + /// Shown decription of the scripture in the UI + var/desc = "" + /// Tooltip shown on-hover. Keep to a few words + var/tip = "" + /// How much power this scripture costs to use + var/power_cost = 0 + /// How much vitality this scripture costs to use + var/vitality_cost = 0 + /// How many cogs are required to be used to use this + var/cogs_required = 0 + /// Time required to invoke this while standing still + var/invocation_time = 0 + /// Text said during invocation, automatically translates to Ratvarian + var/list/invocation_text = list() + /// Icon state of the action button + var/button_icon_state = "telerune" + /// How many people need to invoke this in sight of each other to use + var/invokers_required = 1 + /// What category of scripture is this + var/category = SPELLTYPE_ABSTRACT + /// Only set to false if you call end_invoke somewhere in your scripture + var/end_on_invocation = TRUE + /// How much of a reduction in invocation time should the TRAIT_FASTER_SLAB_INVOKE give, multiplicative + var/fast_invoke_mult = 0.5 + /// Are we only visible/usable if a unique condition is met + var/unique_locked = FALSE //if needed it might be worth turning these into descriptor strings + /// Ref to the mob invoking this + var/mob/living/invoker + /// Ref to the slab invoking this + var/obj/item/clockwork/clockwork_slab/invoking_slab + /// Timer object for the distance between invoking chants + var/invocation_chant_timer = null + /// Sound to play on finish + var/sound/recital_sound = null + +/datum/scripture/New() + . = ..() + if(invokers_required > 1) + desc += " Requires [invokers_required] invokers, should you be in a group." + +/datum/scripture/Destroy(force) + invoker = null + invoking_slab = null + return ..() + + +/// Invoke this scripture, checking if there's valid power and vitality +/datum/scripture/proc/invoke() + if(SSthe_ark.clock_power < power_cost || GLOB.clock_vitality < vitality_cost) + invoke_fail() + + if(invocation_chant_timer) + deltimer(invocation_chant_timer) + invocation_chant_timer = null + + end_invoke() + + return + + SSthe_ark.clock_power -= power_cost + GLOB.clock_vitality -= vitality_cost + invoke_success() + +/// On success of invoking the scripture +/datum/scripture/proc/invoke_success() + return TRUE + +/// On failure of invoking the scripture +/datum/scripture/proc/invoke_fail() + return TRUE + +/// The overall reciting proc for saying every single line for a scripture +/datum/scripture/proc/recital(input_invoke_time) + if(!length(invocation_text)) + return + + var/steps = length(invocation_text) + var/true_invocation_time = input_invoke_time || get_true_invocation_time() + var/time_between_say = true_invocation_time / (steps + 1) + + if(invocation_chant_timer) + deltimer(invocation_chant_timer) + invocation_chant_timer = null + + recite(1, time_between_say, steps) + +/datum/scripture/proc/get_true_invocation_time() + . = invocation_time * (HAS_TRAIT(invoker, TRAIT_FASTER_SLAB_INVOKE) ? fast_invoke_mult : 1) + return . + +/// For reciting an individual line of a scripture +/datum/scripture/proc/recite(text_point, wait_time, stop_at = 0) + if(QDELETED(src)) + return + + invocation_chant_timer = null + + if(!invoking_slab || !invoking_slab.invoking_scripture) + return + + var/invokers_left = invokers_required + if(invokers_left > 1) + for(var/mob/living/potential_invoker in viewers(invoker)) + if(!invokers_left) + break + + if(potential_invoker.stat || !potential_invoker.mind) + continue + + if(potential_invoker?.mind.has_antag_datum(/datum/antagonist/clock_cultist/solo)) // Solo cultists can use all scriptures alone, while group clock cult doesn't get so lucky + invokers_left = 0 + clockwork_say(potential_invoker, text2ratvar(invocation_text[text_point]), TRUE) + break + if(IS_CLOCK(potential_invoker)) + clockwork_say(potential_invoker, text2ratvar(invocation_text[text_point]), TRUE) + invokers_left-- + else + clockwork_say(invoker, text2ratvar(invocation_text[text_point]), TRUE) + + if(recital_sound) + SEND_SOUND(invoker, recital_sound) + + if(text_point < stop_at) + invocation_chant_timer = addtimer(CALLBACK(src, PROC_REF(recite), text_point+1, wait_time, stop_at), wait_time, TIMER_STOPPABLE) + + +/// Check for any special requriements such as not having enough invokers, or not holding the slab +/datum/scripture/proc/check_special_requirements(mob/user) + if(!invoker || !invoking_slab) + message_admins("No invoker for [name]") + return FALSE + + if(invoker.get_active_held_item() != invoking_slab && !iscyborg(invoker)) + to_chat(invoker, span_brass("You fail to invoke [name].")) + return FALSE + + if(HAS_TRAIT(invoker, TRAIT_NO_SLAB_INVOKE)) + to_chat(invoker, span_warning("Something blocks you from invoking scriptures.")) + return FALSE + + var/invokers = 0 + if(locate(/obj/item/toy/plush/ratplush) in range(1, invoker)) //ratvar plushies count as an invoker + invokers++ + + for(var/mob/living/potential_invoker in viewers(invoker)) + if(potential_invoker.stat || !potential_invoker.mind) + continue + + if(IS_CLOCK(potential_invoker)) + invokers++ + + if(potential_invoker?.mind.has_antag_datum(/datum/antagonist/clock_cultist/solo)) // They count for infinite so they can do all scriptures solo + invokers = INFINITY + break + + if(invokers < invokers_required) + to_chat(invoker, span_brass("You need [invokers_required] servants to channel [name]!")) + return FALSE + + if(invoker.reagents && invoker.has_reagent(/datum/reagent/water/holywater)) + to_chat(invoker, span_brass("The holy water inside you is blocking your ability to invoke!")) + return FALSE + + return TRUE + + +/// Start invoking a scripture, calling end_invoke() if it doesn't finish +/datum/scripture/proc/begin_invoke(mob/living/invoking_mob, obj/item/clockwork/clockwork_slab/slab, bypass_unlock_checks = FALSE) + if(invoking_mob.get_active_held_item() != slab && !iscyborg(invoking_mob)) + to_chat(invoking_mob, span_brass("You need to have the [slab.name] in your active hand to recite scriptures.")) + return + + slab.invoking_scripture = src + invoker = invoking_mob + invoking_slab = slab + + if((!slab.owned_scriptures[type] && !bypass_unlock_checks) || unique_locked) //unique_locked scriptures should not even be visible so this should never happen + log_runtime("CLOCKCULT: Attempting to invoke a scripture that has not been unlocked. Either there is a bug, or [ADMIN_LOOKUP(invoker)] is using some wacky exploits.") + end_invoke() + return + + if(!check_special_requirements(invoking_mob)) + end_invoke() + return + + var/true_invocation_time = get_true_invocation_time() + recital(true_invocation_time) + if(do_after(invoking_mob, true_invocation_time, invoking_mob, extra_checks = CALLBACK(src, PROC_REF(check_special_requirements), invoking_mob), hidden = TRUE)) + invoke() + + to_chat(invoking_mob, span_brass("You invoke [name].")) + + if(end_on_invocation) + end_invoke() + else + invoke_fail() + + if(invocation_chant_timer) + deltimer(invocation_chant_timer) + invocation_chant_timer = null + + end_invoke() + +/// End the invoking, nulling things out +/datum/scripture/proc/end_invoke() + invoking_slab.invoking_scripture = null + +// Call these on the instances in the global lists +/// Set a scripture's unique_locked to FALSE and reload the UIs of slabs if set +/datum/scripture/proc/unique_unlock(reload_uis = FALSE) + unique_locked = FALSE + if(reload_uis) + for(var/obj/item/clockwork/clockwork_slab/slab in GLOB.clockwork_slabs) + SStgui.update_uis(slab) + + +/// Set a scripture's unique_locked to TRUE, remove quickbinds of it, and reload the UIs of slabs if set +/datum/scripture/proc/unique_lock(reload_uis = FALSE) + unique_locked = TRUE + for(var/obj/item/clockwork/clockwork_slab/slab in GLOB.clockwork_slabs) + for(var/i in 1 to slab.quick_bound_scriptures.len) + if(slab.quick_bound_scriptures[i]) + var/datum/action/innate/clockcult/quick_bind/quickbound = slab.quick_bound_scriptures[i] + if(istype(src, quickbound.scripture.type)) + slab.quick_bound_scriptures[i] = null + qdel(quickbound) + break + + if(reload_uis) + SStgui.update_uis(slab) + + +// Base create structure scripture +/datum/scripture/create_structure + ///Typepath for the structure to create + var/summoned_structure + ///How long is our creation time multiplied by if the assault has begun + var/assault_invoke_time_mult = 2 + + +/datum/scripture/create_structure/check_special_requirements(mob/user) + . = ..() + if(!.) + return FALSE + + for(var/obj/structure/destructible/clockwork/clockwork_struct in get_turf(invoker)) + if(istype(clockwork_struct, /obj/structure/destructible/clockwork/trap)) + continue + invoker.balloon_alert(invoker, "structure already on tile!") + return FALSE + + return TRUE + +/datum/scripture/create_structure/get_true_invocation_time() + . = ..() + if(GLOB.clock_ark?.current_state >= ARK_STATE_ACTIVE) + . *= (iscogscarab(invoker) ? assault_invoke_time_mult : assault_invoke_time_mult * 2) + +/datum/scripture/create_structure/invoke_success() + return new summoned_structure(get_turf(invoker)) + +//For scriptures that charge the slab, and the slab will affect something +//(stunning etc.) +/datum/scripture/slab + name = "Charge Slab" + end_on_invocation = FALSE + /// Time to perform this + var/use_time = 1 SECONDS + /// Overlay for the item/inhand state while this is invoked + var/slab_overlay = "volt" + /// Progressbar datum for count_down() + var/datum/progressbar/progress + /// How many times this scripture can be used overall, is unchanging + var/uses = 1 + /// Text displayed after use + var/after_use_text = "" + /// Internal spell for pointed/aimed spells + var/datum/action/cooldown/spell/pointed/slab/pointed_spell + /// How many times this can be used this particular invocation, can go down + var/uses_left = 0 + /// How much time left to use this + var/time_left = 0 + /// ID of the loop timer + var/loop_timer_id + +/datum/scripture/slab/New() + . = ..() + pointed_spell = new(src) + pointed_spell.name = src.name + pointed_spell.deactive_msg = "" + pointed_spell.parent_scripture = src + +/datum/scripture/slab/Destroy() + if(!QDELETED(progress)) + progress.end_progress() + + if(!QDELETED(pointed_spell)) + QDEL_NULL(pointed_spell) + + return ..() + +/datum/scripture/slab/invoke() + progress = new(invoker, use_time, invoking_slab) + uses_left = uses + time_left = use_time + invoking_slab.charge_overlay = slab_overlay + invoking_slab.update_overlays() + invoking_slab.active_scripture = src + pointed_spell.set_click_ability(invoker) + count_down() + SSthe_ark.clock_power -= power_cost + GLOB.clock_vitality -= vitality_cost + invoke_success() + +/// Count down the progress bar +/datum/scripture/slab/proc/count_down() + if(QDELETED(src)) + return + + progress.update(time_left) + time_left-- + loop_timer_id = null + + if(time_left > 0) + loop_timer_id = addtimer(CALLBACK(src, PROC_REF(count_down)), 0.1 SECONDS, TIMER_STOPPABLE) + else + end_invocation() + +/// What occurs when an atom is clicked on. +/datum/scripture/slab/proc/click_on(atom/clicked_atom) + SHOULD_CALL_PARENT(TRUE) + + if(!invoker.can_interact_with(clicked_atom)) + return + + if(!apply_effects(clicked_atom)) + return + + uses_left-- + if(uses_left) + return + + if(after_use_text) + clockwork_say(invoker, text2ratvar(after_use_text), TRUE) + + end_invocation(TRUE) + +/// What occurs when the invocation ends +/datum/scripture/slab/proc/end_invocation(silent = FALSE) + SHOULD_CALL_PARENT(TRUE) + + //Remove the timer if there is one currently active + if(loop_timer_id) + deltimer(loop_timer_id) + loop_timer_id = null + + if(!silent) + to_chat(invoker, span_brass("You are no longer invoking [name].")) + progress.end_progress() + + pointed_spell.unset_click_ability(invoker) + invoking_slab.charge_overlay = null + invoking_slab.update_icon() + invoking_slab.active_scripture = null + + end_invoke() + +/// Apply the effects of a scripture to an atom +/datum/scripture/slab/proc/apply_effects(atom/applied_atom) + return TRUE + +/datum/action/cooldown/spell/pointed/slab + /// The scripture datum that this spell is referring to + var/datum/scripture/slab/parent_scripture + +/datum/action/cooldown/spell/pointed/slab/Destroy() + parent_scripture = null + return ..() + +/datum/action/cooldown/spell/pointed/slab/InterceptClickOn(mob/living/clicker, params, atom/target) + parent_scripture?.click_on(target) + +/// Generate all scriptures in a global assoc of name:ref. Only needs to be done once +/proc/generate_clockcult_scriptures() + for(var/categorypath in subtypesof(/datum/scripture)) + var/datum/scripture/clock_script = new categorypath + GLOB.clock_scriptures += clock_script + GLOB.clock_scriptures_by_type[clock_script.type] = clock_script diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/clockwork_armaments.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/clockwork_armaments.dm new file mode 100644 index 00000000000..771c6fdd321 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/clockwork_armaments.dm @@ -0,0 +1,40 @@ +/datum/scripture/clockwork_armaments + name = "Clockwork Armaments" + desc = "Summon clockwork armor and weapons, to be ready for battle." + tip = "Summon clockwork armor and weapons, to be ready for battle." + button_icon_state = "clockwork_armor" + power_cost = STANDARD_CELL_CHARGE * 0.25 + invocation_time = 2 SECONDS + invocation_text = list("Through courage and hope...", "we shall protect thee!") + category = SPELLTYPE_PRESERVATION + cogs_required = 1 + + +/datum/scripture/clockwork_armaments/invoke_success() + var/choice = tgui_input_list(invoker, "What weapon do you want to call upon?", "Clockwork Armaments", list("Brass Spear", "Brass Battlehammer", "Brass Sword")) + + if(!choice) + return FALSE + + var/static/datum/outfit/clockwork_armaments/base_outfit + if(!base_outfit) + base_outfit = new + + var/weapon_path = /obj/item/clockwork/weapon/brass_battlehammer + var/say_the_line = "I've gotten me mallet!" + + switch(choice) + if("Brass Spear") + weapon_path = /obj/item/clockwork/weapon/brass_spear + say_the_line = "My will shall pierce my foes!" + if("Brass Battlehammer") + weapon_path = /obj/item/clockwork/weapon/brass_battlehammer + say_the_line = "I've gotten me mallet!" + if("Brass Sword") + weapon_path = /obj/item/clockwork/weapon/brass_sword + say_the_line = "Let us cut them to pieces!" + + base_outfit.equip(invoker) + + invoker.put_in_hands(new weapon_path, FALSE) + clockwork_say(invoker, text2ratvar(say_the_line), FALSE) diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/dimensional_breach.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/dimensional_breach.dm new file mode 100644 index 00000000000..282a74e82d0 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/dimensional_breach.dm @@ -0,0 +1,39 @@ +/datum/scripture/ark_activation + name = "Ark Invigoration" + desc = "Prepares the Ark for activation, alerting the crew of your existence." + tip = "Prepares the Ark for activation, alerting the crew of your existence." + button_icon_state = "Spatial Gateway" + power_cost = STANDARD_CELL_CHARGE * 1.5 + invocation_time = 15 SECONDS + invocation_text = list("Brightest Engine, take my soul...", "To complete our greatest goal...", "through the rifts you now shall come...", "to show them where the light is from!") + invokers_required = 6 + category = SPELLTYPE_PRESERVATION + recital_sound = 'sound/effects/magic/clockwork/narsie_attack.ogg' //ironic + fast_invoke_mult = 1 + cogs_required = 5 + +/datum/scripture/ark_activation/check_special_requirements(mob/user) + . = ..() + if(!.) + return FALSE + + if(!on_reebe(invoker)) + to_chat(invoker, span_brass("You need to be near the gateway to channel its energy!")) + return FALSE + + if(!GLOB.clock_ark) + to_chat(invoker, span_userdanger("No ark located, contact the admins with an ahelp(f1).")) + return FALSE + + if(SSthe_ark.charged_anchoring_crystals < ANCHORING_CRYSTALS_TO_SUMMON) + to_chat(invoker, span_brass("Reebe is not yet anchored enough to this realm, the ark cannot open until enough anchoring crystals are summoned and protected.")) + return FALSE + + return TRUE + +/datum/scripture/ark_activation/invoke_success() + if(!GLOB.clock_ark) + to_chat(invoker, span_userdanger("No ark located, contact the admins with an ahelp(f1).")) + return FALSE + + GLOB.clock_ark.open_gateway() diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/summon_cogscarab.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/summon_cogscarab.dm new file mode 100644 index 00000000000..6c39d38ba14 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/summon_cogscarab.dm @@ -0,0 +1,38 @@ +/datum/scripture/cogscarab + name = "Summon Cogscarab" + desc = "Summon a Cogscarab shell, which will be possessed by fallen Ratvarian soldiers. Takes longer the more cogscarabs are alive. Requires 30 vitality." + tip = "Use Cogscarabs to fortify Reebe while the human servants convert and sabotage the crew." + button_icon_state = "Cogscarab" + power_cost = STANDARD_CELL_CHARGE * 0.5 + vitality_cost = 30 + invocation_time = 12 SECONDS + invocation_text = list("My fallen brothers,", "Now is the time we rise", "to protect our Lord", "and achieve greatness!") + category = SPELLTYPE_PRESERVATION + cogs_required = 5 + invokers_required = 2 + fast_invoke_mult = 1 + +/datum/scripture/cogscarab/begin_invoke(mob/living/invoking_mob, obj/item/clockwork/clockwork_slab/slab, bypass_unlock_checks) + invocation_time = 12 SECONDS + (6 SECONDS * SSthe_ark.cogscarabs.len) + . = ..() + +/datum/scripture/cogscarab/check_special_requirements(mob/user) + . = ..() + if(!.) + return FALSE + + if(!on_reebe(invoker) || !SSthe_ark.marked_areas[get_area(invoker.loc)]) + to_chat(invoker, span_warning("You must do this on Reebe or Marked Area!")) + return FALSE + + if(length(SSthe_ark.cogscarabs) > MAXIMUM_COGSCARABS) + to_chat(invoker, span_warning("You can't summon anymore cogscarabs.")) + return FALSE + + if(GLOB.clock_ark?.current_state >= ARK_STATE_ACTIVE) + to_chat(invoker, span_warning("It is too late to summon cogscarabs now, Ratvar is coming!")) + return FALSE + return TRUE + +/datum/scripture/cogscarab/invoke_success() + new /obj/effect/mob_spawn/ghost_role/drone/cogscarab(get_turf(invoker)) diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/summon_marauder.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/summon_marauder.dm new file mode 100644 index 00000000000..b86bcd94a1f --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/summon_marauder.dm @@ -0,0 +1,68 @@ +#define MAXIMUM_MARAUDERS 2 + +/datum/scripture/marauder + name = "Summon Clockwork Marauder" + desc = "Summons a Clockwork Marauder, a powerful warrior that can deflect ranged attacks. Requires 100 vitality." + tip = "Use Clockwork Marauders as a powerful soldier to send into combat when the fighting gets rough." + button_icon_state = "Clockwork Marauder" + power_cost = STANDARD_CELL_CHARGE + vitality_cost = 100 + invocation_time = 30 SECONDS + invocation_text = list("Through the fires and flames...", "We fly ever free...", "nothing outshines Engine!") + category = SPELLTYPE_PRESERVATION + cogs_required = 6 + invokers_required = 3 + fast_invoke_mult = 1 + // Ref to the selected observer + var/mob/dead/observer/selected + +/datum/scripture/marauder/Destroy(force) + selected = null + return ..() + +/datum/scripture/marauder/invoke() + var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates( + "Do you want to play as a Clockwork Marauder?", + check_jobban = ROLE_MIDROUND_CLOCK_CULTIST, + role = ROLE_SENTIENCE, + poll_time = 10 SECONDS, + ignore_category = POLL_IGNORE_CONSTRUCT, + alert_pic = /mob/living/basic/clockwork_marauder, + role_name_text = "clockwork marauder" + ) + if(length(candidates)) + selected = pick(candidates) + + if(!selected) + to_chat(invoker, span_brass("There are no ghosts willing to be a Clockwork Marauder!")) + invoke_fail() + + if(invocation_chant_timer) + deltimer(invocation_chant_timer) + invocation_chant_timer = null + + end_invoke() + return FALSE + return ..() + +/datum/scripture/marauder/invoke_success() + var/mob/living/basic/clockwork_marauder/new_mob = new (get_turf(invoker)) + new_mob.visible_message(span_notice("[new_mob] flashes into existance!")) + new_mob.PossessByPlayer(selected.key) + new_mob.mind.add_antag_datum(/datum/antagonist/clock_cultist/clockmob) + to_chat(new_mob, span_brass("You are a Clockwork Marauder! You have a [new_mob.shield_health]-hit shield that will protect you against any damage taken. \ + Have a servant repair you with a welder, should you or your shield become too damaged.")) + selected = null + + +/datum/scripture/marauder/check_special_requirements(mob/user) + . = ..() + if(!.) + return FALSE + + if(length(SSthe_ark.clockwork_marauders) >= MAXIMUM_MARAUDERS) + to_chat(user, span_brass("Your limited power prevents you from creating more than [MAXIMUM_MARAUDERS] Clockwork Marauders.")) + return FALSE + return TRUE + +#undef MAXIMUM_MARAUDERS diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/vanguard.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/vanguard.dm new file mode 100644 index 00000000000..c6bf4a8e045 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/preservation/vanguard.dm @@ -0,0 +1,36 @@ +/datum/scripture/slab/vanguard + name = "Vanguard" + use_time = 30 SECONDS + slab_overlay = "vanguard" + desc = "Provides the user with 30 seconds of stun immunity, however other scriptures cannot be invoked while it is active." + tip = "Gain temporary immunity against batons and disablers." + invocation_text = list("With Engine's power coursing through me...", "I will stop them in their tracks!") + invocation_time = 2 SECONDS + button_icon_state = "Vanguard" + category = SPELLTYPE_PRESERVATION + cogs_required = 2 + power_cost = STANDARD_CELL_CHARGE * 0.15 + +/datum/scripture/slab/vanguard/apply_effects(atom/applied_atom) + return FALSE + +/datum/scripture/slab/vanguard/invoke() + . = ..() + invoker.add_traits(list(TRAIT_STUNIMMUNE, + TRAIT_PUSHIMMUNE, + TRAIT_IGNORESLOWDOWN, + TRAIT_NODISMEMBER), VANGUARD_TRAIT) + to_chat(invoker, span_notice("You feel like nothing can stop you!")) + +/datum/scripture/slab/vanguard/count_down() + . = ..() + if(time_left == 5 SECONDS) + to_chat(invoker, span_userdanger("You start to feel tired again.")) + +/datum/scripture/slab/vanguard/end_invocation(silent) + . = ..() + invoker.remove_traits(list(TRAIT_STUNIMMUNE, + TRAIT_PUSHIMMUNE, + TRAIT_IGNORESLOWDOWN, + TRAIT_NODISMEMBER), VANGUARD_TRAIT) + to_chat(invoker, span_bolddanger("You feel the last of the energy from \the [invoking_slab] leave you.")) //smaller span here because its pretty obvious when it ends anyway diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/abscond.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/abscond.dm new file mode 100644 index 00000000000..b74bcad15df --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/abscond.dm @@ -0,0 +1,21 @@ +/datum/scripture/abscond + name = "Abscond" + desc = "After a long delay recalls you and anyone you are dragging to reebe. Cannot be invoked from a non marked area." + tip = "If using this with a prisoner dont forget to cuff them first." + button_icon_state = "Abscond" + invocation_time = 15 SECONDS + invocation_text = list("Return to our home, the city of cogs.") + category = SPELLTYPE_SERVITUDE + power_cost = STANDARD_CELL_CHARGE * 0.01 + +/datum/scripture/abscond/check_special_requirements(mob/user) + . = ..() + if(!.) + return + + if(!SSthe_ark.marked_areas[get_area(invoker)]) + to_chat(user, span_warning("We can only abscond from marked areas!")) + return FALSE + +/datum/scripture/abscond/invoke_success() + try_servant_warp(invoker, get_turf(pick(GLOB.abscond_markers))) diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/golem_conversion.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/golem_conversion.dm new file mode 100644 index 00000000000..862175fc27c --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/golem_conversion.dm @@ -0,0 +1,31 @@ +//replaces the anchoring crystal scripture if all 3 crystals have been summoned and protected +/datum/scripture/transform_to_golem + name = "Ascend Form" + desc = "Ascend your form to that of a clockwork golem, giving them innate armor, environmental immunity, and faster invoking for most scriptures." + tip = "Can only be used by humaniod servants." + button_icon_state = "Spatial Warp" + power_cost = STANDARD_CELL_CHARGE * 0.5 + invocation_time = 15 SECONDS + invocation_text = list("My form is weak...", "It must ascend...", "To that of clockwork.") + cogs_required = 3 + category = SPELLTYPE_SERVITUDE + +/datum/scripture/transform_to_golem/check_special_requirements(mob/user) + . = ..() + if(!.) + return FALSE + + if(!ishuman(invoker)) + to_chat(invoker, span_warning("This scripture can only be used by humanoid servants.")) + return FALSE + + if(is_species(invoker, /datum/species/clockwork_golem)) + to_chat(invoker, span_notice("You are already a clockwork golem!")) + return FALSE + return TRUE + +/datum/scripture/transform_to_golem/invoke_success() + var/mob/living/carbon/human/human_servant = invoker + human_servant.set_species(/datum/species/clockwork_golem) + human_servant.update_body(TRUE) + human_servant.update_mutations_overlay() diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/hateful_manacles.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/hateful_manacles.dm new file mode 100644 index 00000000000..fedebae250f --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/hateful_manacles.dm @@ -0,0 +1,41 @@ +/datum/scripture/slab/hateful_manacles + name = "Hateful Manacles" + desc = "Forms replicant manacles around a target's wrists that function like handcuffs, restraining the target." + tip = "Handcuff a target at close range to subdue them for conversion or vitality extraction." + button_icon_state = "Hateful Manacles" + power_cost = STANDARD_CELL_CHARGE * 0.05 + invocation_time = 1.5 SECONDS // 1.5 to invoke, 3 to cuff + invocation_text = list("Shackle the heretic...", "Break them in body and spirit!") + slab_overlay = "hateful_manacles" + use_time = 20 SECONDS + cogs_required = 1 + category = SPELLTYPE_SERVITUDE + + +/datum/scripture/slab/hateful_manacles/apply_effects(mob/living/carbon/target_carbon) + if(!iscarbon(target_carbon) || IS_CLOCK(target_carbon)) + return FALSE + + if(target_carbon.handcuffed) + target_carbon.balloon_alert(invoker, "already restrained!") + return FALSE + + playsound(target_carbon, 'sound/items/weapons/handcuffs.ogg', 30, TRUE, -2) + target_carbon.visible_message(span_danger("[invoker] forms a well of energy around [target_carbon], brass appearing at their wrists!"),\ + span_userdanger("[invoker] is trying to restrain you!")) + + if(!do_after(invoker, 3 SECONDS, target = target_carbon, hidden = TRUE) || target_carbon.handcuffed) + return FALSE + + target_carbon.set_handcuffed(new /obj/item/restraints/handcuffs/clockwork(target_carbon)) + target_carbon.update_handcuffed() + log_combat(invoker, target_carbon, "handcuffed") + + return TRUE + + +/obj/item/restraints/handcuffs/clockwork + name = "replicant manacles" + desc = "Heavy manacles made out of freezing-cold metal. It looks like brass, but feels much more solid." + icon_state = "brass_manacles" + item_flags = DROPDEL diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/integration_cog.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/integration_cog.dm new file mode 100644 index 00000000000..abbd4737c65 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/integration_cog.dm @@ -0,0 +1,19 @@ +/datum/scripture/integration_cog + name = "Integration Cog" + desc = "Fabricates an integration cog, which can be inserted into APCs to draw power and unlock scriptures." + tip = "Install integration cogs into APCs to increase your energy stores and unlock new scriptures." + button_icon_state = "Integration Cog" + invocation_time = 1 SECONDS + invocation_text = list("Tick tock Engine...") + category = SPELLTYPE_SERVITUDE + +/datum/scripture/integration_cog/invoke_success() + if(invoker.put_in_hands(new /obj/item/clockwork/integration_cog)) + to_chat(invoker, span_brass("You summon an integration cog into your hands.")) + playsound(src, 'sound/machines/click.ogg', 50) + return TRUE + + else + to_chat(invoker, span_brass("You summon an integration cog on the floor.")) + playsound(src, 'sound/machines/click.ogg', 50) + return FALSE diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/kindle.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/kindle.dm new file mode 100644 index 00000000000..4ddc7873e0a --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/kindle.dm @@ -0,0 +1,102 @@ +#define EFFECT_TIME (7 SECONDS) + +// Clock cult's version of the "bullshit stun hand" + +/datum/scripture/slab/kindle + name = "Kindle" + desc = "Stuns and mutes a target from a short range." + tip = "Best paired with hateful manacels for conversion, they are stunned for 7 seconds and muted for 14." + button_icon_state = "Kindle" + power_cost = STANDARD_CELL_CHARGE * 0.125 + invocation_time = 2 SECONDS + invocation_text = list("Divinity, show them your light!") + after_use_text = "Let the power flow through you!" + slab_overlay = "volt" + use_time = 15 SECONDS + cogs_required = 1 + category = SPELLTYPE_SERVITUDE + +/datum/scripture/slab/kindle/apply_effects(mob/living/hit_mob) + if(!isliving(hit_mob)) + return FALSE + + if(!IS_CLOCK(invoker)) + hit_mob = invoker + + if(IS_CLOCK(hit_mob)) + return FALSE + + // Chaplains are understandably 100% immune + if(hit_mob.can_block_magic(MAGIC_RESISTANCE_HOLY)) + hit_mob.mob_light(color = LIGHT_COLOR_HOLY_MAGIC, range = 2, duration = 10 SECONDS) + + var/mutable_appearance/forbearance = mutable_appearance('icons/mob/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER) + hit_mob.add_overlay(forbearance) + addtimer(CALLBACK(hit_mob, TYPE_PROC_REF(/atom, cut_overlay), forbearance), 10 SECONDS) + + hit_mob.visible_message(span_warning("[hit_mob] stares blankly, as a field of energy flows around them."), \ + span_userdanger("You feel a slight shock as a wave of energy flows past you.")) + + playsound(invoker, 'sound/effects/magic/mm_hit.ogg', 50, TRUE) + return TRUE + + //To make battles more fun, both sides can't bullshit stun hand the other + if(IS_CULTIST(hit_mob)) + hit_mob.mob_light(color = LIGHT_COLOR_BLOOD_MAGIC, range = 2, duration = 30 SECONDS) + + hit_mob.adjust_stutter(15 SECONDS) + hit_mob.adjust_jitter(15 SECONDS) + + var/mob_color = hit_mob.color + hit_mob.color = LIGHT_COLOR_BLOOD_MAGIC + animate(hit_mob, color = mob_color, time = 30 SECONDS) + + hit_mob.say("Fwebar uloft'gib mirlig yro'fara!") + + to_chat(invoker, span_warning("Some force greater than you intervenes! [hit_mob] is protected by Nar'sie!")) + to_chat(hit_mob, span_warning("You are protected by your faith to Nar'sie!")) + + playsound(invoker, 'sound/effects/magic/mm_hit.ogg', 50, TRUE) + return TRUE + + if(IS_HERETIC(hit_mob)) + to_chat(invoker, span_warning("Some force greater than you intervenes! [hit_mob] is protected by the Forgotten Gods!")) + to_chat(hit_mob, span_warning("You are protected by your faith to the Forgotten Gods.")) + var/old_color = hit_mob.color + hit_mob.color = rgb(0, 128, 0) + animate(hit_mob, color = old_color, time = 1 SECONDS, easing = EASE_IN) + hit_mob.adjust_stutter(15 SECONDS) + hit_mob.adjust_jitter(15 SECONDS) + playsound(invoker, 'sound/effects/magic/mm_hit.ogg', 50, TRUE) + return TRUE + + //Successful Invokation + invoker.mob_light(color = LIGHT_COLOR_CLOCKWORK, range = 2, duration = 1 SECONDS) + + if(issilicon(hit_mob)) + var/mob/living/silicon/borgo = hit_mob + borgo.emp_act(EMP_HEAVY) + + else if(iscarbon(hit_mob)) + var/mob/living/carbon/carbon_hit = hit_mob + var/has_mindshield = HAS_TRAIT(hit_mob, TRAIT_MINDSHIELD) + + carbon_hit.adjust_stutter(15 SECONDS) + carbon_hit.adjust_jitter(15 SECONDS) + + carbon_hit.adjust_timed_status_effect(26 SECONDS, /datum/status_effect/speech/slurring/clock) + + carbon_hit.adjust_silence(EFFECT_TIME * (has_mindshield ? 1 : 2)) //enough time to cuff and remove their radio + carbon_hit.AdjustKnockdown(EFFECT_TIME * (has_mindshield ? 0.5 : 1.5)) + //pretty much 0 stun if your on reebe, still good for knockdown though, also only a 1 second stun on mindshielded people + carbon_hit.Stun((has_mindshield ? 0.5 SECONDS : EFFECT_TIME) * ((on_reebe(carbon_hit) && GLOB.clock_ark?.current_state) ? 0.1 : 1)) + + if(hit_mob.client) + var/client_color = hit_mob.client.color + hit_mob.client.color = "#BE8700" + animate(hit_mob.client, color = client_color, time = 2.5 SECONDS) + + playsound(invoker, 'sound/effects/magic/staff_animation.ogg', 50, TRUE) + return TRUE + +#undef EFFECT_TIME diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/sentinels_compromise.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/sentinels_compromise.dm new file mode 100644 index 00000000000..11aad309126 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/sentinels_compromise.dm @@ -0,0 +1,59 @@ +///how much do we heal per do_after() loop +#define HEALED_PER_LOOP 20 + +/datum/scripture/slab/sentinels_compromise + name = "Sentinel's Compromise" + desc = "Heals of non-toxin damage on a target then converts 50% of it back as toxin damage to you." + tip = "Works well with Properity Prisms. Cannot be used by cogscarabs." + power_cost = STANDARD_CELL_CHARGE * 0.15 + cogs_required = 1 + invocation_time = 1 SECONDS //short invocation but using it also takes some time afterwards + invocation_text = list("By the light of Engine...") //the second line is said when used on someone + button_icon_state = "Sentinel's Compromise" + category = SPELLTYPE_SERVITUDE //you have a healing spell please please PLEASE use it + slab_overlay = "compromise" + use_time = 15 SECONDS + recital_sound = 'sound/effects/magic/magic_missile.ogg' + fast_invoke_mult = 0.8 + +/datum/scripture/slab/sentinels_compromise/check_special_requirements(mob/user) + if(issilicon(user)) + invocation_time = 10 * initial(invocation_time) + else + invocation_time = initial(invocation_time) //might be worth making a silicon_invoke() proc or something + return ..() + +/datum/scripture/slab/sentinels_compromise/apply_effects(mob/living/healed_mob) + if(!isliving(healed_mob) || !IS_CLOCK(invoker) || !IS_CLOCK(healed_mob)) + return FALSE + + if(iscogscarab(invoker)) + to_chat(invoker, span_warning("Your form is too frail to take the burden of another.")) + return FALSE + + if(!do_after(invoker, invocation_time, healed_mob)) + return FALSE + + healed_mob.cure_husk() + if(healed_mob.stat == DEAD) //technically the husk healing is free but it should be fine + return FALSE + + healed_mob.blood_volume = BLOOD_VOLUME_NORMAL + healed_mob.set_nutrition(NUTRITION_LEVEL_FULL) + healed_mob.bodytemperature = BODYTEMP_NORMAL + apply_heal(healed_mob) + clockwork_say(invoker, text2ratvar("Wounds will close."), TRUE) + new /obj/effect/temp_visual/heal(get_turf(healed_mob), "#1E8CE1") + return TRUE + +/datum/scripture/slab/sentinels_compromise/proc/apply_heal(mob/living/healed_mob) + var/healed_amount = -healed_mob.heal_ordered_damage(HEALED_PER_LOOP, list(BRUTE, BURN, OXY, BRAIN)) + healed_mob.adjust_stamina_loss(-HEALED_PER_LOOP) + healed_mob.reagents.remove_reagent(/datum/reagent/water/holywater, HEALED_PER_LOOP) + if(isclockgolem(invoker)) + invoker.adjust_fire_loss(healed_amount * 0.5) + else + invoker.adjust_tox_loss(healed_amount * 0.5) + return TRUE + +#undef HEALED_PER_LOOP diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/submission_sigil.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/submission_sigil.dm new file mode 100644 index 00000000000..d67359564ba --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/submission_sigil.dm @@ -0,0 +1,20 @@ +/datum/scripture/create_structure/sigil_submission + name = "Sigil of Submission" + desc = "Summons a sigil of submission, which will convert anyone placed on top of it to the faith of Ratvar after 8 seconds." + tip = "Simply wait after placing a convertee on top, do not interact with the sigil." + button_icon_state = "Sigil of Submission" + power_cost = STANDARD_CELL_CHARGE * 0.2 + invocation_time = 5 SECONDS + invocation_text = list("Relax, animal...", "for I shall show you the truth.") + summoned_structure = /obj/structure/destructible/clockwork/sigil/submission + cogs_required = 1 + category = SPELLTYPE_SERVITUDE + +/datum/scripture/create_structure/sigil_submission/check_special_requirements(mob/user) + . = ..() + if(!.) + return FALSE + + if(user.mind.has_antag_datum(/datum/antagonist/clock_cultist/solo)) + to_chat(user, span_clockyellow("You don't have Justiciar permission to summon this structure...")) + return FALSE diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/vitality_sigil.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/vitality_sigil.dm new file mode 100644 index 00000000000..35abde745d6 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/servitude/vitality_sigil.dm @@ -0,0 +1,11 @@ +/datum/scripture/create_structure/sigil_vitality + name = "Vitality Matrix" + desc = "Summons a vitality matrix, which drains the life force of non servants. Much less vitality is gained from simpler entities." + tip = "If a fellow servant who is dead or mindless is placed on a rune, they will be restored. Bringing back the dead will cost Vitality." + button_icon_state = "Sigil of Vitality" + power_cost = STANDARD_CELL_CHARGE * 0.25 + invocation_time = 5 SECONDS + invocation_text = list("My life in your hands.") + summoned_structure = /obj/structure/destructible/clockwork/sigil/vitality + cogs_required = 2 + category = SPELLTYPE_SERVITUDE diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/structures/anchoring_crystal.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/anchoring_crystal.dm new file mode 100644 index 00000000000..bab20169097 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/anchoring_crystal.dm @@ -0,0 +1,90 @@ +/datum/scripture/create_structure/anchoring_crystal + name = "Anchoring Crystal" + desc = "Summon an anchoring crystal to the station." + tip = "Oops!" //this is set on New() + button_icon_state = "Clockwork Obelisk" + power_cost = STANDARD_CELL_CHARGE * 0.5 + invocation_time = 20 SECONDS + invocation_text = list("Space shall fold...", "Time shall mold...", "Anchor us here...", "Engine is near!") + summoned_structure = /obj/structure/destructible/clockwork/anchoring_crystal + cogs_required = 5 + invokers_required = 3 + category = SPELLTYPE_STRUCTURES + ///how long in seconds until the scripture can be invoked again, pretty much a cooldown + var/static/time_until_invokable = 0 + ///the list of + var/static/list/valid_areas + +/datum/scripture/create_structure/anchoring_crystal/New() + update_info() + . = ..() + RegisterSignal(SSthe_ark, COMSIG_ANCHORING_CRYSTAL_CHARGED, PROC_REF(on_crystal_charged)) + RegisterSignal(SSthe_ark, COMSIG_ANCHORING_CRYSTAL_CREATED, PROC_REF(update_info)) + +/datum/scripture/create_structure/anchoring_crystal/Destroy(force) + UnregisterSignal(SSthe_ark, COMSIG_ANCHORING_CRYSTAL_CHARGED) + UnregisterSignal(SSthe_ark, COMSIG_ANCHORING_CRYSTAL_CREATED) + return ..() + +/datum/scripture/create_structure/anchoring_crystal/process(seconds_per_tick) + time_until_invokable = time_until_invokable - (seconds_per_tick SECONDS) + if(time_until_invokable <= 0) + var/datum/scripture/create_structure/anchoring_crystal/global_datum = GLOB.clock_scriptures_by_type[/datum/scripture/create_structure/anchoring_crystal] + STOP_PROCESSING(SSprocessing, global_datum) + time_until_invokable = 0 + +/datum/scripture/create_structure/anchoring_crystal/check_special_requirements(mob/user) + . = ..() + if(!.) + return FALSE + + if(time_until_invokable) + to_chat(invoker, span_warning("The ark will be stable enough to summon another crystal in [time_until_invokable] seconds.")) + return FALSE + + if(SSthe_ark.charged_anchoring_crystals && !SSthe_ark.valid_crystal_areas[get_area(invoker)]) + var/list/area_list = list() + for(var/area/added_area in SSthe_ark.valid_crystal_areas) + area_list += SSthe_ark.valid_crystal_areas[added_area] + to_chat(invoker, span_warning("This cystal can only be summoned in [english_list(area_list)].")) + return FALSE + + var/area/checked_area = get_area(invoker) + if(!(checked_area?.area_flags & VALID_TERRITORY)) + to_chat(invoker, span_warning("You cannot summon an anchoring crystal here!")) + return FALSE + return TRUE + +/datum/scripture/create_structure/anchoring_crystal/invoke() + if(time_until_invokable) //check again in case they try and make two at once + var/datum/scripture/create_structure/anchoring_crystal/scripture = GLOB.clock_scriptures_by_type[/datum/scripture/create_structure/anchoring_crystal] + START_PROCESSING(SSprocessing, scripture) //make sure we dont brick somehow + to_chat(invoker, span_warning("Another Anchoring Crystal is already charging!")) + return FALSE + . = ..() + +/datum/scripture/create_structure/anchoring_crystal/invoke_success() + . = ..() + time_until_invokable = ANCHORING_CRYSTAL_COOLDOWN + var/datum/scripture/create_structure/anchoring_crystal/scripture = GLOB.clock_scriptures_by_type[/datum/scripture/create_structure/anchoring_crystal] + START_PROCESSING(SSprocessing, scripture) + +/datum/scripture/create_structure/anchoring_crystal/proc/on_crystal_charged() + SIGNAL_HANDLER + if(SSthe_ark.charged_anchoring_crystals >= ANCHORING_CRYSTALS_TO_SUMMON + 2) + unique_lock() + return + +/datum/scripture/create_structure/anchoring_crystal/proc/update_info() + SIGNAL_HANDLER + tip = "With this crystal [anchoring_crystal_charge_message()]" + if(!SSthe_ark.charged_anchoring_crystals) + return + + if(!length(SSthe_ark.valid_crystal_areas)) + desc = "We cannot summon any more anchoring crystals to the station." + else + var/list/area_list = list() + for(var/area/added_area in SSthe_ark.valid_crystal_areas) + area_list += SSthe_ark.valid_crystal_areas[added_area] + desc = "Summon an anchoring crystal to the station, it can be summoned in [english_list(area_list)]." diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/structures/empower_wall.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/empower_wall.dm new file mode 100644 index 00000000000..278bcb07afa --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/empower_wall.dm @@ -0,0 +1,33 @@ +/datum/scripture/slab/empower_wall + name = "Empower Wall" + desc = "Empowers a clockwork wall's stabilization lattice, improving its resilience." + tip = "Empowers a clockwork wall's stabilization lattice, improving its resilience." + button_icon_state = "empower_wall" + power_cost = STANDARD_CELL_CHARGE * 0.1 + invocation_time = 3 SECONDS + invocation_text = list("Strengthen our resolve...", "So we may never fall!") + slab_overlay = "hateful_manacles" + use_time = 30 SECONDS + cogs_required = 3 + category = SPELLTYPE_STRUCTURES + uses = 3 + +/datum/scripture/slab/empower_wall/apply_effects(obj/structure/destructible/clockwork/wall_lattice/applied_to) + if(!istype(applied_to)) + return FALSE + + if(applied_to.is_empowered) + applied_to.balloon_alert(invoker, "\The [applied_to] is already empowered.") + return FALSE + + applied_to.balloon_alert(invoker, "you start to empower \the [applied_to].") + if(!do_after(invoker, 3 SECONDS, applied_to) || applied_to?.is_empowered) + applied_to.balloon_alert(invoker, "you fail to empower \the [applied_to].") + return FALSE + + if(QDELETED(applied_to)) + return FALSE + + applied_to.balloon_alert(invoker, "you empower \the [applied_to].") + applied_to.empower() + return TRUE diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/structures/interdiction_lens.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/interdiction_lens.dm new file mode 100644 index 00000000000..0b0ec0cce21 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/interdiction_lens.dm @@ -0,0 +1,11 @@ +/datum/scripture/create_structure/interdiction + name = "Interdiction Lens" + desc = "Creates a device that will slow non servants in the area and damage mechanised exosuits. Requires power from a sigil of transmission." + tip = "Construct interdiction lens to slow down a hostile assault." + button_icon_state = "Interdiction Lens" + power_cost = STANDARD_CELL_CHARGE * 0.3 + invocation_time = 8 SECONDS + invocation_text = list("Oh great lord...", "may your divinity block the outsiders.") + summoned_structure = /obj/structure/destructible/clockwork/gear_base/powered/interdiction_lens + cogs_required = 4 + category = SPELLTYPE_STRUCTURES diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/structures/ocular_warden.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/ocular_warden.dm new file mode 100644 index 00000000000..e157603536b --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/ocular_warden.dm @@ -0,0 +1,26 @@ +#define OCULAR_WARDEN_PLACE_RANGE 4 + +/datum/scripture/create_structure/ocular_warden + name = "Ocular Warden" + desc = "An eye turret that will fire upon nearby targets." + tip = "Place these around to prevent crew from rushing past your defenses." + button_icon_state = "Ocular Warden" + power_cost = STANDARD_CELL_CHARGE * 0.3 + invocation_time = 5 SECONDS + invocation_text = list("We summon thee to defend our temple!") + summoned_structure = /obj/structure/destructible/clockwork/gear_base/powered/ocular_warden + cogs_required = 3 + category = SPELLTYPE_STRUCTURES + +/datum/scripture/create_structure/ocular_warden/check_special_requirements(mob/user) + . = ..() + if(!.) + return FALSE + + if(locate(/obj/structure/destructible/clockwork/gear_base/powered/ocular_warden) in range(OCULAR_WARDEN_PLACE_RANGE)) + user.balloon_alert(user, "too close to another warden!") + return FALSE + + return TRUE + +#undef OCULAR_WARDEN_PLACE_RANGE diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/structures/prosperity_prism.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/prosperity_prism.dm new file mode 100644 index 00000000000..1edb47dd272 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/prosperity_prism.dm @@ -0,0 +1,26 @@ +//==================================// +// ! Prosperity Prism ! // +//==================================// +/datum/scripture/create_structure/prosperity_prism + name = "Prosperity Prism" + desc = "Creates a prism that will remove all forms of damage from nearby servants over time, along with purging poisons. Requires power from a sigil of transmission." + tip = "Create a prosperity prism to heal servants while defending your base." + button_icon_state = "Prolonging Prism" + power_cost = STANDARD_CELL_CHARGE * 0.3 + invocation_time = 8 SECONDS + invocation_text = list("Your light shall heal the wounds beneath my skin.") + summoned_structure = /obj/structure/destructible/clockwork/gear_base/powered/prosperity_prism + cogs_required = 2 + category = SPELLTYPE_STRUCTURES + + +/datum/scripture/create_structure/prosperity_prism/check_special_requirements(mob/user) + . = ..() + if(!.) + return FALSE + + if(locate(/obj/structure/destructible/clockwork/gear_base/powered/prosperity_prism) in range(3)) // No stacking heals for you + user.balloon_alert(user, "too close to another prism!") + return FALSE + + return TRUE diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/structures/stargazer.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/stargazer.dm new file mode 100644 index 00000000000..43af30a7bec --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/stargazer.dm @@ -0,0 +1,11 @@ +/datum/scripture/create_structure/stargazer + name = "Stargazer" + desc = "Allows you to enchant your weapons giving them unique and improved properties." + tip = "Make your weapons more powerful by enchanting them with stargazers." + button_icon_state = "Stargazer" + power_cost = STANDARD_CELL_CHARGE * 0.25 + invocation_time = 45 SECONDS + invocation_text = list("The light of Engine shall empower my armaments!") + summoned_structure = /obj/structure/destructible/clockwork/gear_base/stargazer + cogs_required = 2 + category = SPELLTYPE_STRUCTURES diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/structures/tinkerers_cache.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/tinkerers_cache.dm new file mode 100644 index 00000000000..5c840c3a7b4 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/tinkerers_cache.dm @@ -0,0 +1,11 @@ +/datum/scripture/create_structure/tinkerers_cache + name = "Tinkerer's Cache" + desc = "Creates a tinkerer's cache, a powerful forge capable of crafting elite equipment." + tip = "Use the cache to create more powerful equipment at the cost of power and time." + button_icon_state = "Tinkerer's Cache" + power_cost = STANDARD_CELL_CHARGE * 0.35 + invocation_time = 45 SECONDS + invocation_text = list("Guide my hand and we shall create greatness.") + summoned_structure = /obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache + cogs_required = 4 + category = SPELLTYPE_STRUCTURES diff --git a/tff_modular/modules/antagonists/clock_cult/scriptures/structures/transmission_sigil.dm b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/transmission_sigil.dm new file mode 100644 index 00000000000..7c8139a69d2 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/scriptures/structures/transmission_sigil.dm @@ -0,0 +1,11 @@ +/datum/scripture/create_structure/sigil_transmission + name = "Sigil of Transmission" + desc = "Summons a sigil of transmission, required to power clockwork structures. Will also drain power from charged objects." + tip = "Power structures using this." + button_icon_state = "Sigil of Transmission" + power_cost = STANDARD_CELL_CHARGE * 0.05 + invocation_time = 5 SECONDS + invocation_text = list("Oh great holy one...", "your energy...", "the power of the holy light!") + summoned_structure = /obj/structure/destructible/clockwork/sigil/transmission + category = SPELLTYPE_STRUCTURES + assault_invoke_time_mult = 1 diff --git a/tff_modular/modules/antagonists/clock_cult/sound/ark_damage.ogg b/tff_modular/modules/antagonists/clock_cult/sound/ark_damage.ogg new file mode 100644 index 00000000000..59092957111 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/ark_damage.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/ark_deathrattle.ogg b/tff_modular/modules/antagonists/clock_cult/sound/ark_deathrattle.ogg new file mode 100644 index 00000000000..fe7de9f5fa2 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/ark_deathrattle.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/ark_recall.ogg b/tff_modular/modules/antagonists/clock_cult/sound/ark_recall.ogg new file mode 100644 index 00000000000..cc409d17825 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/ark_recall.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/ark_scream.ogg b/tff_modular/modules/antagonists/clock_cult/sound/ark_scream.ogg new file mode 100644 index 00000000000..e0d3e2aabf2 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/ark_scream.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/brass_skewer.ogg b/tff_modular/modules/antagonists/clock_cult/sound/brass_skewer.ogg new file mode 100644 index 00000000000..18463054205 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/brass_skewer.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/clockcult_gateway_charging.ogg b/tff_modular/modules/antagonists/clock_cult/sound/clockcult_gateway_charging.ogg new file mode 100644 index 00000000000..5108a3645ca Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/clockcult_gateway_charging.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/clockcult_gateway_closing.ogg b/tff_modular/modules/antagonists/clock_cult/sound/clockcult_gateway_closing.ogg new file mode 100644 index 00000000000..83c8592096e Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/clockcult_gateway_closing.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/eminence_command.ogg b/tff_modular/modules/antagonists/clock_cult/sound/eminence_command.ogg new file mode 100644 index 00000000000..410ac9b7f08 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/eminence_command.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/eminence_selected.ogg b/tff_modular/modules/antagonists/clock_cult/sound/eminence_selected.ogg new file mode 100644 index 00000000000..8185a52e625 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/eminence_selected.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/integration_cog_install.ogg b/tff_modular/modules/antagonists/clock_cult/sound/integration_cog_install.ogg new file mode 100644 index 00000000000..35c13b33abc Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/integration_cog_install.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/lambda.ogg b/tff_modular/modules/antagonists/clock_cult/sound/lambda.ogg new file mode 100644 index 00000000000..dad08b5dc31 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/lambda.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/miss.ogg b/tff_modular/modules/antagonists/clock_cult/sound/miss.ogg new file mode 100644 index 00000000000..e7c3f5ddf12 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/miss.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/ocularwarden_dot1.ogg b/tff_modular/modules/antagonists/clock_cult/sound/ocularwarden_dot1.ogg new file mode 100644 index 00000000000..8976ad8104b Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/ocularwarden_dot1.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/ocularwarden_target.ogg b/tff_modular/modules/antagonists/clock_cult/sound/ocularwarden_target.ogg new file mode 100644 index 00000000000..0cd8b2000cb Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/ocularwarden_target.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/ratvar_reveal.ogg b/tff_modular/modules/antagonists/clock_cult/sound/ratvar_reveal.ogg new file mode 100644 index 00000000000..3809ed7ea3f Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/ratvar_reveal.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/ratvar_rises.ogg b/tff_modular/modules/antagonists/clock_cult/sound/ratvar_rises.ogg new file mode 100644 index 00000000000..8841b358866 Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/ratvar_rises.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/sound/steam_whoosh.ogg b/tff_modular/modules/antagonists/clock_cult/sound/steam_whoosh.ogg new file mode 100644 index 00000000000..b915dbb9ede Binary files /dev/null and b/tff_modular/modules/antagonists/clock_cult/sound/steam_whoosh.ogg differ diff --git a/tff_modular/modules/antagonists/clock_cult/status_effects.dm b/tff_modular/modules/antagonists/clock_cult/status_effects.dm new file mode 100644 index 00000000000..dc9914b29dc --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/status_effects.dm @@ -0,0 +1,84 @@ +#define CLIENT_COLOR_SOURCE_RATVAR "client_color_source_ratvar" + +/datum/status_effect/interdiction + id = "interdicted" + duration = 2.6 SECONDS + status_type = STATUS_EFFECT_REFRESH + tick_interval = 0.2 SECONDS + alert_type = /atom/movable/screen/alert/status_effect/interdiction + /// If we kicked the owner out of running mode + var/running_toggled = FALSE + +/datum/status_effect/interdiction/tick() + if(owner.move_intent != MOVE_INTENT_WALK) + owner.toggle_move_intent() + owner.adjust_confusion_up_to(1 SECONDS, 1 SECONDS) + running_toggled = TRUE + to_chat(owner, span_warning("You know you shouldn't be running here.")) + + owner.add_movespeed_modifier(/datum/movespeed_modifier/clock_interdiction) + +/datum/status_effect/interdiction/on_remove() + owner.remove_movespeed_modifier(/datum/movespeed_modifier/clock_interdiction) + + if(running_toggled && owner.move_intent == MOVE_INTENT_WALK) + owner.toggle_move_intent() + +/atom/movable/screen/alert/status_effect/interdiction + name = "Interdicted" + desc = "I don't think I am meant to go this way." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/hud/screen_alert.dmi' + icon_state = "belligerent" + +/datum/movespeed_modifier/clock_interdiction + multiplicative_slowdown = 1.5 + +/datum/status_effect/clock_warp_sickness + id = "clock_warp_sickness" + alert_type = /atom/movable/screen/alert/status_effect/clock_warp_sickness + +/datum/status_effect/clock_warp_sickness/on_creation(mob/living/new_owner, _duration = 1 SECONDS) + duration = _duration + return ..() + +/datum/status_effect/clock_warp_sickness/on_apply() + . = ..() + owner.add_actionspeed_modifier(/datum/actionspeed_modifier/clock_warp_sickness) + owner.add_movespeed_modifier(/datum/movespeed_modifier/clock_warp_sickness) + owner.adjust_confusion(duration) + owner.adjust_dizzy(duration) + owner.add_client_colour(/datum/client_colour/clock_warp, CLIENT_COLOR_SOURCE_RATVAR) + +/datum/status_effect/clock_warp_sickness/on_remove() + . = ..() + owner.remove_actionspeed_modifier(/datum/actionspeed_modifier/clock_warp_sickness) + owner.remove_movespeed_modifier(/datum/movespeed_modifier/clock_warp_sickness) + owner.remove_client_colour(CLIENT_COLOR_SOURCE_RATVAR) + +/atom/movable/screen/alert/status_effect/clock_warp_sickness + name = "Warp Sickness" + desc = "You are disoriented from recently teleporting." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/mob/actions_clock.dmi' + icon_state = "warp_down" + alerttooltipstyle = "clockwork" + +/datum/movespeed_modifier/clock_warp_sickness + multiplicative_slowdown = 1 + +/datum/actionspeed_modifier/clock_warp_sickness + multiplicative_slowdown = 0.6 + +/datum/client_colour/clock_warp + color = LIGHT_COLOR_CLOCKWORK + priority = 2 + fade_out = 5 + +/datum/status_effect/speech/slurring/clock + id = "clock_slurring" + common_prob = 50 + uncommon_prob = 25 + replacement_prob = 33 + doubletext_prob = 0 + text_modification_file = "slurring_clock_text.json" + +#undef CLIENT_COLOR_SOURCE_RATVAR diff --git a/tff_modular/modules/antagonists/clock_cult/structure_info_element.dm b/tff_modular/modules/antagonists/clock_cult/structure_info_element.dm new file mode 100644 index 00000000000..4752619c625 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structure_info_element.dm @@ -0,0 +1,64 @@ +/datum/element/clockwork_structure_info + element_flags = ELEMENT_DETACH_ON_HOST_DESTROY + + +/datum/element/clockwork_structure_info/Attach(datum/target, list/slots_to_count) + . = ..() + + if(!istype(target, /obj/structure/destructible/clockwork/gear_base)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ATOM_AFTER_ATTACKEDBY, PROC_REF(print_info)) + RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + + +/datum/element/clockwork_structure_info/Detach(datum/target) + . = ..() + UnregisterSignal(target, COMSIG_ATOM_AFTER_ATTACKEDBY) + +/** + * + * This proc is called when the user hits the target with an item, which checks for it being a clockwork slab. + * + * Arguments: + * * source - The structure that was attacked + * * weapon - The item that attacked the structure + * * user - The one who attacked the structure + */ +/datum/element/clockwork_structure_info/proc/print_info(obj/structure/destructible/clockwork/gear_base/source, obj/item/weapon, mob/user, proximity_flag, click_parameters) + SIGNAL_HANDLER + + if(!IS_CLOCK(user) || !istype(weapon, /obj/item/clockwork/clockwork_slab)) + return + + var/assembled_string = "" + + assembled_string += "[source]
" + assembled_string += "This structure is currently [source.anchored ? "anchored" : "unanchored"].
" + + if(istype(source, /obj/structure/destructible/clockwork/gear_base/powered)) + var/obj/structure/destructible/clockwork/gear_base/powered/powered_source = source + assembled_string += "This structure is currently toggled [powered_source.enabled ? "on" : "off"],\ + and is [powered_source.is_powered ? "running" : "not running"].
" + assembled_string += "This structure consumes [powered_source.passive_consumption] W of passive capacity while enabled.
" + assembled_string += "This structure is connected to [LAZYLEN(powered_source.transmission_sigils)]\ + transmission sigil[LAZYLEN(powered_source.transmission_sigils) == 1 ? "" : "s"]." + + to_chat(user, span_brass(assembled_string)) + +/** + * + * This proc is called when a mob examines the structure + * + * Arguments: + * * source - The structure that was examined + * * user - The one who attacked the structure + * * examine_text - The list of text to send the examiner + */ +/datum/element/clockwork_structure_info/proc/on_examine(obj/structure/destructible/clockwork/gear_base/source, mob/examiner, list/examine_text) + SIGNAL_HANDLER + + if(!IS_CLOCK(examiner)) + return + + examine_text += span_brass("You can gain more information by using a Clockwork Slab.") diff --git a/tff_modular/modules/antagonists/clock_cult/structures/_powered.dm b/tff_modular/modules/antagonists/clock_cult/structures/_powered.dm new file mode 100644 index 00000000000..cb61e8f2922 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/_powered.dm @@ -0,0 +1,170 @@ +/obj/structure/destructible/clockwork/gear_base/powered + /// If the structure has its "on" switch flipped. Does not mean it's on, necessarily (needs power and anchoring, too) + var/enabled = FALSE + /// How much power this structure uses passively + var/passive_consumption = 0 + /// Do we actually have power when turned on + var/is_powered = FALSE + /// Lazylist of nearby transmission signals + var/list/transmission_sigils + /// Has an "_inactive" icon state + var/has_off_icon = TRUE + /// Has an "_active" icon state + var/has_on_icon = TRUE + /// Has the ability to toggle power by using an empty hand on it + var/has_power_toggle = TRUE + +/obj/structure/destructible/clockwork/gear_base/powered/Initialize(mapload) + . = ..() + update_icon_state() + LAZYINITLIST(transmission_sigils) + for(var/obj/structure/destructible/clockwork/sigil/transmission/trans_sigil in range(src, SIGIL_TRANSMISSION_RANGE)) + link_to_sigil(trans_sigil) + AddComponent(/datum/component/clockwork_trap/powered_structure) + +/obj/structure/destructible/clockwork/gear_base/powered/Destroy() + for(var/obj/structure/destructible/clockwork/sigil/transmission/trans_sigil as anything in transmission_sigils) + trans_sigil.linked_structures -= src + return ..() + +/obj/structure/destructible/clockwork/gear_base/powered/attack_hand(mob/user) + if(!IS_CLOCK(user)) + return ..() + + try_toggle_power(user) + +/obj/structure/destructible/clockwork/gear_base/powered/wrench_act(mob/living/user, obj/item/tool) + . = ..() + if(!.) + return + + if(anchored) + return + + enabled = FALSE + turn_off() + update_icon_state() + visible_message("[src] powers down as it becomes unanchored from the ground.") + +/obj/structure/destructible/clockwork/gear_base/powered/update_icon_state() + . = ..() + icon_state = base_icon_state || initial(icon_state) + + if(!anchored) + icon_state = base_icon_state + unwrenched_suffix + return + + if(has_off_icon && (!is_powered || !enabled)) + icon_state = base_icon_state + "_inactive" + return + + if(has_on_icon && is_powered) + icon_state = base_icon_state + "_active" + +/obj/structure/destructible/clockwork/gear_base/powered/process(seconds_per_tick) + var/last_powered_state = is_powered + is_powered = length(transmission_sigils) > 0 + if(last_powered_state != is_powered) + if(is_powered) + repowered() + else + depowered() + return FALSE + return TRUE + +/obj/structure/destructible/clockwork/gear_base/powered/proc/try_toggle_power(mob/user) + if(!has_power_toggle) + return + + if(!anchored) + if(user) + balloon_alert(user, "not fastened!") + return + + if(!enabled) + if(!length(transmission_sigils) || !SSthe_ark.adjust_passive_power(passive_consumption, TRUE)) //the actual adjustment is done in repowered() + if(user) + balloon_alert(user, "not enough power!") + return + enabled = TRUE //cant just do an inversion due to icon updates + turn_on() + else + enabled = FALSE + turn_off() + + if(user) + balloon_alert(user, "turned [enabled ? "on" : "off"]") + +/// Turn on the structure, letting it consume power and process again +/obj/structure/destructible/clockwork/gear_base/powered/proc/turn_on() + repowered() + START_PROCESSING(SSthe_ark, src) + +/// Turn off the structure, ceasing its processing +/obj/structure/destructible/clockwork/gear_base/powered/proc/turn_off() + depowered() + STOP_PROCESSING(SSthe_ark, src) + +/// Checks if there's a sigil to power it, calls repower() if changed from depowered to powered, vice versa +/obj/structure/destructible/clockwork/gear_base/powered/proc/check_transmission_sigils() + if(!enabled) + return FALSE + + if(!is_powered) + if(length(transmission_sigils)) + repowered() + return TRUE + return FALSE + else if(!length(transmission_sigils)) + depowered() + return FALSE + return TRUE + +/obj/structure/destructible/clockwork/gear_base/powered/proc/check_powered() + is_powered = length(transmission_sigils) > 0 + return is_powered + +/// Uses power if there's enough to do so +/obj/structure/destructible/clockwork/gear_base/powered/proc/use_energy(amount) + return (has_power_toggle ? check_transmission_sigils() : check_powered()) && SSthe_ark.adjust_clock_power(-amount) + +/// Triggers when the structure runs out of power to use +/obj/structure/destructible/clockwork/gear_base/powered/proc/depowered() + SHOULD_CALL_PARENT(TRUE) + is_powered = FALSE + SSthe_ark.adjust_passive_power(-passive_consumption) + update_icon_state() + +/// Triggers when the structure regains power to use +/obj/structure/destructible/clockwork/gear_base/powered/proc/repowered() + SHOULD_CALL_PARENT(TRUE) + is_powered = length(transmission_sigils) > 0 + SSthe_ark.adjust_passive_power(passive_consumption) + update_icon_state() + +/// Adds a sigil to the linked structure list +/obj/structure/destructible/clockwork/gear_base/powered/proc/link_to_sigil(obj/structure/destructible/clockwork/sigil/transmission/sigil) + LAZYOR(transmission_sigils, sigil) + sigil.linked_structures |= src + +/// Removes a sigil from the linked structure list +/obj/structure/destructible/clockwork/gear_base/powered/proc/unlink_to_sigil(obj/structure/destructible/clockwork/sigil/transmission/sigil) + if(!LAZYFIND(transmission_sigils, sigil)) + return + + LAZYREMOVE(transmission_sigils, sigil) + sigil.linked_structures -= src + check_transmission_sigils() + +/datum/component/clockwork_trap/powered_structure + takes_input = TRUE + +/datum/component/clockwork_trap/powered_structure/trigger() + if(!..()) + return + + var/obj/structure/destructible/clockwork/gear_base/powered/structure = parent + if(!istype(structure)) + return + + structure.try_toggle_power() diff --git a/tff_modular/modules/antagonists/clock_cult/structures/_structure.dm b/tff_modular/modules/antagonists/clock_cult/structures/_structure.dm new file mode 100644 index 00000000000..c4fbe600a25 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/_structure.dm @@ -0,0 +1,52 @@ +/// The base clockwork structure. Can have an alternate desc and will show up in the list of clockwork objects. +/obj/structure/destructible/clockwork + name = "meme structure" + desc = "Some frog or something, the fuck?" + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + icon_state = "rare_pepe" + anchored = TRUE + density = TRUE + resistance_flags = FIRE_PROOF | ACID_PROOF + break_message = span_warning("Sparks fly as the brass structure shatters across the ground.") //The message shown when a structure breaks + break_sound = 'sound/effects/magic/clockwork/anima_fragment_death.ogg' //The sound played when a structure breaks + debris = list( + /obj/structure/fluff/clockwork/alloy_shards/large = 1, + /obj/structure/fluff/clockwork/alloy_shards/medium = 2, + /obj/structure/fluff/clockwork/alloy_shards/small = 3, + ) + ///if we ignore attacks from servants of ratvar instead of taking damage + var/immune_to_servant_attacks = FALSE + ///Shown to servants when they examine + var/clockwork_desc = "" + ///Shown to servants when they examine and are on reebe + var/reebe_desc = "" + ///can this structure be rotated with a crowbar + var/can_rotate = TRUE + ///if set, then the maximum amount of damage this structure can take from take_damage() + var/damage_cap + ///a basic cooldown declare for anything that will use it + COOLDOWN_DECLARE(use_cooldown) + +/obj/structure/destructible/clockwork/Initialize(mapload) + . = ..() + if(clockwork_desc || reebe_desc) + AddElement(/datum/element/clockwork_description, clockwork_desc, reebe_desc) + +/obj/structure/destructible/clockwork/Destroy() + return ..() + +/obj/structure/destructible/clockwork/attacked_by(obj/item/I, mob/living/user) + if(immune_to_servant_attacks && (IS_CLOCK(user))) + return + return ..() + +/obj/structure/destructible/clockwork/crowbar_act(mob/living/user, obj/item/tool) + if(IS_CLOCK(user) && can_rotate) + setDir(turn(dir, 90)) + balloon_alert(user, "rotated [dir2text(dir)]") + return TRUE + +/obj/structure/destructible/clockwork/run_atom_armor(damage_amount, damage_type, damage_flag, attack_dir, armour_penetration, armour_ignorance) + if(damage_cap) + return min(damage_cap, ..()) + return ..() diff --git a/tff_modular/modules/antagonists/clock_cult/structures/anchor_crystal.dm b/tff_modular/modules/antagonists/clock_cult/structures/anchor_crystal.dm new file mode 100644 index 00000000000..975d6f6a9d5 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/anchor_crystal.dm @@ -0,0 +1,192 @@ +#define CRYSTAL_SHIELD_DELAY 50 SECONDS //how long until shields start to recharge +#define CRYSTAL_CHARGING 0 //crystal is currently charging +#define CRYSTAL_LOCATION_ANNOUNCED 1 //the location of the crystal has been anouced to the crew +#define FULLY_CHARGED 2 //the crystal is fully charged +#define SHIELD_ACTIVE "active" //the shield is currently active +#define SHIELD_DEFLECT "deflect" //the shield is currently in its deflecting animation +#define SHIELD_BREAK "break" //the shield is currently in its breaking animation +#define SHIELD_BROKEN "broken" //the shield is currently broken +#define EXTRA_MARKED_AREAS 4 //how many extra adjacent areas do we mark +/obj/structure/destructible/clockwork/anchoring_crystal + name = "Anchoring Crystal" + desc = "A strange crystal that you cant quite seem to focus on." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + icon_state = "obelisk" + break_message = span_warning("As the Anchoring Crystal shatters you swear you hear a faint scream.") + break_sound = 'tff_modular/modules/antagonists/clock_cult/sound/ark_damage.ogg' + immune_to_servant_attacks = TRUE + clockwork_desc = "This will help anchor reebe to this realm, allowing for greater power." + can_rotate = FALSE + resistance_flags = FIRE_PROOF | ACID_PROOF | LAVA_PROOF + armor_type = /datum/armor/anchoring_crystal + max_integrity = 250 //pretty hard to break + ///how many hits this can take before taking structure damage, not using the component as its only for items/mobs + var/shields = 3 + ///what charge state is this crystal + var/charge_state = CRYSTAL_CHARGING + ///what area is this in + var/area/crystal_area + ///timer var for charging + var/charging_for = 0 + ///due to the way overlays are handled we have to handle everything for them within a single SIGNAL_HANDLER proc, this var is used for keeping track of what to set our overlay state to next + var/overlay_state = SHIELD_ACTIVE + ///the list of our charge effect datums + var/static/list/charge_datums + ///the charge effect datum we are currently using + var/datum/anchoring_crystal_charge_effect/charge_effect_datum + ///cooldown for when we were last hit + COOLDOWN_DECLARE(recently_hit_cd) + +/datum/armor/anchoring_crystal + bio = 100 + bomb = 100 //we dont want bombing to be good + energy = 100 + fire = 100 + acid = 100 + melee = -15 //weak to melee, subject to change + laser = 60 //resistant to lasers + bullet = 30 + +/obj/structure/destructible/clockwork/anchoring_crystal/Initialize(mapload) + . = ..() + if(!SSthe_ark.initialized) + SSthe_ark.Initialize() + + crystal_area = get_area(src) + SSthe_ark.anchoring_crystals[src] = 0 + + SEND_SIGNAL(SSthe_ark, COMSIG_ANCHORING_CRYSTAL_CREATED, src) + var/conversion_timer = SSthe_ark.convert_area_turfs(crystal_area) + var/list/adjacent_areas = get_area_edge_turfs(crystal_area, TRUE)[src.z] + var/extra_marks = 0 + while(length(adjacent_areas) && extra_marks < EXTRA_MARKED_AREAS) + var/area/marked_area = pick_n_take(adjacent_areas) + if(marked_area.outdoors || SSthe_ark.marked_areas[marked_area]) + continue + + extra_marks++ + SSthe_ark.marked_areas[marked_area] = TRUE + SSthe_ark.convert_area_turfs(marked_area, 50, conversion_timer) + + priority_announce("Reality warping object aboard the station, emergency shuttle uplink connection lost.", "Higher Dimensional Affairs", ANNOUNCER_SPANOMALIES, has_important_message = TRUE) + send_clock_message(null, span_bigbrass(span_bold("An Anchoring Crystal has been created at [crystal_area], defend it!"))) + START_PROCESSING(SSthe_ark, src) + RegisterSignal(src, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays)) + update_icon() + + SSthe_ark.marked_areas[crystal_area] = TRUE + SSthe_ark.block_shuttle(src) + if(SSthe_ark.valid_crystal_areas) + SSthe_ark.valid_crystal_areas -= crystal_area + +/obj/structure/destructible/clockwork/anchoring_crystal/Destroy() + SSthe_ark.clear_shuttle_interference(src) + return ..() + +/obj/structure/destructible/clockwork/anchoring_crystal/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration) + COOLDOWN_START(src, recently_hit_cd, CRYSTAL_SHIELD_DELAY) + if(shields >= 1) + shields-- + src.visible_message("The attack is deflected by the shield of [src].") + if(shields > 0) + overlay_state = SHIELD_DEFLECT + else + overlay_state = SHIELD_BREAK + do_sparks(2, TRUE, src) + update_icon() + damage_amount = 0 //dont take damage if we have shields + return ..() + +/obj/structure/destructible/clockwork/anchoring_crystal/process(seconds_per_tick) + for(var/mob/living/affected_mob in crystal_area) + if(IS_CLOCK(affected_mob)) + affected_mob.adjust_tox_loss(-2.5 * seconds_per_tick, TRUE, TRUE) //slightly better tox healing as well as better stam healing around it for servants + affected_mob.adjust_stamina_loss(-3.75 * seconds_per_tick, TRUE) + continue + affected_mob.adjust_silence_up_to(5 SECONDS * seconds_per_tick, 30 SECONDS) + + if(charge_state == FULLY_CHARGED) //if fully charged then add the power and return + SSthe_ark.adjust_clock_power(5 * seconds_per_tick, TRUE) + return + + charging_for = min(charging_for + (seconds_per_tick * 10), ANCHORING_CRYSTAL_CHARGE_DURATION) + + if(shields < initial(shields) && COOLDOWN_FINISHED(src, recently_hit_cd)) + playsound(src, 'sound/effects/magic/charge.ogg', 50, TRUE) + shields++ + overlay_state = SHIELD_ACTIVE + update_icon() + + if(charging_for >= ANCHORING_CRYSTAL_CHARGE_DURATION) + finish_charging() + return + + if(charge_state < CRYSTAL_LOCATION_ANNOUNCED && charging_for >= 30 SECONDS) //announce after thirty seconds + charge_state = CRYSTAL_LOCATION_ANNOUNCED + priority_announce("Reality warping object located in [crystal_area].", "Central Command Higher Dimensional Affairs", ANNOUNCER_SPANOMALIES, has_important_message = TRUE) + +/obj/structure/destructible/clockwork/anchoring_crystal/Destroy() + send_clock_message(null, span_bigbrass(span_bold("The Anchoring Crystal at [crystal_area] has been destroyed!"))) + SSthe_ark.anchoring_crystals -= src + STOP_PROCESSING(SSthe_ark, src) + UnregisterSignal(src, COMSIG_ATOM_UPDATE_OVERLAYS) + return ..() + +/obj/structure/destructible/clockwork/anchoring_crystal/examine(mob/user) //needs to be here as it has updating information + . = ..() + if(IS_CLOCK(user) || isobserver(user)) + . += span_brass(\ + "[charge_state == FULLY_CHARGED ? "It is fully charged and is indestructable." : "It will be fully charged in [DisplayTimeText(ANCHORING_CRYSTAL_CHARGE_DURATION-charging_for)]."]") + +//do all the stuff for finishing charging +/obj/structure/destructible/clockwork/anchoring_crystal/proc/finish_charging() + send_clock_message(null, span_bigbrass(span_bold("The Anchoring Crystal at [crystal_area] has fully charged! [anchoring_crystal_charge_message(TRUE)]"))) + charge_state = FULLY_CHARGED + resistance_flags |= INDESTRUCTIBLE + atom_integrity = INFINITY + set_armor(/datum/armor/immune) + desc += "Reality around it shimmers, making it effectively impervious to damage." + priority_announce("Reality in [crystal_area] has been destabilized, all personnel are advised to avoid the area.", \ + "Central Command Higher Dimensional Affairs", ANNOUNCER_SPANOMALIES, has_important_message = TRUE) + SSthe_ark.on_crystal_charged(src) + +//set the shield overlay +/obj/structure/destructible/clockwork/anchoring_crystal/proc/on_update_overlays(atom/crystal, list/overlays) + SIGNAL_HANDLER + + var/mutable_appearance/shield_appearance = mutable_appearance('tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_effects.dmi', \ + overlay_state == SHIELD_BROKEN ? "broken" : "clock_shield", ABOVE_OBJ_LAYER) + if(overlay_state == SHIELD_DEFLECT) + shield_appearance.icon_state = "clock_shield_deflect" + overlay_state = SHIELD_ACTIVE + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon)), 3) + else if(overlay_state == SHIELD_BREAK) + shield_appearance.icon_state = "clock_shield_break" + overlay_state = SHIELD_BROKEN + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon)), 2) + overlays += shield_appearance + +///return a message based off of what this anchoring crystal did/will do for the cult +/proc/anchoring_crystal_charge_message(completed = FALSE) + var/message = "" + switch(SSthe_ark.charged_anchoring_crystals) + if(0) + message = "[completed ? "We can now" : "We will be able to"] support 2 more servants and gain faster build speed with replica fabricators on reebe." + if(ANCHORING_CRYSTALS_TO_SUMMON - 1) + message = "[completed ? "We can now" : "We will be able to"] open the ark." + if(ANCHORING_CRYSTALS_TO_SUMMON) + message = "The Steam Helios, a strong 2 pilot mech, [completed ? "has been" : "will be"] summoned to reebe." + if(ANCHORING_CRYSTALS_TO_SUMMON + 1) + message = "Humaniod servants [completed ? "may now" : "will be able to"] ascend their form to that of a clockwork golem, giving them innate armor, environmental immunity, \ + and faster invoking for most scriptures." + return message + +#undef CRYSTAL_SHIELD_DELAY +#undef CRYSTAL_CHARGING +#undef CRYSTAL_LOCATION_ANNOUNCED +#undef FULLY_CHARGED +#undef SHIELD_ACTIVE +#undef SHIELD_DEFLECT +#undef SHIELD_BREAK +#undef SHIELD_BROKEN +#undef EXTRA_MARKED_AREAS diff --git a/tff_modular/modules/antagonists/clock_cult/structures/brass_window.dm b/tff_modular/modules/antagonists/clock_cult/structures/brass_window.dm new file mode 100644 index 00000000000..3db6cff3dd9 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/brass_window.dm @@ -0,0 +1,75 @@ +/obj/structure/window/reinforced/clockwork + name = "brass window" + desc = "A paper-thin pane of translucent yet reinforced brass." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/window.dmi' + icon_state = "clockwork_window_single" + resistance_flags = FIRE_PROOF | ACID_PROOF + max_integrity = 80 + explosion_block = 2 + decon_speed = 4 SECONDS + glass_type = /obj/item/stack/sheet/bronze + glass_amount = 1 + +/obj/structure/window/reinforced/clockwork/Initialize(mapload, direct) + if(on_reebe(src)) + decon_speed = 1 SECONDS + max_integrity = round(max_integrity * 0.5) //I would like to make it take double damage instead but this about works for now + . = ..() + +/obj/structure/window/reinforced/clockwork/attackby_secondary(obj/item/tool, mob/user, params) + if(state == RWINDOW_SECURE) + if(tool.tool_behaviour == TOOL_WIRECUTTER && IS_CLOCK(user)) + user.visible_message( + span_notice("[user] starts cutting the pane of \the [src] away..."), + span_notice("You start cutting away the pane of \the [src].")) + if(tool.use_tool(src, user, 2 SECONDS, volume = 50)) + state = RWINDOW_BARS_CUT + to_chat(user, span_notice("The window pane falls out of the way exposing the frame bolts.")) + return ..() + +/obj/structure/window/reinforced/clockwork/examine(mob/user) + . = ..() + if(IS_CLOCK(user)) + if(state == RWINDOW_SECURE) + . += span_brass("You see a way to cut the window pane away.") + +/obj/structure/window/reinforced/clockwork/narsie_act() + take_damage(rand(25, 75), BRUTE) + if(!QDELETED(src)) + var/previouscolor = color + color = "#960000" + animate(src, color = previouscolor, time = 8) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_atom_colour)), 8) + +/obj/structure/window/reinforced/clockwork/ratvar_act() + return FALSE + +/obj/structure/window/reinforced/clockwork/rcd_act(mob/user, obj/item/construction/rcd/the_rcd) + return + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/clockwork/spawner, 0) + +/obj/structure/window/reinforced/clockwork/unanchored + anchored = FALSE + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/clockwork/unanchored/spawner, 0) + +/obj/structure/window/reinforced/clockwork/fulltile + icon_state = "clockwork_window-0" + base_icon_state = "clockwork_window" + icon = 'icons/obj/smooth_structures/clockwork_window.dmi' + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE_BRONZE + SMOOTH_GROUP_WINDOW_FULLTILE + canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_BRONZE + fulltile = TRUE + flags_1 = PREVENT_CLICK_UNDER_1 + obj_flags = CAN_BE_HIT + max_integrity = 100 + glass_amount = 2 + +/obj/structure/window/reinforced/clockwork/Initialize(mapload, direct) + new /obj/effect/temp_visual/ratvar/window(get_turf(src)) + return ..() + +/obj/structure/window/reinforced/clockwork/fulltile/unanchored + anchored = FALSE diff --git a/tff_modular/modules/antagonists/clock_cult/structures/eminence_beacon.dm b/tff_modular/modules/antagonists/clock_cult/structures/eminence_beacon.dm new file mode 100644 index 00000000000..a608f3aaa6b --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/eminence_beacon.dm @@ -0,0 +1,81 @@ +/obj/structure/destructible/clockwork/eminence_beacon + name = "Eminence Spire" + desc = "An ancient, brass spire which holds the spirit of a powerful entity conceived by Ratvar to oversee his faithful servants." + icon_state = "tinkerers_daemon" + resistance_flags = INDESTRUCTIBLE + ///are we currently holding a vote for an eminence + var/vote_active = FALSE + ///weakref to our vote timer + var/datum/weakref/vote_timer + ///are we currently polling for an eminence + var/polling = FALSE + +/obj/structure/destructible/clockwork/eminence_beacon/attack_hand(mob/user) + . = ..() + if(!IS_CLOCK(user)) + return + if(vote_active) + if(vote_timer) + deltimer(vote_timer?.resolve()) + vote_timer = null + vote_active = FALSE + send_clock_message(null, "[user] has cancelled the Eminence vote.") + return + if(GLOB.current_eminence) + to_chat(user, span_brass("The Eminence has already been released.")) + return + + var/option = tgui_alert(user, "Becoming the Eminence is not an easy task, be sure you will be able to lead the servants. \ + If you choose to do so, your old form with be destroyed.", "Who shall control the Eminence?", list("Yourself", "A ghost", "Cancel")) + if(vote_active) + balloon_alert(user, "a vote has already been called, if you would like to object you can interact with the spire again.") + return + + if(option == "Yourself") + send_clock_message(null, span_bigbrass("[user] has elected themselves to become the Eminence. Interact with \the [src] to object.")) + vote_timer = WEAKREF(addtimer(CALLBACK(src, PROC_REF(vote_succeed), user), 60 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE)) + else if(option == "A ghost") + send_clock_message(null, span_bigbrass("[user] has elected for a ghost to become the Eminence. Interact with \the [src] to object.")) + vote_timer = WEAKREF(addtimer(CALLBACK(src, PROC_REF(vote_succeed)), 60 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE)) + else + return + vote_active = TRUE + +/obj/structure/destructible/clockwork/eminence_beacon/proc/vote_succeed(mob/living/eminence) //if we select a ghost then we dont call any living procs so this is fine + vote_active = FALSE + if(polling) + return + + if(GLOB.current_eminence) + message_admins("[type] calling vote_succeed() with a set GLOB.current_eminence, this should not be happening.") + return + polling = TRUE + if(!eminence) + var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates( + "Do you want to play as the eminence", + check_jobban = ROLE_MIDROUND_CLOCK_CULTIST, + role = ROLE_SENTIENCE, + poll_time = 10 SECONDS, + alert_pic = /mob/living/eminence, + role_name_text = "eminence" + ) + if(length(candidates)) + eminence = pick(candidates) + + polling = FALSE + if(!(eminence?.client)) + send_clock_message(null, "The Eminence remains in slumber, for now, try waking it again soon.") + return + + var/mob/living/eminence/new_mob = new /mob/living/eminence(get_turf(src)) + if(isobserver(eminence)) + new_mob.PossessByPlayer(eminence.key) + else + var/datum/antagonist/clock_cultist/servant_datum = eminence.mind.has_antag_datum(/datum/antagonist/clock_cultist) + if(servant_datum) + servant_datum.silent = TRUE + servant_datum.on_removal() + eminence.mind.transfer_to(new_mob, TRUE) + eminence.dust(TRUE, TRUE) + new_mob.mind.add_antag_datum(/datum/antagonist/clock_cultist/eminence) + send_clock_message(null, span_bigbrass("The Eminence has risen!")) diff --git a/tff_modular/modules/antagonists/clock_cult/structures/gear_base.dm b/tff_modular/modules/antagonists/clock_cult/structures/gear_base.dm new file mode 100644 index 00000000000..6ab82016744 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/gear_base.dm @@ -0,0 +1,45 @@ +/obj/structure/destructible/clockwork/gear_base + name = "gear base" + desc = "A large cog lying on the floor at feet level." + icon_state = "gear_base" + clockwork_desc = "A large cog lying on the floor at feet level." + anchored = FALSE + break_message = span_warning("Oh, that broke.") + /// What's appeneded to the structure when unanchored + var/unwrenched_suffix = "_unwrenched" + /// If this can be moved at all by unwrenching it + var/can_unwrench = TRUE + + +/obj/structure/destructible/clockwork/gear_base/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_structure_info) + + +/obj/structure/destructible/clockwork/gear_base/wrench_act(mob/living/user, obj/item/tool) + if(!IS_CLOCK(user)) + return + + if(!can_unwrench) + balloon_alert(user, "cannot be unwrenched!") + return + + balloon_alert(user, "[anchored ? "unwrenching" : "wrenching"]...") + + if(!tool.use_tool(src, user, 2 SECONDS, volume = 50)) + return + + visible_message(span_notice("[user] [anchored ? "unwrenches" : "wrenches down"] [src]."), span_notice("You [anchored ? "unwrench" : "wrench"] [src].")) + + anchored = !anchored + update_icon_state() + + return TRUE + + +/obj/structure/destructible/clockwork/gear_base/update_icon_state() + . = ..() + icon_state = initial(icon_state) + + if(!anchored) + icon_state += unwrenched_suffix diff --git a/tff_modular/modules/antagonists/clock_cult/structures/interdiction_lens.dm b/tff_modular/modules/antagonists/clock_cult/structures/interdiction_lens.dm new file mode 100644 index 00000000000..ec142e237fc --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/interdiction_lens.dm @@ -0,0 +1,93 @@ +#define INTERDICTION_LENS_RANGE 4 +#define POWER_PER_PERSON 3 + +/obj/structure/destructible/clockwork/gear_base/powered/interdiction_lens + name = "interdiction lens" + desc = "A mesmerizing light that flashes to a rhythm that you just can't stop tapping to." + clockwork_desc = "A small device which will slow down nearby attackers and projectiles at a large power cost, both active and passive." + icon_state = "interdiction_lens" + base_icon_state = "interdiction_lens" + anchored = TRUE + break_message = span_warning("The interdiction lens breaks into multiple fragments, which gently float to the ground.") + max_integrity = 150 + passive_consumption = 5 + /// Dampening field around the interdiction + var/datum/proximity_monitor/advanced/bubble/projectile_dampener/clockcult/dampening_field + +/obj/structure/destructible/clockwork/gear_base/powered/interdiction_lens/Initialize(mapload) + . = ..() + dampening_field = new(src, INTERDICTION_LENS_RANGE, TRUE, src) + +/obj/structure/destructible/clockwork/gear_base/powered/interdiction_lens/Destroy() + if(enabled) + STOP_PROCESSING(SSobj, src) + QDEL_NULL(dampening_field) + return ..() + +/obj/structure/destructible/clockwork/gear_base/powered/interdiction_lens/process(delta_time) + . = ..() + if(!.) + return + + for(var/mob/living/living_mob in viewers(INTERDICTION_LENS_RANGE, src)) + if(!IS_CLOCK(living_mob) && use_energy(POWER_PER_PERSON)) + living_mob.apply_status_effect(/datum/status_effect/interdiction) + + for(var/obj/vehicle/sealed/mecha/mech in range(INTERDICTION_LENS_RANGE, src)) + var/clock_pilot = FALSE + for(var/mob/living/pilot in mech.occupants) + if(!IS_CLOCK(pilot)) + continue + + clock_pilot = TRUE + break + + if(clock_pilot || !use_energy(POWER_PER_PERSON)) + continue + + mech.emp_act(EMP_LIGHT) + do_sparks(mech, TRUE, mech) + +/obj/structure/destructible/clockwork/gear_base/powered/interdiction_lens/repowered() + . = ..() + flick("interdiction_lens_recharged", src) + + if(istype(dampening_field)) + QDEL_NULL(dampening_field) + + dampening_field = new(src, INTERDICTION_LENS_RANGE, TRUE, src) + +/obj/structure/destructible/clockwork/gear_base/powered/interdiction_lens/depowered() + . = ..() + flick("interdiction_lens_discharged", src) + QDEL_NULL(dampening_field) + +/obj/structure/destructible/clockwork/gear_base/powered/interdiction_lens/free + +/obj/structure/destructible/clockwork/gear_base/powered/interdiction_lens/free/use_energy(amount) + return TRUE + +//Dampening field + +/datum/proximity_monitor/advanced/bubble/projectile_dampener/clockcult/setup_effect_directions() + return NONE // Should be invisible + +/datum/proximity_monitor/advanced/bubble/projectile_dampener/clockcult/draw_effect() + return NONE // Should be invisible + +/datum/proximity_monitor/advanced/bubble/projectile_dampener/clockcult/catch_bullet_effect(obj/projectile/bullet) + if(isliving(bullet.firer)) + var/mob/living/living_firer = bullet.firer + if(IS_CLOCK(living_firer)) + return + return ..() + +/datum/proximity_monitor/advanced/bubble/projectile_dampener/clockcult/release_bullet_effect(obj/projectile/bullet) + if(isliving(bullet.firer)) + var/mob/living/living_firer = bullet.firer + if(IS_CLOCK(living_firer)) + return + return ..() + +#undef INTERDICTION_LENS_RANGE +#undef POWER_PER_PERSON diff --git a/tff_modular/modules/antagonists/clock_cult/structures/lattice.dm b/tff_modular/modules/antagonists/clock_cult/structures/lattice.dm new file mode 100644 index 00000000000..b91b92ee324 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/lattice.dm @@ -0,0 +1,58 @@ +/obj/structure/lattice/clockwork + name = "cog lattice" + desc = "A lightweight support lattice. These hold the Justicar's station together." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/lattice_clockwork.dmi' + icon_state = "lattice_clockwork-0" + base_icon_state = "lattice_clockwork" + smoothing_groups = SMOOTH_GROUP_LATTICE + canSmoothWith = SMOOTH_GROUP_LATTICE + +/obj/structure/lattice/clockwork/Initialize(mapload) + . = ..() + ratvar_act() + if(on_reebe(src)) + resistance_flags |= INDESTRUCTIBLE + +/obj/structure/lattice/clockwork/ratvar_act() + if(ISODD(x+y)) //this check looks to be broken + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/lattice_clockwork_large.dmi' + pixel_x = -9 + pixel_y = -9 + else + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/lattice_clockwork.dmi' + pixel_x = 0 + pixel_y = 0 + return TRUE + +/obj/structure/lattice/catwalk/clockwork + name = "clockwork catwalk" + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/catwalk_clockwork.dmi' + icon_state = "catwalk_clockwork-0" + base_icon_state = "catwalk_clockwork" + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_CATWALK + SMOOTH_GROUP_LATTICE + SMOOTH_GROUP_OPEN_FLOOR + canSmoothWith = SMOOTH_GROUP_CATWALK + +/obj/structure/lattice/catwalk/clockwork/Initialize(mapload) + . = ..() + if(!mapload) + new /obj/effect/temp_visual/ratvar/floor/catwalk(loc) + new /obj/effect/temp_visual/ratvar/beam/catwalk(loc) + if(on_reebe(src)) + resistance_flags |= INDESTRUCTIBLE + +/obj/structure/lattice/catwalk/clockwork/Destroy(force) + if(resistance_flags & INDESTRUCTIBLE) + return + return ..() + +/obj/structure/lattice/catwalk/clockwork/ratvar_act() + if(ISODD(x+y)) + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/catwalk_clockwork_large.dmi' + pixel_x = -9 + pixel_y = -9 + else + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/catwalk_clockwork.dmi' + pixel_x = 0 + pixel_y = 0 + return TRUE diff --git a/tff_modular/modules/antagonists/clock_cult/structures/ocular_warden.dm b/tff_modular/modules/antagonists/clock_cult/structures/ocular_warden.dm new file mode 100644 index 00000000000..22b087aa3cb --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/ocular_warden.dm @@ -0,0 +1,78 @@ +#define FIRE_DELAY (2 SECONDS) +#define FIRE_RANGE 6 +#define BASE_DAMAGE 10 +#define MINIMUM_DAMAGE 10 +#define DAMAGE_FALLOFF 1 +#define SHOOT_POWER_USE 5 + +/obj/structure/destructible/clockwork/gear_base/powered/ocular_warden + name = "ocular warden" + desc = "A wide, open eye that stares intently into your soul. It seems resistant to energy based weapons." + clockwork_desc = "A defensive device that will fight any nearby intruders." + break_message = span_warning("A black ooze leaks from the ocular warden as it slowly sinks to the ground.") + icon_state = "ocular_warden" + base_icon_state = "ocular_warden" + max_integrity = 75 + armor_type = /datum/armor/clockwork_ocular_warden + passive_consumption = 3 + can_unwrench = FALSE + anchored = TRUE + /// Cooldown between firing + COOLDOWN_DECLARE(fire_cooldown) + +/datum/armor/clockwork_ocular_warden + melee = -50 + bullet = -10 + laser = 60 + energy = 60 + bomb = 20 + bio = 0 + +/obj/structure/destructible/clockwork/gear_base/powered/ocular_warden/process(delta_time) + . = ..() + if(!.) + return + + if(!COOLDOWN_FINISHED(src, fire_cooldown)) + return + + //Check hostiles in range + var/list/valid_targets = list() + for(var/mob/living/potential_target in hearers(FIRE_RANGE, src)) + + if(IS_CLOCK(potential_target) || potential_target.stat) + continue + + valid_targets += potential_target + + if(!length(valid_targets)) + return + + if(!use_energy(SHOOT_POWER_USE)) + return + + playsound(src, 'tff_modular/modules/antagonists/clock_cult/sound/ocularwarden_target.ogg', 60, TRUE) + + var/mob/living/target = pick(valid_targets) + if(!target) + return + + dir = get_dir(get_turf(src), get_turf(target)) + + // Apply 10 damage (- 1 for each tile away they are), or 5, whichever is larger + target.apply_damage(max(BASE_DAMAGE - (get_dist(src, target) * DAMAGE_FALLOFF), MINIMUM_DAMAGE) * delta_time, BURN) + to_chat(target, span_boldwarning("You feel as though your soul is being burned!")) + + new /obj/effect/temp_visual/ratvar/ocular_warden(get_turf(target)) + new /obj/effect/temp_visual/ratvar/ocular_warden(get_turf(src)) + + playsound(target, 'tff_modular/modules/antagonists/clock_cult/sound/ocularwarden_dot1.ogg', 60, TRUE) + + COOLDOWN_START(src, fire_cooldown, FIRE_DELAY) + +#undef FIRE_DELAY +#undef FIRE_RANGE +#undef BASE_DAMAGE +#undef MINIMUM_DAMAGE +#undef DAMAGE_FALLOFF +#undef SHOOT_POWER_USE diff --git a/tff_modular/modules/antagonists/clock_cult/structures/prosperity_prism.dm b/tff_modular/modules/antagonists/clock_cult/structures/prosperity_prism.dm new file mode 100644 index 00000000000..f035a1f39b5 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/prosperity_prism.dm @@ -0,0 +1,44 @@ +#define POWER_PER_USE 3 +#define HEAL_PER_USE 2 + +/obj/structure/destructible/clockwork/gear_base/powered/prosperity_prism + name = "prosperity prism" + desc = "A prism that seems to somehow always have its gaze locked to you." + clockwork_desc = "A prism that will heal nearby servants of various damage types, along with purging poisons." + icon_state = "prolonging_prism" + base_icon_state = "prolonging_prism" + anchored = TRUE + break_message = span_warning("The prism falls apart, smoke leaking out into the air.") + max_integrity = 150 + passive_consumption = 5 + ///typecache of chem types to purge + var/static/list/chems_to_purge + +/obj/structure/destructible/clockwork/gear_base/powered/prosperity_prism/Initialize(mapload) + . = ..() + if(!chems_to_purge) + chems_to_purge = typecacheof(list(/datum/reagent/toxin, /datum/reagent/water/holywater)) + +/obj/structure/destructible/clockwork/gear_base/powered/prosperity_prism/process(seconds_per_tick) + . = ..() + if(!.) + return + + for(var/mob/living/possible_cultist in range(3, src)) + if(isnull(possible_cultist) || !IS_CLOCK(possible_cultist) || possible_cultist.health >= possible_cultist.maxHealth || !use_energy(-POWER_PER_USE)) + continue + + var/healed_amount = HEAL_PER_USE * seconds_per_tick + possible_cultist.adjust_stamina_loss(-4 * seconds_per_tick, TRUE) + possible_cultist.adjust_oxy_loss(-healed_amount) + possible_cultist.heal_overall_damage(healed_amount, healed_amount, updating_health = TRUE) + new /obj/effect/temp_visual/heal(get_turf(possible_cultist), "#1E8CE1") + if(!possible_cultist.reagents) + continue + + for(var/datum/reagent/negative_chem in possible_cultist.reagents?.reagent_list) + if(is_type_in_typecache(negative_chem, chems_to_purge)) + possible_cultist.reagents?.remove_reagent(negative_chem.type, 2.5 * seconds_per_tick) + +#undef POWER_PER_USE +#undef HEAL_PER_USE diff --git a/tff_modular/modules/antagonists/clock_cult/structures/sigil/_sigil.dm b/tff_modular/modules/antagonists/clock_cult/structures/sigil/_sigil.dm new file mode 100644 index 00000000000..ff5fb371130 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/sigil/_sigil.dm @@ -0,0 +1,148 @@ +#define SIGIL_INVOCATION_ALPHA 120 +#define SIGIL_INVOKED_ALPHA 200 +#define SIGIL_MATRIX_SCALE 1.2 + +//Sigil base +/obj/structure/destructible/clockwork/sigil + name = "sigil" + desc = "It's a sigil that does something." + max_integrity = 10 + break_sound = null + debris = null + break_message = "The sigil is dispelled." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_effects.dmi' + icon_state = "sigilvitality" + density = FALSE + alpha = 90 + /// How long you stand on the sigil before affect is applied + var/effect_stand_time = 0 + /// The atom/movable that this is currently affecting + var/currently_affecting + /// Color while not used + var/idle_color = "#FFFFFF" + /// Color faded to while someone stands on top + var/invocation_color = "#F1A03B" + /// Color pulsed when effect applied + var/pulse_color = "#EBC670" + /// Color pulsed when effect fails + var/fail_color = "#d47433" + /// Ref to the current timer + var/active_timer + /// TRUE if the affect repeatedly applied an affect to the thing above it + var/looping = FALSE + /// FALSE if the rune can affect non-living atoms + var/living_only = TRUE + +/obj/structure/destructible/clockwork/sigil/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + COMSIG_ATOM_EXITED = PROC_REF(on_exited), + ) + AddElement(/datum/element/connect_loc, loc_connections) + color = idle_color + +/obj/structure/destructible/clockwork/sigil/Destroy() + currently_affecting = null + + if(active_timer) + deltimer(active_timer) + active_timer = null + + return ..() + +/obj/structure/destructible/clockwork/sigil/attack_hand(mob/user) + . = ..() + if(dispel_check(user)) + dispel() + +/// For trap sigils and similar; applies effects when someone/something walks over +/obj/structure/destructible/clockwork/sigil/proc/on_entered(datum/source, atom/movable/entered_movable) + SIGNAL_HANDLER + + if((!isliving(entered_movable) && living_only) || currently_affecting || active_timer) + return + + currently_affecting = entered_movable + if(!effect_stand_time) + apply_effects(entered_movable) + return + + do_sparks(5, TRUE, src) + animate(src, color = invocation_color, alpha = SIGIL_INVOCATION_ALPHA, effect_stand_time) + active_timer = addtimer(CALLBACK(src, PROC_REF(apply_effects), entered_movable), effect_stand_time, TIMER_UNIQUE | TIMER_STOPPABLE) + + +/// For when someone/something leaves the sigil's turf +/obj/structure/destructible/clockwork/sigil/proc/on_exited(datum/source, atom/movable/exited_movable) + SIGNAL_HANDLER + + if(currently_affecting != exited_movable) + return + + currently_affecting = null + animate(src, color = idle_color, alpha = initial(alpha), time = 0.5 SECONDS) + + if(active_timer) + deltimer(active_timer) + active_timer = null + + +/// If the sigil does not affect living, do not inherit this +/obj/structure/destructible/clockwork/sigil/proc/can_affect(atom/movable/movable_apply) + + var/mob/living/living_mob = movable_apply + if(!istype(living_mob)) + return FALSE + + if(living_mob.can_block_magic(MAGIC_RESISTANCE_HOLY)) + return FALSE + + return TRUE + + +/// What happens when the sigil fails to invoke +/obj/structure/destructible/clockwork/sigil/proc/fail_invocation() + active_timer = null + currently_affecting = null + color = fail_color + transform = matrix() * SIGIL_MATRIX_SCALE + alpha = 140 + animate(src, transform = matrix(), color = idle_color, alpha = initial(alpha), time = 0.5 SECONDS) + + +/// Apply the effects to an atom/movable +/obj/structure/destructible/clockwork/sigil/proc/apply_effects(atom/movable/movable_apply) + + if(!can_affect(movable_apply)) + fail_invocation() + return FALSE + + color = pulse_color + transform = matrix() * SIGIL_MATRIX_SCALE + alpha = SIGIL_INVOKED_ALPHA + + if(looping) + animate(src, transform = matrix(), color = invocation_color, alpha = SIGIL_INVOCATION_ALPHA, effect_stand_time) + active_timer = addtimer(CALLBACK(src, PROC_REF(apply_effects), movable_apply), effect_stand_time, TIMER_UNIQUE | TIMER_STOPPABLE) + + else + active_timer = null + currently_affecting = null + animate(src, transform = matrix(), color = idle_color, alpha = initial(alpha), time = 0.5 SECONDS) + + return TRUE + + +/// Dispel the sigil and delete itself +/obj/structure/destructible/clockwork/sigil/proc/dispel() + animate(src, transform = matrix() * 1.5, alpha = 0, time = 3) + QDEL_IN(src, 0.3 SECONDS) + +/// Put any addtional checks you want to do before dispelling here +/obj/structure/destructible/clockwork/sigil/proc/dispel_check(mob/user) + return IS_CLOCK(user) || do_after(user, 10 SECONDS, src) + +#undef SIGIL_INVOCATION_ALPHA +#undef SIGIL_INVOKED_ALPHA +#undef SIGIL_MATRIX_SCALE diff --git a/tff_modular/modules/antagonists/clock_cult/structures/sigil/sigil_submission.dm b/tff_modular/modules/antagonists/clock_cult/structures/sigil/sigil_submission.dm new file mode 100644 index 00000000000..9a418c065f0 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/sigil/sigil_submission.dm @@ -0,0 +1,80 @@ +/obj/structure/destructible/clockwork/sigil/submission + name = "sigil of submission" + desc = "A strange sigil, with otherworldy drawings on it." + clockwork_desc = "A sigil pulsating with a glorious light. Anyone held on top of this for 8 seconds will become a loyal servant of Ratvar." + icon_state = "sigilsubmission" + effect_stand_time = 8 SECONDS + idle_color = "#FFFFFF" + invocation_color = "#e042d8" + pulse_color = "#EBC670" + fail_color = "#d43333" + +/obj/structure/destructible/clockwork/sigil/submission/can_affect(mob/living/checked_mob) + . = ..() + if(!.) + return FALSE + + if(IS_CLOCK(checked_mob)) + return FALSE + + if(HAS_TRAIT(checked_mob, TRAIT_MINDSHIELD)) + return FALSE + return is_convertable_to_clock(checked_mob) + +/obj/structure/destructible/clockwork/sigil/submission/apply_effects(mob/living/converted_mob) + . = ..() + if(!.) + converted_mob.visible_message(span_warning("[converted_mob] resists conversion!")) + return FALSE + + if(converted_mob.client) + var/previous_colour = converted_mob.client.color + converted_mob.client.color = LIGHT_COLOR_CLOCKWORK + animate(converted_mob.client, color = previous_colour, time = 1 SECONDS) + + GLOB.main_clock_cult?.check_member_distribution() + if(isdrone(converted_mob) && (length(SSthe_ark.cogscarabs) < MAXIMUM_COGSCARABS)) + var/mob/living/basic/drone/cogscarab/cogger = new /mob/living/basic/drone/cogscarab(get_turf(src)) + cogger.PossessByPlayer(converted_mob.key) + cogger.mind?.add_antag_datum(/datum/antagonist/clock_cultist) + cogger.visible_message("A light envelops \the [converted_mob]! As the light fades you see it has become a cogscarab!", + span_brass("Ratvar has granted you your freedom, you must protect the ark at all costs!")) + converted_mob.death(TRUE) + return TRUE + + else if(((GLOB.main_clock_cult?.human_servants.len < GLOB.main_clock_cult?.max_human_servants) && ishuman(converted_mob)) || !ishuman(converted_mob)) + var/datum/antagonist/clock_cultist/servant_datum = new + servant_datum.give_slab = FALSE + converted_mob.mind.add_antag_datum(servant_datum) + converted_mob.Paralyze(5 SECONDS) + converted_mob.blood_volume = BLOOD_VOLUME_NORMAL + if(ishuman(converted_mob)) + var/mob/living/carbon/human/human_converted = converted_mob + human_converted.uncuff() + new /obj/item/clockwork/clockwork_slab(get_turf(src)) + + var/brutedamage = converted_mob.get_brute_loss() + var/burndamage = converted_mob.get_brute_loss() + if(brutedamage || burndamage) + converted_mob.adjust_brute_loss(-(round(brutedamage * 0.75))) + converted_mob.adjust_fire_loss(-(round(burndamage * 0.75))) + + converted_mob.visible_message(span_warning("[converted_mob] sits completely motionless as \ + [(brutedamage || burndamage) ? "a bright light pours from [converted_mob.p_their()] wounds as they close." \ + : "as the sigil below [converted_mob.p_them()] glows brightly"]!"), + span_bigbrass("You feel a flash of light and the world spin around you!")) + send_clock_message(null, "[converted_mob] has been converted!") + converted_mob.remove_status_effect(/datum/status_effect/speech/slurring/clock) + return TRUE + + visible_message(span_warning("\The [src] falters as though it cannot support more servants.")) + return FALSE + +/obj/structure/destructible/clockwork/sigil/submission/dispel_check(mob/user) + . = ..() + if(!.) + return + if(active_timer) + if(IS_CLOCK(user) && tgui_alert(user, "Are you sure you want to dispel [src]? It is currently converting [currently_affecting].", "Confirm dispel", list("Yes", "No")) != "Yes") + return FALSE + return TRUE diff --git a/tff_modular/modules/antagonists/clock_cult/structures/sigil/sigil_transmission.dm b/tff_modular/modules/antagonists/clock_cult/structures/sigil/sigil_transmission.dm new file mode 100644 index 00000000000..97496cd841d --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/sigil/sigil_transmission.dm @@ -0,0 +1,108 @@ +//these are not actually the amount given and taken but are instead the amount SSthe_ark.clock_power is adjusted by +#define POWER_GIVE 5 +#define POWER_SIPHON 5 + +/obj/structure/destructible/clockwork/sigil/transmission + name = "sigil of transmission" + desc = "A strange sigil, swirling with a yellow light." + clockwork_desc = "A glorious sigil used to power Ratvarian structures and recharge energy-based objects." + icon_state = "sigiltransmission" + effect_stand_time = 1 SECONDS + idle_color = "#f1a746" + invocation_color = "#f5c529" + pulse_color = "#f8df8b" + fail_color = "#f1a746" + looping = TRUE + living_only = FALSE + /// A list of structures linked to this sigil + var/list/linked_structures = list() + +/obj/structure/destructible/clockwork/sigil/transmission/Initialize(mapload) + . = ..() + + for(var/obj/structure/destructible/clockwork/gear_base/powered/gear_base in range(src, SIGIL_TRANSMISSION_RANGE)) + gear_base.link_to_sigil(src) + //START_PROCESSING(SSthe_ark, src) + +/obj/structure/destructible/clockwork/sigil/transmission/Destroy() + //STOP_PROCESSING(SSthe_ark, src) + for(var/obj/structure/destructible/clockwork/gear_base/powered/gear_base as anything in linked_structures) + gear_base.unlink_to_sigil(src) + + return ..() + +/*/obj/structure/destructible/clockwork/sigil/transmission/process() + for(var/obj/structure/destructible/clockwork/gear_base/powered/gear_base as anything in linked_structures) + if(gear_base.transmission_sigils[1] != src) // [1] Ensures we are the master (first) transmission signal + continue + + gear_base.check_transmission_sigils()*/ + +/obj/structure/destructible/clockwork/sigil/transmission/can_affect(atom/movable/atom_movable) + return (ismecha(atom_movable) || iscyborg(atom_movable) || ishuman(atom_movable)) + +/obj/structure/destructible/clockwork/sigil/transmission/apply_effects(atom/movable/apply_to) + if(ismecha(apply_to)) + var/obj/vehicle/sealed/mecha/target_mech = apply_to + var/is_clockie = FALSE + + for(var/mob/living/living_mob in target_mech.occupants) + if(!IS_CLOCK(living_mob)) + continue + + is_clockie = TRUE // If one person is a cultist, we just say "they good" to the mech itself + break + + var/obj/item/stock_parts/power_store/cell/power_cell = target_mech.cell + + if(!power_cell) + return + + if(is_clockie) + if((power_cell.charge < power_cell.maxcharge) && SSthe_ark.clock_power >= POWER_GIVE) + target_mech.give_power(power_cell.chargerate) + SSthe_ark.clock_power -= POWER_GIVE + + else + if(power_cell.charge) + target_mech.use_energy(power_cell.chargerate) + SSthe_ark.clock_power += POWER_SIPHON + + else if(iscyborg(apply_to)) + var/mob/living/silicon/robot/borg = apply_to + var/obj/item/stock_parts/power_store/cell/power_cell = borg.get_cell() + + if(!power_cell) + return + + if(IS_CLOCK(borg)) + if((power_cell.charge < power_cell.maxcharge) && SSthe_ark.clock_power >= POWER_GIVE) + power_cell.give(power_cell.chargerate) + SSthe_ark.clock_power -= POWER_GIVE + + else if(power_cell.charge > power_cell.chargerate) + power_cell.give(-power_cell.chargerate) + SSthe_ark.clock_power += POWER_SIPHON + + else if(ishuman(apply_to)) + var/mob/living/carbon/human/human = apply_to + var/list/human_contents = human.get_contents() + + for(var/obj/item/content_item as anything in human_contents) + var/obj/item/stock_parts/power_store/cell/power_cell = content_item.get_cell() + + if(!power_cell) + continue + + if(IS_CLOCK(human)) + if((power_cell.charge < power_cell.maxcharge) && SSthe_ark.clock_power >= POWER_GIVE) + power_cell.give(power_cell.chargerate) + SSthe_ark.clock_power -= POWER_GIVE + + else + if(power_cell.charge > power_cell.chargerate) + power_cell.give(-power_cell.chargerate) + SSthe_ark.clock_power += POWER_SIPHON + +#undef POWER_GIVE +#undef POWER_SIPHON diff --git a/tff_modular/modules/antagonists/clock_cult/structures/sigil/sigil_vitality.dm b/tff_modular/modules/antagonists/clock_cult/structures/sigil/sigil_vitality.dm new file mode 100644 index 00000000000..599328088d3 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/sigil/sigil_vitality.dm @@ -0,0 +1,118 @@ +///how much damage do we heal when reviving someone before costing vitality +#define FREE_DAMAGE_HEALED 20 +///how much do we reduce drained mobs health by each siphon +#define HEALTH_DRAINED 15 +/obj/structure/destructible/clockwork/sigil/vitality + name = "vitality matrix" + desc = "A twisting, confusing artifact that drains the unenlightended on contact." + clockwork_desc = "A beautiful artifact that will drain the life of heretics placed on top of it." + icon_state = "sigilvitality" + effect_stand_time = 2.5 SECONDS // You can't permastun someone with this, so you'll need to keep them grabbed + cuffed + idle_color = "#5e87c4" + invocation_color = "#83cbe7" + pulse_color = "#c761d4" + fail_color = "#525a80" + looping = TRUE + +/obj/structure/destructible/clockwork/sigil/vitality/can_affect(mob/living/affected_mob) + if((HAS_TRAIT(affected_mob, TRAIT_HUSK) && !IS_CLOCK(affected_mob)) || HAS_TRAIT(affected_mob, TRAIT_NODEATH) || HAS_TRAIT(affected_mob, TRAIT_NO_SOUL) || HAS_TRAIT(affected_mob, TRAIT_NO_SOUL_BY_VITALITY)) + return FALSE + + if(!ishuman(affected_mob)) + return FALSE + + return TRUE + +/obj/structure/destructible/clockwork/sigil/vitality/dispel_check(mob/user) + . = ..() + if(!.) + return + + if(active_timer) + if(IS_CLOCK(user) && tgui_alert(user, "Are you sure you want to dispel [src]? It is currently siphoning [currently_affecting].", "Confirm dispel", list("Yes", "No")) != "Yes") + return FALSE + return TRUE + +/obj/structure/destructible/clockwork/sigil/vitality/apply_effects(mob/living/affected_mob) + . = ..() + if(!.) + return FALSE + + if(IS_CLOCK(affected_mob)) + deltimer(active_timer) + active_timer = null + var/revived = FALSE + if(affected_mob.stat == DEAD) + var/damage_healed = FREE_DAMAGE_HEALED + ((affected_mob.getMaxHealth() - affected_mob.health) * 0.6) + if(GLOB.clock_vitality >= damage_healed) + GLOB.clock_vitality -= damage_healed + affected_mob.revive(ADMIN_HEAL_ALL) + revived = TRUE + + if(affected_mob.stat != DEAD && (!affected_mob.client || affected_mob.client.is_afk())) + set waitfor = FALSE + var/mob/chosen_one = SSpolling.poll_ghosts_for_target( + "Do you want to play as a [affected_mob.real_name], an inactive clock cultist?", + role = ROLE_MIDROUND_CLOCK_CULTIST, + poll_time = 5 SECONDS, + checked_target = affected_mob, + alert_pic = affected_mob, + role_name_text = "clock cultist" + ) + if(isnull(chosen_one)) + visible_message(span_warning("\The [src] fails to revive [affected_mob]!")) + fail_invocation() + else + to_chat(affected_mob.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.") + message_admins("[key_name_admin(chosen_one)] has taken control of ([key_name_admin(affected_mob)]) to replace an AFK player.") + affected_mob.ghostize(FALSE) + affected_mob.PossessByPlayer(chosen_one.key) + revived = TRUE + if(revived) + var/pronoun_appropriate_demonym = "CLOCK-SIBLING" + if(affected_mob.gender == MALE) + pronoun_appropriate_demonym = "CLOCK-BROTHER" + if(affected_mob.gender == FEMALE) + pronoun_appropriate_demonym = "CLOCK-SISTER" + SEND_SOUND(affected_mob, 'modular_nova/modules/clock_cult/sound/magic/scripture_tier_up.ogg') + to_chat(affected_mob, span_bigbrass("\"[text2ratvar("YOUR SERVITUDE IS NOT FINISHED, [uppertext(affected_mob.real_name)]. RISE, [pronoun_appropriate_demonym], AND BE RENEWED.")]\"")) + affected_mob.visible_message(span_warning("[affected_mob] draws in a huge breath, a bright light shining from [affected_mob.p_their()] eyes."), \ + span_bigbrass("You awaken suddenly from the void. You're alive!")) + return + + affected_mob.Paralyze(1 SECONDS) + if(affected_mob.can_adjust_tox_loss(15)) + affected_mob.adjust_tox_loss(15) + else + affected_mob.adjust_fire_loss(15) + playsound(loc, 'sound/effects/magic/clockwork/ratvar_attack.ogg', 40) + if((affected_mob.stat == DEAD)) + playsound(loc, 'sound/effects/magic/exit_blood.ogg', 60) + to_chat(affected_mob, span_clockred("The last of your life is drained away...")) + GLOB.clock_vitality = min(GLOB.clock_vitality + 40, MAX_CLOCK_VITALITY) // 100 (for clients) total in the ideal situation, since it'll take 6 pulses to go from full to crit + check_special_role(affected_mob) + ADD_TRAIT(affected_mob, TRAIT_NO_SOUL_BY_VITALITY, SIGIL_TRAIT) + return + + affected_mob.visible_message(span_clockred("[affected_mob] looks weak as the color fades from their body."), span_clockred("You feel your soul faltering...")) + GLOB.clock_vitality = min(GLOB.clock_vitality + (affected_mob.client ? 10 : 1), MAX_CLOCK_VITALITY) // Monkey or whatever? You get jackshit + + +/// Checks the role of whoever was killed by the vitality sigil, and does any special code if needed. +/obj/structure/destructible/clockwork/sigil/vitality/proc/check_special_role(mob/living/affected_mob) + if(IS_CULTIST(affected_mob)) + send_clock_message(null, span_clockred("The dog of Nar'sie, [affected_mob] has had their vitality drained, rejoice!")) + GLOB.clock_vitality = min(GLOB.clock_vitality + 20, MAX_CLOCK_VITALITY) + affected_mob.mind?.remove_antag_datum(/datum/antagonist/cult) + else if(IS_HERETIC(affected_mob)) + send_clock_message(null, span_clockred("The heretic, [affected_mob] has had their vitality drained, rejoice!")) + GLOB.clock_vitality = min(GLOB.clock_vitality + 30, MAX_CLOCK_VITALITY) + affected_mob.dust() + var/obj/item/mmi/posibrain/soul_vessel/soul_vessel = new(get_turf(src)) + soul_vessel.transfer_personality(affected_mob) + new /obj/item/gun/ballistic/rifle/lionhunter/clockwork(get_turf(src)) + else + send_clock_message(null, span_clockred("[affected_mob] has had their vitality drained by [src], rejoice!")) + +#undef FREE_DAMAGE_HEALED +#undef HEALTH_DRAINED diff --git a/tff_modular/modules/antagonists/clock_cult/structures/stargazer.dm b/tff_modular/modules/antagonists/clock_cult/structures/stargazer.dm new file mode 100644 index 00000000000..e9c62cb59df --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/stargazer.dm @@ -0,0 +1,128 @@ +//Stargazer structure +/obj/structure/destructible/clockwork/gear_base/stargazer + name = "stargazer" + desc = "A small pedestal, glowing with a divine energy." + clockwork_desc = "Place a weapon upon it to provide special powers and abilities to the weapon." + reebe_desc = "The energies of reebe are interfering with it's abilites, making it only be able to to enchant things at the lowest level." + icon_state = "stargazer" + base_icon_state = "stargazer" + anchored = TRUE + break_message = span_warning("The stargazer collapses.") + ///ref to our visual effect, migtht be able to make this just be an overlay + var/obj/effect/stargazer_light/light_effect + ///how long is our use cooldown + var/stargazer_cooldown = 3 MINUTES + +/obj/structure/destructible/clockwork/gear_base/stargazer/Initialize(mapload) + . = ..() + light_effect = new /obj/effect/stargazer_light(get_turf(src)) + START_PROCESSING(SSobj, src) + +/obj/structure/destructible/clockwork/gear_base/stargazer/Destroy() + STOP_PROCESSING(SSobj, src) + if(!QDELETED(light_effect)) + QDEL_NULL(light_effect) + return ..() + +/obj/structure/destructible/clockwork/gear_base/stargazer/process() + if(QDELETED(light_effect)) + return + for(var/mob/living/viewing_mob in viewers(2, get_turf(src))) + if(IS_CLOCK(viewing_mob)) + if(!light_effect.is_open) + light_effect.open() + return + if(light_effect.is_open) + light_effect.close() + +/obj/structure/destructible/clockwork/gear_base/stargazer/wrench_act(mob/living/user, obj/item/tool) + . = ..() + if(!.) + return + + if(anchored && !light_effect) + light_effect = new /obj/effect/stargazer_light(get_turf(src)) + else if(light_effect) + QDEL_NULL(light_effect) + +/obj/structure/destructible/clockwork/gear_base/stargazer/attackby(obj/item/attacking_item, mob/living/user, params) + if(!anchored) + to_chat(user, span_brass("You need to anchor \the [src] to the floor first.")) + return + + if(!enchanting_checks(attacking_item, user)) + return + + to_chat(user, span_brass("You begin placing \the [attacking_item] onto [src].")) + if(do_after(user, 6 SECONDS, src)) + if(!enchanting_checks(attacking_item, user)) + return + + if(istype(attacking_item, /obj/item) && (istype(attacking_item, /obj/item/clothing) || attacking_item.force) && upgrade_weapon(attacking_item, user)) + COOLDOWN_START(src, use_cooldown, stargazer_cooldown) + return + to_chat(user, span_brass("You cannot upgrade \the [attacking_item].")) + +/obj/structure/destructible/clockwork/gear_base/stargazer/proc/enchanting_checks(obj/item/checked_item, mob/living/user) + if(!COOLDOWN_FINISHED(src, use_cooldown)) + to_chat(user, span_brass("\The [src] is still warming up, it will be ready in [DisplayTimeText(COOLDOWN_TIMELEFT(src, use_cooldown))].")) + return FALSE + + if(HAS_TRAIT(checked_item, TRAIT_STARGAZED)) + to_chat(user, span_brass("\The [checked_item] has already been enchanted!")) + return FALSE + return TRUE + +/obj/structure/destructible/clockwork/gear_base/stargazer/proc/upgrade_weapon(obj/item/upgraded_item, mob/living/user) + if(!attempt_enchantment(upgraded_item, description_span = "")) + return FALSE + //Prevent re-enchanting + ADD_TRAIT(upgraded_item, TRAIT_STARGAZED, STARGAZER_TRAIT) + //Add a glowy colour + upgraded_item.add_atom_colour(rgb(243, 227, 183), ADMIN_COLOUR_PRIORITY) + to_chat(user, span_notice("\The [upgraded_item] glows with a brilliant light!")) + return TRUE + +//The visual effect of the stargazer +/obj/effect/stargazer_light + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + icon_state = "stargazer_closed" + pixel_y = 10 + layer = ABOVE_OBJ_LAYER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + alpha = 160 + ///are we open or closed + var/is_open = FALSE + ///ref to our timer if we have one + var/active_timer + +/obj/effect/stargazer_light/ex_act() + return + +/obj/effect/stargazer_light/Destroy(force) + cancel_timer() + return ..() + +/obj/effect/stargazer_light/proc/finish_opening() + icon_state = "stargazer_light" + active_timer = null + +/obj/effect/stargazer_light/proc/finish_closing() + icon_state = "stargazer_closed" + active_timer = null + +/obj/effect/stargazer_light/proc/open() + icon_state = "stargazer_opening" + cancel_timer() + active_timer = addtimer(CALLBACK(src, PROC_REF(finish_opening)), 2, TIMER_STOPPABLE | TIMER_UNIQUE) + is_open = TRUE + +/obj/effect/stargazer_light/proc/close() + icon_state = "stargazer_closing" + cancel_timer() + active_timer = addtimer(CALLBACK(src, PROC_REF(finish_closing)), 2, TIMER_STOPPABLE | TIMER_UNIQUE) + is_open = FALSE + +/obj/effect/stargazer_light/proc/cancel_timer() + if(active_timer) + deltimer(active_timer) diff --git a/tff_modular/modules/antagonists/clock_cult/structures/the_ark.dm b/tff_modular/modules/antagonists/clock_cult/structures/the_ark.dm new file mode 100644 index 00000000000..79aac6ddbbe --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/the_ark.dm @@ -0,0 +1,251 @@ +GLOBAL_DATUM(clock_ark, /obj/structure/destructible/clockwork/the_ark) //set to be equal to the ark on creation if none +GLOBAL_VAR_INIT(ratvar_risen, FALSE) + +#define ARK_READY_PERIOD 300 SECONDS //how long until the cult is annouced after they reach max members, 5 minutes +#define ARK_GRACE_PERIOD 210 SECONDS //how long until the portals open after the cult is annouced, 3 minutes 30 seconds +#define ARK_ASSAULT_PERIOD 600 //how long the crew has to destroy the ark after the assault begins, 10 minutes +/obj/structure/destructible/clockwork/the_ark + name = "\improper Ark of the Clockwork Justiciar" + desc = "A massive, hulking amalgamation of parts. It seems to be maintaining a very unstable bluespace anomaly." + clockwork_desc = "Nezbere's magnum opus: a hulking clockwork machine capable of combining bluespace and steam power to summon Ratvar. Once activated, \ + its instability will cause one-way bluespace rifts to open across the station to the City of Cogs, so be prepared to defend it at all costs." + max_integrity = 1000 + icon = 'icons/effects/96x96.dmi' + icon_state = "clockwork_gateway_components" + pixel_x = -32 + pixel_y = -32 + immune_to_servant_attacks = TRUE + layer = BELOW_MOB_LAYER + can_rotate = FALSE + break_message = null + break_sound = null + debris = null + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF | FREEZE_PROOF + + ///current charge state of the ark + var/current_state = ARK_STATE_BASE + ///tracker for how long until ratvar is summoned in ARK_STATE_ACTIVE/ARK_STATE_SUMMONING + var/charging_for = 0 + +/obj/structure/destructible/clockwork/the_ark/Initialize(mapload) + . = ..() + if(!GLOB.clock_ark) + GLOB.clock_ark = src + SSpoints_of_interest.make_point_of_interest(src) + + if(!SSthe_ark.initialized) + SSthe_ark.Initialize() + +/obj/structure/destructible/clockwork/the_ark/examine(mob/user) + . = ..() + if(IS_CLOCK(user) || isobserver(user)) + switch(current_state) + if(ARK_STATE_CHARGING) + . += span_brass("The ark has started charging, the crew will soon know our glory!") + if(ARK_STATE_ACTIVE) + . += span_brass("The ark is opening, [charging_for ? "defend it until ratvar arrives in [ARK_ASSAULT_PERIOD - charging_for] seconds." : "prepare to defend it!"]") + if(ARK_STATE_SUMMONING) + . += span_brass("Ratvar has nearly arrived, it will only be [ARK_ASSAULT_PERIOD - charging_for] more seconds!") + if(user.client?.holder) + . += span_warning("ADMIN ONLY WARNING: DELETING THIS WILL END THE ROUND.") + +/obj/structure/destructible/clockwork/the_ark/Destroy() + if(GLOB.clock_ark == src) + GLOB.clock_ark = null + if(GLOB.ratvar_risen) + return ..() + STOP_PROCESSING(SSthe_ark, src) + send_clock_message(null, span_bigbrass("The Ark has been destroyed, Reebe is becoming unstable!")) + for(var/mob/living/current_mob in GLOB.player_list) + if(!on_reebe(current_mob)) + continue + if(IS_CLOCK(current_mob)) + to_chat(current_mob, span_reallybig(span_clockyellow("Your mind is distorted by the distant sound of a thousand screams. [span_reallybig("YOU HAVE FAILED TO PROTECT MY ARK. \ + YOU WILL BE TRAPPED HERE WITH ME TO SUFFER FOREVER...")]"))) + continue + current_mob.SetSleeping(5 SECONDS) + to_chat(current_mob, span_clockyellow("Your mind is distorted by the distant sound of a thousand screams before suddenly everything falls silent.")) + to_chat(current_mob, span_hypnophrase("The only thing you remember is suddenly feeling hard ground beneath you and the safety of home.")) + current_mob.forceMove(find_safe_turf()) + + if(GLOB.narsie_breaching_rune) + if(istype(GLOB.narsie_breaching_rune, /obj/effect/rune/narsie)) + new /obj/narsie(get_turf(GLOB.narsie_breaching_rune)) + else + new /obj/narsie(get_safe_random_station_turf()) + + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(explode_reebe)) + return ..() + +/obj/structure/destructible/clockwork/the_ark/Destroy() + if(current_state >= ARK_STATE_FINAL) + return + ASYNC + if(!(flags_1 & HOLOGRAM_1)) + current_state = ARK_STATE_FINAL + resistance_flags |= INDESTRUCTIBLE + visible_message(span_userdanger("[src] begins to pulse uncontrollably... you might want to run!")) + sound_to_playing_players('sound/effects/clockcult_gateway_disrupted.ogg', 50) + sleep(2.5 SECONDS) + sound_to_playing_players('sound/machines/clockcult/ark_deathrattle.ogg', 50) + sleep(2.7 SECONDS) + explosion(src, 1, 3, 8, 8) + sound_to_playing_players('sound/effects/explosion/explosion_distant.ogg', 50) + for(var/obj/effect/portal/clockcult/portal in GLOB.portals) + qdel(portal) + SSshuttle.clearHostileEnvironment(src) + SSsecurity_level.set_level(SEC_LEVEL_RED) + qdel(src) + return ..() + +/obj/structure/destructible/clockwork/the_ark/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration) + if(current_state == ARK_STATE_FINAL) + return + + . = ..() + if(!.) + return + send_clock_message(null, span_bigbrass("The ark is taking damage!"), sent_sound = 'tff_modular/modules/antagonists/clock_cult/sound/ark_damage.ogg') + flick("clockwork_gateway_damaged", src) + playsound(src, 'tff_modular/modules/antagonists/clock_cult/sound/ark_damage.ogg', 75, FALSE) + +/obj/structure/destructible/clockwork/the_ark/process(seconds_per_tick) + if(current_state >= ARK_STATE_FINAL) + return + + if(current_state >= ARK_STATE_CHARGING) + charging_for = min(charging_for + (seconds_per_tick * DELTA_WORLD_TIME(SSthe_ark)), ARK_ASSAULT_PERIOD) + + if(charging_for >= ARK_ASSAULT_PERIOD) + summon_ratvar() + return + + if(current_state < ARK_STATE_SUMMONING && charging_for >= (ARK_ASSAULT_PERIOD * 0.5)) + current_state = ARK_STATE_SUMMONING + icon_state = "clockwork_gateway_closing" + sound_to_playing_players('tff_modular/modules/antagonists/clock_cult/sound/clockcult_gateway_closing.ogg', 30, TRUE) + + if(current_state >= ARK_STATE_SUMMONING && SPT_PROB(4, seconds_per_tick)) + send_to_playing_players(span_warning("[pick(list("You feel the fabric of reality twist and bend.", \ + "Your mind buzzes with fear.", \ + "You hear otherworldly screams from all around you.", \ + "You feel reality shudder for a moment...", \ + "You feel time and space distorting around you..."))]")) + +/obj/structure/destructible/clockwork/the_ark/proc/prepare_ark() + if(current_state > ARK_STATE_BASE) + return + current_state = ARK_STATE_CHARGING + SSshuttle.registerHostileEnvironment(src) + icon_state = "clockwork_gateway_charging" + send_clock_message(null, span_bigbrass("The Ark's many cogs suddenly whir to life, steam gushing out of its many crevices; it will open in 5 minutes!"), \ + sent_sound = 'modular_nova/modules/clock_cult/sound/magic/scripture_tier_up.ogg') + addtimer(CALLBACK(src, PROC_REF(open_gateway)), ARK_READY_PERIOD) //MOVE THIS TO A LOOP ON THE ARK SS + +/obj/structure/destructible/clockwork/the_ark/proc/open_gateway() + if(current_state >= ARK_STATE_GRACE) + return + current_state = ARK_STATE_GRACE + SSshuttle.registerHostileEnvironment(src) + icon_state = "clockwork_gateway_active" + send_clock_message(null, span_bigbrass("The Ark has been activated, you will be transported soon! Dont forget to gather weapons with your \"Clockwork Armaments\" scripture."), \ + sent_sound = 'sound/effects/magic/clockwork/ark_activation_sequence.ogg') + addtimer(CALLBACK(src, PROC_REF(announce_gateway)), 27 SECONDS) + +/obj/structure/destructible/clockwork/the_ark/proc/announce_gateway() + send_clock_message(null, span_clockyellow("DESTROY THE HERETICS."), sent_sound = 'tff_modular/modules/antagonists/clock_cult/sound/ark_recall.ogg') + sleep(3 SECONDS) + current_state = ARK_STATE_ACTIVE + + for(var/datum/mind/servant_mind in GLOB.main_clock_cult.members) + var/mob/living/servant_mob = servant_mind.current + if(QDELETED(servant_mob)) + continue + + if(GLOB.abscond_markers) + try_servant_warp(servant_mob, get_turf(pick(GLOB.abscond_markers))) + + var/datum/antagonist/clock_cultist/servant_antag = servant_mind.has_antag_datum(/datum/antagonist/clock_cultist) + servant_antag?.add_forbearance(servant_mob) + + sound_to_playing_players('sound/effects/magic/clockwork/invoke_general.ogg', 50) + SSsecurity_level.set_level(SEC_LEVEL_DELTA) + addtimer(CALLBACK(src, PROC_REF(begin_assault)), ARK_GRACE_PERIOD) + + priority_announce("Massive [Gibberish("bluespace", 100)] anomaly detected on all frequencies. All crew are directed to \ + @!$, [text2ratvar("PURGE ALL UNTRUTHS")] <&. the anomalies and destroy their source to prevent further damage to corporate property. This is \ + not a drill. Estimated time of appearance: [ARK_GRACE_PERIOD/10] seconds. Use this time to prepare for an attack on [station_name()]." \ + ,"Central Command Higher Dimensional Affairs", 'sound/effects/magic/clockwork/ark_activation.ogg') + + sound_to_playing_players('tff_modular/modules/antagonists/clock_cult/sound/clockcult_gateway_charging.ogg', 10, TRUE) + log_game("The clock cult has begun opening the Ark of the Clockwork Justiciar.") + +/obj/structure/destructible/clockwork/the_ark/proc/begin_assault() + START_PROCESSING(SSthe_ark, src) + priority_announce("Space-time anomalies detected near the station. Source determined to be a temporal \ + energy pulse emanating from J1523-215. All crew are to enter [text2ratvar("prep#re %o di%")]\ + and destroy the [text2ratvar("I'd like to see you try")], which has been determined to be the source of the \ + pulse to prevent mass damage to Nanotrasen property.", "Anomaly Alert", ANNOUNCER_SPANOMALIES) + + log_game("The opening of the Ark of the Clockwork Justiciar has caused portals to open around the station.") + for(var/i in 1 to 100) + new /obj/effect/portal/clockcult(get_random_station_turf()) + sleep(1) + +/obj/structure/destructible/clockwork/the_ark/proc/summon_ratvar() + if(current_state >= ARK_STATE_FINAL) + return + current_state = ARK_STATE_FINAL + STOP_PROCESSING(SSthe_ark, src) + resistance_flags |= INDESTRUCTIBLE + send_clock_message(null, span_bigbrass("Ratvar approaches, you shall be eternally rewarded for your servitude!"), msg_ghosts = FALSE) + send_to_playing_players(span_warning("You feel time slow down.")) + GLOB.ratvar_risen = TRUE + sound_to_playing_players('tff_modular/modules/antagonists/clock_cult/sound/ratvar_rises.ogg', 100) + + if(GLOB.main_clock_cult) + for(var/datum/mind/current_mind in GLOB.main_clock_cult.members) + var/mob/living/newgod = current_mind.current + if(!newgod) + continue + ADD_TRAIT(newgod, TRAIT_GODMODE, "ratvar") + else + stack_trace("Clockwork ark calling summon_ratvar() with no set main_clock_cult.") + + for(var/mob/living/checked_mob as anything in GLOB.player_list) + if(on_reebe(checked_mob)) //doing an addtimer to insure these run on time as the ark will be getting qdeled at this time + addtimer(CALLBACK(null, GLOBAL_PROC_REF(try_servant_warp), checked_mob, get_safe_random_station_turf()), 12.8 SECONDS) + + var/original_matrix = matrix() + animate(src, transform = original_matrix * 1.5, alpha = 255, time = 125) + sleep(3 SECONDS) + send_to_playing_players(span_warning("You see cracks forming in space around you.")) + sleep(3 SECONDS) + send_to_playing_players(span_warning("You are deafened by the sound of a million screams.")) + sleep(5.5 SECONDS) + send_to_playing_players(span_userdanger("THE JUSTICAR IS HERE.")) + sleep(1 SECONDS) + transform = original_matrix + animate(src, transform = original_matrix * 3, alpha = 0, time = 5) + QDEL_IN(src, 4) + sleep(3) + new /obj/ratvar(get_random_station_turf()) + +/proc/explode_reebe() + var/list/reebe_area_list = get_area_turfs(/area/ruin/powered/reebe/city) + if(length(reebe_area_list)) + for(var/i in 1 to 30) + explosion(pick(reebe_area_list), 0, 2, 4, 4, FALSE) + sleep(5) + if(length(GLOB.abscond_markers)) + explosion(pick(GLOB.abscond_markers), 50, 40, 30, 30, FALSE, TRUE) + SSticker.force_ending = TRUE + +#undef ARK_READY_PERIOD +#undef ARK_GRACE_PERIOD +#undef ARK_ASSAULT_PERIOD + +/obj/effect/rune/narsie/Destroy(force) + if(src == GLOB.narsie_breaching_rune) + GLOB.narsie_breaching_rune = TRUE //we still want to summon even if destroyed + return ..() diff --git a/tff_modular/modules/antagonists/clock_cult/structures/tinkerers_cache.dm b/tff_modular/modules/antagonists/clock_cult/structures/tinkerers_cache.dm new file mode 100644 index 00000000000..bbc07132edd --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/tinkerers_cache.dm @@ -0,0 +1,158 @@ +/obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache + name = "tinkerer's cache" + desc = "A bronze store filled with parts and components." + icon_state = "tinkerers_cache" + base_icon_state = "tinkerers_cache" + clockwork_desc = "Can be used to forge powerful Ratvarian items and traps at the cost of power and time." + reebe_desc = "It's connection to the physical realm is weakened from being on reebe, restricting its ability to make certain items." + anchored = TRUE + break_message = span_warning("The tinkerer's cache melts into a pile of brass.") + has_on_icon = FALSE + has_off_icon = FALSE + has_power_toggle = FALSE + /// Assoc list of the names of all the craftable items to their path + var/static/list/station_craftable + /// Assoc list of items craftable on reebe + var/static/list/reebe_craftable + +/obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache/Initialize(mapload) + . = ..() + if(!length(station_craftable) || !length(reebe_craftable)) + assemble_datum_list() + +/obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache/attack_hand(mob/living/user) + . = ..() + if(.) + return + + if(!IS_CLOCK(user)) + to_chat(user, span_warning("You try to put your hand into [src], but almost burn yourself!")) + return + + if(!anchored) + to_chat(user, span_brass("[src] needs to be anchored to the floor first.")) + return + + if(!length(transmission_sigils)) + to_chat(user, span_brass("[src] isn't connected to power!")) + return + + if(!COOLDOWN_FINISHED(src, use_cooldown)) + to_chat(user, span_brass("[src] is still warming up, it will be ready in [DisplayTimeText(COOLDOWN_TIMELEFT(src, use_cooldown))].")) + return + + var/list/valid_list = (on_reebe(src) ? reebe_craftable : reebe_craftable + station_craftable) + var/datum/tinker_cache_item/chosen_item = tgui_input_list(user, "Select an item to create at the forge.", "Forging", valid_list) + if(!chosen_item) + return + + chosen_item = valid_list[chosen_item] + if(!can_interact(user) || !anchored || !check_powered() || !chosen_item || !COOLDOWN_FINISHED(src, use_cooldown)) + return + + if(!length(transmission_sigils)) + to_chat(user, span_brass("This needs to be connected to a transmission sigil!")) + return + + var/amount_to_create = 1 + if(!chosen_item.time_delay_mult) + amount_to_create = tgui_input_number(user, "How many would you like to create?", "Tinkerers Cache", max_value = 10, min_value = 1) + + if(!use_energy(chosen_item.power_use * amount_to_create)) + to_chat(user, span_brass("You need more power to forge this item.")) + return + + if(chosen_item.time_delay_mult) + COOLDOWN_START(src, use_cooldown, 4 MINUTES * chosen_item.time_delay_mult) + + var/crafting_item = chosen_item.item_path + for(var/i in 1 to amount_to_create) + new crafting_item(get_turf(src)) + playsound(src, 'sound/machines/clockcult/steam_whoosh.ogg', 50) + + to_chat(user, span_brass("You craft [chosen_item.name] to near perfection, \the [src] cooling down. \ + [chosen_item.time_delay_mult ? "It will be available in [DisplayTimeText(COOLDOWN_TIMELEFT(src, use_cooldown))]." : "It is ready to use again."]")) + +// Assemble a list of subtype tinker cache datums +/obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache/proc/assemble_datum_list() + station_craftable = list() + reebe_craftable = list() + for(var/datum/tinker_cache_item/initial_item as anything in subtypesof(/datum/tinker_cache_item)) + initial_item = new initial_item + (!initial_item.allowed_on_reebe ? station_craftable : reebe_craftable)["[initial_item.name] ([initial_item.power_use] W)"] = initial_item + +// This used to be a hardcoded list +/datum/tinker_cache_item + /// Name of the item + var/name = "abstract parent" + /// Path to the object that this will create + var/item_path + /// Amount of power this will consume to create + var/power_use = 0 + /// Multiplier for time delay (default 4m) after producing this item + var/time_delay_mult = 1 + /// Is this item able to be fabricated on reebe + var/allowed_on_reebe = TRUE + +/datum/tinker_cache_item/speed_robes + name = "Robes Of Divinity" + item_path = /obj/item/clothing/suit/clockwork/speed + power_use = 300 + allowed_on_reebe = FALSE + +/datum/tinker_cache_item/invis_cloak + name = "Shrouding Cloak" + item_path = /obj/item/clothing/suit/clockwork/cloak + power_use = 300 + allowed_on_reebe = FALSE + +/datum/tinker_cache_item/sight_goggles + name = "Wraith Spectacles" + item_path = /obj/item/clothing/glasses/clockwork/wraith_spectacles + power_use = 400 + allowed_on_reebe = FALSE + +/datum/tinker_cache_item/hud_visor + name = "Judicial Visor" + item_path = /obj/item/clothing/glasses/clockwork/judicial_visor + power_use = 400 + allowed_on_reebe = FALSE + +/datum/tinker_cache_item/replica_fabricator + name = "Replica Fabricator" + item_path = /obj/item/clockwork/replica_fabricator + power_use = 400 + +/datum/tinker_cache_item/clockwork_slab + name = "Clockwork Slab" + item_path = /obj/item/clockwork/clockwork_slab + power_use = 100 + time_delay_mult = 0.5 + +/datum/tinker_cache_item/tools + name = "Equipped Toolbelt" + item_path = /obj/item/storage/belt/utility/clock + power_use = 150 + time_delay_mult = 0.75 + +/datum/tinker_cache_item/trap + name = "Flipper (Trap)" + item_path = /obj/item/clockwork/trap_placer/flipper + power_use = 50 + time_delay_mult = 0 + +/datum/tinker_cache_item/trap/skewer + name = "Skewer (Trap)" + item_path = /obj/item/clockwork/trap_placer/skewer + +/datum/tinker_cache_item/trap/delay + name = "Delayer (Trigger)" + item_path = /obj/item/wallframe/clocktrap/delay + +/datum/tinker_cache_item/trap/lever + name = "Lever (Trigger)" + item_path = /obj/item/wallframe/clocktrap/lever + +/datum/tinker_cache_item/trap/pressure + name = "Pressure Sensor (Trigger)" + item_path = /obj/item/clockwork/trap_placer/pressure_sensor diff --git a/tff_modular/modules/antagonists/clock_cult/structures/traps/recievers/flipper.dm b/tff_modular/modules/antagonists/clock_cult/structures/traps/recievers/flipper.dm new file mode 100644 index 00000000000..1d943aa34a4 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/traps/recievers/flipper.dm @@ -0,0 +1,60 @@ +#define FLIP_DISTANCE 6 +#define FLIP_SPEED 3 + +/obj/item/clockwork/trap_placer/flipper + name = "flipper" + desc = "A steam powered rotating floor panel. When input is received it will fling anyone on top of it." + icon_state = "pressure_sensor" + result_path = /obj/structure/destructible/clockwork/trap/flipper + clockwork_desc = "A floor panel capable of flinging anyone back when triggered." + +/obj/structure/destructible/clockwork/trap/flipper + name = "flipper" + desc = "A steam powered rotating floor panel. When input is received it will fling anyone on top of it." + icon_state = "flipper" + component_datum = /datum/component/clockwork_trap/flipper + unwrench_path = /obj/item/clockwork/trap_placer/flipper + clockwork_desc = "A floor panel capable of flinging anyone back when triggered. However, it does have a cooldown between uses." + COOLDOWN_DECLARE(flip_cooldown) + /// Time between possible flips + var/cooldown_flip = 10 SECONDS + +/obj/structure/destructible/clockwork/trap/flipper/examine(mob/user) + . = ..() + + if(!COOLDOWN_FINISHED(src, flip_cooldown) && IS_CLOCK(user)) + . += span_brass("It's not ready to activate again yet!") + +/// Send all `atom/movable`s flying in the set direction for a decent distance +/obj/structure/destructible/clockwork/trap/flipper/proc/flip() + if(!COOLDOWN_FINISHED(src, flip_cooldown)) + return + + COOLDOWN_START(src, flip_cooldown, cooldown_flip) + addtimer(CALLBACK(src, PROC_REF(cooldown_done)), cooldown_flip) + + flick("flipping", src) + + for(var/atom/movable/movable_atom in get_turf(src)) + + if(movable_atom.anchored) + continue + + movable_atom.throw_at(get_edge_target_turf(src, dir), FLIP_DISTANCE, FLIP_SPEED) + +/// Visual update when the cooldown's finished +/obj/structure/destructible/clockwork/trap/flipper/proc/cooldown_done() + visible_message(span_brass("[src] whirrs with a loud *CLANK* as it resets.")) + +/datum/component/clockwork_trap/flipper + takes_input = TRUE + +/datum/component/clockwork_trap/flipper/trigger() + if(!..()) + return + + var/obj/structure/destructible/clockwork/trap/flipper/flipper_parent = parent + flipper_parent.flip() + +#undef FLIP_DISTANCE +#undef FLIP_SPEED diff --git a/tff_modular/modules/antagonists/clock_cult/structures/traps/recievers/skewer.dm b/tff_modular/modules/antagonists/clock_cult/structures/traps/recievers/skewer.dm new file mode 100644 index 00000000000..3aaa3850e3b --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/traps/recievers/skewer.dm @@ -0,0 +1,123 @@ +#define SKEWER_DAMAGE 15 +#define SKEWER_BLEED 30 + + +/obj/item/clockwork/trap_placer/skewer + name = "brass skewer" + desc = "A spiked, brass skewer attached to a steam powered extension mechanism." + icon_state = "brass_skewer_extended" + result_path = /obj/structure/destructible/clockwork/trap/skewer + clockwork_desc = "A skewer that can pierce through a target, activated by a linked trigger." + +/obj/structure/destructible/clockwork/trap/skewer + name = "brass skewer" + desc = "A spiked, brass skewer attached to a steam powered extension mechanism." + icon_state = "brass_skewer" + component_datum = /datum/component/clockwork_trap/skewer + unwrench_path = /obj/item/clockwork/trap_placer/skewer + buckle_lying = FALSE + max_integrity = 20 + clockwork_desc = "A skewer that can pierce through a target, activated by a linked trigger." + COOLDOWN_DECLARE(stab_cooldown) + /// If the spear is currently extended + var/extended = FALSE + /// Mutable appearance stab overlay + var/mutable_appearance/stab_overlay + +/datum/armor/raised_clock_skewer + laser = 30 + melee = 50 + bullet = 40 + energy = 30 + +/// Stab any non-clock mobs who stood on the tile +/obj/structure/destructible/clockwork/trap/skewer/proc/stab() + if(extended) + retract() + return + + if(!COOLDOWN_FINISHED(src, stab_cooldown)) + return + + COOLDOWN_START(src, stab_cooldown, 10 SECONDS) + extended = TRUE + icon_state = "[initial(icon_state)]_extended" + var/target_stabbed = FALSE + density = TRUE + set_armor(/datum/armor/raised_clock_skewer) + + for(var/mob/living/stabbed_mob in get_turf(src)) + if(stabbed_mob.incorporeal_move || (stabbed_mob.movement_type & (FLOATING|FLYING))) + continue + + if(IS_CLOCK(stabbed_mob)) + to_chat(stabbed_mob, span_warning("You dodge out of the way of [src]!")) + continue + + if(!buckle_mob(stabbed_mob, TRUE)) + continue + + target_stabbed = TRUE + to_chat(stabbed_mob, span_userdanger("You are impaled by [src]!")) + stabbed_mob.emote("scream") + stabbed_mob.apply_damage(SKEWER_DAMAGE, BRUTE, BODY_ZONE_CHEST) + + if(ishuman(stabbed_mob)) + var/mob/living/carbon/human/stabbed_human = stabbed_mob + stabbed_human.bleed(SKEWER_BLEED) + + if(target_stabbed) + if(!stab_overlay) + stab_overlay = mutable_appearance('tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi', "brass_skewer_pokeybit", layer = ABOVE_MOB_LAYER) + + add_overlay(stab_overlay) + + +/obj/structure/destructible/clockwork/trap/skewer/unbuckle_mob(mob/living/buckled_mob, force, can_fall) + if(force) + return ..() + + if(!buckled_mob.break_do_after_checks()) + return + + balloon_alert(buckled_mob, "climbing off of [src]...") + + if(!do_after(buckled_mob, 5 SECONDS, target = src)) + balloon_alert(buckled_mob, "failed to climb off [src]") + return + + return ..() + + +/obj/structure/destructible/clockwork/trap/skewer/post_unbuckle_mob(mob/living/stabbed_mob) + if(!has_buckled_mobs()) + cut_overlay(stab_overlay) + + +/// Unbuckling mobs and reverting the trap for when the pokey bit goes back in +/obj/structure/destructible/clockwork/trap/skewer/proc/retract() + extended = FALSE + icon_state = initial(icon_state) + density = FALSE + cut_overlay(stab_overlay) + set_armor(null) + for(var/mob/living/stabbed_mob as anything in buckled_mobs) + unbuckle_mob(stabbed_mob, TRUE) + + +/datum/component/clockwork_trap/skewer + takes_input = TRUE + + +/datum/component/clockwork_trap/skewer/trigger() + if(!..()) + return + + var/obj/structure/destructible/clockwork/trap/skewer/trap = parent + if(!istype(trap)) + return + + INVOKE_ASYNC(trap, TYPE_PROC_REF(/obj/structure/destructible/clockwork/trap/skewer, stab)) + +#undef SKEWER_DAMAGE +#undef SKEWER_BLEED diff --git a/tff_modular/modules/antagonists/clock_cult/structures/traps/senders/delay.dm b/tff_modular/modules/antagonists/clock_cult/structures/traps/senders/delay.dm new file mode 100644 index 00000000000..462759d0957 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/traps/senders/delay.dm @@ -0,0 +1,48 @@ +/obj/item/wallframe/clocktrap/delay + name = "clockwork timer" + desc = "A small, detached timer." + icon_state = "delayer" + result_path = /obj/structure/destructible/clockwork/trap/delay + clockwork_desc = "A device that can be attached to walls. When input is received, it will send an output signal a configurable (with multitool) time later." + + +/obj/structure/destructible/clockwork/trap/delay + name = "clockwork timer" + desc = "A small timer attatched to the wall." + icon_state = "delayer" + component_datum = /datum/component/clockwork_trap/delay + unwrench_path = /obj/item/wallframe/clocktrap/delay + max_integrity = 15 + clockwork_desc = "When input is received, it will send an output signal a configurable (with multitool) amount of time later." + /// How much time the signal is delayed + var/delay_time = 1 SECONDS + + + +/obj/structure/destructible/clockwork/trap/delay/multitool_act(mob/living/user, obj/item/tool) + delay_time = tgui_input_number(user, "Input delay time", "Clockwork Timer", 1 SECONDS, 120 SECONDS, 1 SECONDS) + return TRUE + + +/datum/component/clockwork_trap/delay + takes_input = TRUE + sends_input = TRUE + /// If the trap is active or not + var/active = FALSE + + +/datum/component/clockwork_trap/delay/trigger() + if(!..() || active) + return + + active = TRUE + flick("delayer_active", parent) + + var/obj/structure/destructible/clockwork/trap/delay/parent_delayer = parent + addtimer(CALLBACK(src, PROC_REF(finish)), parent_delayer.delay_time) + + +/// Finish the delay, trigger any traps +/datum/component/clockwork_trap/delay/proc/finish() + active = FALSE + trigger_connected() diff --git a/tff_modular/modules/antagonists/clock_cult/structures/traps/senders/lever.dm b/tff_modular/modules/antagonists/clock_cult/structures/traps/senders/lever.dm new file mode 100644 index 00000000000..7dd5926bb85 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/traps/senders/lever.dm @@ -0,0 +1,26 @@ +/obj/item/wallframe/clocktrap/lever + name = "switch" + desc = "A small switch attatched to the wall." + icon_state = "lever" + result_path = /obj/structure/destructible/clockwork/trap/lever + clockwork_desc = "A device that can be attached to walls to allow you to send a signal to linked traps." + + +/obj/structure/destructible/clockwork/trap/lever + name = "switch" + desc = "A small switch attatched to the wall." + icon_state = "lever" + unwrench_path = /obj/item/wallframe/clocktrap/lever + component_datum = /datum/component/clockwork_trap/lever + max_integrity = 75 + clockwork_desc = "A device allows you to send a signal to linked traps." + + +/datum/component/clockwork_trap/lever + sends_input = TRUE + + +/datum/component/clockwork_trap/lever/attack_hand(mob/user) + trigger_connected() + to_chat(user, span_notice("You activate the switch.")) + playsound(user, 'sound/machines/click.ogg', 50) diff --git a/tff_modular/modules/antagonists/clock_cult/structures/traps/senders/pressure_sensor.dm b/tff_modular/modules/antagonists/clock_cult/structures/traps/senders/pressure_sensor.dm new file mode 100644 index 00000000000..b4dc211ced0 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/traps/senders/pressure_sensor.dm @@ -0,0 +1,45 @@ +/obj/item/clockwork/trap_placer/pressure_sensor + name = "pressure plate" + desc = "I wonder what happens if you step on it." + icon_state = "pressure_sensor" + result_path = /obj/structure/destructible/clockwork/trap/pressure_sensor + clockwork_desc = "Allows you to send a signal to linked traps when a non-servant steps on the plate." + + +/obj/structure/destructible/clockwork/trap/pressure_sensor + name = "pressure plate" + desc = "I wonder what happens if you step on it." + icon_state = "pressure_sensor" + unwrench_path = /obj/item/clockwork/trap_placer/pressure_sensor + component_datum = /datum/component/clockwork_trap/pressure_sensor + alpha = 60 + layer = 2.53 + max_integrity = 5 + clockwork_desc = "Allows you to send a signal to linked traps when a non-servant steps on the plate." + + +/datum/component/clockwork_trap/pressure_sensor + sends_input = TRUE + + +/datum/component/clockwork_trap/pressure_sensor/Initialize() + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + ) + AddComponent(/datum/component/connect_loc_behalf, parent, loc_connections) + +/// Trigger all connected traps now that someone's stepped on the tile +/datum/component/clockwork_trap/pressure_sensor/proc/on_entered(datum/source, atom/movable/entered_movable) + SIGNAL_HANDLER + + //Items in hands or boxes shouldn't trigger it + if(!isturf(entered_movable.loc) || !isliving(entered_movable)) + return + + var/mob/living/entered_living = entered_movable + if(IS_CLOCK(entered_living) || entered_living.incorporeal_move || (entered_living.movement_type & (FLOATING|FLYING))) + return + + trigger_connected() + playsound(parent, 'sound/machines/click.ogg', 50) diff --git a/tff_modular/modules/antagonists/clock_cult/structures/traps/trap.dm b/tff_modular/modules/antagonists/clock_cult/structures/traps/trap.dm new file mode 100644 index 00000000000..3e4799c854b --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/structures/traps/trap.dm @@ -0,0 +1,182 @@ +//might be able to refactor these into mech comp stuff +//Thing that you stick on the floor +/obj/item/clockwork/trap_placer + name = "trap" + desc = "don't trust it" + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + w_class = WEIGHT_CLASS_SMALL + /// The path of the trap to make when this is set down + var/result_path = /obj/structure/destructible/clockwork/trap + + +/obj/item/clockwork/trap_placer/attack_self(mob/user) + . = ..() + if(!IS_CLOCK(user)) + return + + if(user.loc != get_turf(src)) + return + + place_trap(get_turf(src), user) + +/obj/item/clockwork/trap_placer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!IS_CLOCK(user) || !isturf(interacting_with)) + return NONE + + place_trap(interacting_with, user) + return ITEM_INTERACT_SUCCESS + + +/obj/item/clockwork/trap_placer/proc/place_trap(atom/target, mob/user) + for(var/obj/structure/destructible/clockwork/trap/trap in target) // No 50-spear instakills please + + if(!istype(trap, result_path)) + continue + + user.balloon_alert(user, "space occupied!") + return + + to_chat(user, span_brass("You place [src], use a clockwork slab to link it to other traps.")) + var/obj/new_obj = new result_path(target) + new_obj.setDir(user.dir) + + qdel(src) + +//Thing you stick on the wall +/obj/item/wallframe/clocktrap + name = "clockwork trap item" + desc = "It's a... Wait what?" + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + pixel_shift = 24 + w_class = WEIGHT_CLASS_SMALL + result_path = /obj/structure/destructible/clockwork/trap + /// What to show the user if they are a clock cultist + var/clockwork_desc = "It seems to be able to be placed on walls." + +/obj/item/wallframe/clocktrap/Initialize(mapload) + . = ..() + AddElement(/datum/element/clockwork_description, clockwork_desc) + +/obj/item/wallframe/clocktrap/try_build(turf/on_wall, mob/user) + if(get_dist(on_wall, user) > 1) + balloon_alert(user, "you are too far!") + return + + var/floor_to_wall = get_dir(user, on_wall) + if(!(floor_to_wall in GLOB.cardinals)) + balloon_alert(user, "stand in line with wall!") + return + + var/turf/user_turf = get_turf(user) + if(!isopenturf(user_turf)) + balloon_alert(user, "cannot place here!") + return + + if(check_wall_item(user_turf, floor_to_wall, wall_external)) + balloon_alert(user, "already something here!") + return + + return TRUE + +//Wall item (either spawned by a wallframe or directly) +/obj/structure/destructible/clockwork/trap + name = "clockwork trap item" + desc = "Probably doesn't do much." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + density = FALSE + layer = LOW_OBJ_LAYER + break_message = span_warning("The intricate looking device falls apart.") + /// What item's produced when this structure is unwrenched + var/unwrench_path = /obj/item/wallframe/clocktrap + /// The component used for the trap's back-end + var/component_datum = /datum/component/clockwork_trap + +/obj/structure/destructible/clockwork/trap/Initialize(mapload) + . = ..() + AddComponent(component_datum) + +/obj/structure/destructible/clockwork/trap/wrench_act(mob/living/user, obj/item/I) + . = ..() + balloon_alert(user, "unwrenching...") + + if(!do_after(user, 5 SECONDS, target = src)) + return + + balloon_alert(user, "detached [src]") + new unwrench_path(get_turf(src)) + + qdel(src) + +//Component +/datum/component/clockwork_trap + /// A list of traps this sends a signal to when this is triggered + var/list/outputs = list() + /// If this sends input (e.g. pressure plate) + var/sends_input = FALSE + /// If this takes input (e.g. skewer) + var/takes_input = FALSE + +/datum/component/clockwork_trap/Initialize() + . = ..() + + if(!istype(parent, /obj/structure/destructible/clockwork)) + return COMPONENT_INCOMPATIBLE + + RegisterSignal(parent, COMSIG_CLOCKWORK_SIGNAL_RECEIVED, PROC_REF(trigger)) + RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(attack_hand)) + RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby)) + +/// Adds an input device to our own `outputs` list, to be sent when it triggers +/datum/component/clockwork_trap/proc/add_input(datum/component/clockwork_trap/input) + outputs |= input.parent + +/// Adds this as an output to the targeted component's `outputs` list +/datum/component/clockwork_trap/proc/add_output(datum/component/clockwork_trap/output) + output.outputs |= parent + +/// Signal proc for when the trap calls CLOCKWORK_SIGNAL_RECEIVED +/datum/component/clockwork_trap/proc/trigger() + SIGNAL_HANDLER + + return TRUE + +/// Signal proc for when the trap has ATOM_ATTACK_HAND called on it +/datum/component/clockwork_trap/proc/attack_hand(mob/user) + SIGNAL_HANDLER + + return + +/// Signal proc when the trap has PARENT_ATTACKBY called on it +/datum/component/clockwork_trap/proc/on_attackby(datum/source, obj/item/attack_item, mob/user) + SIGNAL_HANDLER + + if(!IS_CLOCK(user) || !istype(attack_item, /obj/item/clockwork/clockwork_slab)) + return + + var/obj/item/clockwork/clockwork_slab/slab = attack_item + + if(slab.buffer) + + if(takes_input) + to_chat(user, span_brass("You connect [slab.buffer.parent] to [parent].")) + add_output(slab.buffer) + slab.buffer = null + + else + to_chat(user, span_brass("That device does not accept input.")) + + else + + if(sends_input) + to_chat(user, span_brass("You prepare to connect [parent] with other devices.")) + slab.buffer = src + + else + + to_chat(user, span_brass("That device does not output anything.")) + +/// Sends a signal to activate to every outputting component in `outputs` +/datum/component/clockwork_trap/proc/trigger_connected() + for(var/datum/output as anything in outputs) //must be typecasted because of how SEND_SIGNAL works + + SEND_SIGNAL(output, COMSIG_CLOCKWORK_SIGNAL_RECEIVED) diff --git a/tff_modular/modules/antagonists/clock_cult/temp_visual.dm b/tff_modular/modules/antagonists/clock_cult/temp_visual.dm new file mode 100644 index 00000000000..30bcdefc2b5 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/temp_visual.dm @@ -0,0 +1,221 @@ +#define MENDING_MANTRA_SCALE 2 + +//temporary visual effects(/obj/effect/temp_visual) used by clock stuff +/obj/effect/temp_visual/ratvar + name = "ratvar's light" + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_effects.dmi' + duration = 8 + randomdir = FALSE + layer = ABOVE_NORMAL_TURF_LAYER + + +/obj/effect/temp_visual/ratvar/door + icon_state = "ratvardoorglow" + layer = CLOSED_DOOR_LAYER //above closed doors + + +/obj/effect/temp_visual/ratvar/door/window + icon_state = "ratvarwindoorglow" + layer = ABOVE_WINDOW_LAYER + + +/obj/effect/temp_visual/ratvar/beam + icon_state = "ratvarbeamglow" + + +/obj/effect/temp_visual/ratvar/beam/door + layer = CLOSED_DOOR_LAYER + + +/obj/effect/temp_visual/ratvar/beam/grille + layer = BELOW_OBJ_LAYER + + +/obj/effect/temp_visual/ratvar/beam/itemconsume + layer = HIGH_OBJ_LAYER + + +/obj/effect/temp_visual/ratvar/beam/falsewall + layer = OBJ_LAYER + + +/obj/effect/temp_visual/ratvar/beam/catwalk + layer = LATTICE_LAYER + + +/obj/effect/temp_visual/ratvar/wall + icon_state = "ratvarwallglow" + + +/obj/effect/temp_visual/ratvar/wall/false + layer = OBJ_LAYER + + +/obj/effect/temp_visual/ratvar/floor + icon_state = "ratvarfloorglow" + + +/obj/effect/temp_visual/ratvar/floor/catwalk + layer = LATTICE_LAYER + + +/obj/effect/temp_visual/ratvar/window + icon_state = "ratvarwindowglow" + layer = ABOVE_OBJ_LAYER + + +/obj/effect/temp_visual/ratvar/window/single + icon_state = "ratvarwindowglow_s" + + +/obj/effect/temp_visual/ratvar/gear + icon_state = "ratvargearglow" + layer = BELOW_OBJ_LAYER + + +/obj/effect/temp_visual/ratvar/grille + icon_state = "ratvargrilleglow" + layer = BELOW_OBJ_LAYER + + +/obj/effect/temp_visual/ratvar/grille/broken + icon_state = "ratvarbrokengrilleglow" + + +/obj/effect/temp_visual/ratvar/belligerent + layer = ABOVE_MOB_LAYER + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + icon_state = "belligerent_eye" + pixel_y = 20 + duration = 2 SECONDS + + +/obj/effect/temp_visual/ratvar/belligerent/Initialize(mapload) + . = ..() + animate(src, alpha = 0, time = duration, easing = EASE_OUT) + + +/obj/effect/temp_visual/ratvar/mending_mantra + layer = ABOVE_MOB_LAYER + duration = 2 SECONDS + alpha = 200 + icon_state = "mending_mantra" + light_range = 1.5 + light_color = "#1E8CE1" + + +/obj/effect/temp_visual/ratvar/mending_mantra/Initialize(mapload) + . = ..() + transform = matrix() * MENDING_MANTRA_SCALE + var/matrix/mantra_matrix = transform + mantra_matrix.Turn(90) + animate(src, alpha = 20, time = duration, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL) + animate(src, transform = mantra_matrix, time = duration, flags = ANIMATION_PARALLEL) + + +/obj/effect/temp_visual/ratvar/ocular_warden + name = "warden's gaze" + layer = ABOVE_MOB_LAYER + icon_state = "warden_gaze" + duration = 3 + + +/obj/effect/temp_visual/ratvar/ocular_warden/Initialize(mapload) + . = ..() + pixel_x = rand(-8, 8) + pixel_y = rand(-10, 10) + animate(src, alpha = 0, time = duration, easing = EASE_OUT) + +/obj/effect/temp_visual/ratvar/component + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_objects.dmi' + icon_state = "belligerent_eye" + layer = ABOVE_MOB_LAYER + duration = 1 SECONDS + + +/obj/effect/temp_visual/ratvar/component/Initialize(mapload) + . = ..() + transform = matrix() * 0.75 + pixel_x = rand(-10, 10) + pixel_y = rand(-10, -2) + animate(src, pixel_y = pixel_y + 10, alpha = 50, time = 1 SECONDS, easing = EASE_OUT) + + +/obj/effect/temp_visual/ratvar/component/cogwheel + icon_state = "vanguard_cogwheel" + + +/obj/effect/temp_visual/ratvar/component/capacitor + icon_state = "geis_capacitor" + + +/obj/effect/temp_visual/ratvar/component/alloy + icon_state = "replicant_alloy" + + +/obj/effect/temp_visual/ratvar/component/ansible + icon_state = "hierophant_ansible" + + +/obj/effect/temp_visual/ratvar/warp + name = "spatial distortion" + icon_state = "teleport" + layer = ABOVE_MOB_LAYER + + +/obj/effect/temp_visual/steam + name = "steam" + desc = "Steam! It's hot. It also serves as a game distribution platform." + icon_state = "smoke" + duration = 1.5 SECONDS + + +/obj/effect/temp_visual/steam/Initialize(mapload, steam_direction) + . = ..() + setDir(steam_direction) + var/x_offset = 0 + var/y_offset = 0 + switch(dir) + if(NORTH) + y_offset = 8 + + if(EAST) + x_offset = 4 + y_offset = 4 + + if(SOUTH) + y_offset = 2 + + if(WEST) + x_offset = -4 + y_offset = 4 + + animate(src, pixel_x = x_offset, pixel_y = y_offset, alpha = 50, time = 1.5 SECONDS) + + +/obj/effect/temp_visual/steam_release + name = "all the steam" + +/obj/effect/temp_visual/steam_release/Initialize(mapload) + . = ..() + for(var/cardinal in GLOB.cardinals) + var/turf/cardinal_step = get_step(src, cardinal) + new/obj/effect/temp_visual/steam(cardinal_step, cardinal) + + playsound(src, 'sound/machines/clockcult/steam_whoosh.ogg', 30) + return INITIALIZE_HINT_QDEL + +/obj/effect/temp_visual/ratvar/constructing_effect + icon_state = "replica_fabricator_create" + layer = ABOVE_ALL_MOB_LAYER + plane = ABOVE_GAME_PLANE + anchored = TRUE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + duration = 1 SECONDS + +/obj/effect/temp_visual/ratvar/constructing_effect/Initialize(mapload, create_delay) + duration = create_delay + return ..() + + +#undef MENDING_MANTRA_SCALE diff --git a/tff_modular/modules/antagonists/clock_cult/turf_checker.dm b/tff_modular/modules/antagonists/clock_cult/turf_checker.dm new file mode 100644 index 00000000000..4e04884fdd6 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/turf_checker.dm @@ -0,0 +1,202 @@ +/* +//a simple element that listens for passed signals and then returns based on if get_turf's type is within valid_turfs, if you need more then use the /complex subtype +/datum/component/turf_checker + dupe_mode = COMPONENT_DUPE_ALLOWED + ///list of turf types that are valid for us + var/list/valid_turfs + ///the signal we listen for + var/registered_signal + ///do we listen for COMSIG_MOVABLE_MOVED + var/check_on_move + ///our last validity state, used to save on checks + var/last_validity_state = FALSE + ///a ref to the loc that we listen to the movement of to send our signals + var/atom/watched_holder + ///our parent's recursive locs minus it and watched_holder + var/list/trimmed_recursive_locs + +/** + * valid_turfs - the list of turf types that are valid for when we check + * registered_signal - the signal from parent we listen for to call on_signal_recieved() + * check_on_move - should we check turf every time our parent calls Moved() + * update_state_proc - proc for parent to call when we send COMSIG_TURF_CHECKER_UPDATE_STATE + * register_loc - should we listen to Moved() on locs of our parent as well + */ +/datum/component/turf_checker/Initialize(list/valid_turfs, registered_signal, check_on_move = FALSE, update_state_proc, register_loc = TRUE) + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + + src.valid_turfs = valid_turfs + src.registered_signal = registered_signal + src.check_on_move = check_on_move + if(update_state_proc && check_on_move) + parent.RegisterSignal(src, COMSIG_TURF_CHECKER_UPDATE_STATE, update_state_proc) + +/datum/component/turf_checker/RegisterWithParent() + if(registered_signal) + RegisterSignal(parent, registered_signal, PROC_REF(on_signal_recieved)) + + if(check_on_move) + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_attached_moved)) + trimmed_recursive_locs = list() + get_new_locs(parent) + check_turf(parent) + +/datum/component/turf_checker/UnregisterFromParent() + if(registered_signal) + UnregisterSignal(parent, registered_signal) + + if(check_on_move) + parent.UnregisterSignal(src, COMSIG_TURF_CHECKER_UPDATE_STATE) + UnregisterSignal(parent, COMSIG_MOVABLE_MOVED) + + if(watched_holder != parent) + UnregisterSignal(watched_holder, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING, COMSIG_ATOM_ABSTRACT_EXITED)) + + for(var/atom/recursive_loc in trimmed_recursive_locs) + UnregisterSignal(recursive_loc, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING)) + +/datum/component/turf_checker/proc/on_signal_recieved(atom/movable/checked_atom, atom/movable/check_override, do_check_turf = TRUE, register_to, unregister_from) + SIGNAL_HANDLER + if(register_to) //keeping these here in case your use case can handle this on the attached atom in a cheaper way than the /complex subtype + RegisterSignal(register_to, COMSIG_MOVABLE_MOVED, PROC_REF(check_turf_parent_only)) + + if(unregister_from) + UnregisterSignal(unregister_from, COMSIG_MOVABLE_MOVED) + + if(do_check_turf) + return check_turf(checked_atom, check_override) + +//so we dont override checked_atom with old_loc +/datum/component/turf_checker/proc/check_turf_parent_only(atom/movable/checked_atom) + SIGNAL_HANDLER + check_turf(checked_atom) + +/datum/component/turf_checker/proc/check_turf(atom/movable/checked_atom, atom/movable/check_override) + SIGNAL_HANDLER + if(check_override) + checked_atom = check_override + + var/turf/checked_turf_type = get_turf(checked_atom) + if(!checked_turf_type) + return COMPONENT_CHECKER_INVALID_TURF + + checked_turf_type = checked_turf_type.type + if(!(checked_turf_type in valid_turfs)) + if(check_on_move && last_validity_state) + last_validity_state = FALSE + SEND_SIGNAL(src, COMSIG_TURF_CHECKER_UPDATE_STATE, FALSE, checked_atom) + return COMPONENT_CHECKER_INVALID_TURF + + if(check_on_move && !last_validity_state) + last_validity_state = TRUE + SEND_SIGNAL(src, COMSIG_TURF_CHECKER_UPDATE_STATE, TRUE, checked_atom) + return COMPONENT_CHECKER_VALID_TURF + +/datum/component/turf_checker/proc/get_new_locs() + if(QDELETED(parent)) + return + + var/atom/movable/movable_parent = parent + var/list/attached_locs = movable_parent.get_locs_recursive() + var/atom/highest_holder = attached_locs[length(attached_locs)] + if(highest_holder == parent && watched_holder == parent) + return + + var/list/old_recursive_locs = trimmed_recursive_locs + trimmed_recursive_locs = list() + if(watched_holder != highest_holder) + if(watched_holder != parent) + UnregisterSignal(watched_holder, list(COMSIG_MOVABLE_MOVED, COMSIG_ATOM_ABSTRACT_EXITED, COMSIG_QDELETING)) + + if(highest_holder != parent) + watched_holder = highest_holder + RegisterSignal(highest_holder, COMSIG_MOVABLE_MOVED, PROC_REF(check_turf_parent_only)) + RegisterSignal(highest_holder, COMSIG_ATOM_ABSTRACT_EXITED, PROC_REF(on_holder_exited)) + RegisterSignal(highest_holder, COMSIG_QDELETING, PROC_REF(on_loc_qdeleted)) + if(length(attached_locs) > 2) + trimmed_recursive_locs = attached_locs - list(attached_locs[1], highest_holder) + for(var/atom/recursive_loc in trimmed_recursive_locs) + if(!QDELETED(recursive_loc)) + if(recursive_loc in old_recursive_locs) + old_recursive_locs -= recursive_loc + else + RegisterSignal(recursive_loc, COMSIG_MOVABLE_MOVED, PROC_REF(check_holder)) + RegisterSignal(recursive_loc, COMSIG_QDELETING, PROC_REF(on_loc_qdeleted)) + else + watched_holder = parent + + for(var/atom/old_recursive_loc in old_recursive_locs) + UnregisterSignal(old_recursive_loc, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING)) + +/datum/component/turf_checker/proc/check_holder(atom/movable/moved) + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + if(movable_parent.get_highest_non_turf_loc() != watched_holder) + get_new_locs() + +/datum/component/turf_checker/proc/on_attached_moved(atom/movable/moved, atom/old_loc) + SIGNAL_HANDLER + if(watched_holder == parent) + var/atom/movable/movable_parent = parent + if(movable_parent.get_highest_non_turf_loc() == parent) + check_turf(moved) + return + + get_new_locs() + check_turf(moved) + +/datum/component/turf_checker/proc/on_holder_exited(atom/exited, atom/movable/gone) + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + if(gone == parent || movable_parent.get_highest_non_turf_loc() != watched_holder) + get_new_locs() + check_turf(parent) + +/datum/component/turf_checker/proc/on_loc_qdeleted(atom/destroyed, forced) + SIGNAL_HANDLER + UnregisterSignal(destroyed, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING)) + if(destroyed == watched_holder) + UnregisterSignal(destroyed, COMSIG_ATOM_ABSTRACT_EXITED) + watched_holder = null + else + trimmed_recursive_locs -= destroyed + get_new_locs() +*/ + +//A mob with this compoent will heal on its life() if standing on the given turfs +/datum/component/turf_healing + ///what damage types to heal with a key of how much to heal for + var/list/healing_types = list() + ///typecache of what turfs to heal on + var/list/healing_turfs + +/datum/component/turf_healing/Initialize(list/healing_types, list/healing_turfs) + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE + + if(healing_types) + src.healing_types = healing_types + if(healing_turfs) + src.healing_turfs = typecacheof(healing_turfs) + return ..() + +/datum/component/turf_healing/RegisterWithParent() + RegisterSignal(parent, COMSIG_LIVING_LIFE, PROC_REF(handle_healing)) + +/datum/component/turf_healing/UnregisterFromParent() + UnregisterSignal(parent, COMSIG_LIVING_LIFE) + +/datum/component/turf_healing/proc/handle_healing(mob/living/owner, seconds_per_tick) + SIGNAL_HANDLER + + var/mob/living/healed_mob = parent + var/turf/on_turf = get_turf(healed_mob) + if(!is_type_in_typecache(on_turf, healing_turfs) || (healed_mob.health >= healed_mob.maxHealth)) + return + + for(var/entry in healing_types) + if(entry == STAMINA) + healed_mob.adjust_stamina_loss(-healing_types[entry] * seconds_per_tick) + continue + healed_mob.heal_damage_type((healing_types[entry] * seconds_per_tick), entry) diff --git a/tff_modular/modules/antagonists/clock_cult/turfs/floor.dm b/tff_modular/modules/antagonists/clock_cult/turfs/floor.dm new file mode 100644 index 00000000000..98ca06df537 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/turfs/floor.dm @@ -0,0 +1,94 @@ +/turf/open/indestructible/reebe_void + name = "void" + desc = "A white, empty void, quite unlike anything you've seen before." + icon_state = "reebemap" + layer = SPACE_LAYER + baseturfs = /turf/open/indestructible/reebe_void + planetary_atmos = TRUE + bullet_bounce_sound = null //forever falling + init_air = FALSE + ///is this turf walkable + var/walkable = FALSE + +/turf/open/indestructible/reebe_void/Initialize(mapload) + . = ..() + icon_state = "reebegame" + ADD_TRAIT(src, TRAIT_BRONZE_TURF, TURF_TRAIT) + +/turf/open/indestructible/reebe_void/Enter(atom/movable/movable) + if(walkable) + return ..() + + if(!..()) + return FALSE + else + if(istype(movable, /obj/structure/window)) + return FALSE + if(istype(movable, /obj/projectile)) + return TRUE + return FALSE + +/turf/open/indestructible/reebe_void/RemoveLattice() + return + +/turf/open/indestructible/reebe_void/walkable + icon_state = "reebespawn" + baseturfs = /turf/open/indestructible/reebe_void/walkable + walkable = TRUE + +/turf/open/indestructible/reebe_void/spawning + icon_state = "reebespawn" + +/turf/open/indestructible/reebe_void/spawning/Initialize(mapload) + . = ..() + if(mapload) + if(prob(2)) + new /obj/structure/fluff/clockwork/alloy_shards/large(src) + + if(prob(4)) + new /obj/structure/fluff/clockwork/alloy_shards/medium(src) + + if(prob(6)) + new /obj/structure/fluff/clockwork/alloy_shards/small(src) + +/turf/open/indestructible/reebe_void/spawning/lattices + icon_state = "reebelattice" + +/turf/open/indestructible/reebe_void/spawning/lattices/Initialize(mapload) + . = ..() + if(mapload && prob(40)) + new /obj/structure/lattice/clockwork(src) + +//edge of the reebe map +/turf/open/indestructible/reebe_void/void_edge + icon_state = "reebespawn" + +/turf/open/indestructible/reebe_flooring //used on reebe + name = "clockwork floor" + desc = "You feel a faint warmth from below it." + icon_state = "clockwork_floor" + planetary_atmos = TRUE + baseturfs = /turf/open/indestructible/reebe_flooring + turf_flags = NOJAUNT + +/turf/open/indestructible/reebe_flooring/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_BRONZE_TURF, TURF_TRAIT) + +/turf/open/indestructible/reebe_flooring/ratvar_act() + return FALSE + +/turf/open/indestructible/reebe_flooring/flat + icon_state = "reebe" + +/turf/open/indestructible/reebe_flooring/filled + icon_state = "clockwork_floor_filled" + +/turf/open/floor/engine/clockwork + name = "clockwork floor" + desc = "You feel a faint warmth from below it." + icon_state = "clockwork_floor" + +/turf/open/floor/engine/clockwork/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_BRONZE_TURF, TURF_TRAIT) diff --git a/tff_modular/modules/antagonists/clock_cult/turfs/wall_lattice.dm b/tff_modular/modules/antagonists/clock_cult/turfs/wall_lattice.dm new file mode 100644 index 00000000000..16cfcfed5ca --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/turfs/wall_lattice.dm @@ -0,0 +1,116 @@ +#define BASE_REGEN_PER_SECOND 5 +#define EMPOWERED_REGEN_PER_SECOND 10 +#define BASE_REGEN_DELAY 30 SECONDS +#define EMPOWERED_REGEN_DELAY 10 SECONDS +#define EMPOWER_PASSIVE_DRAIN 0.5 + +/obj/structure/destructible/clockwork/wall_lattice + name = "clockwork stabilization lattice" + desc = "A field of energy around a clockwork wall. If destroyed the wall would quickly implode." + icon = 'tff_modular/modules/antagonists/clock_cult/icons/obj/clockwork_effects.dmi' + icon_state = "wall_energy_lattice" + alpha = 130 + layer = ABOVE_NORMAL_TURF_LAYER + max_integrity = 400 + resistance_flags = ACID_PROOF | FIRE_PROOF | LAVA_PROOF + anchored = TRUE + break_sound = null + armor_type = /datum/armor/clockwork_wall_lattice + break_message = span_warning("The stabilization lattice rapidly collapses, bringing the wall its supporting with it!") + debris = null + damage_cap = 80 + immune_to_servant_attacks = TRUE + can_rotate = FALSE + ///The wall we are linked to + var/turf/closed/wall/clockwork/linked_wall + ///Are we empowered + var/is_empowered = FALSE + ///The game tick we start regenerating at + var/regenerate_at = 0 + ///How much do we regenerate per second + var/regen_per_second = BASE_REGEN_PER_SECOND + ///How long does it take us to start regenerating + var/regen_delay = BASE_REGEN_DELAY + +/obj/structure/destructible/clockwork/wall_lattice/Initialize(mapload, atom/link_to) + . = ..() + linked_wall = link_to + if(linked_wall) + if(!istype(linked_wall)) + stack_trace("clockwork wall lattice at x[src.x], y[src.y], z[src.z] linked to something that was not a clockwork wall([link_to.type])") + return + + var/turf/our_turf = get_turf(src) + if(istype(our_turf, /turf/closed/wall/clockwork)) + linked_wall = our_turf + +/obj/structure/destructible/clockwork/wall_lattice/Destroy() + STOP_PROCESSING(SSthe_ark, src) + var/turf/closed/wall/clockwork/temp = linked_wall //this is to super ensure we dont loop + linked_wall = null + if(!QDELETED(temp)) + temp.dismantle_wall() + return ..() + +/obj/structure/destructible/clockwork/wall_lattice/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration) + if(!regenerate_at) + START_PROCESSING(SSthe_ark, src) + regenerate_at = world.time + regen_delay + return ..() + +/obj/structure/destructible/clockwork/wall_lattice/process(seconds_per_tick) + if(world.time > regenerate_at) + repair_damage(regen_per_second * seconds_per_tick) + + if(atom_integrity >= max_integrity) + regenerate_at = 0 + return PROCESS_KILL + +/obj/structure/destructible/clockwork/wall_lattice/play_attack_sound(damage_amount, damage_type, damage_flag) + playsound(get_turf(src), 'sound/effects/empulse.ogg', 75, TRUE) + +/obj/structure/destructible/clockwork/wall_lattice/proc/empower() + if(is_empowered) + return FALSE + + //SSthe_ark.passive_power -= EMPOWER_PASSIVE_DRAIN + regen_per_second = EMPOWERED_REGEN_PER_SECOND + regen_delay = EMPOWERED_REGEN_DELAY + set_armor(/datum/armor/empowered_clockwork_wall_lattice) + if(regenerate_at) + regenerate_at = regenerate_at - (BASE_REGEN_DELAY - EMPOWERED_REGEN_DELAY) + return TRUE + +/obj/structure/destructible/clockwork/wall_lattice/proc/unempower() + if(!is_empowered) + return FALSE + + //SSthe_ark.passive_power += EMPOWER_PASSIVE_DRAIN + regen_per_second = BASE_REGEN_PER_SECOND + regen_delay = BASE_REGEN_DELAY + set_armor(/datum/armor/clockwork_wall_lattice) + if(regenerate_at) + regenerate_at = regenerate_at + (BASE_REGEN_DELAY - EMPOWERED_REGEN_DELAY) + return TRUE + +/datum/armor/clockwork_wall_lattice + melee = 10 + bullet = 40 + laser = 30 + energy = 30 + bomb = 100 + bio = 100 + +/datum/armor/empowered_clockwork_wall_lattice + melee = 30 + bullet = 60 + laser = 50 + energy = 50 + bomb = 100 + bio = 100 + +#undef BASE_REGEN_PER_SECOND +#undef EMPOWERED_REGEN_PER_SECOND +#undef BASE_REGEN_DELAY +#undef EMPOWERED_REGEN_DELAY +#undef EMPOWER_PASSIVE_DRAIN diff --git a/tff_modular/modules/antagonists/clock_cult/turfs/walls.dm b/tff_modular/modules/antagonists/clock_cult/turfs/walls.dm new file mode 100644 index 00000000000..7b04dc087d6 --- /dev/null +++ b/tff_modular/modules/antagonists/clock_cult/turfs/walls.dm @@ -0,0 +1,153 @@ +/turf/closed/wall/clockwork //version created by clock cultists + name = "clockwork wall" + desc = "A forboding clump of gears that turn on their own. A faint glow emanates from within." + icon = 'icons/turf/walls/clockwork_wall.dmi' + icon_state = "clockwork_wall-0" + base_icon_state = "clockwork_wall" + turf_flags = IS_SOLID + smoothing_flags = SMOOTH_BITMASK + canSmoothWith = null + uses_integrity = FALSE + sheet_type = /obj/item/stack/sheet/bronze + sheet_amount = 2 + girder_type = /obj/structure/girder/bronze + turf_flags = NOJAUNT + hardness = 3 //very hard for hulks to break + //for deconstruction + var/d_state = INTACT + ///Should we spawn with a stabilization lattice + var/should_spawn_lattice = TRUE + ///Our linked lattice + var/obj/structure/destructible/clockwork/wall_lattice/linked_lattice + +/turf/closed/wall/clockwork/Initialize(mapload) + . = ..() + if(should_spawn_lattice) + linked_lattice = new(src, src) + +/turf/closed/wall/clockwork/Destroy() + if(!QDELETED(linked_lattice)) + qdel(linked_lattice) + linked_lattice = null + return ..() + +/turf/closed/wall/clockwork/hulk_recoil(obj/item/bodypart/arm, mob/living/carbon/human/hulkman, damage = 41) + if(IS_CLOCK(hulkman)) //dont recoil for clock cultists + damage = 0 + return ..() + +/turf/closed/wall/clockwork/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) + return + +/turf/closed/wall/clockwork/deconstruction_hints(mob/user) + switch(d_state) + if(INTACT) + if(IS_CLOCK(user)) + return span_notice("You see a way to unwind the gears with a wrench.") + else + return span_notice("You have no idea how this works! You think you see a small cog that could be cut loose.") + if(COVER_COG_REMOVED) + return span_notice("The outer cog has been cut loose, and some inner transmission cogs secured by screws are visable.") + if(TRANSMISSION_COGS_REMOVED) + return span_notice("The transmission cogs have been screwed loose. It looks like you could unbolt the gears now.") + if(GEARS_UNBOLTED) + return span_notice("The main gears have been unbolted and have stopped turning. You see a support beam that looks like it might fall off if heated.") + if(INNER_PANEL_REMOVED) + return span_notice("The support beam has been heated off. It looks like you could pry the rest apart.") + if(GEARS_UNWOUND) + return span_notice("The gears have been unwound with a wrench. You could take the rest apart with a crowbar.") + +/turf/closed/wall/clockwork/try_decon(obj/item/item_tool, mob/user) + switch(d_state) + if(INTACT) + if(IS_CLOCK(user) && item_tool.tool_behaviour == TOOL_WRENCH) + to_chat(user, span_notice("You start to unwind the gears")) + if(next_decon_state(item_tool, user, d_state, GEARS_UNWOUND, "You unwind the gears.")) + return TRUE + if(item_tool.tool_behaviour == TOOL_WIRECUTTER) + item_tool.play_tool_sound(src, 100) + d_state = COVER_COG_REMOVED + to_chat(user, span_notice("You cut the outer cog.")) + return TRUE + + if(COVER_COG_REMOVED) + if(item_tool.tool_behaviour == TOOL_SCREWDRIVER) + to_chat(user, span_notice("You start to unscrew the transmission cogs.")) + if(next_decon_state(item_tool, user, d_state, TRANSMISSION_COGS_REMOVED, "You unscrew the transmission cogs.")) + return TRUE + else if(item_tool.tool_behaviour == TOOL_WIRECUTTER) + item_tool.play_tool_sound(src, 100) + d_state = INTACT + to_chat(user, span_notice("You put the cover cog back in place.")) + return TRUE + + if(TRANSMISSION_COGS_REMOVED) + if(item_tool.tool_behaviour == TOOL_WRENCH) + to_chat(user, span_notice("You start to unbolt the main gears.")) + if(next_decon_state(item_tool, user, d_state, GEARS_UNBOLTED, "You unbolt the main gears.")) + return TRUE + if(item_tool.tool_behaviour == TOOL_SCREWDRIVER) + to_chat(user, span_notice("You start to tighten thetransmission cogs.")) + if(next_decon_state(item_tool, user, d_state, COVER_COG_REMOVED, "You tighten the transmission cogs.")) + return TRUE + + if(GEARS_UNBOLTED) + if(item_tool.tool_behaviour == TOOL_WELDER) + if(!item_tool.tool_start_check(user, amount=0)) + return + to_chat(user, span_notice("You start to weld the support beam loose.")) + if(next_decon_state(item_tool, user, d_state, INNER_PANEL_REMOVED, "You weld the support beam loose.", 3 SECONDS)) + return TRUE + if(item_tool.tool_behaviour == TOOL_WRENCH) + to_chat(user, span_notice("You start to re-attach the main gears.")) + if(next_decon_state(item_tool, user, d_state, TRANSMISSION_COGS_REMOVED, "You re-attach the main gears.")) + return TRUE + + if(INNER_PANEL_REMOVED) + if(item_tool.tool_behaviour == TOOL_CROWBAR) + to_chat(user, span_notice("You start to pry apart the [src].")) + if(next_decon_state(item_tool, user, d_state, sent_message = "You pry apart the [src].")) + dismantle_wall() + return TRUE + if(item_tool.tool_behaviour == TOOL_WELDER) + if(!item_tool.tool_start_check(user, amount=0)) + return + to_chat(user, span_notice("You start to weld the support beam back into place.")) + if(next_decon_state(item_tool, user, d_state, GEARS_UNBOLTED, "You weld the support beam back into place.", 3 SECONDS)) + return TRUE + + if(GEARS_UNWOUND) + if(item_tool.tool_behaviour == TOOL_CROWBAR) + to_chat(user, span_notice("You tart to pry apart the [src].")) + if(next_decon_state(item_tool, user, d_state, sent_message = "You pry apart the [src].")) + dismantle_wall() + return TRUE + if(item_tool.tool_behaviour == TOOL_WRENCH) + to_chat(user, span_notice("You start to re-wind the gears.")) + if(next_decon_state(item_tool, user, d_state, INTACT, "You re-wind the gears.")) + return TRUE + return FALSE + +//do the deconstruction stuff, this really should be a proc on Rwalls as well +/turf/closed/wall/clockwork/proc/next_decon_state(obj/item/used_tool, mob/user, current_state, set_state, sent_message, use_time = 2 SECONDS) + if(on_reebe(src)) + use_time = round(use_time * 0.2, 0.1) //it takes much less time to deconstruct walls on reebe + + if(used_tool.use_tool(src, user, use_time, volume=100)) + if(!istype(src, /turf/closed/wall/clockwork) || d_state != current_state) + return TRUE + if(set_state) + d_state = set_state + to_chat(user, span_notice("[sent_message]")) + return TRUE + +/turf/closed/wall/clockwork/ratvar_act() + return FALSE + +/turf/closed/wall/clockwork/rust_heretic_act() + visible_message(span_warning("\The [src] glows for a second, but is uneffected by the magic!")) + return + +/turf/closed/wall/clockwork/reebe //for mapping on reebe + baseturfs = /turf/open/indestructible/reebe_flooring + should_spawn_lattice = FALSE diff --git a/tgstation.dme b/tgstation.dme index 2b91e359fb5..381861c8000 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -424,6 +424,7 @@ #include "code\__DEFINES\traits\macros.dm" #include "code\__DEFINES\traits\sources.dm" #include "code\__DEFINES\~ff_defines\barsigns.dm" +#include "code\__DEFINES\~ff_defines\clockwork.dm" #include "code\__DEFINES\~ff_defines\DNA.dm" #include "code\__DEFINES\~ff_defines\dynamic.dm" #include "code\__DEFINES\~ff_defines\flavor_misc.dm" @@ -9767,6 +9768,116 @@ #include "tff_modular\modules\antagonism_updates\uplink_item\code\categories\device_tools.dm" #include "tff_modular\modules\antagonism_updates\uplink_item\code\categories\explosive.dm" #include "tff_modular\modules\antagonism_updates\uplink_item\code\categories\stealthy_weapons.dm" +#include "tff_modular\modules\antagonists\clock_cult\area.dm" +#include "tff_modular\modules\antagonists\clock_cult\dimension_theme.dm" +#include "tff_modular\modules\antagonists\clock_cult\dynamic_ruleset.dm" +#include "tff_modular\modules\antagonists\clock_cult\enchantment.dm" +#include "tff_modular\modules\antagonists\clock_cult\globals.dm" +#include "tff_modular\modules\antagonists\clock_cult\hallucinations.dm" +#include "tff_modular\modules\antagonists\clock_cult\helpers.dm" +#include "tff_modular\modules\antagonists\clock_cult\hint_element.dm" +#include "tff_modular\modules\antagonists\clock_cult\holy_water.dm" +#include "tff_modular\modules\antagonists\clock_cult\language.dm" +#include "tff_modular\modules\antagonists\clock_cult\multi_area_bound.dm" +#include "tff_modular\modules\antagonists\clock_cult\outfit.dm" +#include "tff_modular\modules\antagonists\clock_cult\pickup_element.dm" +#include "tff_modular\modules\antagonists\clock_cult\portal.dm" +#include "tff_modular\modules\antagonists\clock_cult\procs.dm" +#include "tff_modular\modules\antagonists\clock_cult\ratvar.dm" +#include "tff_modular\modules\antagonists\clock_cult\reebe_modules.dm" +#include "tff_modular\modules\antagonists\clock_cult\status_effects.dm" +#include "tff_modular\modules\antagonists\clock_cult\structure_info_element.dm" +#include "tff_modular\modules\antagonists\clock_cult\temp_visual.dm" +#include "tff_modular\modules\antagonists\clock_cult\turf_checker.dm" +#include "tff_modular\modules\antagonists\clock_cult\actions\_action.dm" +#include "tff_modular\modules\antagonists\clock_cult\actions\add_warp_area.dm" +#include "tff_modular\modules\antagonists\clock_cult\actions\clockmob_warp.dm" +#include "tff_modular\modules\antagonists\clock_cult\actions\purge_reagents.dm" +#include "tff_modular\modules\antagonists\clock_cult\actions\recall_slab.dm" +#include "tff_modular\modules\antagonists\clock_cult\actions\space_fold.dm" +#include "tff_modular\modules\antagonists\clock_cult\actions\whirring_convergence.dm" +#include "tff_modular\modules\antagonists\clock_cult\actions\emience_teleports\basic_teleports.dm" +#include "tff_modular\modules\antagonists\clock_cult\actions\emience_teleports\linked_abscond.dm" +#include "tff_modular\modules\antagonists\clock_cult\actions\emience_teleports\teleport_to_servant.dm" +#include "tff_modular\modules\antagonists\clock_cult\antag_datums\clock_cult_team.dm" +#include "tff_modular\modules\antagonists\clock_cult\antag_datums\clock_cultist.dm" +#include "tff_modular\modules\antagonists\clock_cult\antag_datums\clockmob_antag_datum.dm" +#include "tff_modular\modules\antagonists\clock_cult\antag_datums\clocksense_alert.dm" +#include "tff_modular\modules\antagonists\clock_cult\antag_datums\eminence_antag_datum.dm" +#include "tff_modular\modules\antagonists\clock_cult\ark_subsystem\_ark_subsystem.dm" +#include "tff_modular\modules\antagonists\clock_cult\ark_subsystem\warp_effects.dm" +#include "tff_modular\modules\antagonists\clock_cult\items\clock_stock_parts.dm" +#include "tff_modular\modules\antagonists\clock_cult\items\clockwork_slab.dm" +#include "tff_modular\modules\antagonists\clock_cult\items\clothing.dm" +#include "tff_modular\modules\antagonists\clock_cult\items\integration_cog.dm" +#include "tff_modular\modules\antagonists\clock_cult\items\reebe.dm" +#include "tff_modular\modules\antagonists\clock_cult\items\replica_fabricator.dm" +#include "tff_modular\modules\antagonists\clock_cult\items\soul_vessel.dm" +#include "tff_modular\modules\antagonists\clock_cult\items\tools.dm" +#include "tff_modular\modules\antagonists\clock_cult\items\weaponry.dm" +#include "tff_modular\modules\antagonists\clock_cult\machines\airlock.dm" +#include "tff_modular\modules\antagonists\clock_cult\machines\clock_sleeper.dm" +#include "tff_modular\modules\antagonists\clock_cult\machines\clockwork_operating_computer.dm" +#include "tff_modular\modules\antagonists\clock_cult\machines\comms_relay.dm" +#include "tff_modular\modules\antagonists\clock_cult\machines\observation_console.dm" +#include "tff_modular\modules\antagonists\clock_cult\mechas\mecha_effects.dm" +#include "tff_modular\modules\antagonists\clock_cult\mechas\mecha_equipment.dm" +#include "tff_modular\modules\antagonists\clock_cult\mechas\steam_helios.dm" +#include "tff_modular\modules\antagonists\clock_cult\mobs\clockwork_golem.dm" +#include "tff_modular\modules\antagonists\clock_cult\mobs\clockwork_marauder.dm" +#include "tff_modular\modules\antagonists\clock_cult\mobs\cogscarab.dm" +#include "tff_modular\modules\antagonists\clock_cult\mobs\eminence.dm" +#include "tff_modular\modules\antagonists\clock_cult\mobs\clock_borgs\clock_borg.dm" +#include "tff_modular\modules\antagonists\clock_cult\mobs\clock_borgs\clock_borg_models.dm" +#include "tff_modular\modules\antagonists\clock_cult\mobs\clock_borgs\clock_borg_modules.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\_scripture.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\preservation\clockwork_armaments.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\preservation\dimensional_breach.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\preservation\summon_cogscarab.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\preservation\summon_marauder.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\preservation\vanguard.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\servitude\abscond.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\servitude\golem_conversion.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\servitude\hateful_manacles.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\servitude\integration_cog.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\servitude\kindle.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\servitude\sentinels_compromise.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\servitude\submission_sigil.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\servitude\vitality_sigil.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\structures\anchoring_crystal.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\structures\empower_wall.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\structures\interdiction_lens.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\structures\ocular_warden.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\structures\prosperity_prism.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\structures\stargazer.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\structures\tinkerers_cache.dm" +#include "tff_modular\modules\antagonists\clock_cult\scriptures\structures\transmission_sigil.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\_powered.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\_structure.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\anchor_crystal.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\brass_window.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\eminence_beacon.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\gear_base.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\interdiction_lens.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\lattice.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\ocular_warden.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\prosperity_prism.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\stargazer.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\the_ark.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\tinkerers_cache.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\sigil\_sigil.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\sigil\sigil_submission.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\sigil\sigil_transmission.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\sigil\sigil_vitality.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\traps\trap.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\traps\recievers\flipper.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\traps\recievers\skewer.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\traps\senders\delay.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\traps\senders\lever.dm" +#include "tff_modular\modules\antagonists\clock_cult\structures\traps\senders\pressure_sensor.dm" +#include "tff_modular\modules\antagonists\clock_cult\turfs\floor.dm" +#include "tff_modular\modules\antagonists\clock_cult\turfs\wall_lattice.dm" +#include "tff_modular\modules\antagonists\clock_cult\turfs\walls.dm" #include "tff_modular\modules\auguments\implants.dm" #include "tff_modular\modules\autoaccent\code\autoaccent.dm" #include "tff_modular\modules\bar_jukebox\code\jukebox.dm" diff --git a/tgui/packages/tgui/interfaces/AntagInfoClockAlt.tsx b/tgui/packages/tgui/interfaces/AntagInfoClockAlt.tsx new file mode 100644 index 00000000000..60a8697b778 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AntagInfoClockAlt.tsx @@ -0,0 +1,37 @@ +import { Icon, Section, Stack } from 'tgui-core/components'; +import { useBackend } from '../backend'; +import { Window } from '../layouts'; +import { type Objective, ObjectivePrintout } from './common/Objectives'; + +type Info = { + antag_name: string; + objectives: Objective[]; + marked_areas: string[]; +}; + +export const AntagInfoClockAlt = (props) => { + const { data } = useBackend(); + const { antag_name, objectives, marked_areas } = data; + return ( + + +
+ + + + {' You are the ' + antag_name + '! '} + + + + + + {'Our marked areas are: ' + marked_areas} + +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/clockcultmidround.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/clockcultmidround.ts new file mode 100644 index 00000000000..485076a32e4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/clockcultmidround.ts @@ -0,0 +1,18 @@ +import { type Antagonist, Category } from '../base'; + +export const MIDROUND_CLOCKWORK_MECHANICAL_DESCRIPTION = ` +You have discovered the secret knowledge and enlightenment at the station. +Perhaps Ratvar himself chose you as his follower, but it doesn't matter. +Now you must restore him to the world of the living... +`; + +const MidroundClockworkCultist: Antagonist = { + key: 'midroundclockworkcultist', + name: 'Midround Clockwork Cultist', + description: [ + MIDROUND_CLOCKWORK_MECHANICAL_DESCRIPTION, + ], + category: Category.Midround, +}; + +export default MidroundClockworkCultist; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/clockcultroundstart.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/clockcultroundstart.ts new file mode 100644 index 00000000000..50a1e90cb38 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/antagonists/antagonists/clockcultroundstart.ts @@ -0,0 +1,15 @@ +import { type Antagonist, Category } from '../base'; + +export const CLOCKWORK_MECHANICAL_DESCRIPTION = ` +You were sent to the station to change the minds of the lost and revive +the Rat'Var. Will you be able to do it, or will you fall like your god? +`; + +const RoundstartClockworkCultist: Antagonist = { + key: 'roundstartclockworkcultist', + name: 'Clockwork Cultist', + description: [CLOCKWORK_MECHANICAL_DESCRIPTION], + category: Category.Roundstart, +}; + +export default RoundstartClockworkCultist;