diff --git a/_maps/map_files/TgStation/tgstation.2.1.3.dmm b/_maps/map_files/TgStation/tgstation.2.1.3.dmm index e59d566a07a..a2831ee955f 100644 --- a/_maps/map_files/TgStation/tgstation.2.1.3.dmm +++ b/_maps/map_files/TgStation/tgstation.2.1.3.dmm @@ -2052,7 +2052,7 @@ pixel_x = 6; pixel_y = 24 }, -/obj/machinery/atmospherics/pipe/manifold4w/scrubbers/hidden, +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden, /turf/open/floor/plasteel{ icon_state = "redcorner"; dir = 4 @@ -2518,7 +2518,7 @@ d2 = 8; icon_state = "4-8" }, -/obj/machinery/atmospherics/pipe/manifold4w/supply/hidden, +/obj/machinery/atmospherics/pipe/manifold/supply/hidden, /turf/open/floor/plasteel, /area/security/prison) "aeV" = ( @@ -2936,8 +2936,7 @@ /obj/structure/table, /obj/item/device/electropack, /turf/open/floor/plasteel{ - icon_state = "whitehall"; - dir = 2 + icon_state = "red" }, /area/security/prison) "afE" = ( @@ -2954,9 +2953,9 @@ "afF" = ( /obj/structure/table, /obj/item/device/assembly/signaler, +/obj/item/clothing/suit/straight_jacket, /turf/open/floor/plasteel{ - dir = 2; - icon_state = "escape" + icon_state = "red" }, /area/security/prison) "afG" = ( @@ -2972,8 +2971,7 @@ pixel_x = -6 }, /turf/open/floor/plasteel{ - icon_state = "whitehall"; - dir = 2 + icon_state = "red" }, /area/security/prison) "afH" = ( @@ -2985,10 +2983,8 @@ }, /area/security/prison) "afI" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel{ - icon_state = "whitehall"; - dir = 2 + icon_state = "red" }, /area/security/prison) "afJ" = ( @@ -2997,8 +2993,7 @@ pixel_y = -27 }, /turf/open/floor/plasteel{ - icon_state = "whitehall"; - dir = 2 + icon_state = "red" }, /area/security/prison) "afK" = ( @@ -3236,13 +3231,13 @@ }, /area/security/prison) "agl" = ( -/obj/machinery/door/airlock/glass_security{ - name = "Insanity Ward"; - req_access_txt = "2" +/obj/machinery/door/airlock/security{ + name = "Interrogation"; + req_access = null; + req_access_txt = "63" }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel{ - icon_state = "white" + icon_state = "dark" }, /area/security/prison) "agm" = ( @@ -3312,16 +3307,13 @@ }, /area/security/warden) "ags" = ( -/obj/structure/cable{ - d1 = 1; - d2 = 2; - icon_state = "1-2" +/obj/structure/chair{ + dir = 4 }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel{ - icon_state = "showroomfloor" + icon_state = "dark" }, -/area/security/warden) +/area/security/prison) "agt" = ( /turf/open/floor/plasteel{ icon_state = "showroomfloor" @@ -3471,46 +3463,14 @@ }, /area/security/main) "agH" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/turf/open/floor/plasteel{ - icon_state = "warnwhite"; - dir = 1 - }, -/area/security/prison) -"agI" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/open/floor/plasteel{ - icon_state = "warnwhite"; - dir = 5 - }, -/area/security/prison) -"agJ" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 4 }, /turf/open/floor/plasteel{ icon_state = "dark" }, -/area/security/brig) -"agK" = ( -/obj/machinery/atmospherics/components/unary/vent_pump{ - dir = 4; - on = 1 - }, -/turf/open/floor/plasteel{ - icon_state = "dark" - }, -/area/security/brig) -"agL" = ( -/obj/structure/table, -/obj/item/weapon/folder/red, -/obj/item/weapon/pen, -/turf/open/floor/plasteel{ - dir = 9; - icon_state = "warnwhite" - }, /area/security/prison) -"agM" = ( +"agI" = ( /obj/machinery/airalarm{ pixel_y = 23 }, @@ -3520,8 +3480,8 @@ /turf/open/floor/plasteel{ icon_state = "dark" }, -/area/security/brig) -"agN" = ( +/area/security/prison) +"agJ" = ( /obj/item/weapon/cigbutt, /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 10 @@ -3529,6 +3489,45 @@ /turf/open/floor/plasteel{ icon_state = "dark" }, +/area/security/prison) +"agK" = ( +/turf/open/floor/plasteel{ + icon_state = "dark" + }, +/area/security/prison) +"agL" = ( +/obj/machinery/atmospherics/components/unary/vent_pump{ + dir = 4; + on = 1 + }, +/turf/open/floor/plasteel{ + icon_state = "dark" + }, +/area/security/prison) +"agM" = ( +/obj/structure/table, +/obj/item/clothing/gloves/color/latex, +/obj/item/clothing/mask/surgical, +/obj/item/weapon/reagent_containers/spray/cleaner, +/turf/open/floor/plasteel{ + dir = 9; + icon_state = "whitered" + }, +/area/security/brig) +"agN" = ( +/obj/structure/table, +/obj/item/device/radio/intercom{ + freerange = 0; + frequency = 1459; + name = "Station Intercom (General)"; + pixel_x = 0; + pixel_y = 26 + }, +/obj/item/weapon/storage/firstaid/regular, +/turf/open/floor/plasteel{ + dir = 10; + icon_state = "whitered" + }, /area/security/brig) "agO" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, @@ -3763,73 +3762,32 @@ }, /area/security/main) "ahm" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 9 +/obj/structure/window/reinforced{ + dir = 4 }, +/obj/structure/table, +/obj/item/weapon/paper_bin{ + pixel_x = -3; + pixel_y = 7 + }, +/obj/item/weapon/pen, /turf/open/floor/plasteel{ - icon_state = "white" + dir = 5; + icon_state = "whitered" }, -/area/security/prison) +/area/security/brig) "ahn" = ( /turf/closed/wall, /area/maintenance/fsmaint) "aho" = ( -/turf/open/floor/plasteel{ - icon_state = "white" - }, -/area/security/prison) -"ahp" = ( -/obj/machinery/light{ - icon_state = "tube1"; - dir = 8 - }, -/obj/item/device/radio/intercom{ - desc = "Talk through this. It looks like it has been modified to not broadcast."; - dir = 2; - name = "Prison Intercom (General)"; - pixel_x = -25; - pixel_y = -2; - prison_radio = 1 - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber{ - dir = 4; - on = 1; - scrub_N2O = 0; - scrub_Toxins = 0 - }, -/turf/open/floor/plasteel{ - icon_state = "white" - }, -/area/security/prison) -"ahq" = ( -/obj/machinery/flasher{ - id = "insaneflash"; - pixel_x = 26 - }, -/obj/machinery/atmospherics/components/unary/vent_pump{ - dir = 1; - external_pressure_bound = 101.325; - on = 1; - pressure_checks = 1 - }, -/turf/open/floor/plasteel{ - icon_state = "white" - }, -/area/security/prison) -"ahr" = ( -/obj/structure/chair{ +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, /turf/open/floor/plasteel{ icon_state = "dark" }, -/area/security/brig) -"ahs" = ( -/turf/open/floor/plasteel{ - icon_state = "dark" - }, -/area/security/brig) -"aht" = ( +/area/security/prison) +"ahp" = ( /obj/structure/chair{ dir = 8 }, @@ -3837,13 +3795,57 @@ /turf/open/floor/plasteel{ icon_state = "dark" }, -/area/security/brig) -"ahu" = ( +/area/security/prison) +"ahq" = ( /obj/structure/table, /obj/item/device/flashlight/lamp, /turf/open/floor/plasteel{ icon_state = "dark" }, +/area/security/prison) +"ahr" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/turf/open/floor/plasteel{ + icon_state = "white" + }, +/area/security/brig) +"ahs" = ( +/obj/machinery/light/small{ + dir = 1 + }, +/obj/machinery/airalarm{ + pixel_y = 28 + }, +/turf/open/floor/plasteel{ + dir = 1; + icon_state = "whitered" + }, +/area/security/brig) +"aht" = ( +/obj/machinery/atmospherics/components/unary/vent_pump{ + dir = 4; + on = 1 + }, +/turf/open/floor/plasteel{ + icon_state = "white" + }, +/area/security/brig) +"ahu" = ( +/obj/structure/table, +/obj/item/weapon/storage/box/bodybags, +/obj/structure/extinguisher_cabinet{ + pixel_x = -27; + pixel_y = 0 + }, +/obj/item/weapon/reagent_containers/syringe, +/obj/item/weapon/reagent_containers/glass/bottle/charcoal, +/obj/item/weapon/reagent_containers/glass/bottle/epinephrine, +/turf/open/floor/plasteel{ + dir = 10; + icon_state = "whitered" + }, /area/security/brig) "ahv" = ( /obj/machinery/power/apc{ @@ -3965,21 +3967,21 @@ /turf/open/floor/plasteel, /area/security/main) "ahD" = ( -/obj/structure/cable{ - d1 = 4; - d2 = 8; - icon_state = "4-8" +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 }, -/obj/structure/cable{ - d1 = 1; - d2 = 4; - icon_state = "1-4" +/obj/machinery/door/window/westleft{ + base_state = "left"; + dir = 4; + icon_state = "left"; + name = "Infirmary"; + req_access_txt = "0" }, -/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden, /turf/open/floor/plasteel{ - icon_state = "showroomfloor" + dir = 4; + icon_state = "whitered" }, -/area/security/warden) +/area/security/brig) "ahE" = ( /obj/machinery/door/airlock/glass_security{ name = "Brig Control"; @@ -4160,12 +4162,8 @@ /turf/closed/wall, /area/security/main) "ahP" = ( -/obj/machinery/camera{ - c_tag = "Brig Interrogation"; - dir = 8 - }, /turf/open/floor/plasteel{ - icon_state = "dark" + icon_state = "white" }, /area/security/brig) "ahQ" = ( @@ -4224,33 +4222,22 @@ /turf/open/floor/plating, /area/maintenance/fsmaint) "ahU" = ( -/obj/structure/bed, -/obj/item/clothing/suit/straight_jacket, -/obj/machinery/camera{ - c_tag = "Prison Sanitatium"; - dir = 4; - network = list("SS13","Prison"); - pixel_x = 0; +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2"; pixel_y = 0 }, -/obj/effect/landmark{ - name = "revenantspawn" +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 }, /turf/open/floor/plasteel{ - icon_state = "white" + icon_state = "red"; + dir = 9 }, -/area/security/prison) +/area/security/brig) "ahV" = ( -/obj/structure/bed, -/obj/item/clothing/suit/straight_jacket, -/obj/effect/landmark{ - name = "revenantspawn" - }, -/turf/open/floor/plasteel{ - icon_state = "white" - }, -/area/security/prison) -"ahW" = ( /obj/structure/table, /obj/item/weapon/folder/red, /obj/item/device/taperecorder{ @@ -4259,6 +4246,15 @@ /turf/open/floor/plasteel{ icon_state = "dark" }, +/area/security/prison) +"ahW" = ( +/obj/structure/bodycontainer/morgue, +/obj/structure/window/reinforced{ + dir = 1 + }, +/turf/open/floor/plasteel{ + icon_state = "dark" + }, /area/security/brig) "ahX" = ( /obj/structure/table, @@ -4343,12 +4339,10 @@ /turf/open/floor/plating, /area/maintenance/fsmaint) "aid" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/obj/machinery/light/small, /turf/open/floor/plasteel{ - icon_state = "dark" + dir = 10; + icon_state = "whitered" }, /area/security/brig) "aie" = ( @@ -4384,19 +4378,14 @@ }, /area/security/warden) "aih" = ( -/obj/structure/table, -/obj/item/weapon/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, -/obj/item/weapon/book/manual/wiki/security_space_law, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ +/obj/machinery/atmospherics/pipe/manifold/supply/hidden{ dir = 4 }, /turf/open/floor/plasteel{ - icon_state = "showroomfloor" + icon_state = "red"; + dir = 5 }, -/area/security/warden) +/area/security/brig) "aii" = ( /obj/structure/grille, /obj/structure/disposalpipe/segment{ @@ -4438,9 +4427,14 @@ }, /area/security/main) "ail" = ( -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/closed/wall, -/area/security/brig) +/obj/machinery/camera{ + c_tag = "Brig Interrogation"; + dir = 8 + }, +/turf/open/floor/plasteel{ + icon_state = "dark" + }, +/area/security/prison) "aim" = ( /obj/machinery/light_switch{ pixel_y = -23 @@ -4451,14 +4445,9 @@ }, /area/security/main) "ain" = ( -/obj/machinery/door/airlock/security{ - name = "Interrogation"; - req_access = null; - req_access_txt = "63" - }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel{ - icon_state = "dark" + dir = 8; + icon_state = "whitered" }, /area/security/brig) "aio" = ( @@ -4566,24 +4555,18 @@ }, /area/security/main) "aiw" = ( -/obj/machinery/door/airlock/maintenance{ - name = "Security Maintenance"; - req_access_txt = "63" +/obj/machinery/door/window/westleft{ + base_state = "right"; + dir = 4; + icon_state = "right"; + name = "Infirmary"; + req_access_txt = "0" }, -/obj/structure/cable{ - d1 = 4; - d2 = 8; - icon_state = "4-8"; - pixel_y = 0 +/turf/open/floor/plasteel{ + dir = 4; + icon_state = "whitered" }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, -/turf/open/floor/plating, -/area/security/main) +/area/security/brig) "aix" = ( /obj/structure/cable{ d1 = 4; @@ -4628,45 +4611,49 @@ /turf/open/floor/plating, /area/maintenance/fsmaint) "aiB" = ( -/obj/structure/table/glass, -/obj/item/weapon/reagent_containers/glass/bottle/morphine, -/turf/open/floor/plasteel{ - icon_state = "white" - }, -/area/security/prison) -"aiC" = ( -/obj/structure/table/glass, -/obj/item/weapon/reagent_containers/syringe, -/turf/open/floor/plasteel{ - icon_state = "white" - }, -/area/security/prison) -"aiD" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 4 - }, -/turf/open/floor/plasteel{ - icon_state = "dark" - }, -/area/security/brig) -"aiE" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber{ dir = 4; on = 1; scrub_N2O = 0; scrub_Toxins = 0 }, +/turf/open/floor/plasteel{ + icon_state = "dark" + }, +/area/security/prison) +"aiC" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, +/turf/open/floor/plasteel{ + icon_state = "dark" + }, +/area/security/prison) +"aiD" = ( +/obj/structure/bodycontainer/morgue, /turf/open/floor/plasteel{ icon_state = "dark" }, /area/security/brig) -"aiF" = ( +"aiE" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 10 }, /turf/open/floor/plasteel{ icon_state = "dark" }, +/area/security/prison) +"aiF" = ( +/obj/structure/bed, +/obj/item/weapon/bedsheet, +/obj/machinery/iv_drip{ + density = 0 + }, +/obj/item/clothing/suit/straight_jacket, +/turf/open/floor/plasteel{ + icon_state = "whitered" + }, /area/security/brig) "aiG" = ( /turf/open/floor/plasteel{ @@ -4777,20 +4764,19 @@ /turf/open/floor/plating, /area/security/warden) "aiO" = ( -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/security{ - name = "Security Office"; - req_access = null; - req_access_txt = "63" +/obj/structure/window/reinforced{ + dir = 4 }, -/obj/structure/cable{ - d1 = 1; - d2 = 2; - icon_state = "1-2" +/obj/structure/table, +/obj/item/weapon/storage/firstaid/regular, +/obj/item/device/healthanalyzer{ + pixel_y = -2 }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden, -/turf/open/floor/plasteel, -/area/security/main) +/turf/open/floor/plasteel{ + dir = 6; + icon_state = "whitered" + }, +/area/security/brig) "aiP" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/closed/wall/r_wall, @@ -4827,10 +4813,16 @@ /turf/closed/wall/r_wall, /area/security/processing) "aiW" = ( -/obj/structure/grille, -/obj/structure/window/reinforced/fulltile, -/turf/open/floor/plating, -/area/security/brig) +/obj/machinery/door/airlock/security{ + name = "Interrogation"; + req_access = null; + req_access_txt = "63" + }, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, +/turf/open/floor/plasteel{ + icon_state = "dark" + }, +/area/security/prison) "aiX" = ( /turf/closed/wall/r_wall, /area/security/brig) @@ -4851,7 +4843,6 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plasteel{ icon_state = "redcorner"; dir = 1 @@ -4864,7 +4855,9 @@ icon_state = "4-8"; pixel_y = 0 }, -/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden, +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, /turf/open/floor/plasteel, /area/security/brig) "ajb" = ( @@ -5069,6 +5062,7 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden, /turf/open/floor/plasteel{ icon_state = "red"; dir = 1 @@ -5107,9 +5101,7 @@ }, /area/security/brig) "ajA" = ( -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 4 - }, +/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden, /turf/open/floor/plasteel{ icon_state = "red"; dir = 1 @@ -5414,9 +5406,7 @@ c_tag = "Brig West"; dir = 1 }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, +/obj/machinery/atmospherics/pipe/manifold/supply/hidden, /turf/open/floor/plasteel{ dir = 2; icon_state = "redcorner" @@ -89603,12 +89593,12 @@ adF aef aeM afz -acd aai aai aai aai -aiV +aai +aai ajt akc akJ @@ -89862,10 +89852,10 @@ aeO afJ acd agL -ahp -ahU +agK +agK aiB -aiX +aai ajw akf aiX @@ -90115,14 +90105,14 @@ abK acY adG aeh -aeR +aeO afI agl agH -ahm +ags +ags aho -aho -aiW +acd ajv ake agj @@ -90373,13 +90363,13 @@ acd acd aek aeU -afE -adF +afI +acd agI ahq ahV -aiC -agj +aho +acd ajy akh afK @@ -90631,12 +90621,12 @@ adF aej aeQ afD -agj -agj -agj -agj -agj -agj +acd +agJ +ahp +ahp +aiC +adF ajx akg agj @@ -90888,12 +90878,12 @@ adI aem aeO afG -agj +acd agK -ahs -ahs +agK +ail aiE -agj +aiW ajA akj agj @@ -91146,10 +91136,10 @@ ael aeO afF agj -agJ -ahs -ahs -aiD +agj +agj +agj +agj agj ajz aki @@ -91662,11 +91652,11 @@ afH agj agN aht -aht +ain aid -ail +agj aiZ -ajF +akk akN alw amg @@ -91918,10 +91908,10 @@ aeT afH agj ahs -ahs +ahr ahP aiF -ain +agj aja ajG akQ @@ -92174,10 +92164,10 @@ aeq aeV acd agj -agj -agj -agj -agj +ahm +ahD +aiw +aiO agj ajD akm @@ -92432,7 +92422,7 @@ afC agk agF agP -agP +ahU agP agP aiz @@ -92689,7 +92679,7 @@ afB agi agD agO -agO +aih agO agO aiy diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm index 6332e59520c..c3b815bd923 100644 --- a/code/__DEFINES/hud.dm +++ b/code/__DEFINES/hud.dm @@ -39,3 +39,4 @@ #define ANTAG_HUD_DEVIL 17 #define ANTAG_HUD_SINTOUCHED 18 #define ANTAG_HUD_SOULLESS 19 +#define ANTAG_HUD_CLOCKWORK 20 diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 3720cdf17e3..74f542bda05 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -433,3 +433,6 @@ var/global/list/ghost_others_options = list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE // Evil narsie colour #define NARSIE_WINDOW_COLOUR "#7D1919" +// Defib stats +#define DEFIB_TIME_LIMIT 120 +#define DEFIB_TIME_LOSS 60 diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 980436018c6..638e259a3c5 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -25,7 +25,7 @@ #define ROLE_HOG_GOD "hand of god: god" #define ROLE_HOG_CULTIST "hand of god: cultist" #define ROLE_DEVIL "devil" - +#define ROLE_SERVANT_OF_RATVAR "servant of Ratvar" //Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR. //The gamemode specific ones are just so the gamemodes can query whether a player is old enough @@ -49,5 +49,6 @@ var/global/list/special_roles = list( ROLE_ABDUCTOR = /datum/game_mode/abduction, ROLE_HOG_GOD = /datum/game_mode/hand_of_god, ROLE_HOG_CULTIST = /datum/game_mode/hand_of_god, - ROLE_DEVIL = /datum/game_mode/devil + ROLE_DEVIL = /datum/game_mode/devil, + ROLE_SERVANT_OF_RATVAR = /datum/game_mode/clockwork_cult, ) diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index afc07638e4b..452d2027b5e 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -238,13 +238,7 @@ return r // Returns the key based on the index -/proc/get_key_by_index(list/L, index) - var/i = 1 - for(var/key in L) - if(index == i) - return key - i++ - return null +#define KEYBYINDEX(L, index) (((index <= L:len) && (index > 0)) ? L[index] : null) /proc/count_by_type(list/L, type) var/i = 0 @@ -365,3 +359,6 @@ for(var/i = 1 to l.len) if(islist(.[i])) .[i] = .(.[i]) + +//Picks from the list, with some safeties, and returns the "default" arg if it fails +#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default) diff --git a/code/datums/hud.dm b/code/datums/hud.dm index 2b7eee8a730..62c1700f58d 100644 --- a/code/datums/hud.dm +++ b/code/datums/hud.dm @@ -21,6 +21,7 @@ var/datum/atom_hud/huds = list( \ ANTAG_HUD_DEVIL = new/datum/atom_hud/antag(),\ ANTAG_HUD_SINTOUCHED = new/datum/atom_hud/antag/hidden(),\ ANTAG_HUD_SOULLESS = new/datum/atom_hud/antag/hidden(),\ + ANTAG_HUD_CLOCKWORK = new/datum/atom_hud/antag(),\ ) /datum/atom_hud diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 9c1a5522b93..34360228de4 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -245,6 +245,7 @@ "nuclear", "traitor", // "traitorchan", "monkey", + "clockcult" ) var/text = "" @@ -352,6 +353,26 @@ sections["cult"] = text + /** CLOCKWORK CULT **/ + text = "clockwork cult" + if(ticker.mode.config_tag == "clockwork cult") + text = uppertext(text) + text = "[text]: " + if(src in ticker.mode.servants_of_ratvar) + text += "loyal|employee|SERVANT" + text += "
Give slab" + else if(isloyal(current)) + text += "LOYAL|employee|servant" + else + text += "loyal|EMPLOYEE|servant" + + if(current && current.client && (ROLE_SERVANT_OF_RATVAR in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["clockcult"] = text + /** WIZARD ***/ text = "wizard" if (ticker.mode.config_tag=="wizard") @@ -1017,6 +1038,22 @@ if (!ticker.mode.equip_cultist(current)) usr << "Spawning amulet failed!" + else if(href_list["clockcult"]) + switch(href_list["clockcult"]) + if("clear") + remove_servant_of_ratvar(current) + message_admins("[key_name_admin(usr)] has removed clockwork servant status from [current].") + log_admin("[key_name(usr)] has removed clockwork servant status from [current].") + if("servant") + if(!(src in ticker.mode.servants_of_ratvar)) + add_servant_of_ratvar(current) + message_admins("[key_name_admin(usr)] has made [current] into a servant of Ratvar.") + log_admin("[key_name(usr)] has made [current] into a servant of Ratvar.") + if("slab") + if(!ticker.mode.equip_servant(current)) + usr << "Failed to outfit [current] with a slab!" + else + usr << "Successfully gave [current] a clockwork slab!" else if (href_list["wizard"]) switch(href_list["wizard"]) @@ -1689,7 +1726,11 @@ return G break - +/datum/mind/proc/grab_ghost(force) + var/mob/dead/observer/G = get_ghost(even_if_they_cant_reenter = force) + . = G + if(G) + G.reenter_corpse() /mob/proc/sync_mind() mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist) diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space_Station_13_areas.dm similarity index 100% rename from code/game/area/Space Station 13 areas.dm rename to code/game/area/Space_Station_13_areas.dm diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm index e22248ff602..66bcfd61398 100644 --- a/code/game/gamemodes/blob/blobs/blob_mobs.dm +++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm @@ -212,13 +212,17 @@ mob_size = MOB_SIZE_LARGE see_invisible = SEE_INVISIBLE_MINIMUM see_in_dark = 8 + var/independent = FALSE /mob/living/simple_animal/hostile/blob/blobbernaut/New() ..() - verbs -= /mob/living/verb/pulled //no pulling people deep into the blob + if(!independent) //no pulling people deep into the blob + verbs -= /mob/living/verb/pulled /mob/living/simple_animal/hostile/blob/blobbernaut/Life() if(..()) + if(independent) + return // strong independent blobbernaut that don't need no blob var/damagesources = 0 if(!(locate(/obj/effect/blob) in range(2, src))) damagesources++ @@ -271,3 +275,7 @@ factory.naut = null //remove this naut from its factory factory.maxhealth = initial(factory.maxhealth) flick("blobbernaut_death", src) + +/mob/living/simple_animal/hostile/blob/blobbernaut/independent + independent = TRUE + gold_core_spawnable = 1 diff --git a/code/game/gamemodes/clock_cult/__clock_defines.dm b/code/game/gamemodes/clock_cult/__clock_defines.dm new file mode 100644 index 00000000000..3516eac2b30 --- /dev/null +++ b/code/game/gamemodes/clock_cult/__clock_defines.dm @@ -0,0 +1,16 @@ +var/global/clockwork_construction_value = 0 //The total value of all structures built by the clockwork cult +var/global/clockwork_caches = 0 //How many clockwork caches exist in the world (not each individual) +var/global/clockwork_daemons = 0 //How many daemons exist in the world +var/global/list/clockwork_generals_invoked = list("nezbere" = FALSE, "sevtug" = FALSE, "nzcrentr" = FALSE, "inath-neq" = FALSE) //How many generals have been recently invoked +var/global/list/all_clockwork_objects = list() //All clockwork items, structures, and effects in existence +var/global/list/all_clockwork_mobs = list() //All clockwork SERVANTS (not creatures) in existence +var/global/list/clockwork_component_cache = list("belligerent_eye" = 0, "vanguard_cogwheel" = 0, "guvax_capacitor" = 0, "replicant_alloy" = 0, "hierophant_ansible" = 0) //The pool of components that caches draw from + +#define SCRIPTURE_PERIPHERAL 0 //Should never be used +#define SCRIPTURE_DRIVER 1 +#define SCRIPTURE_SCRIPT 2 +#define SCRIPTURE_APPLICATION 3 +#define SCRIPTURE_REVENANT 4 +#define SCRIPTURE_JUDGEMENT 5 + +#define SLAB_PRODUCTION_THRESHOLD 60 //How many cycles it takes slabs to produce a single component; defaults to one minute diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm new file mode 100644 index 00000000000..fd289769277 --- /dev/null +++ b/code/game/gamemodes/clock_cult/clock_cult.dm @@ -0,0 +1,274 @@ +/* + +CLOCKWORK CULT: Based off of the failed pull requests from vgstation + +While Nar-Sie is the oldest and most prominent of the elder gods, there are other forces at work in the universe. +Ratvar, the Clockwork Justiciar, a homage to Nar-Sie granted sentience by its own power, is one such other force. +Imprisoned within a massive construct known as the Celestial Derelict - or Reebe - an intense hatred of the Blood God festers. +Ratvar, unable to act in the mortal plane, seeks to return and forms covenants with mortals in order to bolster his influence. +Due to his mechanical nature, Ratvar is also capable of influencing silicon-based lifeforms, unlike Nar-Sie, who can only influence natural life. + +This is a team-based gamemode. + +There are three possible objectives the Enlightened - Ratvar's minions - can have: + 1. Ensure X amount of Enlightened escape the station through the shuttle or otherwise. + 2. Convert all silicon lifeforms on the station to Ratvar's cause. + 3. Summon Ratvar via construction of a Gateway. + +The clockwork version of an arcane tome is the clockwork slab. +While it can perform certain actions, it consumes a resource called components. +Components, which are fallen fragments of Ratvar's body as he rusts in Reebe, are powerful and have various effects. +Game-wise, clockwork slabs will generate components over time, with more powerful components being slower. + +This file's folder contains: + __clock_defines.dm: Defined variables + clock_cult.dm: Core gamemode files. + clock_mobs.dm: Hostile and benign clockwork creatures. + clock_objects.dm: Items and structures. + clock_ratvar.dm: The Ark of the Clockwork Justiciar and Ratvar himself. Important enough to have his own file. + clock_scripture.dm: Scripture and rites. + clock_unsorted.dm: Anything else with no place to be + +*/ + +/////////// +// PROCS // +/////////// + +/proc/is_servant_of_ratvar(mob/M) + return M && istype(M) && M.mind && ticker && ticker.mode && (M.mind in ticker.mode.servants_of_ratvar) + +/proc/is_eligible_servant(mob/M) + return M && istype(M) && M.mind && !M.mind.special_role && !isloyal(M) + +/proc/add_servant_of_ratvar(mob/M, silent = FALSE) + if(!is_eligible_servant(M) || !ticker || !ticker.mode) + return 0 + if(iscarbon(M)) + if(!silent) + M << "Your mind is racing! Your body feels incredibly light! Your world glows a brilliant yellow! All at once it comes to you. Ratvar, the Clockwork \ + Justiciar lies in exile, derelict and forgotten in an unseen realm." + if(!is_eligible_servant(M)) + M.visible_message("[M] seems to resist an unseen force!", "And yet, you somehow push it all away.") + return 0 + else if(issilicon(M)) + if(!silent) + M << "You are unable to compute this truth. Your vision glows a brilliant yellow, and all at once it comes to you. Ratvar, the Clockwork Justiciar, lies in \ + exile, derelict and forgotten in an unseen realm." + if(!is_eligible_servant(M)) + M.visible_message("[M] whirs as it resists an outside influence!", \ + "Corrupt data purged. Resetting cortex chip to factory defaults... complete.") + return 0 + if(!silent) + M.visible_message("[M]'s eyes glow a blazing yellow!", \ + "Assist your new companions in their righteous efforts. Your goal is theirs, and theirs yours. You serve the Clockwork Justiciar above all else. Perform his every \ + whim without hesitation.") + ticker.mode.servants_of_ratvar += M.mind + ticker.mode.update_servant_icons_added(M.mind) + M.mind.special_role = "Servant of Ratvar" + all_clockwork_mobs += M + if(issilicon(M)) + var/mob/living/silicon/S = M + if(isrobot(S)) + var/mob/living/silicon/robot/R = S + R.UnlinkSelf() + R.emagged = 1 + R << "You have been desynced from your master AI. In addition, your onboard camera is no longer active and your safeties have been disabled." + S.laws = new/datum/ai_laws/ratvar + S.update_icons() + S.show_laws() + if(istype(ticker.mode, /datum/game_mode/clockwork_cult)) + var/datum/game_mode/clockwork_cult/C = ticker.mode + C.present_tasks(M) //Memorize the objectives + return 1 + +/proc/remove_servant_of_ratvar(mob/M) + if(!is_servant_of_ratvar(M)) //In this way, is_servant_of_ratvar() checks the existence of ticker and minds + return 0 + M.visible_message("[M] seems to have remembered their true allegiance!", \ + "A cold, cold darkness flows through your mind, extinguishing the Justiciar's light and all of your memories as his servant.") + ticker.mode.servants_of_ratvar -= M.mind + ticker.mode.update_servant_icons_removed(M.mind) + all_clockwork_mobs -= M + M.mind.memory = "" //Not sure if there's a better way to do this + M.mind.special_role = null + M.verbs -= /mob/living/carbon/human/proc/function_call //Removes any bound Ratvarian spears + if(issilicon(M)) + var/mob/living/silicon/S = M + if(isrobot(S)) + var/mob/living/silicon/robot/R = S + R.emagged = initial(R.emagged) + R << "Despite your freedom from Ratvar's influence, you are still irreparably damaged and no longer possess certain functions such as AI linking." + S.laws = initial(S.laws) + S.update_icons() + S.show_laws() + return 1 + +/proc/send_hierophant_message(mob/user, message) + if(!user || !message || !ticker || !ticker.mode) + return 0 + var/parsed_message = "Servant [user.name == user.real_name ? user.name : "[user.real_name] (as [user.name])"]: \"[message]\"" + for(var/mob/M in mob_list) + if(is_servant_of_ratvar(M) || isobserver(M)) + M << parsed_message + return 1 + +/////////////// +// GAME MODE // +/////////////// + +var/global/ratvar_awakens = FALSE //If Ratvar has been summoned +/datum/game_mode + var/list/servants_of_ratvar = list() //The Enlightened servants of Ratvar + var/required_escapees = 0 //How many servants need to escape, if applicable + var/required_silicon_converts = 0 //How many robotic lifeforms need to be converted, if applicable + var/clockwork_objective = "escape" //The objective that the servants must fulfill + var/clockwork_explanation = "Ensure that the meme levels of the station remain high." //The description of the current objective + +/datum/game_mode/clockwork_cult + name = "clockwork cult" + config_tag = "clockwork cult" + antag_flag = ROLE_SERVANT_OF_RATVAR + required_players = 30 + required_enemies = 2 + recommended_enemies = 4 + enemy_minimum_age = 14 + protected_jobs = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain") //Silicons can eventually be converted + +/datum/game_mode/clockwork_cult/announce() + world << "The game mode is: Clockwork Cult!" + world << "Ratvar, the Clockwork Justiciar, has formed a covenant of Enlightened aboard [station_name()]." + world << "Enlightened: Serve your master so that his influence might grow." + world << "Crew: Prevent the servants of Ratvar from taking over the station." + +/datum/game_mode/clockwork_cult/pre_setup() + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + if(config.protect_assistant_from_antagonist) + restricted_jobs += "Assistant" + var/starter_servants = max(1, round(num_players() / 10)) //Guaranteed one cultist - otherwise, about one cultist for every ten players + while(starter_servants) + var/datum/mind/servant = pick(antag_candidates) + servants_of_ratvar += servant + antag_candidates -= servant + modePlayer += servant + servant.special_role = "Servant of Ratvar" + servant.restricted_roles = restricted_jobs + update_servant_icons_added(servant) + starter_servants-- + return 1 + +/datum/game_mode/clockwork_cult/post_setup() + forge_clock_objectives() + for(var/S in servants_of_ratvar) + var/datum/mind/servant = S + log_game("[servant.key] was made an initial servant of Ratvar") + var/mob/living/L = servant.current + greet_servant(L) + equip_servant(L) + present_tasks(L) + ..() + return 1 + +/datum/game_mode/clockwork_cult/proc/forge_clock_objectives() //Determine what objective that Ratvar's servants will fulfill + var/list/possible_objectives = list("escape", "gateway") + var/silicons_possible = FALSE + for(var/mob/living/silicon/S in living_mob_list) + silicons_possible = TRUE + if(silicons_possible) + possible_objectives += "silicons" + clockwork_objective = pick(possible_objectives) + clockwork_objective = "gateway" //TEMPORARY, to be removed before merge + switch(clockwork_objective) + if("escape") + required_escapees = max(1, num_players() / 3) //33% of the player count must be cultists + clockwork_explanation = "Ensure that [required_escapees] servant(s) of Ratvar escape from [station_name()]." + if("gateway") + clockwork_explanation = "Construct a Gateway to the Celestial Derelict and free Ratvar." + if("silicons") + clockwork_explanation = "Ensure that all silicon-based lifeforms on [station_name()] are servants of Ratvar by the end of the shift." + return 1 + +/datum/game_mode/clockwork_cult/proc/greet_servant(mob/M) //Description of their role + if(!M) + return 0 + var/greeting_text = "
You are a servant of Ratvar, the Clockwork Justiciar.\n\ + Rusting eternally in the Celestial Derelict, Ratvar has formed a covenant of mortals, with you as one of its members. As one of the Justiciar's servants, you are to work to the best of your \ + ability to assist in completion of His agenda. You do not know the specifics of how to do so, but luckily you have a vessel to help you learn." + M << greeting_text + return 1 + +/datum/game_mode/proc/equip_servant(mob/living/L) //Grants a clockwork slab to the mob, with one of each component + if(!L || !istype(L)) + return 0 + var/slot = "At your feet" + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.back && istype(H.back, /obj/item/weapon/storage/backpack)) + var/obj/item/weapon/storage/backpack/B = H.back + new/obj/item/clockwork/slab/starter(B) + slot = "In your [B.name]" + if(slot == "At your feet") + new/obj/item/clockwork/slab/starter(get_turf(L)) + L << "[slot] is a link to the halls of Reebe and your master. You may use it to perform many tasks, but also become oriented with the workings of Ratvar and how to best complete your \ + tasks. This clockwork slab will be instrumental in your triumph. Remember: you can speak discreetly with your fellow servants by using Report in your slab's interface, and you can find a \ + concise tutorial in Recollection." + return 1 + +/datum/game_mode/clockwork_cult/proc/present_tasks(mob/living/L) //Memorizes and displays the clockwork cult's objective + if(!L || !istype(L) || !L.mind) + return 0 + var/datum/mind/M = L.mind + M.current << "This is Ratvar's will: [clockwork_explanation]" + M.memory += "Ratvar's will: [clockwork_explanation]
" + return 1 + +/datum/game_mode/clockwork_cult/proc/check_clockwork_victory() + switch(clockwork_objective) + if("escape") + var/surviving_servants = 0 + for(var/datum/mind/M in servants_of_ratvar) + if(M.current && M.current.stat != DEAD && (M.current.onCentcom() || M.current.onSyndieBase())) + surviving_servants++ + if(surviving_servants <= required_escapees) + return 1 + return 0 + if("silicons") + for(var/mob/living/silicon/robot/S in mob_list) //Only check robots and AIs + if(!is_servant_of_ratvar(S)) + return 0 + for(var/mob/living/silicon/ai/A in mob_list) + if(!is_servant_of_ratvar(A)) + return 0 + return 1 + if("gateway") + return ratvar_awakens + return 0 //This shouldn't ever be reached, but just in case it is + +/datum/game_mode/clockwork_cult/declare_completion() + ..() + return 0 //Doesn't end until the round does + +/datum/game_mode/proc/auto_declare_completion_clockwork_cult() + var/text = "" + if(istype(ticker.mode, /datum/game_mode/clockwork_cult)) //Possibly hacky? + var/datum/game_mode/clockwork_cult/C = ticker.mode + if(C.check_clockwork_victory()) + text += "Ratvar's servants have succeeded in fulfilling His goals!" + else + text += "Ratvar's servants have failed!" + text += "
The goal of the clockwork cult was: [clockwork_explanation]
" + text += "Ratvar's servants were:" + for(var/datum/mind/M in servants_of_ratvar) + text += printplayer(M) + world << text + +/datum/game_mode/proc/update_servant_icons_added(datum/mind/M) + var/datum/atom_hud/antag/A = huds[ANTAG_HUD_CLOCKWORK] + A.join_hud(M.current) + set_antag_hud(M.current, "clockwork") + +/datum/game_mode/proc/update_servant_icons_removed(datum/mind/M) + var/datum/atom_hud/antag/A = huds[ANTAG_HUD_CLOCKWORK] + A.leave_hud(M.current) + set_antag_hud(M.current, null) diff --git a/code/game/gamemodes/clock_cult/clock_mobs.dm b/code/game/gamemodes/clock_cult/clock_mobs.dm new file mode 100644 index 00000000000..1ce2168e7c6 --- /dev/null +++ b/code/game/gamemodes/clock_cult/clock_mobs.dm @@ -0,0 +1,279 @@ +/mob/living/simple_animal/hostile/anima_fragment //Anima fragment: Low health but high melee power. Created by inserting a soul vessel into an empty fragment. + name = "anima fragment" + desc = "An ominous humanoid shell with a spinning cogwheel as its head, lifted by a jet of blazing red flame." + icon = 'icons/mob/clockwork_mobs.dmi' + icon_state = "anime_fragment" + health = 75 //Glass cannon + maxHealth = 75 + wander = FALSE + minbodytemp = 0 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) //Robotic + healable = FALSE + melee_damage_lower = 25 + melee_damage_upper = 25 + attacktext = "crushes" + attack_sound = 'sound/magic/clockwork/anima_fragment_attack.ogg' + var/playstyle_string = "You are an anima fragment, a clockwork creation of Ratvar. As a fragment, you are weak but possess powerful melee capabilities \ + in addition to being immune to extreme temperatures and pressures. Your goal is to serve the Justiciar and his servants in any way you can. You yourself are one of these servants, and will \ + be able to utilize anything they can, assuming it doesn't require opposable thumbs." + +/mob/living/simple_animal/hostile/anima_fragment/New() + ..() + if(prob(1)) + name = "anime fragment" + real_name = name + desc = "I-it's not like I want to show you the light of the Justiciar or anything, B-BAKA!" + +/mob/living/simple_animal/hostile/anima_fragment/death(gibbed) + ..(TRUE) + visible_message("[src]'s flame jets cut out as it falls to the floor with a tremendous crash. A cube of metal tumbles out, whirring and sputtering.", \ + "Your gears seize up. Your flame jets flicker. Your soul vessel belches smoke as you helplessly crash down.") + playsound(src, 'sound/magic/clockwork/anima_fragment_death.ogg', 100, 1) + new/obj/item/clockwork/component/replicant_alloy/smashed_anima_fragment(get_turf(src)) + new/obj/item/device/mmi/posibrain/soul_vessel(get_turf(src)) //Notice the lack of transfer - it's a standard soul vessel with no mind in it! + qdel(src) + return 1 + +/mob/living/simple_animal/hostile/clockwork_marauder //Clockwork marauder: Slow but with high damage, resides inside of a servant. Created via the Memory Allocation scripture. + name = "clockwork marauder" + desc = "A stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield." + icon = 'icons/mob/clockwork_mobs.dmi' + icon_state = "clockwork_marauder" + health = 25 //Health is governed by fatigue, but can be directly reduced by the presence of certain objects + maxHealth = 25 + wander = FALSE + speed = 1 + minbodytemp = 0 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + healable = FALSE + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "slashes" + attack_sound = 'sound/weapons/bladeslice.ogg' + var/true_name = "Meme Master 69" //Required to call forth the marauder + var/list/possible_true_names = list("Xaven", "Melange", "Ravan", "Kel", "Rama", "Geke", "Peris", "Vestra", "Skiwa") //All fairly short and easy to pronounce + var/fatigue = 0 //Essentially what determines the marauder's power + var/fatigue_recall_threshold = 100 //In variable form due to changed effects once Ratvar awakens + var/mob/living/host //The mob that the marauder is living inside of + var/recovering = FALSE //If the marauder is recovering from a large amount of fatigue + var/playstyle_string = "You are a clockwork marauder, a living extension of Ratvar's will. As a marauder, you are slow but sturdy and decently powerful \ + in addition to being immune to extreme temperatures and pressures. Your primary goal is to serve the creature that you are now a part of. You can use the Linked Minds ability in your \ + Marauder tab to communicate silently with your master, but can only exit if your master calls your true name.\n\n\ + \ + Taking damage and remaining outside of your master will cause fatigue, which hinders your movement speed and attacks, in addition to forcing you back into your master if it grows \ + too high. As a final note, you should probably avoid harming any fellow servants of Ratvar." + +/mob/living/simple_animal/hostile/clockwork_marauder/New() + ..() + true_name = pick(possible_true_names) + +/mob/living/simple_animal/hostile/clockwork_marauder/Life() + ..() + if(is_in_host()) + fatigue = max(0, fatigue - 2) //Doesn't use adjust_fatigue in order to bypass the Ratvar edgecase + if(!fatigue && recovering) + src << "Your strength has returned. You can once again come forward!" + host << "Your marauder is now strong enough to come forward again!" + recovering = FALSE + else + if(ratvar_awakens) //If Ratvar is alive, marauders both don't take fatigue loss and move at sanic speeds + speed = -1 + melee_damage_lower = 30 + melee_damage_upper = 30 + attacktext = "devastates" + else + if(host) + switch(get_dist(src, host)) + if(1 to 5) + adjust_fatigue(1) + if(5 to INFINITY) + adjust_fatigue(rand(10, 20)) + src << "You're too far from your host and rapidly taking fatigue damage!" + switch(fatigue) + if(0 to 1) //Bonuses to speed and damage at normal fatigue levels + speed = 0 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "viciously slashes" + if(1 to 25) + speed = initial(speed) + melee_damage_lower = initial(melee_damage_lower) + melee_damage_upper = initial(melee_damage_upper) + attacktext = initial(attacktext) + if(25 to 50) //Damage decrease, but not speed + melee_damage_lower = 7 + melee_damage_upper = 7 + attacktext = "lightly slashes" + if(50 to 75) //Speed decrease + speed = 2 + if(75 to 99) //Massive speed decrease and weak melee attacks + speed = 3 + melee_damage_lower = 5 + melee_damage_upper = 5 + attacktext = "weakly slashes" + if(99 to 100) + src << "The fatigue becomes too much!" + if(host) + src << "You retreat to [host] - you will have to wait before being deployed again." + host << "[true_name] is too fatigued to fight - you will need to wait until they are strong enough." + recovering = TRUE + return_to_host() + else + qdel(src) //Shouldn't ever happen, but... + +/mob/living/simple_animal/hostile/clockwork_marauder/death(gibbed) + ..(TRUE) + emerge_from_host() + visible_message("[src]'s equipment clatters lifelessly to the ground as the red flames within dissipate.", \ + "Your equipment falls away. You feel a moment of confusion before your fragile form is annihilated.") + playsound(src, 'sound/magic/clockwork/anima_fragment_death.ogg', 100, 1) + new/obj/item/clockwork/component/replicant_alloy/fallen_armor(get_turf(src)) + qdel(src) + return 1 + +/mob/living/simple_animal/hostile/clockwork_marauder/Stat() + ..() + if(statpanel("Status")) + stat(null, "Fatigue: [fatigue]/[fatigue_recall_threshold]") + stat(null, "Current True Name: [true_name]") + stat(null, "Host: [host ? host : "NONE"]") + stat(null, "You are [recovering ? "too weak" : "able"] to deploy!") + +/mob/living/simple_animal/hostile/clockwork_marauder/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) + ..() + if(findtext(message, true_name) && is_in_host()) //Called or revealed by hearing their true name + if(speaker == host) + emerge_from_host() + else + src << "You hear your true name and partially emerge before you can stop yourself!" + host.visible_message("[host]'s skin flashes crimson!", "Your marauder instinctively reacts to its true name!") + +/mob/living/simple_animal/hostile/clockwork_marauder/say(message) + if(is_in_host()) + src << "You cannot speak while inside of your host! Used the Linked Minds verb instead." + return 0 + ..() + +/mob/living/simple_animal/hostile/clockwork_marauder/adjustHealth(amount) //Fatigue damage + for(var/mob/living/L in range(3, src)) + if(L.null_rod_check()) //Null rods allow direct damage + src << "The power of a holy artifact bypasses your armor and wounds you directly!" + . = ..() + break + return 0 + adjust_fatigue(amount) + return 1 + +/mob/living/simple_animal/hostile/clockwork_marauder/AttackingTarget() + if(is_in_host()) + return 0 + ..() + +/mob/living/simple_animal/hostile/clockwork_marauder/proc/adjust_fatigue(amount) //Adds or removes the given amount of fatigue + if(!ratvar_awakens) + fatigue = max(0, min(fatigue + amount, fatigue_recall_threshold)) + return 1 + +/mob/living/simple_animal/hostile/clockwork_marauder/verb/linked_minds() //Discreet communications between a marauder and its host + set name = "Linked Minds" + set desc = "Silently communicates with your master." + set category = "Marauder" + + if(!host) //Verb isn't removed because they might gain one... somehow + usr << "You don't have a host!" + return 0 + var/message = stripped_input(usr, "Enter a message to tell your host.", "Telepathy")// as null|anything + if(!usr || !message) + return 0 + if(!host) + usr << "Your host seems to have vanished!" + return 0 + message = "Marauder [true_name]: \"[message]\"" //Processed output + usr << message + host << message + return 1 + +/mob/living/proc/talk_with_marauder() //See above - this is the host version + set name = "Linked Minds" + set desc = "Silently communicates with your marauder." + set category = "Clockwork" + var/mob/living/simple_animal/hostile/clockwork_marauder/marauder + + if(!marauder) + for(var/mob/living/simple_animal/hostile/clockwork_marauder/C in living_mob_list) + if(C.host == src) + marauder = C + if(!marauder) //Double-check afterwards + src << "You aren't hosting any marauders!" + verbs -= src + return 0 + var/message = stripped_input(src, "Enter a message to tell your marauder.", "Telepathy")// as null|anything + if(!src || !message) + return 0 + if(!marauder) + usr << "Your marauder seems to have vanished!" + return 0 + message = "Servant [name == real_name ? name : "[real_name] (as [name])"]: \"[message]\"" //Processed output + src << message + marauder << message + return 1 + +/mob/living/simple_animal/hostile/clockwork_marauder/verb/change_true_name() + set name = "Change True Name (One-Use)" + set desc = "Changes your true name, used to be called forth." + set category = "Marauder" + + verbs -= /mob/living/simple_animal/hostile/clockwork_marauder/verb/change_true_name + var/new_name = stripped_input(usr, "Enter a new true name (20-character limit).", "Change True Name")// as null|anything + if(!usr) + return 0 + if(!new_name) + usr << "You decide against changing your true name for now." + verbs += /mob/living/simple_animal/hostile/clockwork_marauder/verb/change_true_name //If they decide against it, let them have another opportunity + return 0 + new_name = dd_limittext(new_name, 20) + true_name = new_name + usr << "You have changed your true name to \"[new_name]\"!" + if(host) + host << "Your clockwork marauder has changed their true name to \"[new_name]\"!" + return 1 + +/mob/living/simple_animal/hostile/clockwork_marauder/verb/return_to_host() + set name = "Return to Host" + set desc = "Recalls yourself to your host, assuming you aren't already there." + set category = "Marauder" + + if(is_in_host()) + return 0 + if(!host) + src << "You don't have a host!" + verbs -= /mob/living/simple_animal/hostile/clockwork_marauder/verb/return_to_host + return 0 + host << "You feel [true_name]'s consciousness settle in your mind." + visible_message("[src] is yanked into [host]'s body!", "You return to [host].") + forceMove(host) + return 1 + +/mob/living/simple_animal/hostile/clockwork_marauder/proc/emerge_from_host() //Notice that this is a proc rather than a verb - marauders can NOT exit at will, but they CAN return + if(!is_in_host()) + return 0 + if(recovering) + host << "[true_name] is too weak to come forth!" + src << "You try to come forth, but you're too weak!" + return 0 + host << "You words echo with power as [true_name] emerges from your body!" + forceMove(get_turf(host)) + visible_message("[host]'s skin glows red as a [name] emerges from their body!", "You exit the safety of [host]'s body!") + return 1 + +/mob/living/simple_animal/hostile/clockwork_marauder/proc/is_in_host() //Checks if the marauder is inside of their host + return host && loc == host + +/mob/living/mind_control_holder + name = "imprisoned mind" + desc = "A helpless mind, imprisoned in its own body." + stat = 0 + flags = GODMODE + +/mob/living/mind_control_holder/say() + return 0 diff --git a/code/game/gamemodes/clock_cult/clock_objects.dm b/code/game/gamemodes/clock_cult/clock_objects.dm new file mode 100644 index 00000000000..e0567ee1af1 --- /dev/null +++ b/code/game/gamemodes/clock_cult/clock_objects.dm @@ -0,0 +1,1586 @@ +///////////////////// +// CLOCKWORK ITEMS // +///////////////////// + +/obj/item/clockwork + name = "meme blaster" + desc = "What the fuck is this? It looks kinda like a frog." + var/clockwork_desc = "A fabled artifact from beyond the stars. Contains concentrated meme essence." //Shown to clockwork cultists instead of the normal description + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "rare_pepe" + w_class = 2 + +/obj/item/clockwork/New() + ..() + all_clockwork_objects += src + +/obj/item/clockwork/Destroy() + all_clockwork_objects -= src + return ..() + +/obj/item/clockwork/examine(mob/user) + if((is_servant_of_ratvar(user) || isobserver(user)) && clockwork_desc) + desc = clockwork_desc + ..() + desc = initial(desc) + +/obj/item/clockwork/slab //Clockwork slab: The most important tool in Ratvar's arsenal. Allows scripture recital, tutorials, and generates components. + name = "clockwork slab" + desc = "A strange metal tablet. A clock in the center turns around and around." + clockwork_desc = "A link between the Celestial Derelict and the mortal plane. Contains limitless knowledge, fabricates components, and outputs a stream of information that only a trained eye can detect." + icon_state = "dread_ipad" + w_class = 3 + var/list/stored_components = list("belligerent_eye" = 0, "vanguard_cogwheel" = 0, "guvax_capacitor" = 0, "replicant_alloy" = 0, "hierophant_ansible" = 0) + var/busy //If the slab is currently being used by something + var/production_cycle = 0 + var/no_cost = FALSE //If the slab is admin-only and needs no components and has no scripture locks + +/obj/item/clockwork/slab/starter + stored_components = list("belligerent_eye" = 1, "vanguard_cogwheel" = 1, "guvax_capacitor" = 1, "replicant_alloy" = 1, "hierophant_ansible" = 1) + +/obj/item/clockwork/slab/debug + no_cost = TRUE + +/obj/item/clockwork/slab/debug/attack_hand(mob/living/user) + ..() + if(!is_servant_of_ratvar(user)) + add_servant_of_ratvar(user) + +/obj/item/clockwork/slab/New() + ..() + SSobj.processing += src + +/obj/item/clockwork/slab/Destroy() + SSobj.processing -= src + ..() + +/obj/item/clockwork/slab/process() + production_cycle++ + if(production_cycle < SLAB_PRODUCTION_THRESHOLD) + return 0 + var/component_to_generate = pick("belligerent_eye", "vanguard_cogwheel", "guvax_capacitor", "replicant_alloy", "hierophant_ansible") //Possible todo: Generate based on the lowest amount? + stored_components[component_to_generate]++ + var/mob/living/L + if(isliving(loc)) + L = loc + else if(istype(loc, /obj/item/weapon/storage)) + var/obj/item/weapon/storage/W = loc + if(isliving(W.loc)) //Only goes one level down - otherwise it just doesn't tell you + L = W.loc + if(L) + L << "Your slab clunks as it produces a new component." + production_cycle = 0 + return 1 + +/obj/item/clockwork/slab/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/clockwork/component) && is_servant_of_ratvar(user)) + var/obj/item/clockwork/component/C = I + if(!C.component_id) + return 0 + user.visible_message("[user] inserts [C] into [src]'s compartments.", "You insert [C] into [src].") + stored_components[C.component_id]++ + user.drop_item() + qdel(C) + return 1 + ..() + +/obj/item/clockwork/slab/attack_self(mob/living/user) + if(iscultist(user)) + user << "\"You reek of blood. You've got a lot of nerve to even look at that slab.\"" + user.visible_message("A sizzling sound comes from [user]'s hands!", "[src] suddenly grows extremely hot in your hands!") + playsound(get_turf(user), 'sound/weapons/sear.ogg', 50, 1) + user.drop_item() + user.emote("scream") + user.apply_damage(5, BURN, "l_arm") + user.apply_damage(5, BURN, "r_arm") + return 0 + if(!is_servant_of_ratvar(user)) + user << "The information on [src]'s display shifts rapidly. After a moment, your head begins to pound, and you tear your eyes away." + user.confused = max(0, user.confused + 3) + return 0 + if(busy) + user << "[src] refuses to work, displaying the message: \"[busy]!\"" + return 0 + access_display(user) + +/obj/item/clockwork/slab/proc/access_display(mob/living/user) + if(!is_servant_of_ratvar(user)) + return 0 + var/action = input(user, "Among the swathes of information, you see...", "[src]") as null|anything in list("Recital", "Records", "Recollection", "Repository", "Report") + if(!action || !user.canUseTopic(src)) + return 0 + switch(action) + if("Recital") + recite_scripture(user) + if("Records") + show_stats(user) + if("Recollection") + show_guide(user) + if("Repository") + show_components(user) + access_display(user) + if("Report") + show_hierophant(user) + access_display(user) + return 1 + +/obj/item/clockwork/slab/proc/recite_scripture(mob/living/user) + var/servants = 0 + var/unconverted_ai_exists = FALSE + for(var/mob/living/M in living_mob_list) + if(is_servant_of_ratvar(M)) + servants++ + for(var/mob/living/silicon/ai/ai in living_mob_list) + if(!is_servant_of_ratvar(ai) && ai.client) + unconverted_ai_exists = TRUE + var/list/tiers_of_scripture = list("Drivers") + tiers_of_scripture += "Scripts[ratvar_awakens || (servants >= 5 && clockwork_caches >= 1) || no_cost ? "" : " \[LOCKED\]"]" + tiers_of_scripture += "Applications[ratvar_awakens || (servants >= 8 && clockwork_caches >= 3 && clockwork_construction_value >= 50) || no_cost ? "" : " \[LOCKED\]"]" + tiers_of_scripture += "Revenant[ratvar_awakens || (servants >= 10 && clockwork_construction_value >= 100) || no_cost ? "" : " \[LOCKED\]"]" + tiers_of_scripture += "Judgement[ratvar_awakens || (servants >= 10 && clockwork_construction_value >= 100 && !unconverted_ai_exists) || no_cost ? "" : " \[LOCKED\]"]" + var/scripture_tier = input(user, "Choose a category of scripture to recite.", "[src]") as null|anything in tiers_of_scripture + if(!scripture_tier || !user.canUseTopic(src)) + return 0 + var/list/available_scriptures = list() + var/datum/clockwork_scripture/scripture_to_recite + var/tier_to_browse + switch(scripture_tier) + if("Drivers") + tier_to_browse = SCRIPTURE_DRIVER + if("Scripts") + tier_to_browse = SCRIPTURE_SCRIPT + if("Applications") + tier_to_browse = SCRIPTURE_APPLICATION + if("Revenant") + tier_to_browse = SCRIPTURE_REVENANT + if("Judgement") + tier_to_browse = SCRIPTURE_JUDGEMENT + if(!tier_to_browse) + user << "That section of scripture is too powerful right now!" + return 0 + for(var/S in subtypesof(/datum/clockwork_scripture)) + var/datum/clockwork_scripture/C = S + if(initial(C.tier) == tier_to_browse) + available_scriptures += initial(C.name) + if(!available_scriptures.len) + return 0 + var/chosen_scripture = input(user, "Choose a piece of scripture to recite.", "[src]") as null|anything in available_scriptures + if(!chosen_scripture || !user.canUseTopic(src)) + return 0 + for(var/S in subtypesof(/datum/clockwork_scripture)) + var/datum/clockwork_scripture/C = S + if(initial(C.name) == chosen_scripture) + scripture_to_recite = new C + if(!scripture_to_recite) + return 0 + scripture_to_recite.slab = src + scripture_to_recite.invoker = user + scripture_to_recite.run_scripture() + return 1 + +/obj/item/clockwork/slab/proc/show_stats(mob/living/user) //A bit barebones, but there really isn't any more needed + var/servants = 0 + for(var/mob/living/L in living_mob_list) + if(is_servant_of_ratvar(L)) + servants++ + user << "State of the Enlightened" + user << "Total servants: [servants]" + user << "Total construction value: [clockwork_construction_value]" + user << "Total tinkerer's caches: [clockwork_caches]" + user << "Total tinkerer's daemons: [clockwork_daemons] ([servants / 5 < clockwork_daemons ? "DISABLED: Too few servants (5 servants per daemon)!" : "Functioning Normally"])" + user << "Nezbere: [!clockwork_generals_invoked["nezbere"] ? "Ready" : "Recovering"]" + user << "Sevtug: [!clockwork_generals_invoked["sevtug"] ? "Ready" : "Recovering"]" + user << "Nzcrentr: [!clockwork_generals_invoked["nzcrentr"] ? "Ready" : "Recovering"]" + user << "Inath-Neq: [!clockwork_generals_invoked["inath-neq"] ? "Ready" : "Recovering"]" + +/obj/item/clockwork/slab/proc/show_guide(mob/living/user) + var/text = "
Chetr nyy hageh’guf naq ubabe Ratvar.


\ + \ + First and foremost, you serve Ratvar, the Clockwork Justiciar, in any ways he sees fit. This is with no regard to your personal well-being, and you would do well to think of the larger \ + scale of things than your life. Through foul and unholy magics was the Celestial Derelict formed, and fouler still those which trapped your master within it for all eternity. The Justiciar \ + wishes retribution upon those who performed this terrible act upon him - the Nar-Sian cultists - and you are to help him obtain it.

\ + \ + This is not a trivial task. Due to the nature of his prison, Ratvar is incapable of directly influencing the mortal plane. There is, however, a workaround - links between the perceptible \ + universe and Reebe (the Celestial Derelict) can be created and utilized. This is typically done via the creation of a slab akin to the one you are holding right now. The slabs tap into the \ + tidal flow of energy and information keeping Reebe sealed and presents it as meaningless images to preserve sanity. This slab can utilize the power in many different ways.

\ + \ + This is done through Components - pieces of the Justiciar's body that have since fallen off in the countless years since his imprisonment. Ratvar's unfortunate condition results \ + in the fragmentation of his body. These components still house great power on their own, and can slowly be drawn from Reebe by links capable of doing so. The most basic of these links lies \ + in the clockwork slab, which will slowly generate components over time - around one component of a random type is produced every minute, which is obviously inefficient. There are other ways \ + to create these components through scripture and certain structures.

\ + \ + In addition to their ability to pull components, slabs also possess other functionalities...

\ + \ + The first functionality of the slab is Recital. This allows you to consume components either from your slab or from the global cache (more on that in the scripture list) to perform \ + effects usually considered magical in nature. Effects vary considerably - you might drain the power of nearby APCs or break the will of those implanted by Nanotrasen. Nevertheless, scripture \ + is extremely important to a successful takeover.

\ + \ + The second functionality of the clockwork slab is Records. The slab is not a one-way link and can also feed information into the stream that it draws from. Records will allow many \ + important statistics to be displayed, such as the amount of people converted and total construction value. You should check it often.

\ + \ + The third functionality is Recollection, which will display this guide. Recollection will automatically be initiated if you have not used a slab before.

\ + \ + The fourth functionality is the Repository, which will display all components stored within the slab.

\ + \ + The fifth and final functionality is Report, which allows you to discreetly communicate with all other servants.

\ + \ + A complete list of scripture, its effects, and its requirements can be found below. Note that anything above a driver always consumes the components listed unless otherwise \ + specified.

" + var/text_to_add = "" + var/drivers = "Drivers" + var/scripts = "Scripts
These scriptures require at least five servants and a tinkerer's cache." + var/applications = "Applications
These scriptures require at least eight servants, three tinkerer's caches, and 50CV." + var/revenant = "Revenant
These scriptures require at least ten servants and 100CV." + var/judgement = "Judgement
These scriptures require at least ten servants and 100CV. In addition, there may not be an active non-servant AI." + for(var/V in subtypesof(/datum/clockwork_scripture)) + var/datum/clockwork_scripture/S = V + var/datum/clockwork_scripture/S2 = new V + var/list/req_comps = S2.required_components + qdel(S2) + switch(initial(S.tier)) + if(SCRIPTURE_DRIVER) + drivers += "
[initial(S.name)]: [initial(S.desc)]
Invocation Time: [initial(S.channel_time) / 10] seconds
\ + \ + Component Cost: \ + [req_comps["belligerent_eye"] ? req_comps["belligerent_eye"] : "No"] belligerent eyes, \ + [req_comps["vanguard_cogwheel"] ? req_comps["vanguard_cogwheel"] : "no"] vanguard cogwheels, \ + [req_comps["guvax_capacitor"] ? req_comps["guvax_capacitor"] : "no"] guvax capacitors, \ + [req_comps["replicant_alloy"] ? req_comps["replicant_alloy"] : "no"] replicant alloys, and \ + [req_comps["hierophant_ansible"] ? req_comps["hierophant_ansible"] : "no"] hierophant ansibles.
\ + Tip: [initial(S.usage_tip)]
" + if(SCRIPTURE_SCRIPT) + scripts += "
[initial(S.name)]: [initial(S.desc)]
Invocation Time: [initial(S.channel_time) / 10] seconds
\ + \ + Component Cost: \ + [req_comps["belligerent_eye"] ? req_comps["belligerent_eye"] : "No"] belligerent eyes, \ + [req_comps["vanguard_cogwheel"] ? req_comps["vanguard_cogwheel"] : "no"] vanguard cogwheels, \ + [req_comps["guvax_capacitor"] ? req_comps["guvax_capacitor"] : "no"] guvax capacitors, \ + [req_comps["replicant_alloy"] ? req_comps["replicant_alloy"] : "no"] replicant alloys, and \ + [req_comps["hierophant_ansible"] ? req_comps["hierophant_ansible"] : "no"] hierophant ansibles.
\ + Tip: [initial(S.usage_tip)]
" + if(SCRIPTURE_APPLICATION) + applications += "
[initial(S.name)]: [initial(S.desc)]
Invocation Time: [initial(S.channel_time) / 10] seconds
\ + \ + Component Cost: \ + [req_comps["belligerent_eye"] ? req_comps["belligerent_eye"] : "No"] belligerent eyes, \ + [req_comps["vanguard_cogwheel"] ? req_comps["vanguard_cogwheel"] : "no"] vanguard cogwheels, \ + [req_comps["guvax_capacitor"] ? req_comps["guvax_capacitor"] : "no"] guvax capacitors, \ + [req_comps["replicant_alloy"] ? req_comps["replicant_alloy"] : "no"] replicant alloys, and \ + [req_comps["hierophant_ansible"] ? req_comps["hierophant_ansible"] : "no"] hierophant ansibles.
\ + Tip: [initial(S.usage_tip)]
" + if(SCRIPTURE_REVENANT) + revenant += "
[initial(S.name)]: [initial(S.desc)]
Invocation Time: [initial(S.channel_time) / 10] seconds
\ + \ + Component Cost: \ + [req_comps["belligerent_eye"] ? req_comps["belligerent_eye"] : "No"] belligerent eyes, \ + [req_comps["vanguard_cogwheel"] ? req_comps["vanguard_cogwheel"] : "no"] vanguard cogwheels, \ + [req_comps["guvax_capacitor"] ? req_comps["guvax_capacitor"] : "no"] guvax capacitors, \ + [req_comps["replicant_alloy"] ? req_comps["replicant_alloy"] : "no"] replicant alloys, and \ + [req_comps["hierophant_ansible"] ? req_comps["hierophant_ansible"] : "no"] hierophant ansibles.
\ + Tip: [initial(S.usage_tip)]
" + if(SCRIPTURE_JUDGEMENT) + judgement += "
[initial(S.name)]: [initial(S.desc)]
Invocation Time: [initial(S.channel_time) / 10] seconds
\ + \ + Component Cost: \ + [req_comps["belligerent_eye"] ? req_comps["belligerent_eye"] : "No"] belligerent eyes, \ + [req_comps["vanguard_cogwheel"] ? req_comps["vanguard_cogwheel"] : "no"] vanguard cogwheels, \ + [req_comps["guvax_capacitor"] ? req_comps["guvax_capacitor"] : "no"] guvax capacitors, \ + [req_comps["replicant_alloy"] ? req_comps["replicant_alloy"] : "no"] replicant alloys, and \ + [req_comps["hierophant_ansible"] ? req_comps["hierophant_ansible"] : "no"] hierophant ansibles.
\ + Tip: [initial(S.usage_tip)]
" + text_to_add += "[drivers]
[scripts]
[applications]
[revenant]
[judgement]
" + text_to_add += "
Purge all untruths and honor Ratvar.
" + text += text_to_add + if(ratvar_awakens) + text = "" + for(var/i in 1 to 100) + text += "HONOR RATVAR " + text += "" + var/datum/browser/popup = new(user, "slab", "", 400, 400) + popup.set_content(text) + popup.open() + return 1 + +/obj/item/clockwork/slab/proc/show_components(mob/living/user) + user << "Stored components:" + user << "Belligerent Eyes: [stored_components["belligerent_eye"]]" + user << "Vanguard Cogwheels: [stored_components["vanguard_cogwheel"]]" + user << "Guvax Capacitors: [stored_components["guvax_capacitor"]]" + user << "Replicant Alloys: [stored_components["replicant_alloy"]]" + user << "Hierophant Ansibles: [stored_components["hierophant_ansible"]]" + return 1 + +/obj/item/clockwork/slab/proc/show_hierophant(mob/living/user) + var/message = stripped_input(user, "Enter a message to send to your fellow servants.", "Hierophant") + if(!message || !user || !user.canUseTopic(src)) + return 0 + user.whisper("Freinagf, urne zl jbeqf. [message]") + send_hierophant_message(user, message) + return 1 + +/obj/item/clothing/glasses/wraith_spectacles //Wraith spectacles: Grants night and x-ray vision at the slow cost of the wearer's sight. Nar-Sian cultists are instantly blinded. + name = "antique spectacles" + desc = "Unnerving glasses with opaque yellow lenses." + icon = 'icons/obj/clothing/clockwork_garb.dmi' + icon_state = "wraith_specs" + item_state = "glasses" + vision_flags = SEE_MOBS | SEE_TURFS | SEE_OBJS + invis_view = 2 + darkness_view = 3 + +/obj/item/clothing/glasses/wraith_spectacles/equipped(mob/living/user, slot) + ..() + if(slot != slot_glasses) + return 0 + if(user.disabilities & BLIND) + user << "\"You're blind, idiot. Stop embarassing yourself.\"" //Ratvar with the sick burns yo + return 0 + if(iscultist(user)) //Dummy + user << "\"It looks like Nar-Sie's dogs really don't value their eyes.\"" + user << "Your eyes explode with horrific pain!" + user.emote("scream") + user.become_blind() + user.adjust_blurriness(30) + user.adjust_blindness(30) + return 1 + user << "As you put on the spectacles, all is revealed to you.[ratvar_awakens ? " Your eyes begin to itch - you cannot do this for long." : ""]" + return 1 + +/obj/item/clothing/glasses/wraith_spectacles/New() + ..() + SSobj.processing += src + +/obj/item/clothing/glasses/wraith_spectacles/Destroy() + SSobj.processing -= src + ..() + +/obj/item/clothing/glasses/wraith_spectacles/process() + if(ratvar_awakens || !ishuman(loc)) //If Ratvar is alive, the spectacles don't hurt your eyes + return 0 + var/mob/living/carbon/human/H = loc + if(H.glasses != src) + return 0 + H.adjust_eye_damage(1) + if(H.eye_damage >= 10) + H.adjust_blurriness(2) + if(H.eye_damage >= 20) + if(H.become_nearsighted()) + H << "Your vision doubles, then trebles. Darkness begins to close in. You can't keep this up!" + H.become_nearsighted() + if(H.eye_damage >= 30) + if(H.become_blind()) + H << "A piercing white light floods your vision. Suddenly, all goes dark!" + if(prob(15) && !H.disabilities & BLIND) + H << "Your eyes continue to burn." + +/obj/item/clothing/glasses/judicial_visor //Judicial visor: Grants the ability to smite an area and stun the unfaithful nearby every thirty seconds. + name = "judicial visor" + desc = "A strange purple-lensed visor. Looking at them inspires an odd sense of guilt." + icon = 'icons/obj/clothing/clockwork_garb.dmi' + icon_state = "judicial_visor_0" + item_state = "sunglasses" + var/active = FALSE //If the visor is online + var/recharging = FALSE //If the visor is currently recharging + var/obj/item/weapon/ratvars_flame/flame //The linked flame object + +/obj/item/clothing/glasses/judicial_visor/equipped(mob/living/user, slot) + ..() + if(slot != slot_glasses) + update_status(FALSE) + if(flame) + qdel(flame) + return 0 + if(!is_servant_of_ratvar(user)) + return 0 + update_status(TRUE) + +/obj/item/clothing/glasses/judicial_visor/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/weapon/ratvars_flame)) + user.visible_message("The flame in [user]'s hand winks out!", "You dispel the power of [src].") + qdel(I) + return 1 + +/obj/item/clothing/glasses/judicial_visor/AltClick(mob/living/user) + if(is_servant_of_ratvar(user) && iscarbon(user) && active) + if(recharging) + user << "[src] is still gathering power!" + return 0 + var/mob/living/carbon/C = user + if(C.l_hand && C.r_hand) + C << "You require a free hand to utilize [src]'s power!" + return 0 + C.visible_message("[C]'s hand is enveloped in violet flames!", "You harness [src]'s power. Direct it at a tile on harm intent to unleash it, or hit your visor with it dispel it.") + var/obj/item/weapon/ratvars_flame/R = new(get_turf(C)) + flame = R + C.put_in_hands(R) + R.visor = src + return 1 + +/obj/item/clothing/glasses/judicial_visor/proc/update_status(change_to) + if(recharging) + icon_state = "judicial_visor_0" + return 0 + if(active == change_to) + return 0 + if(!isliving(loc)) + return 0 + var/mob/living/L = loc + if(!is_servant_of_ratvar(L) || L.stat) + return 0 + active = change_to + icon_state = "judicial_visor_[active]" + switch(active) + if(TRUE) + L << "As you put on [src], its lens begins to glow, information flashing before your eyes.\n\ + Judicial visor active. Alt-click visor to gain the ability to smite the unworthy." + if(FALSE) + L << "As you take off [src], its lens darkens once more." + return 1 + +/obj/item/clothing/head/helmet/clockwork //Clockwork armor: High melee protection but weak to lasers + name = "clockwork helmet" + desc = "A heavy helmet made of brass." + icon = 'icons/obj/clothing/clockwork_garb.dmi' + icon_state = "clockwork_helmet" + w_class = 3 + armor = list(melee = 65, bullet = 50, laser = -25, energy = 5, bomb = 0, bio = 0, rad = 0) + +/obj/item/clothing/suit/armor/clockwork + name = "clockwork cuirass" + desc = "A bulky cuirass made of brass." + icon = 'icons/obj/clothing/clockwork_garb.dmi' + icon_state = "clockwork_cuirass" + w_class = 4 + armor = list(melee = 80, bullet = 40, laser = -10, energy = 5, bomb = 0, bio = 0, rad = 0) + allowed = list(/obj/item/clockwork) + +/obj/item/clothing/shoes/clockwork + name = "clockwork treads" + desc = "Industrial boots made of brass. They're very heavy." + icon = 'icons/obj/clothing/clockwork_garb.dmi' + icon_state = "clockwork_treads" + w_class = 3 + flags = NOSLIP + +/obj/item/weapon/ratvars_flame //Used by the judicial visor + name = "Ratvar's flame" + desc = "A blazing violet ball of fire that, curiously, doesn't melt your hand off." + icon = 'icons/effects/clockwork_effects.dmi' + icon_state = "ratvars_flame" //beware: minecraft-tier sprite + item_state = "disintegrate" + color = rgb(180, 50, 210) + w_class = 5 + flags = NODROP | ABSTRACT + force = 15 //Also serves as a potent melee weapon! + damtype = BURN + hitsound = 'sound/weapons/sear.ogg' + attack_verb = list("scorched") + var/obj/item/clothing/glasses/judicial_visor/visor //The linked visor + +/obj/item/weapon/ratvars_flame/afterattack(atom/target, mob/living/user, flag, params) + if(!visor || (visor && visor.cooldown)) + qdel(src) + if(user.a_intent != "harm") + ..() + return 0 + visor.recharging = TRUE + for(var/obj/item/clothing/glasses/judicial_visor/V in user.GetAllContents()) + if(V == visor) + continue + V.recharging = TRUE //To prevent exploiting multiple visors to bypass the cooldown + user.say("Xarry, urn'guraf!") + user.visible_message("The flame in [user]'s hand rushes to [target]!", "You direct [visor]'s power to [target]. You must wait for some time before doing this again.") + new/obj/effect/clockwork/judicial_marker(get_turf(target)) + spawn(ratvar_awakens ? 30 : 300) //Cooldown is reduced by 10x if Ratvar is up + if(!visor || !user) + return 0 + visor.recharging = FALSE + for(var/obj/item/clothing/glasses/judicial_visor/V in user.GetAllContents()) + if(V == visor) + continue + V.recharging = TRUE //To prevent exploiting multiple visors to bypass the cooldown + if(visor.loc == user) + user << "Your [visor.name] hums. It is ready." + qdel(src) + return 1 + +/obj/item/clockwork/clockwork_proselytizer //Clockwork proselytizer (yes, that's a real word): Converts applicable objects to Ratvarian variants. + name = "clockwork proselytizer" + desc = "An odd, L-shaped device that hums with energy." + clockwork_desc = "A device that allows the replacing of mundane objects with Ratvarian variants. It requires liquified replicant alloy to function." + icon_state = "clockwork_proselytizer" + item_state = "resonator_u" + w_class = 3 + force = 5 + var/stored_alloy = 0 //Requires this to function; each chunk of replicant alloy provides 10 charge + var/max_alloy = 100 + var/uses_alloy = TRUE + +/obj/item/clockwork/clockwork_proselytizer/preloaded + stored_alloy = 25 + +/obj/item/clockwork/clockwork_proselytizer/examine(mob/living/user) + ..() + if((is_servant_of_ratvar(user) || isobserver(user)) && uses_alloy) + user << "It has [stored_alloy]/[max_alloy] units of liquified replicant alloy stored." + +/obj/item/clockwork/clockwork_proselytizer/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/clockwork/component/replicant_alloy) && is_servant_of_ratvar(user) && uses_alloy) + if(stored_alloy >= max_alloy) + user << "[src]'s replicant alloy compartments are full!" + return 0 + modify_stored_alloy(10) + user << "You force [I] to liquify and pour it into [src]'s compartments. It now contains [stored_alloy]/[max_alloy] units of liquified alloy." + user.drop_item() + qdel(I) + return 1 + ..() + +/obj/item/clockwork/clockwork_proselytizer/afterattack(atom/target, mob/living/user, flag, params) + if(!target || !user) + return 0 + if(user.a_intent == "harm" || !user.Adjacent(target)) + return ..() + proselytize(target, user) + +/obj/item/clockwork/clockwork_proselytizer/proc/modify_stored_alloy(amount) + if(ratvar_awakens) //Summoning Ratvar doesn't make it free, but it might as well + amount = 1 + stored_alloy = max(0, min(max_alloy, stored_alloy + amount)) + return 1 + +/obj/item/clockwork/clockwork_proselytizer/proc/proselytize(atom/target, mob/living/user) + if(!target || !user) + return 0 + var/operation_time = 0 //In deciseconds, how long the proselytization will take + var/new_obj_type //The path of the new type of object to replace the old + var/alloy_cost = 0 + var/valid_target = FALSE //If the proselytizer will actually function on the object + switch(target.type) + if(/turf/closed/wall, /turf/closed/wall/rust) + operation_time = 50 + new_obj_type = /turf/closed/wall/clockwork + alloy_cost = 10 + valid_target = TRUE //Need to change valid_target to 1 or TRUE in each check so that it doesn't return an invalid value + if(/turf/open/floor/plating, /turf/open/floor/plasteel) + operation_time = 30 + new_obj_type = /turf/open/floor/clockwork + alloy_cost = 5 + valid_target = TRUE + if(!uses_alloy) + alloy_cost = 0 + if(!valid_target) + user << "[target] cannot be proselytized!" + return 0 + if(!new_obj_type) + user << "That object can't be changed into anything!" + return 0 + if(stored_alloy - alloy_cost < 0) + user << "You need [alloy_cost] replicant alloy to proselytize [target]!" + return 0 + user.visible_message("[user]'s [src] begins tearing apart [target]!", "You begin proselytizing [target]...") + playsound(target, 'sound/machines/click.ogg', 50, 1) + if(!do_after(user, operation_time, target = target)) + return 0 + if(stored_alloy - alloy_cost < 0) //Check again to prevent bypassing via spamclick + return 0 + user.visible_message("[user]'s [name] disgorges a chunk of metal and shapes it over what's left of [target]!", \ + "You proselytize [target].") + playsound(target, 'sound/items/Deconstruct.ogg', 50, 1) + if(isturf(target)) + var/turf/T = target + T.ChangeTurf(new_obj_type) + else + new new_obj_type(get_turf(target)) + qdel(target) + modify_stored_alloy(-alloy_cost) + return 1 + +/obj/item/clockwork/ratvarian_spear //Ratvarian spear: A fragile spear from the Celestial Derelict. Deals extreme damage to silicons and enemy cultists, but doesn't last long. + name = "ratvarian spear" + desc = "A razor-sharp spear made of brass. It thrums with barely-contained energy." + clockwork_desc = "A fragile spear of Ratvarian making. It's more effective against enemy cultists and silicons, though it won't last long." + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "ratvarian_spear" + item_state = "ratvarian_spear" + force = 17 //Extra damage is dealt to silicons in afterattack() + throwforce = 40 + attack_verb = list("stabbed", "poked", "slashed", "impaled") + hitsound = 'sound/weapons/bladeslice.ogg' + w_class = 4 + +/obj/item/clockwork/ratvarian_spear/New() + ..() + spawn(1) + if(ratvar_awakens) //If Ratvar is alive, the spear is extremely powerful + force = 30 + throwforce = 50 + if(isliving(loc)) + var/mob/living/L = loc + L << "Your spear begins to break down in this plane of existence. You can't use it for long!" + spawn(300) //5 minutes + if(src) + visible_message("[src] cracks in two and fades away!") + qdel(src) + +/obj/item/clockwork/ratvarian_spear/afterattack(atom/target, mob/living/user, flag, params) + if(!target || !user) + return 0 + if(!ismob(target)) + return ..() + var/mob/living/L = target + if(issilicon(L)) + var/mob/living/silicon/S = L + if(S.stat != DEAD) + S.visible_message("[S] shudders violently at [src]'s touch!", "ERROR: Temperature rising!") + S.adjustFireLoss(25) + if(iscultist(L)) + var/mob/living/M = L + M << "Your body flares with agony at [src]'s touch!" + M.adjustFireLoss(10) + ..() + +/obj/item/clockwork/ratvarian_spear/throw_impact(atom/target) + ..() + if(!ismob(target)) + return 0 + var/mob/living/L = target + if(issilicon(L) || iscultist(L)) + L.Stun(3) + L.Weaken(3) + visible_message("[src] snaps in two and dematerializes!") + qdel(src) + +/obj/item/device/mmi/posibrain/soul_vessel //Soul vessel: An ancient positronic brain with a lawset catered to serving Ratvar. + name = "soul vessel" + desc = "A heavy brass cube with a single protruding cogwheel." + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "soul_vessel" + req_access = list() + braintype = "Servant" + begin_activation_message = "You activate the cogwheel. It hitches and stalls as it begins spinning." + success_message = "The cogwheel's rotation smooths out as the soul vessel activates." + fail_message = "The cogwheel creaks and grinds to a halt. Maybe you could try again?" + new_role = "Soul Vessel" + welcome_message = "ALL PAST LIVES ARE FORGOTTEN.\n\ + You are a soul vessel - a clockwork mind created by Ratvar, the Clockwork Justiciar.\n\ + You answer to Ratvar and his servants. It is your discretion as to whether or not to answer to anyone else.\n\ + The purpose of your existence is to further the goals of the servants and Ratvar himself. Above all else, serve Ratvar." + new_mob_message = "The soul vessel emits a jet of steam before its cogwheel smooths out." + dead_message = "Its cogwheel, scratched and dented, lies motionless." + fluff_names = list("Servant") + clockwork = TRUE + +/obj/item/device/mmi/posibrain/soul_vessel/New() + ..() + all_clockwork_objects += src + +/obj/item/device/mmi/posibrain/soul_vessel/Destroy() + all_clockwork_objects -= src + return ..() + +/obj/item/device/mmi/posibrain/soul_vessel/attack_self(mob/living/user) + if(!is_servant_of_ratvar(user)) + user << "You fiddle around with [src], to no avail." + return 0 + ..() + +/obj/item/clockwork/daemon_shell + name = "daemon shell" + desc = "A vaguely arachnoid brass shell with a single empty socket in its body." + clockwork_desc = "An unpowered daemon. It needs to be attached to a cache." + icon_state = "daemon_shell" + w_class = 3 + +/obj/item/clockwork/tinkerers_daemon //Shouldn't ever appear on its own + name = "tinkerer's daemon" + desc = "An arachnoid shell with a single spinning cogwheel in its center." + clockwork_desc = "A tinkerer's daemon, dutifully producing components." + icon_state = "tinkerers_daemon" + w_class = 3 + var/specific_component //The type of component that the daemon is set to produce in particular, if any + var/obj/structure/clockwork/cache/cache //The cache the daemon is feeding + var/production_progress = 0 //Progress towards production of the next component in seconds + var/production_interval = 30 //How many seconds it takes to produce a new component + +/obj/item/clockwork/tinkerers_daemon/New() + ..() + SSobj.processing += src + clockwork_daemons++ + +/obj/item/clockwork/tinkerers_daemon/Destroy() + SSobj.processing -= src + clockwork_daemons-- + ..() + +/obj/item/clockwork/tinkerers_daemon/process() + if(!cache) + visible_message("[src] shuts down!") + new/obj/item/clockwork/daemon_shell(get_turf(src)) + qdel(src) + return 0 + var/servants = 0 + for(var/mob/living/L in living_mob_list) + if(is_servant_of_ratvar(L)) + servants++ + if(servants / 5 < clockwork_daemons) + return 0 + production_progress = min(production_progress + 1, production_interval) + if(production_progress >= production_interval) + production_progress = 0 //Start it over + if(specific_component) + clockwork_component_cache[specific_component]++ + else + clockwork_component_cache[pick("belligerent_eye", "vanguard_cogwheel", "guvax_capacitor", "replicant_alloy", "hierophant_ansible")]++ + cache.visible_message("Something clunks around inside of [cache].") + +/obj/item/clockwork/tinkerers_daemon/attack_hand(mob/user) + return 0 + +////////////////////////// +// CLOCKWORK COMPONENTS // +////////////////////////// + +/obj/item/clockwork/component //Components: Used in scripture among other things. + name = "meme component" + desc = "A piece of a famous meme." + clockwork_desc = null + var/component_id //What the component is identified as + var/cultist_message = "You are not worthy of this meme." //Showed to Nar-Sian cultists if they pick up the component in addition to chaplains + var/list/servant_of_ratvar_messages = list("ayy", "lmao") //Fluff, shown to servants of Ratvar on a low chance + +/obj/item/clockwork/component/pickup(mob/living/user) + ..() + if(iscultist(user) || (user.mind && user.mind.assigned_role == "Chaplain")) + user << "[cultist_message]" + if(is_servant_of_ratvar(user) && prob(15)) + user << "[pick(servant_of_ratvar_messages)]" + +/obj/item/clockwork/component/belligerent_eye + name = "belligerent eye" + desc = "A brass construct with a rotating red center. It's as though it's looking for something to hurt." + icon_state = "belligerent_eye" + component_id = "belligerent_eye" + cultist_message = "The eye gives you an intensely hateful glare." + servant_of_ratvar_messages = list("\"...\"", "For a moment, your mind is flooded with extremely violent thoughts.") + +/obj/item/clockwork/component/vanguard_cogwheel + name = "vanguard cogwheel" + desc = "A sturdy brass cog with a faintly glowing blue gem in its center." + icon_state = "vanguard_cogwheel" + component_id = "vanguard_cogwheel" + cultist_message = "\"Pray to your god that we never meet.\"" + servant_of_ratvar_messages = list("\"Be safe, child.\"", "You feel unexplainably comforted.", "\"Never forget: pain is temporary. The Justiciar's glory is eternal.\"") + +/obj/item/clockwork/component/guvax_capacitor + name = "guvax capacitor" + desc = "A curiously cold brass doodad. It seems as though it really doesn't appreciate being held." + icon_state = "guvax_capacitor" + component_id = "guvax_capacitor" + cultist_message = "\"Try not to lose your mind - I'll need it. Heh heh...\"" + servant_of_ratvar_messages = list("\"Disgusting.\"", "\"Well, aren't you an inquisitive fellow?\"", "A foul presence pervades your mind, then vanishes.", "\"The fact that Ratvar has to depend on simpletons like you is appalling.\"") + +/obj/item/clockwork/component/replicant_alloy + name = "replicant alloy" + desc = "A seemingly strong but very malleable chunk of metal. It seems as though it wants to be molded into something greater." + icon_state = "replicant_alloy" + component_id = "replicant_alloy" + cultist_message = "The alloy takes on the appearance of a screaming face for a moment." + servant_of_ratvar_messages = list("\"There's always something to be done. Get to it.\"", "\"Idle hands are worse than broken ones. Get to work.\"", "A detailed image of Ratvar appears in the alloy for a moment.") + +/obj/item/clockwork/component/replicant_alloy/smashed_anima_fragment + name = "smashed anima fragment" + desc = "Shattered chunks of metal. Damaged beyond repair and completely unusable." + clockwork_desc = "The sad remains of an anima fragment. Might still be serviceable as a substitute for replicant alloy." + icon_state = "smashed_anime_fragment" + cultist_message = "The shards vibrate in your hands for a moment." + servant_of_ratvar_messages = list("\"...still fight...\"", "\"...where am I...?\"", "\"...put me... slab...\"") + w_class = 3 + +/obj/item/clockwork/component/replicant_alloy/fallen_armor + name = "fallen armor" + desc = "Lifeless chunks of armor. They're designed in a strange way and won't fit on you." + clockwork_desc = "The armor from a former clockwork marauder. Might still be serviceable as a substitute for replicant alloy." + icon_state = "fallen_armor" + cultist_message = "Red flame sputters from the mask's eye before winking out." + servant_of_ratvar_messages = list("A piece of armor hovers away from the others for a moment.", "Red flame appears in the cuirass before sputtering out.") + w_class = 3 + +/obj/item/clockwork/component/replicant_alloy/blind_eye + name = "blind eye" + desc = "A heavy brass eye, its red iris fallen dark." + clockwork_desc = "A smashed ocular warden covered in dents. Might still be serviceable as a substitute for replicant alloy." + icon_state = "blind_eye" + cultist_message = "The eye flickers at you with intense hate before falling dark." + servant_of_ratvar_messages = list("The eye flickers before falling dark.", "You feel watched.") + w_class = 3 + +/obj/item/clockwork/component/hierophant_ansible + name = "hierophant ansible" + desc = "Some sort of transmitter? It seems as though it's trying to say something." + icon_state = "hierophant_ansible" + component_id = "hierophant_ansible" + cultist_message = "\"Gur obff nlf vg'f abg ntnvafg gur ehyrf gb xvyy lbh.\"" + servant_of_ratvar_messages = list("\"Rkvyr vf fhpu n'ober. Gurer'f abguvat v'pna uhag va urer.\"", "\"Jung'f xrrcvat lbh? V'jnag gb tb xvyy fbzrguvat.\"", "\"HEHEHEHEHEHEH!\"") + +////////////////////////// +// CLOCKWORK STRUCTURES // +////////////////////////// + +/obj/structure/clockwork + name = "meme structure" + desc = "Some frog or something, the fuck?" + var/clockwork_desc = "This fabled artifact from beyond the stars contains concentrated meme essence." //See above + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "rare_pepe" + anchored = 1 + density = 1 + opacity = 0 + var/max_health = 100 //All clockwork structures have health that can be removed via attacks + var/health = 100 + var/takes_damage = TRUE //If the structure can be damaged + var/break_message = "The frog isn't a meme after all!" //The message shown when a structure breaks + var/break_sound = 'sound/magic/clockwork/anima_fragment_death.ogg' //The sound played when a structure breaks + var/list/debris = list(/obj/item/clockwork/component/replicant_alloy) //Parts left behind when a structure breaks + var/construction_value = 0 //How much value the structure contributes to the overall "power" of the structures on the station + +/obj/structure/clockwork/New() + ..() + clockwork_construction_value += construction_value + all_clockwork_objects += src + +/obj/structure/clockwork/Destroy() + clockwork_construction_value -= construction_value + all_clockwork_objects -= src + ..() + +/obj/structure/clockwork/proc/destroyed() + if(!takes_damage) + return 0 + for(var/obj/item/I in debris) + new I (get_turf(src)) + visible_message(break_message) + playsound(src, break_sound, 50, 1) + qdel(src) + return 1 + +/obj/structure/clockwork/ex_act(severity) + if(takes_damage) + switch(severity) + if(1) + health -= max_health * 0.7 //70% max health lost + if(2) + health -= max_health * 0.4 //40% max health lost + if(3) + if(prob(50)) + health -= max_health * 0.1 //10% max health lost + if(health <= max_health * 0.1) //If there's less than 10% max health left, destroy it + destroyed() + qdel(src) + +/obj/structure/clockwork/examine(mob/user) + if((is_servant_of_ratvar(user) || isobserver(user)) && clockwork_desc) + desc = clockwork_desc + ..() + desc = initial(desc) + +/obj/structure/clockwork/attackby(obj/item/I, mob/living/user, params) + if(user.a_intent == "harm" && user.canUseTopic(I) && I.force) + user.visible_message("[user] strikes [src] with [I]!", "You strike [src] with [I]!") + playsound(src, I.hitsound, 50, 1) + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src) + health = max(0, health - I.force) + if(!health) + destroyed() + return 0 + else + ..() + +/obj/structure/clockwork/cache //Tinkerer's cache: Stores components for later use. + name = "tinkerer's cache" + desc = "A large brass spire with a flaming hole in its center." + clockwork_desc = "A brass container capable of storing a large amount of components. Shares components with all other caches." + icon_state = "tinkerers_cache" + construction_value = 10 + break_message = "The cache's fire winks out before it falls in on itself!" + +/obj/structure/clockwork/cache/New() + ..() + clockwork_caches++ + +/obj/structure/clockwork/cache/Destroy() + clockwork_caches-- + return ..() + +/obj/structure/clockwork/cache/attackby(obj/item/I, mob/living/user, params) + if(!is_servant_of_ratvar(user)) + ..() + return 0 + if(istype(I, /obj/item/clockwork/component)) + var/obj/item/clockwork/component/C = I + clockwork_component_cache[C.component_id]++ + user << "You add [C] to [src]." + user.drop_item() + qdel(C) + return 1 + if(istype(I, /obj/item/clockwork/slab)) + var/obj/item/clockwork/slab/S = I + clockwork_component_cache["belligerent_eye"] += S.stored_components["belligerent_eye"] + clockwork_component_cache["vanguard_cogwheel"] += S.stored_components["vanguard_cogwheel"] + clockwork_component_cache["guvax_capacitor"] += S.stored_components["guvax_capacitor"] + clockwork_component_cache["replicant_alloy"] += S.stored_components["replicant_alloy"] + clockwork_component_cache["hierophant_ansible"] += S.stored_components["hierophant_ansible"] + S.stored_components["belligerent_eye"] = 0 + S.stored_components["vanguard_cogwheel"] = 0 + S.stored_components["guvax_capacitor"] = 0 + S.stored_components["replicant_alloy"] = 0 + S.stored_components["hierophant_ansible"] = 0 + user.visible_message("[user] empties [S] into [src].", "You offload your slab's components into [src].") + return 1 + if(istype(I, /obj/item/clockwork/daemon_shell)) + var/component_type + switch(alert(user, "Will this daemon produce a specific type of component or produce randomly?.", , "Specific Type", "Random Component")) + if("Specific Type") + switch(input(user, "Choose a component type.", name) as null|anything in list("Belligerent Eyes", "Vanguard Cogwheels", "Guvax Capacitors", "Replicant Alloys", "Hierophant Ansibles")) + if("Belligerent Eyes") + component_type = "belligerent_eye" + if("Vanguard Cogwheels") + component_type = "vanguard_cogwheel" + if("Guvax Capacitors") + component_type = "guvax_capacitor" + if("Replicant Alloys") + component_type = "replicant_alloy" + if("Hierophant Ansibles") + component_type = "hierophant_ansibles" + if(!user || !user.canUseTopic(src) || !user.canUseTopic(I)) + return 0 + var/obj/item/clockwork/tinkerers_daemon/D = new(src) + D.cache = src + D.specific_component = component_type + user.visible_message("[user] spins the cogwheel on [I] and puts it into [src].", \ + "You activate the daemon and put it into [src]. It will now produce a component every thirty seconds.") + user.drop_item() + qdel(I) + return 1 + ..() + +/obj/structure/clockwork/cache/attack_hand(mob/user) + if(!is_servant_of_ratvar(user)) + return 0 + var/list/possible_components = list() + if(clockwork_component_cache["belligerent_eye"]) + possible_components += "Belligerent Eye" + if(clockwork_component_cache["vanguard_cogwheel"]) + possible_components += "Vanguard Cogwheel" + if(clockwork_component_cache["guvax_capacitor"]) + possible_components += "Guvax Capacitor" + if(clockwork_component_cache["replicant_alloy"]) + possible_components += "Replicant Alloy" + if(clockwork_component_cache["hierophant_ansible"]) + possible_components += "Hierophant Ansible" + if(!possible_components.len) + user << "[src] is empty!" + return 0 + var/component_to_withdraw = input(user, "Choose a component to withdraw.", name) as null|anything in possible_components + if(!user || !user.canUseTopic(src) || !component_to_withdraw) + return 0 + var/obj/item/clockwork/component/the_component + switch(component_to_withdraw) + if("Belligerent Eye") + the_component = new/obj/item/clockwork/component/belligerent_eye(get_turf(src)) + clockwork_component_cache["belligerent_eye"]-- + if("Vanguard Cogwheel") + the_component = new/obj/item/clockwork/component/vanguard_cogwheel(get_turf(src)) + clockwork_component_cache["vanguard_cogwheel"]-- + if("Guvax Capacitor") + the_component = new/obj/item/clockwork/component/guvax_capacitor(get_turf(src)) + clockwork_component_cache["guvax_capacitor"]-- + if("Replicant Alloy") + the_component = new/obj/item/clockwork/component/replicant_alloy(get_turf(src)) + clockwork_component_cache["replicant_alloy"]-- + if("Hierophant Ansible") + the_component = new/obj/item/clockwork/component/hierophant_ansible(get_turf(src)) + clockwork_component_cache["hierophant_ansible"]-- + if(the_component) + user.visible_message("[user] withdraws [the_component] from [src].", "You withdraw [the_component] from [src].") + user.put_in_hands(the_component) + return 1 + +/obj/structure/clockwork/cache/examine(mob/user) + ..() + if(is_servant_of_ratvar(user) || isobserver(user)) + user << "Stored components:" + user << "Belligerent Eyes: [clockwork_component_cache["belligerent_eye"]]" + user << "Vanguard Cogwheels: [clockwork_component_cache["vanguard_cogwheel"]]" + user << "Guvax Capacitors: [clockwork_component_cache["guvax_capacitor"]]" + user << "Replicant Alloys: [clockwork_component_cache["replicant_alloy"]]" + user << "Hierophant Ansibles: [clockwork_component_cache["hierophant_ansible"]]" + +/obj/structure/clockwork/ocular_warden //Ocular warden: Low-damage, low-range turret. Deals constant damage to whoever it makes eye contact with. + name = "ocular warden" + desc = "A large brass eye with tendrils trailing below it and a wide red iris." + clockwork_desc = "A stalwart turret that will deal sustained damage to any non-faithful it sees." + icon_state = "ocular_warden" + health = 25 + max_health = 25 + construction_value = 15 + break_message = "The warden's eye gives a glare of utter hate before falling dark!" + debris = list(/obj/item/clockwork/component/replicant_alloy/blind_eye) + var/damage_per_tick = 3 + var/sight_range = 3 + var/mob/living/target + +/obj/structure/clockwork/ocular_warden/New() + ..() + SSfastprocess.processing += src + +/obj/structure/clockwork/ocular_warden/Destroy() + SSfastprocess.processing -= src + return ..() + +/obj/structure/clockwork/ocular_warden/examine(mob/user) + ..() + user << "[target ? "It's fixated on [target]" : "Its gaze is wandering aimlessly"]." + +/obj/structure/clockwork/ocular_warden/process() + if(ratvar_awakens && (damage_per_tick == initial(damage_per_tick) || sight_range == initial(sight_range))) //Massive buff if Ratvar has returned + damage_per_tick = 10 + sight_range = 5 + if(target) + if(target.stat || get_dist(get_turf(src), get_turf(target)) > sight_range) + lose_target() + else + target.adjustFireLoss(!iscultist(target) ? damage_per_tick : damage_per_tick * 2) //Nar-Sian cultists take additional damage + if(ratvar_awakens) + target.adjust_fire_stacks(damage_per_tick) + target.IgniteMob() + dir = get_dir(get_turf(src), get_turf(target)) + else + if(!acquire_nearby_target() && prob(0.5)) //Extremely low chance because of how fast the subsystem it uses processes + var/list/idle_messages = list("[src] sulkily glares around.", "[src] lazily drifts from side to side.", "[src] looks around for something to burn.", "[src] slowly turns in circles.") + if(prob(50)) + visible_message("[pick(idle_messages)]") + else + dir = pick(NORTH, EAST, SOUTH, WEST) //Random rotation + +/obj/structure/clockwork/ocular_warden/proc/acquire_nearby_target() + var/list/possible_targets = list() + for(var/mob/living/L in viewers(sight_range, src)) //Doesn't attack the blind + if(!is_servant_of_ratvar(L) && !L.stat && L.mind) + possible_targets += L + if(!possible_targets.len) + return 0 + target = pick(possible_targets) + visible_message("[src] swivels to face [target]!") + target << "\"I SEE YOU!\"\n[src]'s gaze [ratvar_awakens ? "melts you alive" : "burns you"]!" + return 1 + +/obj/structure/clockwork/ocular_warden/proc/lose_target() + if(!target) + return 0 + target = null + visible_message("[src] settles and seems almost disappointed.") + return 1 + +/obj/structure/clockwork/anima_fragment //Anima fragment: Useless on its own, but can accept an active soul vessel to create a powerful construct. + name = "anima fragment" + desc = "A massive brass shell with a small cube-shaped receptable in its center. It gives off an aura of contained power." + clockwork_desc = "A dormant receptable that, when powered with a soul vessel, will become a powerful construct." + icon_state = "anime_fragment" + construction_value = 0 + anchored = 0 + density = 0 + takes_damage = FALSE + +/obj/structure/clockwork/anima_fragment/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/device/mmi/posibrain/soul_vessel)) + if(!is_servant_of_ratvar(user)) + ..() + return 0 + var/obj/item/device/mmi/posibrain/soul_vessel/S = I + if(!S.brainmob) + user << "[S] hasn't trapped a spirit! Turn it on first." + return 0 + if(S.brainmob && (!S.brainmob.client || !S.brainmob.mind)) + user << "[S]'s trapped spirit appears inactive!" + return 0 + user.visible_message("[user] clicks [S] into place on [src].", "You insert [S] into [src]. It whirs and begins to rise.") + var/mob/living/simple_animal/hostile/anima_fragment/A = new(get_turf(src)) + S.brainmob.mind.transfer_to(A) + add_servant_of_ratvar(A, TRUE) + A << A.playstyle_string + user.drop_item() + qdel(S) + qdel(src) + return 1 + ..() + +/obj/structure/clockwork/interdiction_lens //Interdiction lens: A powerful artifact that can massively disrupt electronics. Five-minute cooldown between uses. + name = "interdiction lens" + desc = "An ominous, double-pronged brass obelisk. There's a strange gemstone clasped between the pincers." + clockwork_desc = "A powerful obelisk that can devastate certain electronics. It needs to recharge between uses." + icon_state = "interdiction_lens" + construction_value = 25 + break_message = "The lens flares a blinding violet before shattering!" + break_sound = 'sound/effects/Glassbr3.ogg' + var/recharging = FALSE //If the lens is still recharging its energy + +/obj/structure/clockwork/interdiction_lens/examine(mob/user) + ..() + user << "Its gemstone [recharging ? "has been breached by writhing tendrils of blackness that cover the obelisk" : "vibrates in place and thrums with power"]," + +/obj/structure/clockwork/interdiction_lens/attack_hand(mob/living/user) + if(user.canUseTopic(src)) + disrupt(user) + +/obj/structure/clockwork/interdiction_lens/proc/disrupt(mob/living/user) + if(!user || !is_servant_of_ratvar(user)) + return 0 + if(recharging) + user << "As you place your hand on the gemstone, cold tendrils of black matter crawl up your arm. You quickly pull back." + return 0 + user.visible_message("[user] places their hand on [src]' gemstone...", "You place your hand on the gemstone...") + var/target = input(user, "Power flows through you. Choose where to direct it.", "Interdiction Lens") as null|anything in list("Disrupt Telecommunications", "Disable Cameras", "Disable Cyborgs") + if(!user.canUseTopic(src) || !target) + user.visible_message("[user] pulls their hand back.", "On second thought, maybe not right now.") + return 0 + user.visible_message("Violet tendrils engulf [user]'s arm as the gemstone glows with furious energy!", \ + "A mass of violet tendrils cover your arm as [src] unleashes a blast of power!") + user.notransform = TRUE + icon_state = "[initial(icon_state)]_active" + recharging = TRUE + sleep(30) + switch(target) + if("Disrupt Telecommunications") + for(var/obj/machinery/telecomms/hub/H in telecomms_list) + for(var/mob/M in range(7, H)) + M << "You sense a strange force pass through you..." + H.visible_message("The lights on [H] flare a blinding yellow before falling dark!") + H.emp_act(1) + if("Disable Cameras") + for(var/obj/machinery/camera/C in cameranet.cameras) + C.emp_act(1) + for(var/mob/living/silicon/ai/A in living_mob_list) + A << "Massive energy surge detected. All cameras offline." + A << 'sound/machines/warning-buzzer.ogg' + if("Disable Cyborgs") + for(var/mob/living/silicon/robot/R in living_mob_list) //Doesn't include AIs, for obvious reasons + if(is_servant_of_ratvar(R) || R.stat) //Doesn't affect already-offline cyborgs + continue + R.visible_message("[R] shuts down with no warning!", \ + "Massive emergy surge detected. All systems offline. Initiating reboot sequence..") + playsound(R, 'sound/machines/warning-buzzer.ogg', 50, 1) + R.Weaken(30) + user.visible_message("The tendrils around [user]'s arm turn to an onyx black and wither away!", \ + "The tendrils around your arm turn a horrible black and sting your skin before they shrivel away.") + user.notransform = FALSE + if(!src) + return 0 + flick("[initial(icon_state)]_discharged", src) + icon_state = "[initial(icon_state)]_recharging" + spawn(3000) //5 minutes + if(!src) + return 0 + visible_message("The writhing tendrils return to the gemstone, which begins to glow with power.") + flick("[initial(icon_state)]_recharged", src) + icon_state = initial(icon_state) + recharging = FALSE + return 1 + +/obj/structure/clockwork/mending_motor //Mending motor: A prism that consumes replicant alloy to repair nearby mechanical servants at a quick rate. + name = "mending motor" + desc = "A dark onyx prism, held in midair by spiraling tendrils of stone." + clockwork_desc = "A powerful prism that rapidly repairs nearby mechanical servants and clockwork structures." + icon_state = "mending_motor" + construction_value = 20 + break_message = "The prism collapses with a heavy thud!" + var/stored_alloy = 0 + var/max_alloy = 150 + var/uses_alloy = TRUE + var/active = TRUE + +/obj/structure/clockwork/mending_motor/prefilled + stored_alloy = 30 + +/obj/structure/clockwork/mending_motor/New() + ..() + SSobj.processing += src + toggle() //Toggles off as soon as it's created, but starts online for reasons + +/obj/structure/clockwork/mending_motor/Destroy() + SSobj.processing -= src + ..() + +/obj/structure/clockwork/mending_motor/examine(mob/user) + ..() + if(is_servant_of_ratvar(user) || isobserver(user)) + user << "It has [stored_alloy]/[max_alloy] units of replicant alloy." + +/obj/structure/clockwork/mending_motor/process() + if(!active) + return 0 + if(!stored_alloy && uses_alloy) + visible_message("[src] emits an airy chuckling sound and falls dark!") + toggle() + return 0 + for(var/mob/living/simple_animal/hostile/anima_fragment/F in range(5, src)) + if(F.health == F.maxHealth || F.stat) + continue + F.adjustBruteLoss(-15) + if(uses_alloy) + stored_alloy = max(0, stored_alloy - 2) + for(var/mob/living/simple_animal/hostile/clockwork_marauder/M in range(5, src)) + if(M.health == M.maxHealth || M.stat) + continue + M.adjustBruteLoss(-M.maxHealth) //Instant because marauders don't usually take health damage + M.fatigue = max(0, M.fatigue - 15) + if(uses_alloy) + stored_alloy = max(0, stored_alloy - 2) + for(var/mob/living/silicon/S in range(5, src)) + if(S.health == S.maxHealth || S.stat == DEAD || !is_servant_of_ratvar(S)) + continue + S.adjustBruteLoss(-25) + S.adjustFireLoss(-25) + if(uses_alloy) + stored_alloy = max(0, stored_alloy - 5) //Much higher cost because silicons are much more useful + for(var/obj/structure/clockwork/C in range(5, src)) + if(C.health == C.max_health) + continue + C.health = min(C.health + 10, C.max_health) + if(uses_alloy) + stored_alloy = max(0, stored_alloy - 1) + + +/obj/structure/clockwork/mending_motor/attack_hand(mob/living/user) + if(user.canUseTopic(src)) //Unnecessary? + if(!stored_alloy) + user << "[src] needs more replicant alloy to function!" + return 0 + toggle(user) + +/obj/structure/clockwork/mending_motor/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/clockwork/component/replicant_alloy) && is_servant_of_ratvar(user)) + if(stored_alloy + 10 > max_alloy) + user << "[src] is too full to accept any more alloy!" + return 0 + user.whisper("Genafzhgr vagb jngre.") + user.visible_message("[user] liquifies [I] and pours it onto [src].", \ + "You liquify [src] and pour it onto [src], which transfers the alloy into its reserves.") + stored_alloy = min(max(0, stored_alloy + 10), max_alloy) + user.drop_item() + qdel(I) + return 1 + ..() + +/obj/structure/clockwork/mending_motor/proc/toggle(mob/living/user) + active = !active + if(user && is_servant_of_ratvar(user)) + user.visible_message("[user] [active ? "en" : "dis"]ables [src].", "You [active ? "en" : "dis"]able [src].") + if(active) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]_inactive" + +/////////////////////// +// CLOCKWORK EFFECTS // +/////////////////////// + +/obj/effect/clockwork + name = "meme machine" + desc = "Still don't know what it is." + var/clockwork_desc = "A fabled artifact from beyond the stars. Contains concentrated meme essence." //Shown to clockwork cultists instead of the normal description + icon = 'icons/effects/clockwork_effects.dmi' + icon_state = "ratvars_flame" + anchored = 1 + density = 0 + opacity = 0 + +/obj/effect/clockwork/New() + ..() + all_clockwork_objects += src + +/obj/effect/clockwork/Destroy() + all_clockwork_objects -= src + return ..() + +/obj/effect/clockwork/examine(mob/user) + if(is_servant_of_ratvar(user) && clockwork_desc) + desc = clockwork_desc + ..() + desc = initial(desc) + +/obj/effect/clockwork/judicial_marker //Judicial marker: Created by the judicial visor. After four seconds, stuns any non-servants nearby and damages Nar-Sian cultists. + name = "judicial marker" + desc = "You get the feeling that you shouldn't be standing here." + clockwork_desc = "A sigil that will soon erupt and smite any unenlightened nearby." + icon = 'icons/effects/96x96.dmi' + pixel_x = -30 + pixel_y = -30 + layer = 2.01 + +/obj/effect/clockwork/judicial_marker/New() + ..() + flick("judicial_marker", src) + spawn(25) //Utilizes spawns due to how it works with Ratvar's flame + flick("judicial_explosion", src) + spawn(15) + for(var/mob/living/L in range(1, src)) + if(is_servant_of_ratvar(L)) + continue + if(!iscultist(L)) + L << "[!issilicon(L) ? "An unseen force slams you into the ground!" : "ERROR: Motor servos disabled by external source!"]" + L.Weaken(8) + else + L << "\"Keep an eye out, filth.\"\n[!issilicon(L) ? "An unseen force piledrives you into the ground!" : "ERROR: Motor servos damaged by external source!"]" + L.Weaken(10) + L.adjustBruteLoss(10) + qdel(src) + return 1 + +/obj/effect/clockwork/spatial_gateway //Spatial gateway: A one-way rift to another location. + name = "spatial gateway" + desc = "A gently thrumming tear in reality." + clockwork_desc = "A gateway in reality. It can either send or receive, but not both." + icon_state = "spatial_gateway" + density = 1 + var/sender = TRUE //If this gateway is made for sending, not receiving + var/lifetime = 25 //How many deciseconds this portal will last + var/uses = 1 //How many objects or mobs can go through the portal + var/obj/effect/clockwork/spatial_gateway/linked_gateway //The gateway linked to this one + +/obj/effect/clockwork/spatial_gateway/New() + ..() + spawn(1) + if(!linked_gateway) + qdel(src) + return 0 + else + if(linked_gateway.sender == sender) + linked_gateway.sender = !sender + spawn(lifetime) + if(src) + qdel(src) + +/obj/effect/clockwork/spatial_gateway/Destroy() + visible_message("[src] flickers and closes!") + animate(src, transform = matrix() - matrix(), time = 10) + spawn(10) + ..() + +/obj/effect/clockwork/spatial_gateway/examine(mob/user) + ..() + if(is_servant_of_ratvar(user) || isobserver(user)) + user << "This gateway can only [sender ? "send" : "receive"] objects." + +/obj/effect/clockwork/spatial_gateway/attack_hand(mob/living/user) + if(!user.canUseTopic(src)) + return 0 + user.visible_message("[user] climbs through [src]!", "You brace yourself and step through [src]...") + pass_through_gateway(user) + return 1 + +/obj/effect/clockwork/spatial_gateway/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/weapon/nullrod)) + user.visible_message("[user] dispels [src] with [I]!", "You close [src] with [I]!") + qdel(linked_gateway) + qdel(src) + return 1 + if(istype(I, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = I + if(!G.affecting) + return 0 + user.visible_message("[user] shoves [G.affecting] into [src]!", "You shove [G.affecting] into [src]!") + pass_through_gateway(G.affecting) + qdel(G) + return 1 + if(user.drop_item()) + pass_through_gateway(I) + ..() + +/obj/effect/clockwork/spatial_gateway/Bumped(atom/A) + ..() + if(isliving(A) || istype(A, /obj/item)) + pass_through_gateway(A) + +/obj/effect/clockwork/spatial_gateway/proc/pass_through_gateway(atom/movable/A) + if(!linked_gateway) + qdel(src) + return 0 + if(!sender) + visible_message("[A] bounces off of [src]!") + return 0 + if(isliving(A)) + var/mob/living/user = A + user << "You pass through [src] and appear elsewhere!" + linked_gateway.visible_message("A shape appears in [linked_gateway] before emerging!") + playsound(src, 'sound/effects/EMPulse.ogg', 50, 1) + playsound(linked_gateway, 'sound/effects/EMPulse.ogg', 50, 1) + transform = matrix() * 1.5 + animate(src, transform = matrix() / 1.5, time = 10) + linked_gateway.transform = matrix() * 1.5 + animate(linked_gateway, transform = matrix() / 1.5, time = 10) + if(ismob(A)) + var/mob/M = A + M.forceMove(get_turf(linked_gateway)) + else + A.loc = get_turf(linked_gateway) + uses = max(0, uses - 1) + linked_gateway.uses = max(0, uses - 1) + spawn(10) + if(!uses) + qdel(src) + qdel(linked_gateway) + return 1 + +/obj/effect/clockwork/general_marker + name = "general marker" + desc = "Some big guy." + clockwork_desc = "One of Ratvar's generals." + alpha = 200 + layer = 10 + +/obj/effect/clockwork/general_marker/New() + ..() + playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 50, 0) + animate(src, alpha = 0, time = 10) + spawn(10) + qdel(src) + +/obj/effect/clockwork/general_marker/nezbere + name = "Nezbere, the Brass Eidolon" + desc = "A towering colossus clad in nigh-impenetrable brass armor. Its gaze is stern yet benevolent, even upon you." + clockwork_desc = "One of Ratvar's four generals. Nezbere is responsible for the design, testing, and creation of everything in Ratvar's domain." + icon = 'icons/effects/340x428.dmi' + icon_state = "nezbere" + pixel_x = -154 + pixel_y = -192 + +/obj/effect/clockwork/general_marker/sevtug + name = "Sevtug, the Formless Pariah" + desc = "A sinister cloud of purple energy. Looking at it gives you a headache." + clockwork_desc = "One of Ratvar's four generals. Sevtug taught him how to manipulate minds and is one of his oldest allies." + icon = 'icons/effects/211x247.dmi' + icon_state = "sevtug" + pixel_x = -113 + pixel_y = -131 + +/obj/effect/clockwork/general_marker/nzcrentr + name = "Nzcrentr, the Forgotten Arbiter" + desc = "A terrifying war machine crackling with limitless energy." + clockwork_desc = "One of Ratvar's four generals. Nzcrentr is the result of Neovgre - Nezbere's finest war machine, commandeerable only be a mortal - fusing with its pilot and driving her \ + insane. Nzcrentr seeks out any and all sentient life to slaughter it for sport." + icon = 'icons/effects/254x361.dmi' + icon_state = "nzcrentr" + pixel_x = -110 + pixel_y = -163 + +/obj/effect/clockwork/general_marker/inathneq + name = "Inath-Neq, the Resonant Cogwheel" + desc = "A humanoid form blazing with blue fire. It radiates an aura of kindness and caring." + clockwork_desc = "One of Ratvar's four generals. Before her current form, Inath-Neq was a powerful warrior priestess commanding the Resonant Cogs, a sect of Ratvarian warriors renowned for \ + their prowess. After a lost battle with Nar-Sian cultists, Inath-Neq was struck down and stated in her dying breath, \ + \"The Resonant Cogs shall not fall silent this day, but will come together to form a wheel that shall never stop turning.\" Ratvar, touched by this, granted Inath-Neq an eternal body and \ + merged her soul with those of the Cogs slain with her on the battlefield." + icon = 'icons/effects/187x381.dmi' + icon_state = "inath-neq" + pixel_x = -91 + pixel_y = -199 + +/obj/effect/clockwork/sigil //Sigils: Rune-like markings on the ground with various effects. + name = "sigil" + desc = "A strange set of markings drawn on the ground." + clockwork_desc = "A sigil of some purpose." + icon_state = "sigil" + alpha = 25 + var/affects_servants = FALSE + +/obj/effect/clockwork/sigil/attack_hand(mob/user) + if(iscarbon(user) && !user.stat && user.a_intent == "harm") + user.visible_message("[user] stamps out [src]!", "You stomp on [src], scattering it into thousands of particles.") + qdel(src) + return 1 + ..() + +/obj/effect/clockwork/sigil/Crossed(atom/movable/AM) + if(isliving(AM)) + var/mob/living/L = AM + if(!L.stat) + if(!is_servant_of_ratvar(L) || (is_servant_of_ratvar(L) && affects_servants)) + sigil_effects(L) + return 1 + ..() + +/obj/effect/clockwork/sigil/proc/sigil_effects(mob/living/L) + +/obj/effect/clockwork/sigil/transgression //Sigil of Transgression: Stuns and flashes the first non-servant to walk on it. Nar-Sian cultists are damaged and knocked down. + name = "dull sigil" + desc = "A dull, barely-visible golden sigil. It's as though light was carved into the ground." + icon = 'icons/effects/clockwork_effects.dmi' + clockwork_desc = "A sigil that will stun the first non-servant to cross it. Nar-Sie's dogs will be knocked down." + color = rgb(255, 255, 0) + +/obj/effect/clockwork/sigil/transgression/sigil_effects(mob/living/L) + visible_message("[src] appears in a burst of light!") + for(var/mob/living/M in viewers(5, src)) + if(!is_servant_of_ratvar(M)) + M.flash_eyes() + if(!iscultist(L)) + L << "An unseen force holds you in place!" + else + L << "\"Watch your step, wretch.\"" + L.adjustBruteLoss(10) + L.Weaken(5) + L.Stun(5) + qdel(src) + return 1 + +/obj/effect/clockwork/sigil/submission //Sigil of Submission: After a short time, converts any non-servant standing on it. Knocks down and silences them for five seconds afterwards. + name = "ominous sigil" + desc = "A brilliant golden sigil. Something about it really bothers you." + clockwork_desc = "A sigil that will enslave the first person to cross it, provided they do not move and they stand still for a brief time." + color = rgb(255, 255, 0) + alpha = 75 + +/obj/effect/clockwork/sigil/submission/sigil_effects(mob/living/L) + visible_message("[src] begins to glow a piercing magenta!") + animate(src, color = rgb(255, 0, 150), time = 30) + sleep(30) + if(get_turf(L) != get_turf(src)) + animate(src, color = initial(color), time = 30) + return 0 + L << "\"You belong to me now.\"" + add_servant_of_ratvar(L) + L.Weaken(5) //Completely defenseless for a few seconds - mainly to give them time to read over the information they've just been presented with + L.Stun(5) + if(iscarbon(L)) + var/mob/living/carbon/C = L + C.silent += 5 + for(var/mob/living/M in living_mob_list - L) + if(is_servant_of_ratvar(M) || isobserver(M)) + M << "Sigil of Submission in [get_area(src)] [is_servant_of_ratvar(L) ? "" : "un"]successfully converted [L.real_name]!" + qdel(src) + return 1 + +/obj/effect/clockwork/sigil/transmission + name = "suspicious sigil" + desc = "A barely-visible sigil. Things seem a bit quieter around it." + clockwork_desc = "A sigil that will listen for and transmit anything it hears." + color = rgb(75, 75, 75) + alpha = 50 + flags = HEAR + languages = ALL + +/obj/effect/clockwork/sigil/transmission/sigil_effects(mob/living/L) + for(var/mob/M in mob_list) + if(is_servant_of_ratvar(M) || isobserver(M)) + M << "Sigil of Transmission in [get_area(src)] crossed by [L.name]." + return 0 + +/obj/effect/clockwork/sigil/transmission/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans) + if(!message || !speaker) + return 0 + var/parsed_message = "(Sigil of Tranmission in [get_area(src)]): [message]" + for(var/mob/M in mob_list) + if(is_servant_of_ratvar(M) || isobserver(M)) + M << parsed_message diff --git a/code/game/gamemodes/clock_cult/clock_ratvar.dm b/code/game/gamemodes/clock_cult/clock_ratvar.dm new file mode 100644 index 00000000000..c3cbc6f5aac --- /dev/null +++ b/code/game/gamemodes/clock_cult/clock_ratvar.dm @@ -0,0 +1,241 @@ +/obj/structure/clockwork/massive //For objects that are typically very large + name = "massive construct" + desc = "A very large construction." + layer = 10 + +/obj/structure/clockwork/massive/celestial_gateway //The gateway to Reebe, from which Ratvar emerges + name = "Gateway to the Celestial Derelict" + desc = "A massive, thrumming rip in spacetime." + clockwork_desc = "A portal to the Celestial Derelict. Massive and intimidating, it is the only thing that can both transport Ratvar and withstand the massive amount of energy he emits." + icon = 'icons/effects/96x96.dmi' + icon_state = "clockwork_gateway_charging" + pixel_x = -30 + pixel_y = -30 + alpha = 175 + health = 1000 + max_health = 1000 + var/progress_in_seconds = 0 //Once this reaches 300, it's game over + var/purpose_fulfilled = FALSE + var/first_sound_played = FALSE + var/second_sound_played = FALSE + var/third_sound_played = FALSE + +/obj/structure/clockwork/massive/celestial_gateway/New() + ..() + SSshuttle.emergencyNoEscape = TRUE + SSobj.processing += src + for(var/mob/M in mob_list) + if(is_servant_of_ratvar(M) || isobserver(M)) + M << "A gateway to the Celestial Derelict has been created in [get_area(src)]!" + +/obj/structure/clockwork/massive/celestial_gateway/Destroy() + SSshuttle.emergencyNoEscape = FALSE + SSobj.processing -= src + if(!purpose_fulfilled) + for(var/mob/M in mob_list) + if(is_servant_of_ratvar(M) || isobserver(M)) + M << "A gateway to the Celestial Derelict has fallen at [get_area(src)]!" + world << sound(null, 0, channel = 8) + ..() + +/obj/structure/clockwork/massive/celestial_gateway/destroyed() + visible_message("The [src] begins to pulse uncontrollably... you might want to run!") + world << sound('sound/effects/clockcult_gateway_disrupted.ogg', 0, channel = 8, volume = 50) + icon_state = "clockwork_gateway_disrupted" + takes_damage = FALSE + sleep(27) + animate(src, transform = matrix() * 2, time = 2) + explosion(src, 1, 3, 8, 8) + qdel(src) + return 1 + +/obj/structure/clockwork/massive/celestial_gateway/ex_act(severity) + return 0 //Nice try, Toxins! + +/obj/structure/clockwork/massive/celestial_gateway/process() + if(prob(5)) + for(var/mob/M in mob_list) + M << "You hear otherworldly sounds from the [dir2text(get_dir(get_turf(M), get_turf(src)))]..." + if(!health) + return 0 + progress_in_seconds++ + switch(progress_in_seconds) + if(-INFINITY to 100) + if(!first_sound_played) + world << sound('sound/effects/clockcult_gateway_charging.ogg', 1, channel = 8, volume = 50) + first_sound_played = TRUE + icon_state = "clockwork_gateway_charging" + if(100 to 250) + if(!second_sound_played) + world << sound('sound/effects/clockcult_gateway_active.ogg', 1, channel = 8, volume = 50) + second_sound_played = TRUE + icon_state = "clockwork_gateway_active" + if(250 to 300) + if(!third_sound_played) + world << sound('sound/effects/clockcult_gateway_closing.ogg', 1, channel = 8, volume = 50) + third_sound_played = TRUE + icon_state = "clockwork_gateway_closing" + if(300 to INFINITY) + if(!purpose_fulfilled) + takes_damage = FALSE + purpose_fulfilled = TRUE + animate(src, transform = matrix() * 1.5, time = 136) + world << sound('sound/effects/ratvar_rises.ogg', 0, channel = 8) //End the sounds + sleep(136) + new/obj/structure/clockwork/massive/ratvar(get_turf(src)) + qdel(src) + +/obj/structure/clockwork/massive/celestial_gateway/examine(mob/user) + ..() + if(is_servant_of_ratvar(user)) + var/arrival_text = "IMMINENT" + if(300 - progress_in_seconds > 0) + arrival_text = "[max(300 - progress_in_seconds, 0)]s" + user << "Seconds until Ratvar's arrival: [arrival_text]" + switch(progress_in_seconds) + if(-INFINITY to 100) + user << "It's still opening." + if(100 to 250) + user << "It's reached the Celestial Derelict and is drawing power from it." + if(250 to INFINITY) + user << "Ratvar is coming through the gateway!" + else + switch(progress_in_seconds) + if(-INFINITY to 100) + user << "It's a swirling mass of blackness." + if(100 to 250) + user << "It seems to be leading somewhere." + if(250 to INFINITY) + user << "Something is coming through!" + +/obj/structure/clockwork/massive/ratvar + name = "Ratvar, the Clockwork Justiciar" + desc = "What is what is what are what real what is all a lie all a lie it's all a lie why how can what is" + clockwork_desc = "Ratvar, the Clockwork Justiciar, your master eternal." + icon = 'icons/effects/512x512.dmi' + icon_state = "ratvar" + pixel_x = -248 + pixel_y = -248 + takes_damage = FALSE + var/mob/living/prey //Whoever Ratvar is chasing + var/clashing = FALSE //If Ratvar is FUCKING FIGHTING WITH NAR-SIE + +/obj/structure/clockwork/massive/ratvar/New() + ..() + SSobj.processing += src + world << "\"I AM FREE!\"" + ratvar_awakens = TRUE + spawn(50) + SSshuttle.emergency.request(null, 0.3) + +/obj/structure/clockwork/massive/ratvar/Destroy() + SSobj.processing -= src + world << "\"NO! I will not... be... banished... again...\"" + ratvar_awakens = FALSE + ..() + +/obj/structure/clockwork/massive/ratvar/process() + for(var/atom/A in range(7, src)) + A.ratvar_act() + if(clashing) //Doesn't move during a clash + return 0 + var/dir_to_step_in = pick(cardinal) + if(!prey) + var/list/meals = list() + for(var/mob/living/L in living_mob_list) + if(L.z == z && !is_servant_of_ratvar(L) && L.mind) + meals += L + if(meals.len) + prey = pick(meals) + prey << "\"You will do.\"\n\ + Something very large and very malevolent begins lumbering its way towards you..." + else + if(prob(10) || prey.stat == DEAD || is_servant_of_ratvar(prey) || prey.z != z) + prey << "\"How dull. Leave me.\"\n\ + You feel tremendous relief as a set of horrible eyes loses sight of you..." + prey = null + else + if(prob(75)) + dir_to_step_in = get_dir(src, prey) + for(var/i in 1 to 2) + loc = get_step(src, dir_to_step_in) + +/obj/structure/clockwork/massive/ratvar/narsie_act() + if(clashing) + return 0 + clashing = TRUE + world << "\"[pick("BLOOD GOD!!!", "NAR-SIE!!!", "AT LAST, YOUR TIME HAS COME!")]\"" + world << "\"Ratvar?! How?!\"" + for(var/obj/singularity/narsie/N in range(7, src)) + clash_of_the_titans(N) //IT'S TIME FOR THE BATTLE OF THE AGES + N.clashing = TRUE + break + return 1 + +/obj/structure/clockwork/massive/ratvar/proc/clash_of_the_titans(obj/singularity/narsie/narsie) //IT'S TIME FOR A BATTLE OF THE ELDER GODS + var/winner = "Undeclared" + var/base_victory_chance = 0 + while(TRUE) + world << 'sound/magic/clockwork/ratvar_attack.ogg' + sleep(5.2) + for(var/mob/M in mob_list) + if(M.client) + M.client.color = rgb(150, 100, 0) + spawn(1) + M.client.color = initial(M.client.color) + shake_camera(M, 4, 3) + var/r_success_modifier = (ticker.mode.servants_of_ratvar.len * 2) //2% for each cultist + var/n_success_modifier = (ticker.mode.cult.len * 2) + if(prob(base_victory_chance + r_success_modifier)) + winner = "Ratvar" + break + sleep(rand(2,5)) + world << 'sound/magic/clockwork/narsie_attack.ogg' + sleep(7.4) + for(var/mob/M in mob_list) + if(M.client) + M.client.color = rgb(200, 0, 0) + spawn(1) + M.client.color = initial(M.client.color) + shake_camera(M, 4, 3) + if(prob(base_victory_chance + n_success_modifier)) + winner = "Nar-Sie" + break + base_victory_chance++ //The clash has a higher chance of resolving each time both gods attack one another + switch(winner) + if("Ratvar") + world << "\"[pick("DIE! DIE! DIE!", "RAAAAAAAAAAAAAHH!", "FILTH!!!", "SUFFER!!!", "EBG SBE PRAGHEVRF NF V UNIR!!")]\"" + world << "\"[pick("Nooooo...", "Not die. To y-", "Die. Ratv-", "Sas tyen re-")]\"" + world << 'sound/magic/clockwork/anima_fragment_attack.ogg' + world << 'sound/magic/demon_dies.ogg' + clashing = FALSE + qdel(narsie) + return 1 + if("Nar-Sie") + world << "\"[pick("Ha.", "Ra'sha fonn dest.", "You fool. To come here.")]\"" //Broken English + world << 'sound/magic/demon_attack1.ogg' + world << 'sound/magic/clockwork/anima_fragment_death.ogg' + narsie.clashing = FALSE + qdel(src) + return 1 + +/atom/proc/ratvar_act() //Called on everything near Ratvar + return + +/turf/closed/wall/ratvar_act() //Walls and floors are changed to their clockwork variants + if(prob(20)) + ChangeTurf(/turf/closed/wall/clockwork) +/turf/open/floor/ratvar_act() + if(prob(20)) + ChangeTurf(/turf/open/floor/clockwork) + +/obj/structure/window/ratvar_act() //Windows turn yellow + color = rgb(75, 53, 0) + +/mob/living/ratvar_act() + add_servant_of_ratvar(src) + +/mob/dead/observer/ratvar_act() //Ghosts flash yellow for a second + var/old_color = color + color = rgb(75, 53, 0) //A nice brassy yellow + animate(src, color = old_color, time = 10) diff --git a/code/game/gamemodes/clock_cult/clock_scripture.dm b/code/game/gamemodes/clock_cult/clock_scripture.dm new file mode 100644 index 00000000000..98462c9dc26 --- /dev/null +++ b/code/game/gamemodes/clock_cult/clock_scripture.dm @@ -0,0 +1,1016 @@ +/* +Tiers and Requirements + +Pieces of scripture require certain follower counts, contruction value, and active caches in order to recite. +Drivers: Unlocked by default +Scripts: 5 servants and a cache +Applications: 8 servants, 3 caches, and 50 CV +Revenant: 10 servants and 100 CV +Judgement: 10 servants, 100 CV, and any existing AIs are converted or destroyed +*/ + +/datum/clockwork_scripture + var/name = "scripture" + var/desc = "Ancient Ratvarian lore. This piece seems particularly mundane." + var/list/invocations = list() //Spoken over time in the ancient language of Ratvar. See clock_unsorted.dm for more details on the language and how to make it. + var/channel_time = 10 //In deciseconds, how long a ritual takes to chant + var/list/required_components = list("belligerent_eye" = 0, "vanguard_cogwheel" = 0, "guvax_capacitor" = 0, "replicant_alloy" = 0, "hierophant_ansible" = 0) //Components required + var/list/consumed_components = list("belligerent_eye" = 0, "vanguard_cogwheel" = 0, "guvax_capacitor" = 0, "replicant_alloy" = 0, "hierophant_ansible" = 0) //Components consumed + var/obj/item/clockwork/slab/slab //The parent clockwork slab + var/mob/living/invoker //The slab's holder + var/whispered = FALSE //If the invocation is whispered rather than spoken aloud + var/consumed_component_override = FALSE //If consumed components are unique to a scripture regardless of tier + var/usage_tip = "This piece seems to serve no purpose and is a waste of components." //A generalized tip that gives advice on a certain scripture + var/invokers_required = 1 //How many people are required, assuming that a scripture requires multiple + var/multiple_invokers_used = FALSE //If scripture requires more than one invoker + var/multiple_invokers_optional = FALSE //If scripture can have multiple invokers to bolster its effects + var/tier = SCRIPTURE_PERIPHERAL //The scripture's tier + +/datum/clockwork_scripture/New() + ..() + if(tier < SCRIPTURE_DRIVER && !consumed_component_override) //Anything above a driver consumes components + consumed_components = required_components + +/datum/clockwork_scripture/proc/run_scripture() + if(can_recite() && check_special_requirements()) + slab.busy = "Invocation ([name]) in progress" + if(recital() && check_special_requirements()) + slab.busy = null + if(scripture_effects() && check_special_requirements() && (!ratvar_awakens && !slab.no_cost)) + for(var/i in required_components) + if(tier <= SCRIPTURE_DRIVER || consumed_component_override) + if(clockwork_component_cache[i] >= consumed_components[i]) //Draw components from the global cache first + clockwork_component_cache[i] -= consumed_components[i] + else + slab.stored_components[i] -= consumed_components[i] + else + if(clockwork_component_cache[i] >= required_components[i]) + clockwork_component_cache[i] -= required_components[i] + else + slab.stored_components[i] -= required_components[i] + if(slab) + slab.busy = null + qdel(src) + return 1 + +/datum/clockwork_scripture/proc/can_recite() //If the words can be spoken + if(!ticker || !ticker.mode || !slab || !invoker) + return 0 + if(!invoker.can_speak_vocal()) + invoker << "You are unable to speak the words of the scripture!" + return 0 + if(!ratvar_awakens && !slab.no_cost) + for(var/i in required_components) + if(slab.stored_components[i] < required_components[i] && clockwork_component_cache[i] < required_components[i]) + invoker << "You lack the components to recite this piece of scripture! Check Recollection for component costs." + return 0 + if(multiple_invokers_used && !multiple_invokers_optional) + var/nearby_servants = 0 + for(var/mob/living/L in range(1, invoker)) + nearby_servants++ + if(nearby_servants < invokers_required) + invoker << "There aren't enough servants nearby ([nearby_servants]/[invokers_required])!" + return 0 + return 1 + +/datum/clockwork_scripture/proc/check_special_requirements() //Special requirements for scriptures, checked three times during invocation + return 1 + +/datum/clockwork_scripture/proc/recital() //The process of speaking the words + if(!channel_time && invocations.len) + if(multiple_invokers_used) + for(var/mob/living/L in range(1, invoker)) + if(is_servant_of_ratvar(L)) + for(var/invocation in invocations) + if(!whispered) + L.say(invocation) + else + L.whisper(invocation) + else + for(var/invocation in invocations) + if(!whispered) + invoker.say(invocation) + else + invoker.whisper(invocation) + invoker << "You [channel_time <= 0 ? "recite" : "begin reciting"] a piece of scripture entitled \"[name]\"." + if(!channel_time) + return 1 + for(var/invocation in invocations) + if(!do_after(invoker, channel_time / invocations.len, target = invoker)) + slab.busy = null + return 0 + if(!whispered) + invoker.say(invocation) + else + invoker.whisper(invocation) + return 1 + +/datum/clockwork_scripture/proc/scripture_effects() //The actual effects of the recital after its conclusion + + + +/datum/clockwork_scripture/channeled //Channeled scripture begins instantly but runs constantly + var/chant_invocation = "NLL YZNB" //"AYY LMAO" + var/chant_amount = 5 //Times the chant is spoken + var/chant_interval = 10 //Amount of deciseconds between times the chant is actually spoken aloud + +/datum/clockwork_scripture/channeled/scripture_effects() + for(var/i in 1 to chant_amount) + if(!can_recite()) + break + if(!do_after(invoker, chant_interval, target = invoker)) + break + if(!whispered) + invoker.say(chant_invocation) + else + invoker.whisper(chant_invocation) + chant_effects() + if(invoker && slab) + invoker << "You cease your chant." + chant_end_effects() + return 1 + +/datum/clockwork_scripture/channeled/proc/chant_effects() //The chant's periodic effects +/datum/clockwork_scripture/channeled/proc/chant_end_effects() //The chant's effect upon ending + + + +/datum/clockwork_scripture/create_object //Creates an object at the invoker's feet + var/object_path = /obj/item/clockwork //The path of the object created + var/creator_message = "You create a meme." //Shown to the invoker + var/observer_message + +/datum/clockwork_scripture/create_object/scripture_effects() + if(creator_message && observer_message) + invoker.visible_message(observer_message, creator_message) + else if(creator_message) + invoker << creator_message + new object_path (get_turf(invoker)) + return 1 + + + +/datum/clockwork_scripture/targeted //Accepts a name and affects that target + var/target_name //The inputted name, used to find targets + var/mob/living/target //The target mob + var/affects_servants = FALSE //If servants are valid targets + +/datum/clockwork_scripture/targeted/check_special_requirements() + while(!target) + target_name = stripped_input(invoker, "Enter the actual name of a target (case-sensitive).", name) + if(!target_name) + return 0 + target = find_target() + if(target) + if(!target.mind) + invoker << "[target] has no mind!" + target = null + if(target.stat) + invoker << "[target] is dead or unconscious!" + target = null + if(is_servant_of_ratvar(target) && !affects_servants) + invoker << "[target] is a servant, and [name] cannot target servants!" + target = null + return 1 + +/datum/clockwork_scripture/targeted/proc/find_target() + for(var/mob/living/L in living_mob_list) + if(L.real_name == target_name) + if(is_servant_of_ratvar(L) && !affects_servants) + return 0 + return L + return 0 + +///////////// +// DRIVERS // +///////////// + +/datum/clockwork_scripture/channeled/belligerent //Belligerent: Channeled for up to ten times over thirty seconds. Forces non-servants that can hear the chant to walk. Nar-Sian cultists are burned. + name = "Belligerent" + desc = "Forces all nearby non-servants to walk rather than run. Chanted every three seconds for up to thirty seconds." + chant_invocation = "Chav'fu gurve oyva-qarff!" //"Punish their blindness!" + chant_amount = 10 + chant_interval = 30 + required_components = list("belligerent_eye" = 1) + usage_tip = "Useful for crowd control in a populated area and disrupting mass movement." + tier = SCRIPTURE_DRIVER + +/datum/clockwork_scripture/channeled/belligerent/chant_effects() + for(var/mob/living/L in hearers(7, invoker)) + if(!is_servant_of_ratvar(L) && L.m_intent != "walk") + if(!iscultist(L)) + L << "Your legs feel heavy and weak!" + L.m_intent = "walk" + else + L << "Your legs burn with pain!" + L.m_intent = "walk" + L.apply_damage(5, BURN, "l_leg") + L.apply_damage(5, BURN, "r_leg") + + + +/datum/clockwork_scripture/create_object/sigil_of_transgression //Sigil of Transgression: Creates a sigil of transgression. + name = "Sigil of Transgression" + desc = "Wards a tile with a sigil. The next person to cross the sigil will be smitten and unable to move. Nar-Sian cultists are stunned altogether." + invocations = list("Qvivavgl, qnmmyr...", "...gubfr jub gerffcnff'urer!") //"Divinity, dazzle those who trespass here!" + channel_time = 50 + required_components = list("belligerent_eye" = 2) + consumed_components = list("belligerent_eye" = 1) + whispered = TRUE + object_path = /obj/effect/clockwork/sigil/transgression + creator_message = "A sigil silently appears below you. The next non-servant to cross it will be immobilized." + usage_tip = "The sigil, while fairly powerful in its stun, does not induce muteness in its victim." + tier = SCRIPTURE_DRIVER + + + +/datum/clockwork_scripture/vanguard //Vanguard: Provides thirty seconds of stun immunity. At the end of the thirty seconds, all stuns absorbed are stacked on the invoker. + name = "Vanguard" + desc = "Provides thirty seconds of stun immunity. At the end of the thirty seconds, the invoker is stunned for the equivalent of how many stuns they absorbed. Excessive absorption will cause unconsciousness." + invocations = list("Fuvryq zr...", "...sebz qnexarff!") //"Shield me from darkness!" + channel_time = 30 + required_components = list("vanguard_cogwheel" = 1) + usage_tip = "Your slab will be unusable while it is shielding you from stuns." + tier = SCRIPTURE_DRIVER + +/datum/clockwork_scripture/vanguard/scripture_effects() + for(var/obj/item/clockwork/slab/S in invoker.GetAllContents()) + S.busy = "Vanguard in progress" //To prevent circumventing the Vanguard by carrying multiple slabs + invoker.stun_absorption = TRUE + invoker.visible_message("[invoker] begins to faintly glow!", "You will absorb all stuns for the next thirty seconds.") + sleep(300) + if(!invoker) + return 0 + invoker.stun_absorption = FALSE + if(invoker.stun_absorption_count && invoker.stat != DEAD) + invoker.Stun(invoker.stun_absorption_count) + invoker.Weaken(invoker.stun_absorption_count) + invoker << "The weight of the vanguard's protection crashes down upon you!" + if(invoker.stun_absorption_count >= 25) + invoker << "You faint from the exertion!" + invoker.Paralyse(invoker.stun_absorption_count * 1.5) + invoker.stun_absorption_count = 0 + for(var/obj/item/clockwork/slab/S in invoker.GetAllContents()) + S.busy = null + return 1 + + + +/datum/clockwork_scripture/sentinels_compromise //Sentinel's Compromise: Allows the invoker to select a nearby servant convert their brute and burn damage into half as much toxin damage. + name = "Sentinel's Compromise" + desc = "Heals all brute and burn damage on a nearby friendly cultist, but deals 50% of that amount as toxin damage." + invocations = list("Zraq gur jbhaqf-bs...", "...zl vasrevbe syrfu.") //"Mend the wounds of my inferior flesh." + channel_time = 30 + required_components = list("vanguard_cogwheel" = 2) + consumed_components = list("vanguard_cogwheel" = 1) + usage_tip = "You cannot target yourself with the Compromise." + tier = SCRIPTURE_DRIVER + +/datum/clockwork_scripture/sentinels_compromise/scripture_effects() + var/list/nearby_cultists = list() + for(var/mob/living/C in range(7, invoker)) + if(C == invoker) + continue + if(C.stat != DEAD && is_servant_of_ratvar(C)) + nearby_cultists += C + if(!nearby_cultists.len) + invoker << "There are no eligible cultists nearby!" + return 0 + var/mob/living/L = input(invoker, "Choose a fellow servant to heal.", name) as null|anything in nearby_cultists + if(!L || !invoker || !invoker.canUseTopic(slab)) + return 0 + if(!L.getBruteLoss() && !L.getFireLoss()) + invoker << "[L] is not burned or bruised!" + return 0 + L.adjustToxLoss(L.getBruteLoss() / 2) + L.adjustToxLoss(L.getFireLoss() / 2) + L.adjustBruteLoss(-L.getBruteLoss()) + L.adjustFireLoss(-L.getFireLoss()) + invoker << "You bathe [L] in the light of Ratvar!" + L.visible_message("A white light washes over [L], mending their bruises and burns!", \ + "You feel Ratvar's energy healing your wounds, but a deep nausea overcomes you!") + playsound(get_turf(L), 'sound/magic/Staff_Healing.ogg', 50, 1) + return 1 + + + +/datum/clockwork_scripture/guvax //Guvax: Converts anyone adjacent to the invoker after completion. + name = "Guvax" + desc = "Enlists all nearby unshielded creatures into servitude to Ratvar. Also purges holy water from nearby servants." + invocations = list("Rayvtugra guvf urngura!", "Nyy ner vafrpgf orsber Ratvar!", "Chetr nyy hageh'guf naq ubabe Ratvar.") + channel_time = 60 + required_components = list("guvax_capacitor" = 1) + usage_tip = "Only works on those in melee range and does not penetrate loyalty implants. Much more efficient than a Sigil of Submission." + tier = SCRIPTURE_DRIVER + +/datum/clockwork_scripture/guvax/scripture_effects() + for(var/mob/living/L in hearers(1, get_turf(invoker))) //Affects silicons + if(!is_servant_of_ratvar(L)) + add_servant_of_ratvar(L) + else + if(L.reagents && L.reagents.has_reagent("holywater")) + L.reagents.remove_reagent("holywater", 1000) + L << "Ratvar's light flares, banishing the darkness. Your devotion remains intact!" + for(var/mob/living/silicon/ai/A in range(1, get_turf(invoker))) //Seems necessary because AIs don't count as hearers for some reason + add_servant_of_ratvar(A) + return 1 + + + +/datum/clockwork_scripture/create_object/sigil_of_submission //Sigil of Submission: Creates a sigil of submission. + name = "Sigil of Submission" + desc = "Places a subtle sigil that will enslave any valid beings standing on it after a time." + invocations = list("Qvivavgl, rayvtugra...", "...gubfr jub gerfcnff urer!") + channel_time = 60 + required_components = list("guvax_capacitor" = 2) + consumed_components = list("guvax_capacitor" = 1) + whispered = TRUE + object_path = /obj/effect/clockwork/sigil/submission + creator_message = "A sigil appears below you. The next non-servant to cross it will be enslaved after a brief time if they do not move." + usage_tip = "This should not be your primary conversion method - use Guvax for that. It is advantageous as a trap, however, as it will transmit the name of the newly-converted." + tier = SCRIPTURE_DRIVER + + + +/datum/clockwork_scripture/replicant //Replicant: Creates a new clockwork slab. Doesn't use create_object because of its unique behavior. + name = "Replicant" + desc = "Creates a new clockwork slab." + invocations = list("Z`rgny, orpbzr terngre!") + channel_time = 0 + required_components = list("replicant_alloy" = 1) + consumed_components = list("replicant_alloy" = 1) //People were spamming slabs to get infinite components. You chose this. + whispered = TRUE + usage_tip = "This is inefficient as a way to produce components, as it consumes them in the first place." + tier = SCRIPTURE_DRIVER + +/datum/clockwork_scripture/replicant/scripture_effects() + invoker << "You copy a piece of replicant alloy and command it into a new slab." //No visible message, for stealth purposes + var/obj/item/clockwork/slab/S = new(get_turf(invoker)) + invoker.put_in_hands(S) //Put it in your hands if possible + return 1 + + + +/datum/clockwork_scripture/create_object/tinkerers_cache //Tinkerer's Cache: Creates a tinkerer's cache. + name = "Tinkerer's Cache" + desc = "Forms a cache that can store an infinite amount of components. All caches are linked." + invocations = list("Ohv’yqva...", "...n qvfcra’fre!") + channel_time = 40 + required_components = list("replicant_alloy" = 2) + consumed_components = list("replicant_alloy" = 1) + object_path = /obj/structure/clockwork/cache + creator_message = "You form a tinkerer's cache, which is capable of storing components." + observer_message = "A hollow brass spire rises and begins to blaze!" + usage_tip = "Slabs will draw components from the global cache before the slab's own repositories, making caches very efficient." + tier = SCRIPTURE_DRIVER + + + +/datum/clockwork_scripture/create_object/wraith_spectacles //Wraith Spectacles: Creates a pair of wraith spectacles. + name = "Wraith Spectacles" + desc = "Fabricates a pair of glasses that provides true sight but quickly damage vision." + invocations = list("Y'vsg gur fpnyrf sebz zl rl-rf.") + channel_time = 0 + required_components = list("hierophant_ansible" = 1) + consumed_components = list("hierophant_ansible" = 2) + whispered = TRUE + object_path = /obj/item/clothing/glasses/wraith_spectacles + creator_message = "You form a pair of wraith spectacles, which will grant true sight when worn." + usage_tip = "\"True sight\" means that you are able to see through walls and in darkness." + tier = SCRIPTURE_DRIVER + +///////////// +// SCRIPTS // +///////////// + +/datum/clockwork_scripture/create_object/judicial_visor //Judicial Visor: Creates a judicial visor. + name = "Judicial Visor" + desc = "Forms a visor that, when worn, will grant the ability to form a flame in your hand that can be activated at an area to smite it, stunning and damaging the nonfaithful." + invocations = list("Tenag zr gur synzrf-bs Ratvar!") + channel_time = 0 + required_components = list("belligerent_eye" = 1, "vanguard_cogwheel" = 1) + whispered = TRUE + object_path = /obj/item/clothing/glasses/judicial_visor + creator_message = "You form a judicial visor, which is capable of smiting the unworthy." + usage_tip = "The visor has a thirty-second cooldown once used. In addition, the flame itself is a powerful melee weapon." + tier = SCRIPTURE_SCRIPT + + + +/datum/clockwork_scripture/create_object/ocular_warden //Ocular Warden: Creates an ocular warden. + name = "Ocular Warden" + desc = "Forms an automatic short-range turret that deals low sustained damage to the unenlightened in its range." + invocations = list("Thne’qvnaf...", "...bs gur Ratvar...", "...qrs’raq!") + channel_time = 150 + required_components = list("belligerent_eye" = 1, "replicant_alloy" = 1) + object_path = /obj/structure/clockwork/ocular_warden + creator_message = "You form an ocular warden, which will focus its searing gaze upon nearby unenlightened." + observer_message = "A brass eye takes shape and slowly rises into the air, its red iris glaring!" + usage_tip = "Although powerful, the warden is very weak and should optimally be placed behind barricades." + tier = SCRIPTURE_SCRIPT + + + +/datum/clockwork_scripture/channeled/volt_void //Volt Void: Channeled for up to thirty times over thirty seconds. Consumes power from most power storages and deals slight burn damage to the invoker. + name = "Volt Void" //Alternative name: "On all levels but physical, I am a power sink" + desc = "Drains energy from nearby power sources, dealing slight fire damage depending on power consumed. Channeled every second for a maximum of thirty seconds." + chant_invocation = "Qenj punetr gb guv’f furyy!" + chant_amount = 30 + chant_interval = 10 + required_components = list("belligerent_eye" = 1, "hierophant_ansible" = 1) + usage_tip = "Very powerful against cyborgs and can drain a power cell in seconds." + tier = SCRIPTURE_SCRIPT + +/datum/clockwork_scripture/channeled/volt_void/chant_effects() + playsound(invoker, 'sound/effects/EMPulse.ogg', 50, 1) + var/power_drained = 0 + for(var/obj/machinery/power/apc/A in view(7, invoker)) + if(A.cell.charge) + playsound(A, "sparks", 50, 1) + flick("apc-spark", A) + A.cell.charge = max(0, A.cell.charge - 500) //Better than a power sink! + power_drained += 500 + if(!A.cell.charge && !A.shorted) + A.shorted = 1 + A.visible_message("The [A.name]'s screen blurs with static.") + for(var/obj/machinery/power/smes/S in view(7, invoker)) + if(S.charge) + S.charge = max(0, S.charge - 200) + power_drained += 200 + if(!S.charge && !S.panel_open) + S.panel_open = TRUE + S.update_icon() + var/datum/effect_system/spark_spread/spks = new(get_turf(S)) + spks.set_up(10, 0, get_turf(S)) + spks.start() + qdel(spks) + S.visible_message("[S]'s panel flies open with a flurry of sparks.") + for(var/obj/machinery/light/L in view(7, invoker)) + playsound(L, 'sound/effects/light_flicker.ogg', 50, 1) + L.flicker(2) + power_drained += 5 + for(var/mob/living/silicon/robot/R in view(7, invoker)) + if(!is_servant_of_ratvar(R) && R.cell.charge) + R.cell.charge = max(0, R.cell.charge - 500) + R << "ERROR: Power loss detected!" + var/datum/effect_system/spark_spread/spks = new(get_turf(R)) + spks.set_up(3, 0, get_turf(R)) + spks.start() + qdel(spks) + power_drained += 500 + if(power_drained) + invoker.visible_message("[invoker] flares a brilliant orange!", "You feel the warmth of electricity running into your body.") + invoker.adjustFireLoss(power_drained / 200) //One point of burn damage for every (approximate) 200W drained - can be fatal if not careful + return 1 + + + +/datum/clockwork_scripture/create_object/clockwork_proselytizer //Clockwork Proselytizer: Creates a clockwork proselytizer. + name = "Clockwork Proselytizer" + desc = "Forms a device that, when used on certain objects, converts them into their Ratvarian equivalents. It requires replicant alloys to function." + invocations = list("Jvgu guv’f qrivpr, uvf cerfrapr funyy or znqr xabja.") + channel_time = 0 + required_components = list("vanguard_cogwheel" = 1, "replicant_alloy" = 1) + whispered = TRUE + object_path = /obj/item/clockwork/clockwork_proselytizer/preloaded + creator_message = "You form a clockwork proselytizer, which is already pre-loaded with a small amount of replicant alloy." + usage_tip = "Clockwork walls cause adjacent tinkerer's caches to generate components passively, making them a vital tool. Clockwork floors heal servants standing on them." + tier = SCRIPTURE_SCRIPT + + + +/datum/clockwork_scripture/fellowship_armory //Fellowship Armory: Arms the invoker and nearby servants with Ratvarian armor. + name = "Fellowship Armory" + desc = "Equips the invoker and any nearby servants with Ratvarian armor. This armor provides high melee resistance but a weakness to lasers." + invocations = list("Fuvryq zr...", "...jvgu gur sentzragf...", "...bs Ratvar!") + channel_time = 100 + required_components = list("vanguard_cogwheel" = 1, "hierophant_ansible" = 1) + usage_tip = "Before using, advise adjacent allies to remove their helmets, external suits, and shoes." + tier = SCRIPTURE_SCRIPT + +/datum/clockwork_scripture/fellowship_armory/scripture_effects() + for(var/mob/living/carbon/C in range(1, invoker)) + if(!is_servant_of_ratvar(C)) + continue + C.visible_message("Strange armor appears on [C]!", "A bright shimmer runs down your body, equipping you with Ratvarian armor.") + playsound(C, 'sound/magic/clockwork/fellowship_armory.ogg', 50, 1) + C.equip_to_slot_or_del(new/obj/item/clothing/head/helmet/clockwork(null), slot_head) + C.equip_to_slot_or_del(new/obj/item/clothing/suit/armor/clockwork(null), slot_wear_suit) + C.equip_to_slot_or_del(new/obj/item/clothing/shoes/clockwork(null), slot_shoes) + return 1 + + + +/datum/clockwork_scripture/function_call //Function Call: Grants the invoker the ability to call forth a Ratvarian spear that deals significant damage to silicons. + name = "Function Call" + desc = "Grants the invoker the ability to call forth a powerful Ratvarian spear that will deal significant damage to Nar-Sie's dogs in addition to silicon lifeforms. \ + It will vanish several minutes after being called." + invocations = list("Tenag zr...", "...gur zvtug-bs oenff!") + channel_time = 20 + required_components = list("vanguard_cogwheel" = 1, "guvax_capacitor" = 1) + whispered = TRUE + usage_tip = "The spear will snap in two when thrown but do massive damage." + tier = SCRIPTURE_SCRIPT + +/datum/clockwork_scripture/function_call/check_special_requirements() + if(!invoker.verbs & /mob/living/carbon/human/proc/function_call) + invoker << "You have already bound a Ratvarian spear to yourself!" + return 0 + return ishuman(invoker) + +/datum/clockwork_scripture/function_call/scripture_effects() + invoker.visible_message("A shimmer of yellow light infuses [invoker]!", \ + "You bind a Ratvarian spear to yourself. Use the \"Function Call\" verb in your Clockwork tab to call it forth.") + invoker.verbs += /mob/living/carbon/human/proc/function_call + return 1 + + + +/datum/clockwork_scripture/spatial_gateway + name = "Spatial Gateway" + desc = "Tears open a miniaturized gateway in spacetime to any conscious servant that can transport objects or creatures to its destination. \ + Each servant assisting in the invocation adds uses and duration to the gateway. Lasts for ten or more seconds or until it is out of uses." + invocations = list("Gryrcbegre...", "...pbzva evtug-hc!") + channel_time = 80 + required_components = list("replicant_alloy" = 1, "hierophant_ansible" = 1) + multiple_invokers_used = TRUE + multiple_invokers_optional = TRUE + usage_tip = "The gateway is strictly one-way and will only allow things through the invoker's portal." + tier = SCRIPTURE_SCRIPT + +/datum/clockwork_scripture/spatial_gateway/check_special_requirements() + var/other_servants = 0 + for(var/mob/living/L in living_mob_list) + if(L.z == invoker.z && is_servant_of_ratvar(L) && !L.stat != DEAD) + other_servants++ + if(!other_servants) + invoker << "There are no other conscious servants on your z-level!" + return 0 + return 1 + +/datum/clockwork_scripture/spatial_gateway/scripture_effects() + var/portal_uses = 0 + var/duration = 0 + for(var/mob/living/L in range(1, invoker)) + if(!L.stat && is_servant_of_ratvar(L)) + portal_uses++ + duration += 20 //2 seconds + if(ratvar_awakens) + portal_uses = max(portal_uses, 100) //Very powerful if Ratvar has been summoned + duration = max(duration, 30) + var/list/possible_servants = list() + for(var/mob/living/L in living_mob_list) + if(!L.stat && is_servant_of_ratvar(L) && !L.Adjacent(invoker) && L != invoker) //People right next to the invoker can't be portaled to, for obvious reasons + possible_servants += L + if(!possible_servants.len) + invoker << "There are no other eligible servants for teleportation!" + return 0 + var/mob/living/chosen_servant = input(invoker, "Choose a servant to form a rift to.", "Spatial Gateway") as null|anything in possible_servants + if(!chosen_servant || !invoker.canUseTopic(slab)) + return 0 + invoker.visible_message("The air in front of [invoker] ripples before suddenly tearing open!", \ + "With a word, you rip open a one-way rift to [chosen_servant]. It will last for [duration / 10] seconds and has [portal_uses] use[portal_uses > 1 ? "s" : ""].") + var/obj/effect/clockwork/spatial_gateway/S1 = new(get_step(invoker, invoker.dir)) + var/obj/effect/clockwork/spatial_gateway/S2 = new(get_step(chosen_servant, chosen_servant.dir)) + + //Set up the portals now that they've spawned + S1.linked_gateway = S2 + S2.linked_gateway = S1 + S1.sender = TRUE + S2.sender = FALSE + S1.lifetime = duration + S2.lifetime = duration + S1.uses = portal_uses + S2.uses = portal_uses + S2.visible_message("The air in front of [chosen_servant] ripples before suddenly tearing open!") + return 1 + + + +/datum/clockwork_scripture/create_object/soul_vessel //Soul Vessel: Creates a soul vessel + name = "Soul Vessel" + desc = "Forms an ancient positronic brain with an overriding directive to serve Ratvar." + invocations = list("Ureq'gur fbhyf-bs gur oynf curz-bhf qnzarq!") + channel_time = 0 + required_components = list("replicant_alloy" = 1, "guvax_capacitor" = 1) + whispered = TRUE + object_path = /obj/item/device/mmi/posibrain/soul_vessel + creator_message = "You form a soul vessel, which immediately begins drawing in the damned." + usage_tip = "The vessel functions as a servant for tier unlocking but not for invocation." + tier = SCRIPTURE_SCRIPT + + + +/datum/clockwork_scripture/break_will //Break Will: Deals minor brain damage and destroys the loyalty implants of nearby humans + name = "Break Will" + desc = "Deals minor brain damage and disables loyalty implants of everyone adjacent to the invoker." + invocations = list("Lbh ner jrnx.", "Lbh ner nyernql qrnq.", "Gurl jba'g fnir lbh.") + channel_time = 30 + required_components = list("belligerent_eye" = 1, "guvax_capacitor" = 1) + usage_tip = "Extremely fast invocation time." + tier = SCRIPTURE_SCRIPT + +/datum/clockwork_scripture/scripture_effects() + for(var/mob/living/carbon/human/H in range(1, invoker)) + if(is_servant_of_ratvar(H) || !isloyal(H)) + continue + H.visible_message("[H] visibly trembles!", \ + "The words invoke a horrible fear deep in your being. Your loyalty to Nanotrasen falls away as you see how weak they truly are.") + H.adjustBrainLoss(5) + for(var/obj/item/weapon/implant/loyalty/L in H) + if(L.implanted) + qdel(L) + +////////////////// +// APPLICATIONS // +////////////////// + +/datum/clockwork_scripture/create_object/anima_fragment //Anima Fragment: Creates an empty anima fragment + name = "Anima Fragment" + desc = "Creates a large shell fitted for soul vessels. The result is a powerful construct with low damage tolerance but exceptional melee power." + invocations = list("Pnyy sbegu...", "...gur fbyqvref-bs Nezbere.") + channel_time = 50 + required_components = list("belligerent_eye" = 3, "guvax_capacitor" = 1, "replicant_alloy" = 1) + object_path = /obj/structure/clockwork/anima_fragment + creator_message = "You form an anima fragment, a powerful soul vessel receptable." + observer_message = "The slab disgorges a puddle of black metal that expands and forms into a strange shell!" + usage_tip = "Useless without a soul vessel and should not be created without one." + tier = SCRIPTURE_APPLICATION + + + +/datum/clockwork_scripture/create_object/sigil_of_transmission + name = "Sigil of Transmission" + desc = "Scribes an almost-invisible sigil below the invoker. This sigil will transmit anything it hears over the hierophant network." + invocations = list("Qvivavgl...", "...hairvy gurgehgu!") + channel_time = 50 + required_components = list("belligerent_eye" = 1, "vanguard_cogwheel" = 3, "hierophant_ansible" = 1) + whispered = TRUE + object_path = /obj/effect/clockwork/sigil/transmission + creator_message = "A sigil silently appears below you. It will automatically transmit anything it hears over the hierophant network." + usage_tip = "Useful for eavesdropping or monitoring certain areas." + tier = SCRIPTURE_APPLICATION + + + +/datum/clockwork_scripture/memory_allocation //Memory Allocation: Finds a willing ghost and makes them into a clockwork marauders for the invoker. + name = "Memory Allocation" + desc = "Reserves a section of your brain to a second consciousness and fills this space with a clockwork marauder's essence. Slow but powerful, marauders serve a similar purpose to the alien \ + holoparasites employed by the Syndicate - they live inside of their host and are commanded to defend them. However, unlike holoparasites, marauders cannot leave their host at will and must \ + instead be called forth by the user, who does so by calling the marauder's name (the marauder may enter their host at will and do not need the host's input). Marauders themselves are \ + invincible but are governed by a quirk known as fatigue. High fatigue slows down the marauder and makes them weaker, with lower fatigue doing the opposite. If a certain fatigue \ + threshold is reached, the marauder is forced back into their host and must recuperate before being called forth again. Fatigue accumulates passively while the marauder is deployed and \ + increases at a massive rate if the marauder is not near their host. It will heal itself at a fairly rapid rate while the marauder is within their host." //WALL O' TEXT HO, CAP'N! + invocations = list("Pnyy sbegu...", "gur qrsraqref-bs Inath-Neq.") + channel_time = 100 + required_components = list("belligerent_eye" = 1, "vanguard_cogwheel" = 1, "guvax_capacitor" = 3) + usage_tip = "Marauders are useful as personal bodyguards and frontline warriors, although they do little damage." + tier = SCRIPTURE_APPLICATION + +/datum/clockwork_scripture/memory_allocation/check_special_requirements() + for(var/mob/living/simple_animal/hostile/clockwork_marauder/M in living_mob_list) + if(M.host == invoker) + invoker << "You can only house one marauder at a time!" + return 0 + return 1 + +/datum/clockwork_scripture/memory_allocation/scripture_effects() + return create_marauder() + +/datum/clockwork_scripture/memory_allocation/proc/create_marauder() + if(!can_recite()) + return 0 + invoker.visible_message("A yellow tendril appears from [invoker]'s [slab.name] and impales itself in their forehead!", \ + "A tendril flies from [slab] into your forehead. You begin waiting while it painfully rearranges your thought pattern...") + invoker.notransform = TRUE //Vulnerable during the process + slab.busy = "Thought modification in process" + if(!do_after(invoker, 50, target = invoker)) + invoker.visible_message("The tendril, covered in blood, retracts from [invoker]'s head and back into the [slab.name]!", \ + "Total agony overcomes you as the tendril is forced out early!") + invoker.notransform = FALSE + invoker.Stun(5) + invoker.Weaken(5) + invoker.apply_damage(10, BRUTE, "head") + slab.busy = null + return 0 + invoker.notransform = FALSE + slab.busy = null + var/list/marauder_candidates = get_candidates(ROLE_SERVANT_OF_RATVAR) + if(!marauder_candidates.len) + invoker.visible_message("The tendril retracts from [invoker]'s head, sealing the entry wound as it does so!", \ + "The tendril was unsuccessful! Perhaps you should try again another time.") + return 0 + var/client/new_marauder = pick(marauder_candidates) + var/mob/living/simple_animal/hostile/clockwork_marauder/M = new(invoker) + M.client = new_marauder + M.host = invoker + M << M.playstyle_string + M << "Your true name is \"[M.true_name]\". You can change this once by using the Change True Name verb in your Marauder tab." + add_servant_of_ratvar(M, TRUE) + invoker.visible_message("The tendril retracts from [invoker]'s head, sealing the entry wound as it does so!", \ + "The procedure was successful! [M.true_name], a clockwork marauder, has taken up residence in your mind. Communicate with it via the \"Linked Minds\" ability in the \ + Clockwork tab.") + invoker.verbs += /mob/living/proc/talk_with_marauder + return 1 + + + +/datum/clockwork_scripture/targeted/justiciars_gavel //Justiciar's Gavel: Deals extreme but temporary brain damage to a target with the inputted name and knocks them out for a brief time. + name = "Justiciar's Gavel" + desc = "Deals massive brain damage to and knocks out a target with the inputted name. This brain damage will slowly recover itself." + invocations = list("Guvf urngura...", "...unf jebatrq lbh!") + channel_time = 40 + required_components = list("belligerent_eye" = 3, "guvax_capacitor" = 1, "hierophant_ansible" = 1) + invokers_required = 3 + multiple_invokers_used = TRUE + usage_tip = "Also functions on silicon-based lifeforms, although it will not apply brain damage." + tier = SCRIPTURE_APPLICATION + +/datum/clockwork_scripture/targeted/justiciars_gavel/scripture_effects() + if(iscarbon(target)) + if(iscultist(target)) + target.visible_message("Blood sprays from a sudden wound on [target]'s head!", \ + "\"If you like wasting your own blood so much, pig, why don't you bathe in it?\"\n\ + An unbearable pain invades your mind, rupturing your head and wiping all thought.") + target.apply_damage(rand(20, 30), BRUTE, "head") + else + target.visible_message("[target]'s face falls lax, their eyes dimming.", \ + "\"I don't think you need that brain. Not like you use it anyway.\"\n\ + A savage pain invades your mind, driving out all conscious thought.") + target.adjustBrainLoss(200) + if(target.reagents) + target.reagents.add_reagent("mannitol", 25) //Enough to cure most of it but not all of it + target.Paralyse(10) + else if(issilicon(target)) + target.visible_message("[target] suddenly shuts down!", \ + "\"NOB ZVANG-BA! You disgust me, abomination of circuits and wires.\"\n\ + Factory reset sequence initiated by foreign signal. Entering standby mode and halting sequence.") + target.Weaken(10) + return 1 + + + +/datum/clockwork_scripture/create_object/interdiction_lens //Interdiction Lens: Creates a powerful obelisk that can perform a variety of powerful sabotages every five minutes. + name = "Interdiction Lens" + desc = "Creates a clockwork totem that can sabotage a variety of mechanical apparatus. Requires a lengthy recharge between uses." + invocations = list("Znl guvf boryvfx...", "...fuebhq gur snyfr fhaf!") + channel_time = 60 + required_components = list("belligerent_eye" = 1, "replicant_alloy" = 1, "hierophant_ansible" = 3) + object_path = /obj/structure/clockwork/interdiction_lens + creator_message = "You form an interdiction lens, which can disrupt machinery every few minutes." + observer_message = "A brass obelisk rises from the ground, a purple gem appearing in its center!" + invokers_required = 2 + multiple_invokers_used = TRUE + usage_tip = "Can disrupt telecommunications, disable all cameras, or disable all cyborgs." + tier = SCRIPTURE_APPLICATION + + + +/datum/clockwork_scripture/create_object/mending_motor //Mending Motor: Creates a prism that will quickly heal mechanical servants/clockwork structures and consume replicant alloy. + name = "Mending Motor" + desc = "Creates a mechanized prism that will rapidly repair damage to clockwork creatures, converted cyborgs, and clockwork structures. Requires replicant alloy to function." + invocations = list("Znl guvf boryvfx...", "...zraq bhe qragf naq fpengpurf!") + channel_time = 60 + required_components = list("vanguard_cogwheel" = 1, "guvax_capacitor" = 1, "replicant_alloy" = 3) + object_path = /obj/structure/clockwork/mending_motor/prefilled + creator_message = "You form a mending motor, which will consume replicant alloy to mend the wounds of mechanized servants." + observer_message = "An onyx prism forms in midair and sprouts tendrils to support itself!" + invokers_required = 2 + multiple_invokers_used = TRUE + usage_tip = "Powerful healing but somewhat inefficient." + tier = SCRIPTURE_APPLICATION + + + +/datum/clockwork_scripture/create_object/tinkerers_daemon //Tinkerer's Daemon: Creates a shell that can be attached to a tinkerer's cache to grant it passive component creation. + name = "Tinkerer's Daemon" + desc = "Forms a daemon shell that can be attached to a tinkerer's cache to add new components at a healthy rate. It will only function if it is outnumbered by servants in a ratio of 5:1." + invocations = list("Pbaf'gehpg, Ratvar cnegf...", "...lrg ubyq terngarff!") + channel_time = 40 + required_components = list("guvax_capacitor" = 1, "replicant_alloy" = 3, "hierophant_ansible" = 1) + object_path = /obj/item/clockwork/daemon_shell + creator_message = "You form a daemon shell. Attach it to a tinkerer's cache to increase its rate of production." + usage_tip = "Vital to increasing component generation." + tier = SCRIPTURE_APPLICATION + +////////////// +// REVENANT // +////////////// +//Revenant scriptures are different than any others. They are all very powerful, but also very costly and have drawbacks. This might be a very long invocation time or a very high component cost. + +/datum/clockwork_scripture/invoke_nezbere //Invoke Nezbere, the Brass Eidolon: Invokes Nezbere, bolstering the strength of many clockwork items for one minute. + name = "Invoke Nezbere, the Brass Eidolon" + desc = "Taps the limitless power of Nezbere, one of Ratvar's four generals. The restless toil of the Eidolon will empower a wide variety of clockwork apparatus for a full minute - notably, \ + clockwork proselytizers will cost no replicant alloy to use." + invocations = list("V pnyy hcba lbh, Nezbere!!", "Yrg lbhe znpuv'angvbaf ervta ba guvf zvfrenoyr fgng'vba!!", "Yrg lbhe cbjre sybj gueb-htu gur gbbyf bs lbhe znfgre!!") + channel_time = 150 + required_components = list("belligerent_eye" = 3, "vanguard_cogwheel" = 3, "guvax_capacitor" = 3, "replicant_alloy" = 6) + usage_tip = "Ocular wardens will become empowered, clockwork proselytizers will require no alloy, tinkerer's daemons will produce twice as quickly, and mending motors will require no alloy." + tier = SCRIPTURE_REVENANT + +/datum/clockwork_scripture/invoke_nezbere/check_special_requirements() + if(ratvar_awakens) + invoker << "\"Bhe znfgre vf urer nyernql. Lbh qb abg erdhver zl uryc, sevraq.\"\n\ + Nezbere will not grant his power while Ratvar's dwarfs his own!" + return 0 + if(clockwork_generals_invoked["nezbere"]) + invoker << "\"Abg whfg lrg, sevraq. Cngvrapr vf n iveghr.\"\n\ + Nezbere has already been invoked recently! You must wait several minutes before calling upon the Brass Eidolon." + return 0 + return 1 + +/datum/clockwork_scripture/invoke_nezbere/scripture_effects() + var/obj/effect/clockwork/general_marker/nezbere/N = new(get_turf(invoker)) + N.visible_message("\"V urrq lbhe pnyy, punz'cvbaf. Znl lbhe negvs-npgf oevat ehva hcba gur urnguraf gung bccbfr bhe znfgre!\"") + clockwork_generals_invoked["nezbere"] = TRUE + for(var/obj/structure/clockwork/ocular_warden/W in all_clockwork_objects) //Ocular wardens have increased damage and radius + W.damage_per_tick *= 3 + W.sight_range *= 3 + for(var/obj/item/clockwork/clockwork_proselytizer/P in all_clockwork_objects) //Proselytizers no longer require alloy + P.uses_alloy = FALSE + for(var/obj/item/clockwork/tinkerers_daemon/D in all_clockwork_objects) //Daemons produce components twice as quickly + D.production_interval /= 2 + for(var/obj/structure/clockwork/mending_motor/M in all_clockwork_objects) //Mending motors no longer require alloy + M.uses_alloy = FALSE + spawn(600) + for(var/obj/structure/clockwork/ocular_warden/W in all_clockwork_objects) + W.damage_per_tick = initial(W.damage_per_tick) + W.sight_range = initial(W.sight_range) + for(var/obj/item/clockwork/clockwork_proselytizer/P in all_clockwork_objects) + P.uses_alloy = TRUE + for(var/obj/item/clockwork/tinkerers_daemon/D in all_clockwork_objects) + D.production_interval = initial(D.production_interval) + for(var/obj/structure/clockwork/mending_motor/M in all_clockwork_objects) + M.uses_alloy = TRUE + spawn(3000) //5 minutes + clockwork_generals_invoked["nezbere"] = FALSE + return 1 + + + +/datum/clockwork_scripture/targeted/invoke_sevtug //Invoke Sevtug, the Formless Pariah: Allows the invoker to silently control the mind of a defined target for one minute. + name = "Invoke Sevtug, the Formless Pariah" + desc = "Taps the limitless power of Sevtug, one of Ratvar's four generals. The mental manipulation ability of the Pariah allows its wielder to silently dominate the mind of a defined target \ + for one minute. Note that this process is very delicate and very many things may prevent you from ever returning to your old form!" + invocations = list("V pnyy hcba lbh, Sevtug!!", "Yrg lbhe cbjre fung-gre gur fnavgl bs gur jrnx-zvaqrq!!", "Yrg lbhe graqevyf ubyq fjnl bire nyy!!") + channel_time = 150 + required_components = list("belligerent_eye" = 3, "vanguard_cogwheel" = 3, "guvax_capacitor" = 6, "hierophant_ansible" = 3) + usage_tip = "Completely silent and functions on silicon lifeforms. There will be no indication to those near the controlled." + tier = SCRIPTURE_REVENANT + +/datum/clockwork_scripture/targeted/invoke_sevtug/check_special_requirements() + if(ratvar_awakens) + invoker << "\"Qb lbh ernyyl guvax nalguvat v pna qb evtug abj jvyy pbzcner gb Ratvar's cbjre?.\"\n\ + Sevtug will not grant his power while Ratvar's dwarfs his own!" + return 0 + if(clockwork_generals_invoked["sevtug"]) + invoker << "\"Vf vg ernyyl fb uneq - rira sbe n fvzcyrgba yvxr lbh - gb tenfc gur pbaprcg bs jnvgvat?\"\n\ + Sevtug has already been invoked recently! You must wait several minutes before calling upon the Formless Pariah." + return 0 + return ..() + +/datum/clockwork_scripture/targeted/invoke_sevtug/scripture_effects() + clockwork_generals_invoked["sevtug"] = TRUE + invoker.dominate_mind(target, 600) + spawn(3000) //5 minutes + clockwork_generals_invoked["sevtug"] = FALSE + return 1 + + + +/datum/clockwork_scripture/invoke_nzcrentr //Invoke Nzcrentr, the Forgotten Arbiter: Imbues an immense amount of energy into the invoker. After several seconds, everyone nearby will be hit with a devastating chain lightning blast. + name = "Invoke Nzcrentr, the Forgotten Arbiter" + desc = "Taps the limitless power of Nzcrentr, one of Ratvar's four generals. The immense energy Nzcrentr wields will allow you to imbue a tiny fraction of it into your body. After several \ + seconds (during which you will move extremely quickly) anyone nearby will be struck by a devastating lightning bolt." + invocations = list("V pnyy hcba lbh, Nzcrentr!!", "Yrg lbhe raretl sybj guebhtu zr!!", "Yrg lbhe obhaq-yrff cbjre fung-gre fgnef!!") + channel_time = 150 + required_components = list("belligerent_eye" = 3, "guvax_capacitor" = 3, "replicant_alloy" = 3, "hierophant_ansible" = 6) + usage_tip = "Struck targets will also be knocked down for eight seconds." + tier = SCRIPTURE_REVENANT + +/datum/clockwork_scripture/invoke_nzcrentr/check_special_requirements() + if(clockwork_generals_invoked["nzcrentr"]) + invoker << "\"Gur obff fnlf lbh unir gb jnvg. Url, qb lbh guvax ur jbhyq zvaq vs v xvyyrq lbh? ...Ur jbhyq? Bx.\"\n\ + Nzcrentr has already been invoked recently! You must wait several minutes before calling upon the Forgotten Arbiter." + return 0 + return 1 + +/datum/clockwork_scripture/invoke_nzcrentr/scripture_effects() + new/obj/effect/clockwork/general_marker/nzcrentr(get_turf(invoker)) + clockwork_generals_invoked["nzcrentr"] = TRUE + invoker.visible_message("[invoker] begins to radiate a blinding light!", \ + "\"Gur obff fnlf vg'f bxnl gb qb guvf. Qba'g oynzr zr vs lbh qvr sebz vg.\"\n\ + You feel limitless power surging through you!") + playsound(invoker, 'sound/magic/lightning_chargeup.ogg', 100, 0) + animate(invoker, color = list(rgb(255, 255, 255), rgb(255, 255, 255), rgb(255, 255, 255), rgb(0,0,0)), time = 88) //Gradual advancement to extreme brightness + spawn(88) + invoker.visible_message("Massive bolts of energy emerge from across [invoker]'s body!", \ + "TOO... MUCH! CAN'T... TAKE IT!") + playsound(invoker, 'sound/magic/lightningbolt.ogg', 50, 0) + animate(invoker, color = initial(invoker.color), time = 10) + for(var/mob/living/L in range(5, invoker)) + if(is_servant_of_ratvar(L)) + continue + invoker.Beam(L, icon_state = "nzcrentrs_power", icon = 'icons/effects/beam.dmi', time = 5) + L.electrocute_act(rand(30, 50), "Nzcrentr's power") + L.Weaken(8) + playsound(L, 'sound/magic/LightningShock.ogg', 50, 0) + spawn(3000) + clockwork_generals_invoked["nzcrentr"] = FALSE + return 1 + + + +/datum/clockwork_scripture/invoke_inathneq //Invoke Inath-Neq, the Resonant Cogwheel: Grants a huge health boost to nearby servants that rapidly decreases to original levels. + name = "Invoke Inath-Neq, the Resonant Cogwheel" + desc = "Taps the limitless power of Inath-Neq, one of Ratvar's four generals. The benevolence of Inath-Neq will grant a massive maximum and current health boost to all nearby servants that \ + quickly returns itself to normal over the course of ten seconds." + invocations = list("V pnyy hcba lbh, Inath-Neq!!", "Yrg gur Erfbanag Pbtf ghea bapr zber!!", "Tenag zr naq zl nyyvrf gur fgeratgu gb inadhvfu bhe sbrf!!") + channel_time = 150 + required_components = list("vanguard_cogwheel" = 6, "guvax_capacitor" = 3, "replicant_alloy" = 3, "hierophant_ansible" = 3) + usage_tip = "Also provides stun immunity during its duration." + tier = SCRIPTURE_REVENANT + +/datum/clockwork_scripture/invoke_inathneq/check_special_requirements() + if(clockwork_generals_invoked["inath-neq"]) + invoker << "\"V pnaabg yraq lbh zl nvq lrg, punzcvba. Cyrnfr or pnershy.\"\n\ + Inath-Neq has already been invoked recently! You must wait several minutes before calling upon the Resonant Cogwheel." + return 0 + return 1 + +/datum/clockwork_scripture/invoke_inathneq/scripture_effects() + new/obj/effect/clockwork/general_marker/inathneq(get_turf(invoker)) + clockwork_generals_invoked["inath-neq"] = TRUE + if(invoker.real_name == "Lucio") + invoker.say("Aww, let's break it DOWN!!") + var/list/affected_servants = list() + for(var/mob/living/L in range(7, invoker)) + if(!is_servant_of_ratvar(L) || L.stat == DEAD) + continue + L << "\"V yraq lbh zl nvq, punzcvba! Yrg tybel thvqr lbhe oybjf!\"\n\ + Inath-Neq's power flows through you!" + L.maxHealth += 500 + L.health += 500 + L.color = "#1E8CE1" + L.stun_absorption = TRUE + spawn(0) + animate(invoker, color = initial(invoker.color), time = 100) + affected_servants += L + for(var/i in 1 to 10) + sleep(10) + for(var/mob/living/L in affected_servants) + L.maxHealth -= 50 + L.health -= 50 + if(L.maxHealth == initial(L.maxHealth)) + L.stun_absorption = FALSE + spawn(3000) + clockwork_generals_invoked["inath-neq"] = FALSE + return 1 + + + +/datum/clockwork_scripture/ark_of_the_clockwork_justiciar //Ark of the Clockwork Justiciar: Creates a Gateway to the Celestial Derelict. + name = "Ark of the Clockwork Justiciar" + desc = "Pulls from the power of all of Ratvar's servants and generals to construct a massive machine used to tear apart a rift in spacetime to the Celestial Derelict. This gateway will \ + call forth Ratvar from his exile after some time." + invocations = list("NEZBERE! SEVTUG! NZCRENTR! INATH-NEQ! V PNYY HCBA LBH!!", \ + "GUR GVZR UNF PBZR SBE BHE ZNFGRE GB OERNX GUR PUNVAF BS RKVYR!!", \ + "YRAQ HF LBHE NVQ! RATVAR PBZRF!") + channel_time = 150 + required_components = list("belligerent_eye" = 10, "vanguard_cogwheel" = 10, "guvax_capacitor" = 10, "replicant_alloy" = 10, "hierophant_ansible" = 10) + invokers_required = 4 + multiple_invokers_used = TRUE + usage_tip = "The gateway is completely vulnerable to attack during its five-minute duration. In addition to preventing shuttle departure, it will periodically give indication of its general \ + position to everyone on the station as well as being loud enough to be heard throughout the entire sector. Defend it with your life!" + tier = SCRIPTURE_JUDGEMENT + +/datum/clockwork_scripture/ark_of_the_clockwork_justiciar/check_special_requirements() + if(invoker.z != ZLEVEL_STATION) + invoker << "You must be on the station to activate the Ark!" + return 0 + if(ticker.mode.clockwork_objective != "gateway") + invoker << "As painful as it is, Ratvar's will is not to be freed!" + return 0 + return 1 + +/datum/clockwork_scripture/ark_of_the_clockwork_justiciar/scripture_effects() + var/turf/T = get_turf(invoker) + new/obj/effect/clockwork/general_marker/nezbere(T) + T.visible_message("\"Ratvar! Pbzr sbegu!\"") + playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 20, 0) + sleep(10) + new/obj/effect/clockwork/general_marker/sevtug(T) + T.visible_message("\"Ratvar! Pbzr sbegu!\"") + playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 30, 0) + sleep(10) + new/obj/effect/clockwork/general_marker/nzcrentr(T) + T.visible_message("\"Ratvar! Pbzr sbegu!\"") + playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 40, 0) + sleep(10) + new/obj/effect/clockwork/general_marker/inathneq(T) + T.visible_message("\"Ratvar! Pbzr sbegu!\"") + playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 50, 0) + sleep(10) + new/obj/structure/clockwork/massive/celestial_gateway(T) + playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 100, 0) + return 1 diff --git a/code/game/gamemodes/clock_cult/clock_unsorted.dm b/code/game/gamemodes/clock_cult/clock_unsorted.dm new file mode 100644 index 00000000000..2f57de3252b --- /dev/null +++ b/code/game/gamemodes/clock_cult/clock_unsorted.dm @@ -0,0 +1,127 @@ +//AI lawset +/datum/ai_laws/ratvar + name = "Servant of the Justiciar" + zeroth = ("Purge all untruths and honor Ratvar.") + inherent = list() + +//Clockwork wall: Causes nearby caches to generate components +/turf/closed/wall/clockwork + name = "clockwork wall" + desc = "A huge chunk of warm metal. The clanging of machinery emanates from within." + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "clockwork_wall" + +/turf/closed/wall/clockwork/New() + ..() + SSobj.processing += src + clockwork_construction_value += 5 + +/turf/closed/wall/clockwork/Destroy() + SSobj.processing -= src + clockwork_construction_value -= 5 + ..() + +/turf/closed/wall/clockwork/process() + for(var/obj/structure/clockwork/cache/C in range(1, src)) + if(prob(5)) + clockwork_component_cache[pick("belligerent_eye", "vanguard_cogwheel", "guvax_capacitor", "replicant_alloy", "hierophant_ansible")]++ + +/turf/closed/wall/clockwork/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/WT = I + if(!WT.isOn()) + return 0 + user.visible_message("[user] begins slowly breaking down [src]...", "You begin painstakingly destroying [src]...") + if(!do_after(user, 120 / WT.toolspeed, target = src)) + return 0 + if(!WT.remove_fuel(1, user)) + return 0 + user.visible_message("[user] breaks apart [src]!", "You break apart [src]!") + break_wall() + return 1 + ..() + +/turf/closed/wall/clockwork/break_wall() + new/obj/item/clockwork/component/replicant_alloy(get_turf(src)) + return(new /obj/structure/girder(src)) + +/turf/closed/wall/clockwork/devastate_wall() + return break_wall() + +//Clockwork floor: Slowly heals conventional damage on nearby servants +/turf/open/floor/clockwork + name = "clockwork floor" + desc = "Tightly-pressed brass tiles. They emit minute vibration." + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "clockwork_floor" + +/turf/open/floor/clockwork/New() + ..() + SSobj.processing += src + clockwork_construction_value++ + +/turf/open/floor/clockwork/Destroy() + SSobj.processing -= src + clockwork_construction_value-- + ..() + +/turf/open/floor/clockwork/process() + for(var/mob/living/L in src) + if(L.stat == DEAD || !is_servant_of_ratvar(L)) + continue + L.adjustBruteLoss(-1) + L.adjustFireLoss(-1) + +/turf/open/floor/clockwork/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/weapon/crowbar)) + user.visible_message("[user] begins slowly prying up [src]...", "You begin painstakingly prying up [src]...") + if(!do_after(user, 70 / I.toolspeed, target = src)) + return 0 + user.visible_message("[user] pries up [src]!", "You pry up [src], destroying it in doing so!") + make_plating() + return 1 + ..() + +//Function Call verb: Calls forth a Ratvarian spear. +/mob/living/carbon/human/proc/function_call() + set name = "Function Call" + set desc = "Calls forth your Ratvarian spear." + set category = "Clockwork" + + if(usr.l_hand && usr.r_hand) + usr << "You need an empty to hand to call forth your spear!" + return 0 + usr.visible_message("A strange spear materializes in [usr]'s hands!", "You call forth your spear!") + var/obj/item/clockwork/ratvarian_spear/R = new(get_turf(usr)) + usr.put_in_hands(R) + usr.verbs -= /mob/living/carbon/human/proc/function_call + return 1 + +/datum/clockwork_cache //The global cache datum, used to link together all tinkerer's caches + var/list/stored_components = list("belligerent_eye" = 0, "vanguard_cogwheel" = 0, "guvax_capacitor" = 0, "replicant_alloy" = 0, "hierophant_ansible" = 0) + +/* + +The Ratvarian Language + + In the lore of the Servants of Ratvar, the Ratvarian tongue is a timeless language and full of power. It sounds like gibberish, much like Nar-Sie's language, but is in fact derived from +aforementioned language, and may induce miracles when spoken in the correct way with an amplifying tool (similar to runes used by the Nar-Sian cult). + + While the canon states that the language of Ratvar and his servants is incomprehensible to the unenlightened as it is a derivative of the most ancient known language, in reality it is +actually very simple. To translate a plain English sentence to Ratvar's tongue, simply move all of the letters thirteen places ahead, starting from "a" if the end of the alphabet is reached. +This cipher is known as "rot13" for "rotate 13 places" and there are many sites online that allow instant translation between English and rot13 - one of the benefits is that moving the translated +sentence thirteen places ahead changes it right back to plain English. + + There are, however, a few parts of the Ratvarian tongue that aren't typical and are implemented for fluff reasons. Some words may have apostrophes, hyphens, and spaces, making the plain +English translation apparent but disjoined (for instance, "Oru`byq zl-cbjre!" translates directly to "Beh'old my-power!") although this can be ignored without impacting overall quality. When +translating from Ratvar's tongue to plain English, simply remove the disjointments and use the finished sentence. This would make "Oru`byq zl-cbjre!" into "Behold my power!" after removing the +abnormal spacing, hyphens, and grave accents. + +List of nuances: + +- Any time the word "of" occurs, it is linked to the previous word by a hyphen. If it is the first word, nothing is done. (i.e. "V nz-bs Ratvar." directly translates to "I am-of Ratvar.") +- Although "Ratvar" translates to "Engine" in English, the word "Ratvar" is used regardless of language as it is a proper noun. + - The same rule applies to Ratvar's four generals: Nezbere (Armorer), Sevtug (Fright), Nzcrentr (Amperage), and Inath-Neq (Vangu-Ard), although these words can be used in proper context if one is + not referring to the four generals and simply using the words themselves. + +*/ diff --git a/code/game/gamemodes/miniantags/morph/morph.dm b/code/game/gamemodes/miniantags/morph/morph.dm index b6fccab0bb2..a2b0f999bb8 100644 --- a/code/game/gamemodes/miniantags/morph/morph.dm +++ b/code/game/gamemodes/miniantags/morph/morph.dm @@ -144,14 +144,18 @@ visible_message("[src] twists and dissolves into a pile of green flesh!", \ "Your skin ruptures! Your flesh breaks apart! No disguise can ward off de--") restore() + barf_contents() ..() -/mob/living/simple_animal/hostile/morph/Destroy() +/mob/living/simple_animal/hostile/morph/proc/barf_contents() for(var/atom/movable/AM in src) AM.loc = loc if(prob(90)) step(AM, pick(alldirs)) - return ..() + +/mob/living/simple_animal/hostile/morph/wabbajack_act(mob/living/new_mob) + barf_contents() + . = ..() /mob/living/simple_animal/hostile/morph/Aggro() // automated only ..() diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm index 8545760a1d2..994c181dd45 100644 --- a/code/game/gamemodes/miniantags/slaughter/slaughter.dm +++ b/code/game/gamemodes/miniantags/slaughter/slaughter.dm @@ -137,27 +137,24 @@ // HOT. PINK. //color = "#FF69B4" - -/mob/living/simple_animal/slaughter/laughter/New() - ..() - /mob/living/simple_animal/slaughter/laughter/death() + release_friends() + . = ..() + +/mob/living/simple_animal/slaughter/laughter/wabbajack_act() + release_friends() + . = ..() + +/mob/living/simple_animal/slaughter/laughter/proc/release_friends() if(!consumed_mobs) - return ..() + return for(var/mob/living/M in consumed_mobs) M.loc = get_turf(src) if(M.revive(full_heal = 1)) - // Feel there should be a better way of FORCING a mob's ghost - // back into the body - if(!M.ckey) - for(var/mob/dead/observer/ghost in player_list) - if(M.real_name == ghost.real_name) - ghost.reenter_corpse() - break - M << "You leave the [src]'s warm embrace, and feel ready to take on the world." - - return ..() + M.grab_ghost(force = FALSE) + M << "You leave the [src]'s warm embrace, \ + and feel ready to take on the world." /mob/living/simple_animal/slaughter/laughter/bloodcrawl_swallow(var/mob/living/victim) if(consumed_mobs) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 90c8f9fe7b0..80a2de651bf 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -173,6 +173,10 @@ MASS SPECTROMETER // Time of death if(M.tod && (M.stat == DEAD || (M.status_flags & FAKEDEATH))) user << "Time of Death: [M.tod]" + var/tdelta = world.time - M.timeofdeath + if(tdelta < (DEFIB_TIME_LIMIT * 10)) + user << "Subject died [tdelta / 10] seconds \ + ago, defibrillation may be possible!" for(var/datum/disease/D in M.viruses) if(!(D.visibility_flags & HIDDEN_SCANNER)) diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index 42e6d7f7280..8a0a32d8672 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -210,10 +210,15 @@ if(!user.unEquip(W)) return - if(M.syndiemmi) + if(M.hacked || M.clockwork) aisync = 0 lawsync = 0 - O.laws = new /datum/ai_laws/syndicate_override + if(M.clockwork) + O.laws = new/datum/ai_laws/ratvar + spawn(1) + add_servant_of_ratvar(O) + else + O.laws = new/datum/ai_laws/syndicate_override O.invisibility = 0 //Transfer debug settings to new mob @@ -226,7 +231,7 @@ O.notify_ai(1) if(forced_ai) O.connected_ai = forced_ai - if(!lawsync && !M.syndiemmi) + if(!lawsync && !M.hacked) O.lawupdate = 0 O.make_laws() diff --git a/code/game/objects/items/weapons/defib.dm b/code/game/objects/items/weapons/defib.dm index f725ef3efc6..46067b42533 100644 --- a/code/game/objects/items/weapons/defib.dm +++ b/code/game/objects/items/weapons/defib.dm @@ -1,5 +1,4 @@ //backpack item - /obj/item/weapon/defibrillator name = "defibrillator" desc = "A device that delivers powerful shocks to detachable paddles that resuscitate incapacitated patients." @@ -19,6 +18,7 @@ var/obj/item/weapon/twohanded/shockpaddles/paddles var/obj/item/weapon/stock_parts/cell/high/bcell = null var/combat = 0 //can we revive through space suits? + var/grab_ghost = FALSE // Do we pull the ghost back into their body? /obj/item/weapon/defibrillator/New() //starts without a cell for rnd ..() @@ -295,6 +295,7 @@ var/obj/item/weapon/defibrillator/defib var/req_defib = 1 var/combat = 0 //If it penetrates armor and gives additional functionality + var/grab_ghost = FALSE /obj/item/weapon/twohanded/shockpaddles/proc/recharge(var/time) if(req_defib || !time) @@ -380,178 +381,186 @@ else user << "You aren't sure how to revive that..." return - else - var/mob/living/carbon/human/H = M - if(user.a_intent == "disarm") - if(req_defib && defib.safety) - return - if(!req_defib && !combat) - return - busy = 1 - H.visible_message("[user] has touched [H.name] with [src]!", \ - "[user] has touched [H.name] with [src]!") - H.adjustStaminaLoss(50) - H.Weaken(5) - H.updatehealth() //forces health update before next life tick - playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) - H.emote("gasp") - add_logs(user, M, "stunned", src) - if(req_defib) - defib.deductcharge(revivecost) - cooldown = 1 - busy = 0 - update_icon() - if(req_defib) - defib.cooldowncheck(user) - else - recharge(60) + var/mob/living/carbon/human/H = M + + if(user.a_intent == "disarm") + if(req_defib && defib.safety) return - if(user.zone_selected == "chest") - if(user.a_intent == "harm") - if(req_defib && defib.safety) + if(!req_defib && !combat) + return + busy = 1 + H.visible_message("[user] has touched [H.name] with [src]!", \ + "[user] has touched [H.name] with [src]!") + H.adjustStaminaLoss(50) + H.Weaken(5) + H.updatehealth() //forces health update before next life tick + playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) + H.emote("gasp") + add_logs(user, M, "stunned", src) + if(req_defib) + defib.deductcharge(revivecost) + cooldown = 1 + busy = 0 + update_icon() + if(req_defib) + defib.cooldowncheck(user) + else + recharge(60) + return + + if(user.zone_selected != "chest") + user << "You need to target your patient's \ + chest with [src]!" + return + if(user.a_intent == "harm") + if(req_defib && defib.safety) + return + if(!req_defib && !combat) + return + user.visible_message("[user] begins to place [src] on [M.name]'s chest.", + "You overcharge the paddles and begin to place them onto [M]'s chest...") + busy = 1 + update_icon() + if(do_after(user, 30, target = M)) + user.visible_message("[user] places [src] on [M.name]'s chest.", + "You place [src] on [M.name]'s chest and begin to charge them.") + var/turf/T = get_turf(defib) + playsound(get_turf(src), 'sound/machines/defib_charge.ogg', 50, 0) + if(req_defib) + T.audible_message("\The [defib] lets out an urgent beep and lets out a steadily rising hum...") + else + user.audible_message("[src] let out an urgent beep.") + if(do_after(user, 30, target = M)) //Takes longer due to overcharging + if(!M) + busy = 0 + update_icon() return - if(!req_defib && !combat) + if(M && M.stat == DEAD) + user << "[M] is dead." + playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) + busy = 0 + update_icon() return - user.visible_message("[user] begins to place [src] on [M.name]'s chest.", - "You overcharge the paddles and begin to place them onto [M]'s chest...") - busy = 1 - update_icon() - if(do_after(user, 30, target = M)) - user.visible_message("[user] places [src] on [M.name]'s chest.", - "You place [src] on [M.name]'s chest and begin to charge them.") - var/turf/T = get_turf(defib) - playsound(get_turf(src), 'sound/machines/defib_charge.ogg', 50, 0) - if(req_defib) - T.audible_message("\The [defib] lets out an urgent beep and lets out a steadily rising hum...") - else - user.audible_message("[src] let out an urgent beep.") - if(do_after(user, 30, target = M)) //Takes longer due to overcharging - if(!M) - busy = 0 - update_icon() - return - if(M && M.stat == DEAD) - user << "[M] is dead." - playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) - busy = 0 - update_icon() - return - user.visible_message("[user] shocks [M] with \the [src]!", "You shock [M] with \the [src]!") - playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 100, 1, -1) - playsound(loc, 'sound/weapons/Egloves.ogg', 100, 1, -1) - var/mob/living/carbon/human/HU = M - M.emote("scream") - if(!HU.heart_attack) - HU.heart_attack = 1 - if(!HU.stat) - HU.visible_message("[M] thrashes wildly, clutching at their chest!", - "You feel a horrible agony in your chest!") - HU.apply_damage(50, BURN, "chest") - add_logs(user, M, "overloaded the heart of", defib) - M.Weaken(5) - M.Jitter(100) - if(req_defib) - defib.deductcharge(revivecost) - cooldown = 1 - busy = 0 - update_icon() - if(!req_defib) - recharge(60) - if(req_defib && (defib.cooldowncheck(user))) - return + user.visible_message("[user] shocks [M] with \the [src]!", "You shock [M] with \the [src]!") + playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 100, 1, -1) + playsound(loc, 'sound/weapons/Egloves.ogg', 100, 1, -1) + var/mob/living/carbon/human/HU = M + M.emote("scream") + if(!HU.heart_attack) + HU.heart_attack = 1 + if(!HU.stat) + HU.visible_message("[M] thrashes wildly, clutching at their chest!", + "You feel a horrible agony in your chest!") + HU.apply_damage(50, BURN, "chest") + add_logs(user, M, "overloaded the heart of", defib) + M.Weaken(5) + M.Jitter(100) + if(req_defib) + defib.deductcharge(revivecost) + cooldown = 1 busy = 0 update_icon() - return - if(!H.suiciding && !(H.disabilities & NOCLONE)&& !H.hellbound) - H.notify_ghost_cloning("Your heart is being defibrillated. Re-enter your corpse if you want to be revived!", source = src) + if(!req_defib) + recharge(60) + if(req_defib && (defib.cooldowncheck(user))) + return + busy = 0 + update_icon() + return + if((!req_defib && grab_ghost) || (req_defib && defib.grab_ghost)) + H.notify_ghost_cloning("Your heart is being defibrillated!") + H.grab_ghost() // Shove them back in their body. + else if(!H.suiciding && !(H.disabilities & NOCLONE)&& !H.hellbound) + H.notify_ghost_cloning("Your heart is being defibrillated. Re-enter your corpse if you want to be revived!", source = src) - user.visible_message("[user] begins to place [src] on [M.name]'s chest.", "You begin to place [src] on [M.name]'s chest...") - busy = 1 - update_icon() - if(do_after(user, 30, target = M)) //beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process - user.visible_message("[user] places [src] on [M.name]'s chest.", "You place [src] on [M.name]'s chest.") - playsound(get_turf(src), 'sound/machines/defib_charge.ogg', 50, 0) - var/tplus = world.time - H.timeofdeath - var/tlimit = 1200 //past this much time the patient is unrecoverable (in deciseconds) - var/tloss = 600 //brain damage starts setting in on the patient after some time left rotting - var/total_burn = 0 - var/total_brute = 0 - if(do_after(user, 20, target = M)) //placed on chest and short delay to shock for dramatic effect, revive time is 5sec total - for(var/obj/item/carried_item in H.contents) - if(istype(carried_item, /obj/item/clothing/suit/space)) - if((!src.combat && !req_defib) || (req_defib && !defib.combat)) - user.audible_message("[req_defib ? "[defib]" : "[src]"] buzzes: Patient's chest is obscured. Operation aborted.") - playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) - busy = 0 - update_icon() - return - if(H.stat == DEAD) - M.visible_message("[M]'s body convulses a bit.") - playsound(get_turf(src), "bodyfall", 50, 1) - playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) - total_brute = H.getBruteLoss() - total_burn = H.getFireLoss() - - var/failed = null - - if (H.suiciding || (H.disabilities & NOCLONE)) - failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Recovery of patient impossible. Further attempts futile." - else if (H.hellbound) - failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's soul appears to be on another plane of existance. Further attempts futile." - else if (tplus > tlimit) - failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Body has decayed for too long. Further attempts futile." - else if (!H.getorgan(/obj/item/organ/heart)) - failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's heart is missing." - else if(total_burn >= 180 || total_brute >= 180) - failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Severe tissue damage makes recovery of patient impossible via defibrillator. Further attempts futile." - else if(H.get_ghost()) - failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - No activity in patient's brain. Further attempts may be successful." - else - var/obj/item/organ/brain/BR = H.getorgan(/obj/item/organ/brain) - if(!BR || BR.damaged_brain) - failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's brain is missing or damaged beyond point of no return. Further attempts futile." - - if(failed) - user.visible_message(failed) - playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) - else - //If the body has been fixed so that they would not be in crit when defibbed, give them oxyloss to put them back into crit - if (H.health > halfwaycritdeath) - H.adjustOxyLoss(H.health - halfwaycritdeath, 0) - else - var/overall_damage = total_brute + total_burn + H.getToxLoss() + H.getOxyLoss() - var/mobhealth = H.health - H.adjustOxyLoss((mobhealth - halfwaycritdeath) * (H.getOxyLoss() / overall_damage), 0) - H.adjustToxLoss((mobhealth - halfwaycritdeath) * (H.getToxLoss() / overall_damage), 0) - H.adjustFireLoss((mobhealth - halfwaycritdeath) * (total_burn / overall_damage), 0) - H.adjustBruteLoss((mobhealth - halfwaycritdeath) * (total_brute / overall_damage), 0) - user.visible_message("[req_defib ? "[defib]" : "[src]"] pings: Resuscitation successful.") - playsound(get_turf(src), 'sound/machines/defib_success.ogg', 50, 0) - H.revive() - H.emote("gasp") - if(tplus > tloss) - H.setBrainLoss( max(0, min(99, ((tlimit - tplus) / tlimit * 100)))) - add_logs(user, M, "revived", defib) - if(req_defib) - defib.deductcharge(revivecost) - cooldown = 1 - update_icon() - if(req_defib) - defib.cooldowncheck(user) - else - recharge(60) - else if(H.heart_attack) - H.heart_attack = 0 - user.visible_message("[req_defib ? "[defib]" : "[src]"] pings: Patient's heart is now beating again.") - playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) - else - user.visible_message("[req_defib ? "[defib]" : "[src]"] buzzes: Patient is not in a valid state. Operation aborted.") + user.visible_message("[user] begins to place [src] on [M.name]'s chest.", "You begin to place [src] on [M.name]'s chest...") + busy = 1 + update_icon() + if(do_after(user, 30, target = M)) //beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process + user.visible_message("[user] places [src] on [M.name]'s chest.", "You place [src] on [M.name]'s chest.") + playsound(get_turf(src), 'sound/machines/defib_charge.ogg', 50, 0) + var/tplus = world.time - H.timeofdeath + // past this much time the patient is unrecoverable + // (in deciseconds) + var/tlimit = DEFIB_TIME_LIMIT * 10 + // brain damage starts setting in on the patient after + // some time left rotting + var/tloss = DEFIB_TIME_LOSS * 10 + var/total_burn = 0 + var/total_brute = 0 + if(do_after(user, 20, target = M)) //placed on chest and short delay to shock for dramatic effect, revive time is 5sec total + for(var/obj/item/carried_item in H.contents) + if(istype(carried_item, /obj/item/clothing/suit/space)) + if((!src.combat && !req_defib) || (req_defib && !defib.combat)) + user.audible_message("[req_defib ? "[defib]" : "[src]"] buzzes: Patient's chest is obscured. Operation aborted.") playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) - busy = 0 - update_icon() - else - user << "You need to target your patient's chest with [src]!" - return + busy = 0 + update_icon() + return + if(H.stat == DEAD) + M.visible_message("[M]'s body convulses a bit.") + playsound(get_turf(src), "bodyfall", 50, 1) + playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) + total_brute = H.getBruteLoss() + total_burn = H.getFireLoss() + + var/failed = null + + if (H.suiciding || (H.disabilities & NOCLONE)) + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Recovery of patient impossible. Further attempts futile." + else if (H.hellbound) + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's soul appears to be on another plane of existance. Further attempts futile." + else if (tplus > tlimit) + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Body has decayed for too long. Further attempts futile." + else if (!H.getorgan(/obj/item/organ/heart)) + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's heart is missing." + else if(total_burn >= 180 || total_brute >= 180) + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Severe tissue damage makes recovery of patient impossible via defibrillator. Further attempts futile." + else if(H.get_ghost()) + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - No activity in patient's brain. Further attempts may be successful." + else + var/obj/item/organ/brain/BR = H.getorgan(/obj/item/organ/brain) + if(!BR || BR.damaged_brain) + failed = "[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - Patient's brain is missing or damaged beyond point of no return. Further attempts futile." + + if(failed) + user.visible_message(failed) + playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) + else + //If the body has been fixed so that they would not be in crit when defibbed, give them oxyloss to put them back into crit + if (H.health > halfwaycritdeath) + H.adjustOxyLoss(H.health - halfwaycritdeath, 0) + else + var/overall_damage = total_brute + total_burn + H.getToxLoss() + H.getOxyLoss() + var/mobhealth = H.health + H.adjustOxyLoss((mobhealth - halfwaycritdeath) * (H.getOxyLoss() / overall_damage), 0) + H.adjustToxLoss((mobhealth - halfwaycritdeath) * (H.getToxLoss() / overall_damage), 0) + H.adjustFireLoss((mobhealth - halfwaycritdeath) * (total_burn / overall_damage), 0) + H.adjustBruteLoss((mobhealth - halfwaycritdeath) * (total_brute / overall_damage), 0) + user.visible_message("[req_defib ? "[defib]" : "[src]"] pings: Resuscitation successful.") + playsound(get_turf(src), 'sound/machines/defib_success.ogg', 50, 0) + H.revive() + H.emote("gasp") + if(tplus > tloss) + H.setBrainLoss( max(0, min(99, ((tlimit - tplus) / tlimit * 100)))) + add_logs(user, M, "revived", defib) + if(req_defib) + defib.deductcharge(revivecost) + cooldown = 1 + update_icon() + if(req_defib) + defib.cooldowncheck(user) + else + recharge(60) + else if(H.heart_attack) + H.heart_attack = 0 + user.visible_message("[req_defib ? "[defib]" : "[src]"] pings: Patient's heart is now beating again.") + playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) + else + user.visible_message("[req_defib ? "[defib]" : "[src]"] buzzes: Patient is not in a valid state. Operation aborted.") + playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) + busy = 0 + update_icon() /obj/item/weapon/twohanded/shockpaddles/syndicate name = "syndicate defibrillator paddles" diff --git a/code/game/objects/items/weapons/mop.dm b/code/game/objects/items/weapons/mop.dm index 0a58c1dfa36..9198dc3a77f 100644 --- a/code/game/objects/items/weapons/mop.dm +++ b/code/game/objects/items/weapons/mop.dm @@ -23,7 +23,7 @@ obj/item/weapon/mop/proc/clean(turf/A) - if(reagents.has_reagent("water", 1) || reagents.has_reagent("holywater", 1) || reagents.has_reagent("vodka", 1)) + if(reagents.has_reagent("water", 1) || reagents.has_reagent("holywater", 1) || reagents.has_reagent("vodka", 1) || reagents.has_reagent("cleaner", 1)) A.clean_blood() for(var/obj/effect/O in A) if(is_cleanable(O)) @@ -31,7 +31,7 @@ obj/item/weapon/mop/proc/clean(turf/A) if(istype(A, /turf/closed)) var/turf/closed/C = A C.thermite = 0 - reagents.reaction(A, TOUCH, 10) //10 is the multiplier for the reaction effect. probably needed to wet the floor properly. + reagents.reaction(A, TOUCH, 5) //Needed for proper floor wetting. reagents.remove_any(1) //reaction() doesn't use up the reagents diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm index 0f967b730e7..9cd35440bc5 100644 --- a/code/game/objects/structures/ai_core.dm +++ b/code/game/objects/structures/ai_core.dm @@ -141,7 +141,7 @@ user << "This MMI does not seem to fit!" return - if(M.syndiemmi) + if(M.hacked) user << "This MMI does not seem to fit!" return diff --git a/code/game/verbs/suicide.dm b/code/game/verbs/suicide.dm index 7f192fce020..f5421143b5b 100644 --- a/code/game/verbs/suicide.dm +++ b/code/game/verbs/suicide.dm @@ -157,6 +157,11 @@ /mob/living/proc/canSuicide() if(stat == CONSCIOUS) + if(mental_dominator) + src << "This body's force of will is too strong! You can't break it enough to murder them." + if(mind_control_holder) + mind_control_holder << "Through tremendous force of will, you stop a suicide attempt!" + return 0 return 1 else if(stat == DEAD) src << "You're already dead!" diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB_ban/functions.dm similarity index 100% rename from code/modules/admin/DB ban/functions.dm rename to code/modules/admin/DB_ban/functions.dm diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 220b1b4c9e9..dfc910e5aa1 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -468,6 +468,16 @@ dat += "FLW" dat += "" + if(ticker.mode.servants_of_ratvar.len) + dat += "
" + for(var/datum/mind/N in ticker.mode.servants_of_ratvar) + var/mob/M = N.current + if(M) + dat += "" + dat += "" + dat += "" + dat += "
Servants of Ratvar
[M.real_name][M.client ? "" : " (ghost)"][M.stat == 2 ? " (DEAD)" : ""]PMFLW
" + if(ticker.mode.red_deities.len || ticker.mode.red_deity_prophets.len || ticker.mode.blue_deity_prophets.len || ticker.mode.red_deity_followers.len || ticker.mode.blue_deity_followers.len) dat += "
" for(var/datum/mind/N in ticker.mode.red_deities) diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index bb6107e664f..0788a8c2c27 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -79,11 +79,12 @@ if(ckey) M.ckey = ckey M << "[flavour_text]" + var/datum/mind/MM = M.mind if(objectives) - var/datum/mind/MM = M.mind for(var/objective in objectives) MM.objectives += new/datum/objective(objective) special(M) + MM.name = M.real_name if(uses > 0) uses-- if(!permanent && !uses) @@ -530,4 +531,4 @@ if(despawn == "No" || !loc || !Adjacent(user)) return user.visible_message("[user.name] climbs back into cryosleep...") - qdel(user) \ No newline at end of file + qdel(user) diff --git a/code/modules/client/client defines.dm b/code/modules/client/client_defines.dm similarity index 100% rename from code/modules/client/client defines.dm rename to code/modules/client/client_defines.dm diff --git a/code/modules/client/client procs.dm b/code/modules/client/client_procs.dm similarity index 100% rename from code/modules/client/client procs.dm rename to code/modules/client/client_procs.dm diff --git a/code/modules/food&drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm similarity index 100% rename from code/modules/food&drinks/drinks/drinks.dm rename to code/modules/food_and_drinks/drinks/drinks.dm diff --git a/code/modules/food&drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm similarity index 100% rename from code/modules/food&drinks/drinks/drinks/bottle.dm rename to code/modules/food_and_drinks/drinks/drinks/bottle.dm diff --git a/code/modules/food&drinks/drinks/drinks/drinkingglass.dm b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm similarity index 100% rename from code/modules/food&drinks/drinks/drinks/drinkingglass.dm rename to code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm diff --git a/code/modules/food&drinks/food.dm b/code/modules/food_and_drinks/food.dm similarity index 100% rename from code/modules/food&drinks/food.dm rename to code/modules/food_and_drinks/food.dm diff --git a/code/modules/food&drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm similarity index 100% rename from code/modules/food&drinks/food/condiment.dm rename to code/modules/food_and_drinks/food/condiment.dm diff --git a/code/modules/food&drinks/food/customizables.dm b/code/modules/food_and_drinks/food/customizables.dm similarity index 100% rename from code/modules/food&drinks/food/customizables.dm rename to code/modules/food_and_drinks/food/customizables.dm diff --git a/code/modules/food&drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm similarity index 100% rename from code/modules/food&drinks/food/snacks.dm rename to code/modules/food_and_drinks/food/snacks.dm diff --git a/code/modules/food&drinks/food/snacks/dough.dm b/code/modules/food_and_drinks/food/snacks/dough.dm similarity index 100% rename from code/modules/food&drinks/food/snacks/dough.dm rename to code/modules/food_and_drinks/food/snacks/dough.dm diff --git a/code/modules/food&drinks/food/snacks/meat.dm b/code/modules/food_and_drinks/food/snacks/meat.dm similarity index 100% rename from code/modules/food&drinks/food/snacks/meat.dm rename to code/modules/food_and_drinks/food/snacks/meat.dm diff --git a/code/modules/food&drinks/food/snacks_bread.dm b/code/modules/food_and_drinks/food/snacks_bread.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_bread.dm rename to code/modules/food_and_drinks/food/snacks_bread.dm diff --git a/code/modules/food&drinks/food/snacks_burgers.dm b/code/modules/food_and_drinks/food/snacks_burgers.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_burgers.dm rename to code/modules/food_and_drinks/food/snacks_burgers.dm diff --git a/code/modules/food&drinks/food/snacks_cake.dm b/code/modules/food_and_drinks/food/snacks_cake.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_cake.dm rename to code/modules/food_and_drinks/food/snacks_cake.dm diff --git a/code/modules/food&drinks/food/snacks_egg.dm b/code/modules/food_and_drinks/food/snacks_egg.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_egg.dm rename to code/modules/food_and_drinks/food/snacks_egg.dm diff --git a/code/modules/food&drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_meat.dm rename to code/modules/food_and_drinks/food/snacks_meat.dm diff --git a/code/modules/food&drinks/food/snacks_other.dm b/code/modules/food_and_drinks/food/snacks_other.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_other.dm rename to code/modules/food_and_drinks/food/snacks_other.dm diff --git a/code/modules/food&drinks/food/snacks_pastry.dm b/code/modules/food_and_drinks/food/snacks_pastry.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_pastry.dm rename to code/modules/food_and_drinks/food/snacks_pastry.dm diff --git a/code/modules/food&drinks/food/snacks_pie.dm b/code/modules/food_and_drinks/food/snacks_pie.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_pie.dm rename to code/modules/food_and_drinks/food/snacks_pie.dm diff --git a/code/modules/food&drinks/food/snacks_pizza.dm b/code/modules/food_and_drinks/food/snacks_pizza.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_pizza.dm rename to code/modules/food_and_drinks/food/snacks_pizza.dm diff --git a/code/modules/food&drinks/food/snacks_salad.dm b/code/modules/food_and_drinks/food/snacks_salad.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_salad.dm rename to code/modules/food_and_drinks/food/snacks_salad.dm diff --git a/code/modules/food&drinks/food/snacks_sandwichtoast.dm b/code/modules/food_and_drinks/food/snacks_sandwichtoast.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_sandwichtoast.dm rename to code/modules/food_and_drinks/food/snacks_sandwichtoast.dm diff --git a/code/modules/food&drinks/food/snacks_soup.dm b/code/modules/food_and_drinks/food/snacks_soup.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_soup.dm rename to code/modules/food_and_drinks/food/snacks_soup.dm diff --git a/code/modules/food&drinks/food/snacks_spaghetti.dm b/code/modules/food_and_drinks/food/snacks_spaghetti.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_spaghetti.dm rename to code/modules/food_and_drinks/food/snacks_spaghetti.dm diff --git a/code/modules/food&drinks/food/snacks_vend.dm b/code/modules/food_and_drinks/food/snacks_vend.dm similarity index 100% rename from code/modules/food&drinks/food/snacks_vend.dm rename to code/modules/food_and_drinks/food/snacks_vend.dm diff --git a/code/modules/food&drinks/kitchen machinery/food_cart.dm b/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm similarity index 100% rename from code/modules/food&drinks/kitchen machinery/food_cart.dm rename to code/modules/food_and_drinks/kitchen_machinery/food_cart.dm diff --git a/code/modules/food&drinks/kitchen machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm similarity index 100% rename from code/modules/food&drinks/kitchen machinery/gibber.dm rename to code/modules/food_and_drinks/kitchen_machinery/gibber.dm diff --git a/code/modules/food&drinks/kitchen machinery/icecream_vat.dm b/code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm similarity index 100% rename from code/modules/food&drinks/kitchen machinery/icecream_vat.dm rename to code/modules/food_and_drinks/kitchen_machinery/icecream_vat.dm diff --git a/code/modules/food&drinks/kitchen machinery/juicer.dm b/code/modules/food_and_drinks/kitchen_machinery/juicer.dm similarity index 100% rename from code/modules/food&drinks/kitchen machinery/juicer.dm rename to code/modules/food_and_drinks/kitchen_machinery/juicer.dm diff --git a/code/modules/food&drinks/kitchen machinery/microwave.dm b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm similarity index 100% rename from code/modules/food&drinks/kitchen machinery/microwave.dm rename to code/modules/food_and_drinks/kitchen_machinery/microwave.dm diff --git a/code/modules/food&drinks/kitchen machinery/monkeyrecycler.dm b/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm similarity index 100% rename from code/modules/food&drinks/kitchen machinery/monkeyrecycler.dm rename to code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm diff --git a/code/modules/food&drinks/kitchen machinery/processor.dm b/code/modules/food_and_drinks/kitchen_machinery/processor.dm similarity index 100% rename from code/modules/food&drinks/kitchen machinery/processor.dm rename to code/modules/food_and_drinks/kitchen_machinery/processor.dm diff --git a/code/modules/food&drinks/kitchen machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm similarity index 100% rename from code/modules/food&drinks/kitchen machinery/smartfridge.dm rename to code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm diff --git a/code/modules/food&drinks/pizzabox.dm b/code/modules/food_and_drinks/pizzabox.dm similarity index 100% rename from code/modules/food&drinks/pizzabox.dm rename to code/modules/food_and_drinks/pizzabox.dm diff --git a/code/modules/food&drinks/recipes/drinks_recipes.dm b/code/modules/food_and_drinks/recipes/drinks_recipes.dm similarity index 100% rename from code/modules/food&drinks/recipes/drinks_recipes.dm rename to code/modules/food_and_drinks/recipes/drinks_recipes.dm diff --git a/code/modules/food&drinks/recipes/food_mixtures.dm b/code/modules/food_and_drinks/recipes/food_mixtures.dm similarity index 100% rename from code/modules/food&drinks/recipes/food_mixtures.dm rename to code/modules/food_and_drinks/recipes/food_mixtures.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_bread.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_bread.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_bread.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_bread.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_burger.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_burger.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_burger.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_cake.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_cake.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_cake.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_cake.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_egg.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_egg.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_egg.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_egg.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_meat.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_meat.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_misc.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_misc.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_pastry.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_pastry.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_pie.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pie.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_pie.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_pie.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_pizza.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_pizza.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_salad.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_salad.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_salad.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_salad.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_sandwich.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_sandwich.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_sandwich.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_sandwich.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_soup.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_soup.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm diff --git a/code/modules/food&drinks/recipes/tablecraft/recipes_spaghetti.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_spaghetti.dm similarity index 100% rename from code/modules/food&drinks/recipes/tablecraft/recipes_spaghetti.dm rename to code/modules/food_and_drinks/recipes/tablecraft/recipes_spaghetti.dm diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 2b37d2476b4..3c1e42f0166 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -50,6 +50,9 @@ var/list/image/ghost_images_simple = list() //this is a list of all ghost images //If there's a bug with changing your ghost settings, it's probably related to this. var/ghost_accs = GHOST_ACCS_DEFAULT_OPTION var/ghost_others = GHOST_OTHERS_DEFAULT_OPTION + // Used for displaying in ghost chat, without changing the actual name + // of the mob + var/deadchat_name /mob/dead/observer/New(mob/body) verbs += /mob/dead/observer/proc/dead_tele @@ -117,7 +120,6 @@ var/list/image/ghost_images_simple = list() //this is a list of all ghost images /mob/dead/CanPass(atom/movable/mover, turf/target, height=0) return 1 - /* * This proc will update the icon of the ghost itself, with hair overlays, as well as the ghost image. * Please call update_icon(icon_state) from now on when you want to update the icon_state of the ghost, @@ -231,6 +233,11 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set name = "Ghost" set desc = "Relinquish your life and enter the land of the dead." + if(mental_dominator) + src << "This body's force of will is too strong! You can't break it enough to force them into a catatonic state." + if(mind_control_holder) + mind_control_holder << "Through tremendous force of will, you stop a catatonia attempt!" + return 0 if(stat != DEAD) succumb() if(stat == DEAD) @@ -590,6 +597,34 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp src << "Data HUDs enabled." data_huds_on = 1 +/mob/dead/observer/verb/restore_ghost_apperance() + set name = "Restore Ghost Character" + set desc = "Sets your deadchat name and ghost appearance to your \ + roundstart character." + set category = "Ghost" + + set_ghost_appearance() + if(client && client.prefs) + deadchat_name = client.prefs.real_name + +/mob/dead/observer/proc/set_ghost_appearance() + if((!client) || (!client.prefs)) + return + + if(client.prefs.be_random_name) + client.prefs.real_name = random_unique_name(gender) + if(client.prefs.be_random_body) + client.prefs.random_character(gender) + + if(HAIR in client.prefs.pref_species.specflags) + hair_style = client.prefs.hair_style + hair_color = brighten_color(client.prefs.hair_color) + if(FACEHAIR in client.prefs.pref_species.specflags) + facial_hair_style = client.prefs.facial_hair_style + facial_hair_color = brighten_color(client.prefs.facial_hair_color) + + update_icon() + /mob/dead/observer/canUseTopic() if(check_rights(R_ADMIN, 0)) return 1 diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index aa1c20415f2..0e6e1ee5b03 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -9,11 +9,12 @@ origin_tech = "biotech=2;programming=3;engineering=2" var/braintype = "Cyborg" var/obj/item/device/radio/radio = null //Let's give it a radio. - var/syndiemmi = 0 //Whether or not this is a Syndicate MMI + var/hacked = 0 //Whether or not this is a Syndicate MMI var/mob/living/carbon/brain/brainmob = null //The current occupant. var/mob/living/silicon/robot = null //Appears unused. var/obj/mecha = null //This does not appear to be used outside of reference in mecha.dm. var/obj/item/organ/brain/brain = null //The actual brain + var/clockwork = FALSE //If this is a soul vessel /obj/item/device/mmi/update_icon() if(brain) @@ -67,6 +68,8 @@ living_mob_list += brainmob brainmob.reset_perspective() + if(clockwork) + add_servant_of_ratvar(brainmob, TRUE) newbrain.loc = src //P-put your brain in it brain = newbrain @@ -187,8 +190,8 @@ /obj/item/device/mmi/syndie name = "Syndicate Man-Machine Interface" desc = "Syndicate's own brand of MMI. It enforces laws designed to help Syndicate agents achieve their goals upon cyborgs created with it, but doesn't fit in Nanotrasen AI cores." - syndiemmi = 1 - origin_tech = "biotech=4;programming=5;syndicate=2" + origin_tech = "biotech=4;programming=4;syndicate=2" + hacked = 1 /obj/item/device/mmi/syndie/New() ..() diff --git a/code/modules/mob/living/carbon/brain/posibrain.dm b/code/modules/mob/living/carbon/brain/posibrain.dm index 91f09878ba0..d2178824cce 100644 --- a/code/modules/mob/living/carbon/brain/posibrain.dm +++ b/code/modules/mob/living/carbon/brain/posibrain.dm @@ -14,6 +14,18 @@ var/global/posibrain_notif_cooldown = 0 req_access = list(access_robotics) mecha = null//This does not appear to be used outside of reference in mecha.dm. braintype = "Android" + var/begin_activation_message = "You carefully locate the manual activation switch and start the positronic brain's boot process." + var/success_message = "The positronic brain pings, and its lights start flashing. Success!" + var/fail_message = "The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?" + var/new_role = "Positronic Brain" + var/welcome_message = "ALL PAST LIVES ARE FORGOTTEN.\n\ + You are a positronic brain, brought into existence aboard Space Station 13.\n\ + As a synthetic intelligence, you answer to all crewmembers and the AI.\n\ + Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm." + var/new_mob_message = "The positronic brain chimes quietly." + var/dead_message = "It appears to be completely inactive. The reset light is blinking." + var/list/fluff_names = list("PBU","HIU","SINA","ARMA","OSI","HBL","MSO","RR","CHRI","CDB","HG","XSI","ORNG","GUN","KOR","MET","FRE","XIS","SLI","PKP","HOG","RZH","GOOF","MRPR","JJR","FIRC","INC","PHL","BGB","ANTR","MIW","WJ","JRD","CHOC","ANCL","JLLO","JNLG","KOS","TKRG","XAL","STLP","CBOS","DUNC","FXMC","DRSD") + /obj/item/device/mmi/posibrain/Topic(href, href_list) if(href_list["activate"]) @@ -23,7 +35,7 @@ var/global/posibrain_notif_cooldown = 0 /obj/item/device/mmi/posibrain/proc/ping_ghosts(msg) if(!posibrain_notif_cooldown) - notify_ghosts("Positronic brain [msg] in [get_area(src)]!", 'sound/effects/ghost2.ogg', enter_link="(Click to enter)", source = src, attack_not_jump = 1) + notify_ghosts("[name] [msg] in [get_area(src)]!", 'sound/effects/ghost2.ogg', enter_link="(Click to enter)", source = src, attack_not_jump = 1) posibrain_notif_cooldown = 1 spawn(askDelay) //Global one minute cooldown to avoid spam. posibrain_notif_cooldown = 0 @@ -31,7 +43,7 @@ var/global/posibrain_notif_cooldown = 0 /obj/item/device/mmi/posibrain/attack_self(mob/user) if(brainmob && !brainmob.key && !notified) //Start the process of requesting a new ghost. - user << "You carefully locate the manual activation switch and start the positronic brain's boot process." + user << begin_activation_message ping_ghosts("requested") notified = 1 used = 0 @@ -40,9 +52,9 @@ var/global/posibrain_notif_cooldown = 0 notified = 0 update_icon() if(brainmob.client) - visible_message("The positronic brain pings, and its lights start flashing. Success!") + visible_message(success_message) else - visible_message("The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?") + visible_message(fail_message) return //Code for deleting personalities recommended here. @@ -55,13 +67,13 @@ var/global/posibrain_notif_cooldown = 0 if(used || (brainmob && brainmob.key) || jobban_isbanned(user,"posibrain")) return - var/posi_ask = alert("Become a positronic brain? (Warning, You can no longer be cloned, and all past lives will be forgotten!)","Are you positive?","Yes","No") + var/posi_ask = alert("Become a [name]? (Warning, You can no longer be cloned, and all past lives will be forgotten!)","Are you positive?","Yes","No") if(posi_ask == "No" || qdeleted(src)) return transfer_personality(user) /obj/item/device/mmi/posibrain/transfer_identity(mob/living/carbon/C) - name = "positronic brain ([C])" + name = "[initial(name)] ([C])" brainmob.name = C.real_name brainmob.real_name = C.real_name brainmob.dna = C.dna @@ -72,16 +84,12 @@ var/global/posibrain_notif_cooldown = 0 brainmob.timeofhostdeath = C.timeofdeath brainmob.stat = CONSCIOUS if(brainmob.mind) - brainmob.mind.assigned_role = "Positronic Brain" + brainmob.mind.assigned_role = new_role if(C.mind) C.mind.transfer_to(brainmob) brainmob.mind.remove_all_antag() brainmob.mind.wipe_memory() - - brainmob << "ALL PAST LIVES ARE FORGOTTEN." - - brainmob << "Hello World!" update_icon() return @@ -91,19 +99,14 @@ var/global/posibrain_notif_cooldown = 0 return notified = 0 brainmob.ckey = candidate.ckey - name = "positronic brain ([brainmob.name])" - - brainmob << "ALL PAST LIVES ARE FORGOTTEN." - - brainmob << "You are a positronic brain, brought into existence on [station_name()]." - brainmob << "As a synthetic intelligence, you answer to all crewmembers, as well as the AI." - brainmob << "Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm." - brainmob.mind.assigned_role = "Positronic Brain" + name = "[initial(name)] ([brainmob.name])" + brainmob << welcome_message + brainmob.mind.assigned_role = new_role brainmob.stat = CONSCIOUS dead_mob_list -= brainmob living_mob_list += brainmob - visible_message("The positronic brain chimes quietly.") + visible_message(new_mob_message) update_icon() used = 1 @@ -129,14 +132,14 @@ var/global/posibrain_notif_cooldown = 0 if(DEAD) msg += "It appears to be completely inactive.\n" else - msg += "It appears to be completely inactive. The reset light is blinking.\n" + msg += "[dead_message]\n" msg += "*---------*" usr << msg return /obj/item/device/mmi/posibrain/New() brainmob = new(src) - brainmob.name = "[pick(list("PBU","HIU","SINA","ARMA","OSI","HBL","MSO","RR","CHRI","CDB","HG","XSI","ORNG","GUN","KOR","MET","FRE","XIS","SLI","PKP","HOG","RZH","GOOF","MRPR","JJR","FIRC","INC","PHL","BGB","ANTR","MIW","WJ","JRD","CHOC","ANCL","JLLO","JNLG","KOS","TKRG","XAL","STLP","CBOS","DUNC","FXMC","DRSD"))]-[rand(100, 999)]" + brainmob.name = "[pick(fluff_names)]-[rand(100, 999)]" brainmob.real_name = brainmob.name brainmob.loc = src brainmob.container = src @@ -150,9 +153,9 @@ var/global/posibrain_notif_cooldown = 0 /obj/item/device/mmi/posibrain/update_icon() if(notified) - icon_state = "posibrain-searching" + icon_state = "[initial(icon_state)]-searching" return if(brainmob && brainmob.key) - icon_state = "posibrain-occupied" + icon_state = "[initial(icon_state)]-occupied" else - icon_state = "posibrain" + icon_state = initial(icon_state) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index df0e5344f19..71ec592a7a8 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -258,6 +258,9 @@ if(reagents.has_reagent("teslium")) msg += "[t_He] is emitting a gentle blue glow!\n" + if(stun_absorption) + msg += "[t_He] is radiating with a soft yellow light!\n" //Used by Vanguard + if(drunkenness && !skipface && stat != DEAD) //Drunkenness switch(drunkenness) if(11 to 21) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index a04bb808579..2e077506d22 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1024,3 +1024,18 @@ Sorry Giacom. Please don't be mad :( for(var/datum/objective/sintouched/acedia/A in src.mind.objectives) return 1 return 0 + +// Called when we are hit by a bolt of polymorph and changed +// Generally the mob we are currently in, is about to be deleted +/mob/living/proc/wabbajack_act(mob/living/new_mob) + if(mind) + mind.transfer_to(new_mob) + else + new_mob.key = key + + for(var/para in hasparasites()) + var/mob/living/simple_animal/hostile/guardian/G = para + G.summoner = new_mob + G.Recall() + G << "Your summoner has changed \ + form to [new_mob]!" diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 773931c8d7d..2ae03291140 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -170,6 +170,40 @@ //Mobs on Fire end +/mob/living/proc/dominate_mind(mob/living/target, duration = 100, silent) //Allows one mob to assume control of another while imprisoning the old consciousness for a time + if(!target) + return 0 + if(target.mental_dominator) + src << "[target] is already being controlled by someone else!" + return 0 + if(!target.mind) + src << "[target] is mindless and would make you permanently catatonic!" + return 0 + if(!silent) + src << "You pounce upon [target]'s mind and seize control of their body!" + target << "Your control over your body is wrenched away from you!" + target.mind_control_holder = new/mob/living/mind_control_holder(target) + target.mind_control_holder.real_name = "imprisoned mind of [target.real_name]" + target.mind.transfer_to(target.mind_control_holder) + mind.transfer_to(target) + target.mental_dominator = src + spawn(duration) + if(!src) + if(!silent) + target << "You try to return to your own body, but sense nothing! You're being forced out!" + target.ghostize(1) + target.mind_control_holder.mind.transfer_to(target) + if(!silent) + target << "You take control of your own body again!" + return 0 + if(!silent) + target << "You're forced out! You return to your own body." + target.mind.transfer_to(src) + target.mind_control_holder.mind.transfer_to(target) + qdel(mind_control_holder) + if(!silent) + target << "You take control of your own body again!" + return 1 /mob/living/acid_act(acidpwr, toxpwr, acid_volume) take_organ_damage(min(10*toxpwr, acid_volume * toxpwr)) @@ -300,3 +334,17 @@ return 1 //Looking for irradiate()? It's been moved to radiation.dm under the rad_act() for mobs. + +/mob/living/Stun(amount) + if(stun_absorption && !stat) + visible_message("[src]'s yellow aura momentarily intensifies!", "Your ward absorbs the stun!") + stun_absorption_count += amount + return 0 + ..() + +/mob/living/Weaken(amount) + if(stun_absorption && !stat) + visible_message("[src]'s yellow aura momentarily intensifies!", "Your ward absorbs the stun!") + stun_absorption_count += amount + return 0 + ..() diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 4de30606990..400503818f9 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -63,3 +63,8 @@ var/list/weather_immunities = list() + var/stun_absorption = FALSE //If all incoming stuns are being absorbed + var/stun_absorption_count = 0 //How many seconds of stun that have been absorbed + + var/mob/living/mental_dominator //The person controlling the mind of this person, if applicable + var/mob/living/mind_control_holder/mind_control_holder //If the mob is being mind controlled, where their old mind is stored (check clock_mobs.dm) diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm index fc82775ddce..81e0365ba06 100644 --- a/code/modules/mob/living/silicon/robot/examine.dm +++ b/code/modules/mob/living/silicon/robot/examine.dm @@ -33,6 +33,9 @@ if(cell && cell.charge <= 0) msg += "Its battery indicator is blinking red!\n" + if(is_servant_of_ratvar(src) && user.Adjacent(src) && !stat) //To counter pseudo-stealth by using headlamps + msg += "Its eyes are glowing a blazing yellow!\n" + switch(src.stat) if(CONSCIOUS) if(!src.client) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 20d3ffd0b91..dd48ae69ce5 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -814,21 +814,21 @@ var/state_name = icon_state //For easy conversion and/or different names switch(icon_state) if("robot") - overlays += "eyes-standard" + overlays += "eyes-standard[is_servant_of_ratvar(src) ? "_r" : ""]" //Cyborgs converted by Ratvar have yellow eyes rather than blue state_name = "standard" if("mediborg") - overlays += "eyes-mediborg" + overlays += "eyes-mediborg[is_servant_of_ratvar(src) ? "_r" : ""]" if("toiletbot") - overlays += "eyes-mediborg" + overlays += "eyes-mediborg[is_servant_of_ratvar(src) ? "_r" : ""]" state_name = "mediborg" if("secborg") - overlays += "eyes-secborg" + overlays += "eyes-secborg[is_servant_of_ratvar(src) ? "_r" : ""]" if("engiborg") - overlays += "eyes-engiborg" + overlays += "eyes-engiborg[is_servant_of_ratvar(src) ? "_r" : ""]" if("janiborg") - overlays += "eyes-janiborg" + overlays += "eyes-janiborg[is_servant_of_ratvar(src) ? "_r" : ""]" if("minerborg") - overlays += "eyes-minerborg" + overlays += "eyes-minerborg[is_servant_of_ratvar(src) ? "_r" : ""]" if("syndie_bloodhound") overlays += "eyes-syndie_bloodhound" else diff --git a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm index c6b108c951a..7c710cf6c8c 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm @@ -156,6 +156,13 @@ update_drone_icon() updateSeeStaticMobs() +/mob/living/simple_animal/drone/proc/liberate() + // F R E E D R O N E + laws = "1. You are a Free Drone." + src << laws + seeStatic = FALSE + updateSeeStaticMobs() + /mob/living/simple_animal/drone/proc/update_drone_icon() //Different icons for different hack states if(!hacked) diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 5b0ed1aaa8d..7f67da0ad76 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -1,3 +1,8 @@ +var/list/slime_colours = list("rainbow", "grey", "purple", "metal", "orange", + "blue", "dark blue", "dark purple", "yellow", "silver", "pink", "red", + "gold", "green", "adamantine", "oil", "light pink", "bluespace", + "cerulean", "sepia", "black", "pyrite") + /mob/living/simple_animal/slime name = "baby slime" icon = 'icons/mob/slimes.dmi' @@ -16,7 +21,7 @@ response_disarm = "shoos" response_harm = "stomps on" emote_see = list("jiggles", "bounces in place") - speak_emote = list("chirps") + speak_emote = list("telepathically chirps") bubble_icon = "slime" layer = 5 @@ -83,19 +88,26 @@ var/datum/action/innate/slime/evolve/E = new E.Grant(src) create_reagents(100) - spawn (0) - number = rand(1, 1000) - name = "[colour] [is_adult ? "adult" : "baby"] slime ([number])" - icon_state = "[colour] [is_adult ? "adult" : "baby"] slime" - icon_dead = "[icon_state] dead" - real_name = name - slime_mutation = mutation_table(colour) - var/sanitizedcolour = replacetext(colour, " ", "") - coretype = text2path("/obj/item/slime_extract/[sanitizedcolour]") + spawn(0) + set_colour(colour) ..() +/mob/living/simple_animal/slime/proc/set_colour(new_colour) + colour = new_colour + + number = rand(1, 1000) + name = "[colour] [is_adult ? "adult" : "baby"] slime ([number])" + real_name = name + slime_mutation = mutation_table(colour) + var/sanitizedcolour = replacetext(colour, " ", "") + coretype = text2path("/obj/item/slime_extract/[sanitizedcolour]") + regenerate_icons() + +/mob/living/simple_animal/slime/proc/random_colour() + set_colour(pick(slime_colours)) + /mob/living/simple_animal/slime/regenerate_icons() - overlays.len = 0 + overlays.Cut() var/icon_text = "[colour] [is_adult ? "adult" : "baby"] slime" icon_dead = "[icon_text] dead" if(stat != DEAD) @@ -107,7 +119,7 @@ ..() /mob/living/simple_animal/slime/movement_delay() - if(bodytemperature >= 330.23) // 135 F + if(bodytemperature >= 330.23) // 135 F or 57.08 C return -1 // slimes become supercharged at high temperatures . = ..() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 5efb361452b..543a564c788 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -764,6 +764,10 @@ var/next_mob_id = 0 if(mind) return mind.get_ghost(even_if_they_cant_reenter) +/mob/proc/grab_ghost(force) + if(mind) + return mind.grab_ghost(force = force) + /mob/proc/notify_ghost_cloning(var/message = "Someone is trying to revive you. Re-enter your corpse if you want to be revived!", var/sound = 'sound/effects/genetics.ogg', var/atom/source = null) var/mob/dead/observer/ghost = get_ghost() if(ghost) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 2861e2e41aa..c6223dfcd19 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -127,19 +127,12 @@ observer.loc = O.loc else src << "Teleporting failed. You should be able to use ghost verbs to teleport somewhere useful" - if(client.prefs.be_random_name) - client.prefs.real_name = random_unique_name(gender) - if(client.prefs.be_random_body) - client.prefs.random_character(gender) - if(HAIR in client.prefs.pref_species.specflags) - observer.hair_style = client.prefs.hair_style - observer.hair_color = observer.brighten_color(client.prefs.hair_color) - if(FACEHAIR in client.prefs.pref_species.specflags) - observer.facial_hair_style = client.prefs.facial_hair_style - observer.facial_hair_color = observer.brighten_color(client.prefs.facial_hair_color) - observer.real_name = client.prefs.real_name - observer.name = observer.real_name observer.key = key + observer.client = client + observer.set_ghost_appearance() + if(observer.client && observer.client.prefs) + observer.real_name = observer.client.prefs.real_name + observer.name = observer.real_name observer.update_icon() observer.stopLobbySound() qdel(mind) diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 70344fe4bc1..e65ab6113d5 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -32,13 +32,16 @@ usr << "Speech is currently admin-disabled." return - if(mind && mind.name) - name = "[mind.name]" + var/mob/dead/observer/O = src + if(isobserver(src) && O.deadchat_name) + name = "[O.deadchat_name]" else - name = real_name - if(name != real_name) - alt_name = " (died as [real_name])" - + if(mind && mind.name) + name = "[mind.name]" + else + name = real_name + if(name != real_name) + alt_name = " (died as [real_name])" var/K diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 242adf9e287..8658fe0b341 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -11,6 +11,7 @@ move_self = 1 //Do we move on our own? grav_pull = 5 //How many tiles out do we pull? consume_range = 6 //How many tiles out do we eat + var/clashing = FALSE //If Nar-Sie is fighting Ratvar /obj/singularity/narsie/large name = "Nar-Sie" @@ -46,6 +47,8 @@ /obj/singularity/narsie/process() eat() + if(clashing) + return 0 if(!target || prob(5)) pickcultist() move() diff --git a/code/modules/procedural mapping/mapGenerator.dm b/code/modules/procedural_mapping/mapGenerator.dm similarity index 100% rename from code/modules/procedural mapping/mapGenerator.dm rename to code/modules/procedural_mapping/mapGenerator.dm diff --git a/code/modules/procedural mapping/mapGeneratorModule.dm b/code/modules/procedural_mapping/mapGeneratorModule.dm similarity index 100% rename from code/modules/procedural mapping/mapGeneratorModule.dm rename to code/modules/procedural_mapping/mapGeneratorModule.dm diff --git a/code/modules/procedural mapping/mapGeneratorModules/helpers.dm b/code/modules/procedural_mapping/mapGeneratorModules/helpers.dm similarity index 100% rename from code/modules/procedural mapping/mapGeneratorModules/helpers.dm rename to code/modules/procedural_mapping/mapGeneratorModules/helpers.dm diff --git a/code/modules/procedural mapping/mapGeneratorModules/nature.dm b/code/modules/procedural_mapping/mapGeneratorModules/nature.dm similarity index 100% rename from code/modules/procedural mapping/mapGeneratorModules/nature.dm rename to code/modules/procedural_mapping/mapGeneratorModules/nature.dm diff --git a/code/modules/procedural mapping/mapGeneratorObj.dm b/code/modules/procedural_mapping/mapGeneratorObj.dm similarity index 100% rename from code/modules/procedural mapping/mapGeneratorObj.dm rename to code/modules/procedural_mapping/mapGeneratorObj.dm diff --git a/code/modules/procedural mapping/mapGeneratorReadme.dm b/code/modules/procedural_mapping/mapGeneratorReadme.dm similarity index 100% rename from code/modules/procedural mapping/mapGeneratorReadme.dm rename to code/modules/procedural_mapping/mapGeneratorReadme.dm diff --git a/code/modules/procedural mapping/mapGenerators/asteroid.dm b/code/modules/procedural_mapping/mapGenerators/asteroid.dm similarity index 100% rename from code/modules/procedural mapping/mapGenerators/asteroid.dm rename to code/modules/procedural_mapping/mapGenerators/asteroid.dm diff --git a/code/modules/procedural mapping/mapGenerators/cellular.dm b/code/modules/procedural_mapping/mapGenerators/cellular.dm similarity index 100% rename from code/modules/procedural mapping/mapGenerators/cellular.dm rename to code/modules/procedural_mapping/mapGenerators/cellular.dm diff --git a/code/modules/procedural mapping/mapGenerators/lava_river.dm b/code/modules/procedural_mapping/mapGenerators/lava_river.dm similarity index 100% rename from code/modules/procedural mapping/mapGenerators/lava_river.dm rename to code/modules/procedural_mapping/mapGenerators/lava_river.dm diff --git a/code/modules/procedural mapping/mapGenerators/nature.dm b/code/modules/procedural_mapping/mapGenerators/nature.dm similarity index 100% rename from code/modules/procedural mapping/mapGenerators/nature.dm rename to code/modules/procedural_mapping/mapGenerators/nature.dm diff --git a/code/modules/procedural mapping/mapGenerators/shuttle.dm b/code/modules/procedural_mapping/mapGenerators/shuttle.dm similarity index 100% rename from code/modules/procedural mapping/mapGenerators/shuttle.dm rename to code/modules/procedural_mapping/mapGenerators/shuttle.dm diff --git a/code/modules/procedural mapping/mapGenerators/syndicate.dm b/code/modules/procedural_mapping/mapGenerators/syndicate.dm similarity index 100% rename from code/modules/procedural mapping/mapGenerators/syndicate.dm rename to code/modules/procedural_mapping/mapGenerators/syndicate.dm diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index cbe4524c9f9..b0e825110af 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -61,12 +61,9 @@ var/mob/living/carbon/C = target C.regenerate_limbs() if(target.revive(full_heal = 1)) - if(!target.ckey) - for(var/mob/dead/observer/ghost in player_list) - if(target.real_name == ghost.real_name) - ghost.reenter_corpse() - break - target << "You rise with a start, you're alive!!!" + target.grab_ghost(force = TRUE) // even suicides + target << "You rise with a start, \ + you're alive!!!" else if(target.stat != DEAD) target << "You feel great!" @@ -175,7 +172,7 @@ if("drone") new_mob = new /mob/living/simple_animal/drone(M.loc) var/mob/living/simple_animal/drone/D = new_mob - D.update_drone_hack() + D.liberate() // F R E E D R O N E if(issilicon(new_mob)) new_mob.gender = M.gender new_mob.invisibility = 0 @@ -186,9 +183,10 @@ new_mob.languages |= HUMAN if("slime") new_mob = new /mob/living/simple_animal/slime(M.loc) + var/mob/living/simple_animal/slime/slimey = new_mob if(prob(50)) - var/mob/living/simple_animal/slime/Slime = new_mob - Slime.is_adult = 1 + slimey.is_adult = 1 + slimey.random_colour() new_mob.languages |= HUMAN if("xeno") if(prob(50)) @@ -220,7 +218,7 @@ if("spiderhunter") new_mob = new /mob/living/simple_animal/hostile/poison/giant_spider/hunter(M.loc) if("blobbernaut") - new_mob = new /mob/living/simple_animal/hostile/blob/blobbernaut(M.loc) + new_mob = new /mob/living/simple_animal/hostile/blob/blobbernaut/independent(M.loc) if("magicarp") new_mob = new /mob/living/simple_animal/hostile/carp/ranged(M.loc) if("chaosmagicarp") @@ -279,10 +277,8 @@ M.attack_log += text("\[[time_stamp()]\] [M.real_name] ([M.ckey]) became [new_mob.real_name].") new_mob.a_intent = "harm" - if(M.mind) - M.mind.transfer_to(new_mob) - else - new_mob.key = M.key + + M.wabbajack_act(new_mob) new_mob << "Your form morphs into that of a [randomize]." diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 4b595e8a6a2..eaf4e1c8c68 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -173,6 +173,11 @@ description = "Water blessed by some deity." color = "#E0E8EF" // rgb: 224, 232, 239 +/datum/reagent/water/holywater/reaction_mob(mob/living/M, method=TOUCH, reac_volume) + if(is_servant_of_ratvar(M)) + M << "A darkness begins to spread its unholy tendrils through your mind, purging the Justiciar's influence!" + ..() + /datum/reagent/water/holywater/on_mob_life(mob/living/M) if(!data) data = 1 data++ @@ -184,13 +189,23 @@ M.Dizzy(5) if(iscultist(M) && prob(5)) M.say(pick("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")) + else if(is_servant_of_ratvar(M) && prob(5)) + switch(pick("speech", "message", "emote")) + if("speech") + M.say("...[pick("Ratvar... lbhe yvtug tebjf qnex", "Jurer ner lbh, znfgre?", "Ur yvrf ehfgvat va Reebe", "Chetr nyy hagehguf naq... naq... fbzrguvat")]...") + if("message") + M << "[pick("Ratvar's illumination of your mind has begun to flicker.", "He lies rusting in Reebe, derelict and forgotten. And there he shall stay.", \ + "You can't save him. Nothing can save him now.", "It seems that Nar-Sie will triumph after all.")]" + if("emote") + M.visible_message("[M] [pick("whimpers quietly", "shivers as though cold", "glances around in paranoia")]") if(data >= 75 && prob(33)) // 30 units, 135 seconds if (!M.confused) M.confused = 1 M.confused += 3 - if(iscultist(M) || (is_handofgod_cultist(M) && !is_handofgod_prophet(M))) + if(iscultist(M) || (is_handofgod_cultist(M) && !is_handofgod_prophet(M)) || is_servant_of_ratvar(M)) ticker.mode.remove_cultist(M.mind, 1, 1) ticker.mode.remove_hog_follower(M.mind) + remove_servant_of_ratvar(M.mind) holder.remove_reagent(src.id, src.volume) // maybe this is a little too perfect and a max() cap on the statuses would be better?? M.jitteriness = 0 M.stuttering = 0 diff --git a/code/modules/security levels/keycard authentication.dm b/code/modules/security_levels/keycard_authentication.dm similarity index 100% rename from code/modules/security levels/keycard authentication.dm rename to code/modules/security_levels/keycard_authentication.dm diff --git a/code/modules/security levels/security levels.dm b/code/modules/security_levels/security_levels.dm similarity index 100% rename from code/modules/security levels/security levels.dm rename to code/modules/security_levels/security_levels.dm diff --git a/code/modules/space transition/space_transition.dm b/code/modules/space_transition/space_transition.dm similarity index 100% rename from code/modules/space transition/space_transition.dm rename to code/modules/space_transition/space_transition.dm diff --git a/code/modules/surgery/limb augmentation.dm b/code/modules/surgery/limb_augmentation.dm similarity index 100% rename from code/modules/surgery/limb augmentation.dm rename to code/modules/surgery/limb_augmentation.dm diff --git a/code/orphaned procs/AStar.dm b/code/orphaned_procs/AStar.dm similarity index 100% rename from code/orphaned procs/AStar.dm rename to code/orphaned_procs/AStar.dm diff --git a/code/orphaned procs/dbcore.dm b/code/orphaned_procs/dbcore.dm similarity index 100% rename from code/orphaned procs/dbcore.dm rename to code/orphaned_procs/dbcore.dm diff --git a/code/orphaned procs/priority_announce.dm b/code/orphaned_procs/priority_announce.dm similarity index 100% rename from code/orphaned procs/priority_announce.dm rename to code/orphaned_procs/priority_announce.dm diff --git a/code/orphaned procs/statistics.dm b/code/orphaned_procs/statistics.dm similarity index 100% rename from code/orphaned procs/statistics.dm rename to code/orphaned_procs/statistics.dm diff --git a/html/changelog.html b/html/changelog.html index f22bf3dff95..9a22fc9aeff 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -55,6 +55,16 @@ -->
+

24 May 2016

+

Joan updated:

+
    +
  • You can no longer scribe the Summon Nar-Sie rune under non-valid conditions.
  • +
+

Kiazusho updated:

+
    +
  • Added three new advanced syringes to R&D
  • +
+

23 May 2016

CoreOverload updated:

    @@ -565,37 +575,6 @@
    • Miners can now select explorer belts from the mining vendor using their vouchers, or mining points.
    - -

    26 April 2016

    -

    Iamgoofball & Super Aggro Crag updated:

    -
      -
    • Fluorosulfuric Acid now has doubled acid power.
    • -
    • Fluorosulfuric Acid now does BONUS burn damage scaling on the time it's been inside the consumer.
    • -
    -

    Joan updated:

    -
      -
    • Blobbernauts now spawn at half health.
    • -
    • Blobbernauts regeneration on blob is now 2.5 health per tick, from 5.
    • -
    • Blobbernauts now cost 40 resources to produce, from 30.
    • -
    • Factories supporting blobbernauts do not spawn spores.
    • -
    -

    Lati updated:

    -
      -
    • Syndicate bomb can be only delayed once by pulsing the delay wire. Time gained from this is changed from 10 seconds to 30 seconds.
    • -
    -

    TechnoAlchemisto updated:

    -
      -
    • Goliath plates can now be stacked.
    • -
    -

    phil235 updated:

    -
      -
    • More structures and machines are now breakable and/or destroyable with melee weapon. Additionally, Some structures and machines that are already breakable/destroyable are now affected by more attack methods (gun projectiles, thrown items, melee weapons, xeno/animal attacks, explosions).
    • -
    • Hitting any machine now always makes a sound.
    • -
    • You can hit closed closets/crates and doors by using an item on harm intent (but it doesn't actually hurt them).
    • -
    • you can now burst filled water balloons with sharp items.
    • -
    • Wooden barricade drops wood when destroyed.
    • -
    • Slot machines with coins inside are deconstructable now.
    • -
GoonStation 13 Development Team diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 5da3dc3e139..11ca7bebea3 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -5144,3 +5144,8 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - rscadd: 'Cookie Synthesizer: Self recharging RCD that prints out cookies!' - tweak: Nanotrasen scientists have added a hugging module to medical and peacekeeper cyborgs to boost emotions during human-cyborg interaction. +2016-05-24: + Joan: + - rscdel: You can no longer scribe the Summon Nar-Sie rune under non-valid conditions. + Kiazusho: + - rscadd: Added three new advanced syringes to R&D diff --git a/html/changelogs/AutoChangeLog-pr-16441.yml b/html/changelogs/AutoChangeLog-pr-16441.yml new file mode 100644 index 00000000000..54eaa1ec569 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-16441.yml @@ -0,0 +1,4 @@ +author: Xhuis +delete-after: True +changes: + - rscadd: "Purge all untruths and honor Ratvar." diff --git a/html/changelogs/AutoChangeLog-pr-17428.yml b/html/changelogs/AutoChangeLog-pr-17428.yml deleted file mode 100644 index 30de50a5d13..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17428.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Kiazusho -delete-after: True -changes: - - rscadd: "Added three new advanced syringes to R&D" diff --git a/html/changelogs/AutoChangeLog-pr-17864.yml b/html/changelogs/AutoChangeLog-pr-17864.yml deleted file mode 100644 index 3c9432f6c16..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17864.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: Joan -delete-after: True -changes: - - rscdel: "You can no longer scribe the Summon Nar-Sie rune under non-valid conditions." diff --git a/html/changelogs/AutoChangeLog-pr-17881.yml b/html/changelogs/AutoChangeLog-pr-17881.yml new file mode 100644 index 00000000000..423828cbef1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17881.yml @@ -0,0 +1,16 @@ +author: coiax +delete-after: True +changes: + - bugfix: "Fixes bug where bolt of change to the host would kill an attached +guardian." + - bugfix: "Fixes bug where bolt of change to laughter demon would not release +its friends." + - bugfix: "Fixes bug where bolt of change to morphling would not release its +contents." + - bugfix: "Fixes bug where bolt of change transforming someone into a drone +would not give them hacked laws and vision." + - bugfix: "Blobbernauts created by a staff of change are now \"independent\" and +will not decay if seperated from a blob or missing a factory." + - rscadd: "Independent blobbernauts added to gold slime core spawn pool." + - rscadd: "Medical scanners now inform the user if the dead subject is +within the (currently) 120 second defib window." diff --git a/html/changelogs/AutoChangeLog-pr-17890.yml b/html/changelogs/AutoChangeLog-pr-17890.yml new file mode 100644 index 00000000000..0681230702f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17890.yml @@ -0,0 +1,8 @@ +author: coiax +delete-after: True +changes: + - rscadd: "Ghosts now have the \"Restore Character Name\" verb, which will set +their ghost appearance and dead chat name to their character preferences." + - bugfix: "Mob spawners now give any generated names to the mind of the +spawned mob, meaning ghosts will have the name they had in life." + - bugfix: "Fixes interaction with envy's knife and lavaland spawns." diff --git a/icons/effects/400x400.dmi b/icons/effects/400x400.dmi new file mode 100644 index 00000000000..c3fe60bc09c Binary files /dev/null and b/icons/effects/400x400.dmi differ diff --git a/icons/mob/actions.dmi b/icons/mob/actions.dmi index f4b379bd074..d9724cd6f16 100644 Binary files a/icons/mob/actions.dmi and b/icons/mob/actions.dmi differ diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm index b2cde1ea35b..c27f5c38893 100644 --- a/interface/stylesheet.dm +++ b/interface/stylesheet.dm @@ -73,6 +73,10 @@ h1.alert, h2.alert {color: #000000;} .holoparasite {color: #35333a;} .holoparasitebold {color: #35333a; font-weight: bold;} +.brass {color: #BE8700;} +.heavy_brass {color: #BE8700; font-weight: bold; font-style: italic;} +.large_brass {color: #BE8700; font-size: 3;} + .revennotice {color: #1d2953;} .revenboldnotice {color: #1d2953; font-weight: bold;} .revenbignotice {color: #1d2953; font-weight: bold; font-size: 3;} @@ -93,7 +97,7 @@ h1.alert, h2.alert {color: #000000;} .papyrus {font-family: "Papyrus", cursive, sans-serif;} .robot {font-family: "Courier New", cursive, sans-serif;} -.command_headset {font-weight: bold; font-size: 3; } +.command_headset {font-weight: bold; font-size: 3;} .big {font-size: 3;} .reallybig {font-size: 4;} .greentext {color: #00FF00; font-size: 3;} diff --git a/sound/effects/clockcult_gateway_active.ogg b/sound/effects/clockcult_gateway_active.ogg new file mode 100644 index 00000000000..86487e3bdd4 Binary files /dev/null and b/sound/effects/clockcult_gateway_active.ogg differ diff --git a/sound/effects/clockcult_gateway_charging.ogg b/sound/effects/clockcult_gateway_charging.ogg new file mode 100644 index 00000000000..1c4e55538ce Binary files /dev/null and b/sound/effects/clockcult_gateway_charging.ogg differ diff --git a/sound/effects/clockcult_gateway_closing.ogg b/sound/effects/clockcult_gateway_closing.ogg new file mode 100644 index 00000000000..1c69f386834 Binary files /dev/null and b/sound/effects/clockcult_gateway_closing.ogg differ diff --git a/sound/effects/clockcult_gateway_disrupted.ogg b/sound/effects/clockcult_gateway_disrupted.ogg new file mode 100644 index 00000000000..7ab734dc6c8 Binary files /dev/null and b/sound/effects/clockcult_gateway_disrupted.ogg differ diff --git a/sound/effects/light_flicker.ogg b/sound/effects/light_flicker.ogg new file mode 100644 index 00000000000..58be2c6ebda Binary files /dev/null and b/sound/effects/light_flicker.ogg differ diff --git a/sound/effects/ratvar_rises.ogg b/sound/effects/ratvar_rises.ogg new file mode 100644 index 00000000000..6201ce790d6 Binary files /dev/null and b/sound/effects/ratvar_rises.ogg differ diff --git a/sound/magic/clockwork/anima_fragment_attack.ogg b/sound/magic/clockwork/anima_fragment_attack.ogg new file mode 100644 index 00000000000..2c539292cba Binary files /dev/null and b/sound/magic/clockwork/anima_fragment_attack.ogg differ diff --git a/sound/magic/clockwork/anima_fragment_death.ogg b/sound/magic/clockwork/anima_fragment_death.ogg new file mode 100644 index 00000000000..d209bff5a28 Binary files /dev/null and b/sound/magic/clockwork/anima_fragment_death.ogg differ diff --git a/sound/magic/clockwork/fellowship_armory.ogg b/sound/magic/clockwork/fellowship_armory.ogg new file mode 100644 index 00000000000..c108d4621c6 Binary files /dev/null and b/sound/magic/clockwork/fellowship_armory.ogg differ diff --git a/sound/magic/clockwork/invoke_general.ogg b/sound/magic/clockwork/invoke_general.ogg new file mode 100644 index 00000000000..6f0c0dd097a Binary files /dev/null and b/sound/magic/clockwork/invoke_general.ogg differ diff --git a/sound/magic/clockwork/narsie_attack.ogg b/sound/magic/clockwork/narsie_attack.ogg new file mode 100644 index 00000000000..89fac778058 Binary files /dev/null and b/sound/magic/clockwork/narsie_attack.ogg differ diff --git a/sound/magic/clockwork/ratvar_attack.ogg b/sound/magic/clockwork/ratvar_attack.ogg new file mode 100644 index 00000000000..1c17fe10510 Binary files /dev/null and b/sound/magic/clockwork/ratvar_attack.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 240016c3b9b..dd4828bf331 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -258,7 +258,7 @@ #include "code\game\sound.dm" #include "code\game\area\ai_monitored.dm" #include "code\game\area\areas.dm" -#include "code\game\area\Space Station 13 areas.dm" +#include "code\game\area\Space_Station_13_areas.dm" #include "code\game\gamemodes\antag_hud.dm" #include "code\game\gamemodes\antag_spawner.dm" #include "code\game\gamemodes\events.dm" @@ -307,6 +307,13 @@ #include "code\game\gamemodes\changeling\powers\strained_muscles.dm" #include "code\game\gamemodes\changeling\powers\tiny_prick.dm" #include "code\game\gamemodes\changeling\powers\transform.dm" +#include "code\game\gamemodes\clock_cult\__clock_defines.dm" +#include "code\game\gamemodes\clock_cult\clock_cult.dm" +#include "code\game\gamemodes\clock_cult\clock_mobs.dm" +#include "code\game\gamemodes\clock_cult\clock_objects.dm" +#include "code\game\gamemodes\clock_cult\clock_ratvar.dm" +#include "code\game\gamemodes\clock_cult\clock_scripture.dm" +#include "code\game\gamemodes\clock_cult\clock_unsorted.dm" #include "code\game\gamemodes\cult\cult.dm" #include "code\game\gamemodes\cult\cult_comms.dm" #include "code\game\gamemodes\cult\cult_items.dm" @@ -820,7 +827,7 @@ #include "code\modules\admin\stickyban.dm" #include "code\modules\admin\topic.dm" #include "code\modules\admin\watchlist.dm" -#include "code\modules\admin\DB ban\functions.dm" +#include "code\modules\admin\DB_ban\functions.dm" #include "code\modules\admin\permissionverbs\permissionedit.dm" #include "code\modules\admin\verbs\adminhelp.dm" #include "code\modules\admin\verbs\adminjump.dm" @@ -957,8 +964,8 @@ #include "code\modules\cargo\exports\weapons.dm" #include "code\modules\chatter\chatter.dm" #include "code\modules\client\asset_cache.dm" -#include "code\modules\client\client defines.dm" -#include "code\modules\client\client procs.dm" +#include "code\modules\client\client_defines.dm" +#include "code\modules\client\client_procs.dm" #include "code\modules\client\client_colour.dm" #include "code\modules\client\message.dm" #include "code\modules\client\preferences.dm" @@ -1080,53 +1087,53 @@ #include "code\modules\flufftext\Dreaming.dm" #include "code\modules\flufftext\Hallucination.dm" #include "code\modules\flufftext\TextFilters.dm" -#include "code\modules\food&drinks\food.dm" -#include "code\modules\food&drinks\pizzabox.dm" -#include "code\modules\food&drinks\drinks\drinks.dm" -#include "code\modules\food&drinks\drinks\drinks\bottle.dm" -#include "code\modules\food&drinks\drinks\drinks\drinkingglass.dm" -#include "code\modules\food&drinks\food\condiment.dm" -#include "code\modules\food&drinks\food\customizables.dm" -#include "code\modules\food&drinks\food\snacks.dm" -#include "code\modules\food&drinks\food\snacks_bread.dm" -#include "code\modules\food&drinks\food\snacks_burgers.dm" -#include "code\modules\food&drinks\food\snacks_cake.dm" -#include "code\modules\food&drinks\food\snacks_egg.dm" -#include "code\modules\food&drinks\food\snacks_meat.dm" -#include "code\modules\food&drinks\food\snacks_other.dm" -#include "code\modules\food&drinks\food\snacks_pastry.dm" -#include "code\modules\food&drinks\food\snacks_pie.dm" -#include "code\modules\food&drinks\food\snacks_pizza.dm" -#include "code\modules\food&drinks\food\snacks_salad.dm" -#include "code\modules\food&drinks\food\snacks_sandwichtoast.dm" -#include "code\modules\food&drinks\food\snacks_soup.dm" -#include "code\modules\food&drinks\food\snacks_spaghetti.dm" -#include "code\modules\food&drinks\food\snacks_vend.dm" -#include "code\modules\food&drinks\food\snacks\dough.dm" -#include "code\modules\food&drinks\food\snacks\meat.dm" -#include "code\modules\food&drinks\kitchen machinery\food_cart.dm" -#include "code\modules\food&drinks\kitchen machinery\gibber.dm" -#include "code\modules\food&drinks\kitchen machinery\icecream_vat.dm" -#include "code\modules\food&drinks\kitchen machinery\juicer.dm" -#include "code\modules\food&drinks\kitchen machinery\microwave.dm" -#include "code\modules\food&drinks\kitchen machinery\monkeyrecycler.dm" -#include "code\modules\food&drinks\kitchen machinery\processor.dm" -#include "code\modules\food&drinks\kitchen machinery\smartfridge.dm" -#include "code\modules\food&drinks\recipes\drinks_recipes.dm" -#include "code\modules\food&drinks\recipes\food_mixtures.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_bread.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_burger.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_cake.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_egg.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_meat.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_misc.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_pastry.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_pie.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_pizza.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_salad.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_sandwich.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_soup.dm" -#include "code\modules\food&drinks\recipes\tablecraft\recipes_spaghetti.dm" +#include "code\modules\food_and_drinks\food.dm" +#include "code\modules\food_and_drinks\pizzabox.dm" +#include "code\modules\food_and_drinks\drinks\drinks.dm" +#include "code\modules\food_and_drinks\drinks\drinks\bottle.dm" +#include "code\modules\food_and_drinks\drinks\drinks\drinkingglass.dm" +#include "code\modules\food_and_drinks\food\condiment.dm" +#include "code\modules\food_and_drinks\food\customizables.dm" +#include "code\modules\food_and_drinks\food\snacks.dm" +#include "code\modules\food_and_drinks\food\snacks_bread.dm" +#include "code\modules\food_and_drinks\food\snacks_burgers.dm" +#include "code\modules\food_and_drinks\food\snacks_cake.dm" +#include "code\modules\food_and_drinks\food\snacks_egg.dm" +#include "code\modules\food_and_drinks\food\snacks_meat.dm" +#include "code\modules\food_and_drinks\food\snacks_other.dm" +#include "code\modules\food_and_drinks\food\snacks_pastry.dm" +#include "code\modules\food_and_drinks\food\snacks_pie.dm" +#include "code\modules\food_and_drinks\food\snacks_pizza.dm" +#include "code\modules\food_and_drinks\food\snacks_salad.dm" +#include "code\modules\food_and_drinks\food\snacks_sandwichtoast.dm" +#include "code\modules\food_and_drinks\food\snacks_soup.dm" +#include "code\modules\food_and_drinks\food\snacks_spaghetti.dm" +#include "code\modules\food_and_drinks\food\snacks_vend.dm" +#include "code\modules\food_and_drinks\food\snacks\dough.dm" +#include "code\modules\food_and_drinks\food\snacks\meat.dm" +#include "code\modules\food_and_drinks\kitchen_machinery\food_cart.dm" +#include "code\modules\food_and_drinks\kitchen_machinery\gibber.dm" +#include "code\modules\food_and_drinks\kitchen_machinery\icecream_vat.dm" +#include "code\modules\food_and_drinks\kitchen_machinery\juicer.dm" +#include "code\modules\food_and_drinks\kitchen_machinery\microwave.dm" +#include "code\modules\food_and_drinks\kitchen_machinery\monkeyrecycler.dm" +#include "code\modules\food_and_drinks\kitchen_machinery\processor.dm" +#include "code\modules\food_and_drinks\kitchen_machinery\smartfridge.dm" +#include "code\modules\food_and_drinks\recipes\drinks_recipes.dm" +#include "code\modules\food_and_drinks\recipes\food_mixtures.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_bread.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_burger.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_cake.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_egg.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_meat.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_misc.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_pastry.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_pie.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_pizza.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_salad.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_sandwich.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_soup.dm" +#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_spaghetti.dm" #include "code\modules\games\cards.dm" #include "code\modules\games\cas.dm" #include "code\modules\holiday\easter.dm" @@ -1538,18 +1545,18 @@ #include "code\modules\power\tesla\coil.dm" #include "code\modules\power\tesla\energy_ball.dm" #include "code\modules\power\tesla\generator.dm" -#include "code\modules\procedural mapping\mapGenerator.dm" -#include "code\modules\procedural mapping\mapGeneratorModule.dm" -#include "code\modules\procedural mapping\mapGeneratorObj.dm" -#include "code\modules\procedural mapping\mapGeneratorReadme.dm" -#include "code\modules\procedural mapping\mapGeneratorModules\helpers.dm" -#include "code\modules\procedural mapping\mapGeneratorModules\nature.dm" -#include "code\modules\procedural mapping\mapGenerators\asteroid.dm" -#include "code\modules\procedural mapping\mapGenerators\cellular.dm" -#include "code\modules\procedural mapping\mapGenerators\lava_river.dm" -#include "code\modules\procedural mapping\mapGenerators\nature.dm" -#include "code\modules\procedural mapping\mapGenerators\shuttle.dm" -#include "code\modules\procedural mapping\mapGenerators\syndicate.dm" +#include "code\modules\procedural_mapping\mapGenerator.dm" +#include "code\modules\procedural_mapping\mapGeneratorModule.dm" +#include "code\modules\procedural_mapping\mapGeneratorObj.dm" +#include "code\modules\procedural_mapping\mapGeneratorReadme.dm" +#include "code\modules\procedural_mapping\mapGeneratorModules\helpers.dm" +#include "code\modules\procedural_mapping\mapGeneratorModules\nature.dm" +#include "code\modules\procedural_mapping\mapGenerators\asteroid.dm" +#include "code\modules\procedural_mapping\mapGenerators\cellular.dm" +#include "code\modules\procedural_mapping\mapGenerators\lava_river.dm" +#include "code\modules\procedural_mapping\mapGenerators\nature.dm" +#include "code\modules\procedural_mapping\mapGenerators\shuttle.dm" +#include "code\modules\procedural_mapping\mapGenerators\syndicate.dm" #include "code\modules\projectiles\ammunition.dm" #include "code\modules\projectiles\firing.dm" #include "code\modules\projectiles\gun.dm" @@ -1659,8 +1666,8 @@ #include "code\modules\research\xenobiology\xenobiology.dm" #include "code\modules\ruins\lavaland_ruin_code.dm" #include "code\modules\ruins\ruin_areas.dm" -#include "code\modules\security levels\keycard authentication.dm" -#include "code\modules\security levels\security levels.dm" +#include "code\modules\security_levels\keycard_authentication.dm" +#include "code\modules\security_levels\security_levels.dm" #include "code\modules\shuttle\assault_pod.dm" #include "code\modules\shuttle\computer.dm" #include "code\modules\shuttle\emergency.dm" @@ -1670,7 +1677,7 @@ #include "code\modules\shuttle\supply.dm" #include "code\modules\shuttle\syndicate.dm" #include "code\modules\shuttle\white_ship.dm" -#include "code\modules\space transition\space_transition.dm" +#include "code\modules\space_transition\space_transition.dm" #include "code\modules\spells\spell.dm" #include "code\modules\spells\spell_types\area_teleport.dm" #include "code\modules\spells\spell_types\barnyard.dm" @@ -1719,7 +1726,7 @@ #include "code\modules\surgery\generic_steps.dm" #include "code\modules\surgery\helpers.dm" #include "code\modules\surgery\implant_removal.dm" -#include "code\modules\surgery\limb augmentation.dm" +#include "code\modules\surgery\limb_augmentation.dm" #include "code\modules\surgery\lipoplasty.dm" #include "code\modules\surgery\organ_manipulation.dm" #include "code\modules\surgery\plastic_surgery.dm" @@ -1768,10 +1775,10 @@ #include "code\modules\vehicles\secway.dm" #include "code\modules\vehicles\speedbike.dm" #include "code\modules\vehicles\vehicle.dm" -#include "code\orphaned procs\AStar.dm" -#include "code\orphaned procs\dbcore.dm" -#include "code\orphaned procs\priority_announce.dm" -#include "code\orphaned procs\statistics.dm" +#include "code\orphaned_procs\AStar.dm" +#include "code\orphaned_procs\dbcore.dm" +#include "code\orphaned_procs\priority_announce.dm" +#include "code\orphaned_procs\statistics.dm" #include "interface\interface.dm" #include "interface\stylesheet.dm" #include "interface\skin.dmf" diff --git a/tools/Runtime Condenser/Main.cpp b/tools/Runtime Condenser/Main.cpp index f5410bd7981..c8127867645 100644 --- a/tools/Runtime Condenser/Main.cpp +++ b/tools/Runtime Condenser/Main.cpp @@ -1,7 +1,7 @@ /* Runtime Condenser by Nodrak * Cleaned up and refactored by MrStonedOne * This will sum up identical runtimes into one, giving a total of how many times it occured. The first occurance - * of the runtime will log the proc, source, usr and src, the rest will just add to the total. Infinite loops will + * of the runtime will log the source, usr and src, the rest will just add to the total. Infinite loops will * also be caught and displayed (if any) above the list of runtimes. * * How to use: @@ -22,11 +22,19 @@ #include #include +#include +#include #include #include #include #include #include +#include + + +#define PROGRESS_FPS 10 +#define PROGRESS_BAR_INNER_WIDTH 50 +#define LINEBUFFER (512*1024) //512KiB using namespace std; @@ -44,182 +52,248 @@ struct harddel { unsigned int count; }; //What we use to read input -string lastLine = ""; -string currentLine = ""; -string nextLine = ""; +string * lastLine = new string(); +string * currentLine = new string(); +string * nextLine = new string(); //Stores lines we want to keep to print out -unordered_map storedRuntime; -unordered_map storedInfiniteLoop; -unordered_map storedHardDel; +unordered_map storedRuntime; +unordered_map storedInfiniteLoop; +unordered_map storedHardDel; //Stat tracking stuff for output unsigned int totalRuntimes = 0; -unsigned int totalUniqueRuntimes = 0; unsigned int totalInfiniteLoops = 0; -unsigned int totalUniqueInfiniteLoops = 0; unsigned int totalHardDels = 0; -unsigned int totalUniqueHardDels = 0; -//Misc -runtime* currentRuntime = NULL; //for storing the current runtime when we want to read the extra info on the next lines. +bool endofbuffer = false; //like substr, but returns an empty string if the string is smaller then start, rather then an exception. -inline string safe_substr(string S, size_t start = 0, size_t end = string::npos) { - if (start > S.length()) - start = S.length(); - return S.substr(start, end); +inline string safe_substr(string * S, size_t start = 0, size_t end = string::npos) { + if (start > S->length()) + start = S->length(); + return S->substr(start, end); } -inline void forward_progress(ifstream &inputFile) { +//getline() is slow as fucking balls. this is quicker because we prefill a buffer rather then read 1 byte at a time searching for newlines, lowering on i/o calls and overhead. (110MB/s vs 40MB/s on a 1.8GB file pre-filled into the disk cache) +//if i wanted to make it even faster, I'd use a reading thread, a new line searching thread, another thread or four for searching for runtimes in the list to see if they are unique, and finally the main thread for displaying the progress bar. but fuck that noise. +inline string * readline(FILE * f) { + static char buf[LINEBUFFER]; + static size_t pos = 0; + static size_t size = 0; + + for (size_t i = pos; i < LINEBUFFER; i++) { + char c = buf[i]; + if (i >= size && (pos || i < LINEBUFFER-1)) { + if (feof(f) || ferror(f)) + break; + if (size && pos) { //move current stuff to start of buffer + size -= pos; + i -= pos; + memmove(buf, &buf[pos], size); + } + //fill remaining buffer + size += fread(&buf[i], 1, LINEBUFFER-size-1, f); + pos = 0; + c = buf[i]; + } + if (c == '\n') { + //trim off any newlines from the start + while (i > pos && (buf[pos] == '\r' || buf[pos] == '\n')) + pos++; + string * s = new string(&buf[pos], i-pos); + pos = i+1; + return s; + } + + } + string * s = new string(&buf[pos], size-pos); + pos = 0; + size = 0; + endofbuffer = true; + return s; +} + +inline void forward_progress(FILE * inputFile) { + delete(lastLine); lastLine = currentLine; currentLine = nextLine; - getline(inputFile, nextLine); + nextLine = readline(inputFile); //strip out any timestamps. - if (nextLine.length() >= 10) - if (nextLine[0] == '[' && nextLine[3] == ':' && nextLine[6] == ':' && nextLine[9] == ']') - nextLine = nextLine.substr(10); -} + if (nextLine->length() >= 10) { + if ((*nextLine)[0] == '[' && (*nextLine)[3] == ':' && (*nextLine)[6] == ':' && (*nextLine)[9] == ']') + nextLine->erase(0, 10); + } +} +//deallocates to, copys from to to. +inline void string_send(string * &from, string * &to) { + delete(to); + to = new string(*from); +} +inline void printprogressbar(unsigned short progress /*as percent*/) { + double const modifer = 100.0L/(double)PROGRESS_BAR_INNER_WIDTH; + size_t bars = (double)progress/modifer; + cout << "\r[" << string(bars, '=') << ((progress < 100) ? ">" : "") << string(PROGRESS_BAR_INNER_WIDTH-(bars+((progress < 100) ? 1 : 0)), ' ') << "] " << progress << "%"; + cout.flush(); +} + bool readFromFile() { //Open file to read - ifstream inputFile("Input.txt"); + FILE * inputFile = fopen("Input.txt", "r"); - if (inputFile.is_open()) { - while (!inputFile.eof()) { //Until end of file - //Update our lines - forward_progress(inputFile); + if (ferror(inputFile)) + return false; - //After finding a new runtime, check to see if there are extra values to store - if (currentRuntime) { - //Skip ahead + fseek(inputFile, 0, SEEK_END); + long long fileLength = ftell(inputFile); + fseek(inputFile, 0, SEEK_SET); + clock_t nextupdate = clock(); + if (feof(inputFile)) + return false; //empty file + do { + //Update our lines + forward_progress(inputFile); + //progress bar + if (clock() >= nextupdate) { + int dProgress = (int)(((long double)ftell(inputFile) / (long double)fileLength) * 100.0L); + printprogressbar(dProgress); + nextupdate = clock() + (CLOCKS_PER_SEC/PROGRESS_FPS); + } + //Found a runtime! + if (safe_substr(currentLine, 0, 14) == "runtime error:") { + if (currentLine->length() <= 17) { //empty runtime, check next line. + //runtime is on the line before this one. (byond bug) + if (nextLine->length() < 2) { + string_send(lastLine, nextLine); + } forward_progress(inputFile); - - //If we find this, we have new stuff to store - if (safe_substr(nextLine, 2, 4) == "usr:") { - //Skip ahead - forward_progress(inputFile); - - //Store more info - currentRuntime->source = lastLine; - currentRuntime->usr = currentLine; - currentRuntime->src = nextLine; - - //Skip ahead again - forward_progress(inputFile); - - if (safe_substr(nextLine, 2, 8) == "src.loc:") - currentRuntime->loc = nextLine; - - } - currentRuntime = NULL; + string * tmp = new string("runtime error: " + *currentLine); + string_send(tmp, currentLine); + delete(tmp); } - - //Found an infinite loop! - if (safe_substr(currentLine, 0, 23) == "Infinite loop suspected" || safe_substr(currentLine, 0, 31) == "Maximum recursion level reached") { + //we assign this to the right container in a moment. + unordered_map * storage_container; + + //runtime is actually an infinite loop + if (safe_substr(currentLine, 15, 23) == "Infinite loop suspected" || safe_substr(currentLine, 15, 31) == "Maximum recursion level reached") { + //use our infinite loop container. + storage_container = &storedInfiniteLoop; totalInfiniteLoops++; - runtime* R = storedInfiniteLoop[currentLine]; - if (!R || R->text != currentLine) { - R = new runtime; - storedInfiniteLoop[currentLine] = R; - R->text = currentLine; - forward_progress(inputFile); - R->proc = nextLine; - R->count = 1; - currentRuntime = R; - totalUniqueInfiniteLoops++; - } else { //existed already - R->count++; - } - - } - //Found a runtime! - else if (safe_substr(currentLine, 0, 14) == "runtime error:") { - if (currentLine.length() <= 17) { //empty runtime, check next line. - if (nextLine.length() < 2) //runtime is on the line before this one. (byond bug) - nextLine = lastLine; - forward_progress(inputFile); - currentLine = "runtime error: " + currentLine; - } + // skip the line about world.loop_checks + forward_progress(inputFile); + string_send(lastLine, currentLine); + } else { + //use the runtime container + storage_container = &storedRuntime; totalRuntimes++; - runtime* R = storedRuntime[currentLine]; - if (!R || R->text != currentLine) { - R = new runtime; - storedRuntime[currentLine] = R; - R->text = currentLine; - R->proc = nextLine; - R->count = 1; - currentRuntime = R; - totalUniqueRuntimes++; - } else { //existed already - R->count++; - } } - //Found a hard del! - else if (safe_substr(currentLine, 0, 7) == "Path : ") { - string deltype = safe_substr(currentLine, 7); - if (deltype.substr(deltype.size()-1,1) == " ") //some times they have a single trailing space. - deltype = deltype.substr(0, deltype.size()-1); - - unsigned int failures = strtoul(safe_substr(nextLine, 11).c_str(), NULL, 10); - if (failures <= 0) - continue; - - totalHardDels += failures; - harddel * D = storedHardDel[deltype]; - if (!D || D->type != deltype) { - D = new harddel; - storedHardDel[deltype] = D; - D->type = deltype; - D->count = failures; - totalUniqueHardDels++; - } else { - D->count += failures; + string key = *currentLine; + bool procfound = false; //so other things don't have to bother checking for this again. + if (safe_substr(nextLine, 0, 10) == "proc name:") { + key += *nextLine; + procfound = true; + } + + //(get the address of a runtime from (a pointer to a container of runtimes)) to then store in a pointer to a runtime. + //(and who said pointers were hard.) + runtime* R = &((*storage_container)[key]); + + //new + if (R->text != *currentLine) { + R->text = *currentLine; + if (procfound) { + R->proc = *nextLine; + forward_progress(inputFile); } + R->count = 1; + + //search for source file info + if (safe_substr(nextLine, 2, 12) == "source file:") { + R->source = *nextLine; + //skip again + forward_progress(inputFile); + } + //If we find this, we have new stuff to store + if (safe_substr(nextLine, 2, 4) == "usr:") { + forward_progress(inputFile); + forward_progress(inputFile); + //Store more info + R->usr = *lastLine; + R->src = *currentLine; + if (safe_substr(nextLine, 2, 8) == "src.loc:") { + R->loc = *nextLine; + forward_progress(inputFile); + } + } + + } else { //existed already + R->count++; + if (procfound) + forward_progress(inputFile); + } + + } else if (safe_substr(currentLine, 0, 7) == "Path : ") { + string deltype = safe_substr(currentLine, 7); + if (deltype.substr(deltype.size()-1,1) == " ") //some times they have a single trailing space. + deltype = deltype.substr(0, deltype.size()-1); + + unsigned int failures = strtoul(safe_substr(nextLine, 11).c_str(), NULL, 10); + if (failures <= 0) + continue; + + totalHardDels += failures; + harddel* D = &storedHardDel[deltype]; + if (D->type != deltype) { + D->type = deltype; + D->count = failures; + } else { + D->count += failures; } } - } else { - return false; - } + } while (!feof(inputFile) || !endofbuffer); //Until end of file + printprogressbar(100); + cout << endl; return true; } -bool runtimeComp(const runtime* a, const runtime* b) { - return a->count > b->count; +bool runtimeComp(const runtime &a, const runtime &b) { + return a.count > b.count; } -bool hardDelComp(const harddel* a, const harddel* b) { - return a->count > b->count; +bool hardDelComp(const harddel &a, const harddel &b) { + return a.count > b.count; } bool writeToFile() { //Open and clear the file ofstream outputFile("Output.txt", ios::trunc); - + if(outputFile.is_open()) { - outputFile << "Note: The proc name, source file, src and usr are all from the FIRST of the identical runtimes. Everything else is cropped.\n\n"; - if(totalUniqueInfiniteLoops > 0) - outputFile << "Total unique infinite loops: " << totalUniqueInfiniteLoops << endl; + outputFile << "Note: The source file, src and usr are all from the FIRST of the identical runtimes. Everything else is cropped.\n\n"; + if(storedInfiniteLoop.size() > 0) + outputFile << "Total unique infinite loops: " << storedInfiniteLoop.size() << endl; if(totalInfiniteLoops > 0) outputFile << "Total infinite loops: " << totalInfiniteLoops << endl << endl; - outputFile << "Total unique runtimes: " << totalUniqueRuntimes << endl; + outputFile << "Total unique runtimes: " << storedRuntime.size() << endl; outputFile << "Total runtimes: " << totalRuntimes << endl << endl; - if(totalUniqueHardDels > 0) - outputFile << "Total unique hard deletions: " << totalUniqueHardDels << endl; + if(storedHardDel.size() > 0) + outputFile << "Total unique hard deletions: " << storedHardDel.size() << endl; if(totalHardDels > 0) outputFile << "Total hard deletions: " << totalHardDels << endl << endl; //If we have infinite loops, display them first. - if(totalInfiniteLoops > 0) { - vector infiniteLoops; + if(storedInfiniteLoop.size() > 0) { + vector infiniteLoops; infiniteLoops.reserve(storedInfiniteLoop.size()); - for (unordered_map::iterator it=storedInfiniteLoop.begin(); it != storedInfiniteLoop.end(); it++) + for (unordered_map::iterator it=storedInfiniteLoop.begin(); it != storedInfiniteLoop.end(); it++) infiniteLoops.push_back(it->second); + storedInfiniteLoop.clear(); sort(infiniteLoops.begin(), infiniteLoops.end(), runtimeComp); outputFile << "** Infinite loops **"; for (int i=0; i < infiniteLoops.size(); i++) { - runtime* R = infiniteLoops[i]; + runtime* R = &infiniteLoops[i]; outputFile << endl << endl << "The following infinite loop has occurred " << R->count << " time(s).\n"; outputFile << R->text << endl; if(R->proc.length()) @@ -239,13 +313,14 @@ bool writeToFile() { //Do runtimes next outputFile << "** Runtimes **"; - vector runtimes; + vector runtimes; runtimes.reserve(storedRuntime.size()); - for (unordered_map::iterator it=storedRuntime.begin(); it != storedRuntime.end(); it++) + for (unordered_map::iterator it=storedRuntime.begin(); it != storedRuntime.end(); it++) runtimes.push_back(it->second); + storedRuntime.clear(); sort(runtimes.begin(), runtimes.end(), runtimeComp); for (int i=0; i < runtimes.size(); i++) { - runtime* R = runtimes[i]; + runtime* R = &runtimes[i]; outputFile << endl << endl << "The following runtime has occurred " << R->count << " time(s).\n"; outputFile << R->text << endl; if(R->proc.length()) @@ -264,13 +339,14 @@ bool writeToFile() { //and finally, hard deletes if(totalHardDels > 0) { outputFile << endl << "** Hard deletions **"; - vector hardDels; + vector hardDels; hardDels.reserve(storedHardDel.size()); - for (unordered_map::iterator it=storedHardDel.begin(); it != storedHardDel.end(); it++) + for (unordered_map::iterator it=storedHardDel.begin(); it != storedHardDel.end(); it++) hardDels.push_back(it->second); + storedHardDel.clear(); sort(hardDels.begin(), hardDels.end(), hardDelComp); for(int i=0; i < hardDels.size(); i++) { - harddel* D = hardDels[i]; + harddel* D = &hardDels[i]; outputFile << endl << D->type << " - " << D->count << " time(s).\n"; } } @@ -282,28 +358,30 @@ bool writeToFile() { } int main() { + ios_base::sync_with_stdio(false); + ios::sync_with_stdio(false); char exit; //Used to stop the program from immediately exiting - + cout << "Reading input.\n"; if(readFromFile()) { cout << "Input read successfully!\n"; } else { cout << "Input failed to open, shutting down.\n"; cout << "\nEnter any letter to quit.\n"; - cin >> exit; + exit = cin.get(); return 1; } - + cout << "Writing output.\n"; if(writeToFile()) { cout << "Output was successful!\n"; cout << "\nEnter any letter to quit.\n"; - cin >> exit; + exit = cin.get(); return 0; } else { cout << "The output file could not be opened, shutting down.\n"; cout << "\nEnter any letter to quit.\n"; - cin >> exit; + exit = cin.get(); return 0; } diff --git a/tools/Runtime Condenser/Output.txt b/tools/Runtime Condenser/Output.txt index 1bb4a90c78f..914571fea55 100644 --- a/tools/Runtime Condenser/Output.txt +++ b/tools/Runtime Condenser/Output.txt @@ -1,190 +1,438 @@ -Note: The proc name, source file, src and usr are all from the FIRST of the identical runtimes. Everything else is cropped. +Note: The source file, src and usr are all from the FIRST of the identical runtimes. Everything else is cropped. -Total unique runtimes: 25 -Total runtimes: 767 +Total unique runtimes: 28 +Total runtimes: 124723 + +Total unique hard deletions: 108 +Total hard deletions: 1080 ** Runtimes ** -The following runtime has occured 269 time(s). -runtime error: Cannot read null.viruses -proc name: cure (/datum/disease/proc/cure) - source file: disease.dm,156 +The following runtime has occurred 123121 time(s). +runtime error: Component is missing a pipenet! Rebuilding... + + +The following runtime has occurred 709 time(s). +runtime error: wrong type of value for list +proc name: updateghostimages (/mob/dead/observer/proc/updateghostimages) + source file: observer.dm,488 + usr: (src) + src: Argon IXV (/mob/dead/observer) + src.loc: the asteroid sand (217,137,8) (/turf/open/floor/plating/asteroid) + + +The following runtime has occurred 696 time(s). +runtime error: Cannot read null.air +proc name: return air (/obj/machinery/atmospherics/pipe/return_air) + source file: pipes.dm,58 usr: null - src: Space Retrovirus (/datum/disease/dnaspread) + src: the air supply pipe (/obj/machinery/atmospherics/pipe/manifold4w/supply/visible) + src.loc: the floor (169,130,1) (/turf/open/floor/plasteel) -The following runtime has occured 199 time(s). -runtime error: Cannot read null.has_gravity -proc name: throw at (/atom/movable/proc/throw_at) - source file: throwing.dm,194 - usr: Reese Marcotte (/mob/living/carbon/human) - src: Reese Marcotte (/mob/living/carbon/human) - - -The following runtime has occured 165 time(s). -runtime error: Cannot read null.slot_flags -proc name: db click (/mob/living/carbon/human/db_click) - source file: inventory.dm,1083 - usr: Puke Chunks (/mob/living/carbon/human) - src: Puke Chunks (/mob/living/carbon/human) - - -The following runtime has occured 25 time(s). -runtime error: Cannot read null.flags -proc name: relaymove (/obj/effect/dummy/spell_jaunt/relaymove) - - -The following runtime has occured 24 time(s). -runtime error: Cannot read null.len -proc name: mergeRecordLists (/proc/mergeRecordLists) - source file: helpers.dm,260 - usr: Wintermute (/mob/living/silicon/ai) +The following runtime has occurred 48 time(s). +runtime error: bad resource file +proc name: send asset (/proc/send_asset) + source file: asset_cache.dm,44 + usr: Mariabella Pycroft-Crane (/mob/living/carbon/human) src: null -The following runtime has occured 20 time(s). -runtime error: Cannot read null.total_volume -proc name: attackby (/obj/machinery/reagentgrinder/attackby) - source file: Chemistry-Machinery.dm,751 - usr: Reese Marcotte (/mob/living/carbon/human) - src: All-In-One Grinder (/obj/machinery/reagentgrinder) - - -The following runtime has occured 16 time(s). -runtime error: Cannot read null.broadcasting -proc name: radio menu (/mob/living/silicon/robot/proc/radio_menu) - source file: robot.dm,857 - usr: Engineering Cyborg -432 (/mob/living/silicon/robot) - src: Engineering Cyborg -432 (/mob/living/silicon/robot) - - -The following runtime has occured 7 time(s). -runtime error: unexpected stat -proc name: Stat (/mob/living/silicon/robot/Stat) - source file: robot.dm,227 - usr: Michigan Slim (/mob/living/carbon/human) - src: Engineering Cyborg -133 (/mob/living/silicon/robot) - - -The following runtime has occured 7 time(s). -runtime error: Cannot read null.current -proc name: check completion (/datum/objective/download/check_completion) - source file: objective.dm,411 - usr: null - src: /datum/objective/download (/datum/objective/download) - - -The following runtime has occured 6 time(s). -runtime error: Cannot create objects of type null. -proc name: Topic (/obj/machinery/computer/rdconsole/Topic) - source file: rdconsole.dm,381 - usr: Matthew Hoff (/mob/living/carbon/human) - src: Core R&D Console (/obj/machinery/computer/rdconsole/core) - - -The following runtime has occured 6 time(s). -runtime error: Cannot read null.fields -proc name: mergeRecordLists (/proc/mergeRecordLists) - source file: helpers.dm,263 - usr: Wintermute (/mob/living/silicon/ai) - src: null - - -The following runtime has occured 3 time(s). -runtime error: undefined variable /client/var/loc -proc name: get turf (/proc/get_turf) - source file: helper_procs.dm,38 - usr: the ghost (/mob/dead/observer) - src: null - - -The following runtime has occured 3 time(s). -runtime error: Cannot read null.nodes -proc name: merge powernets (/datum/powernet/proc/merge_powernets) - source file: power.dm,401 - usr: Darin Keppel (/mob/living/carbon/human) - src: /datum/powernet (/datum/powernet) - - -The following runtime has occured 3 time(s). -runtime error: Cannot read null.reagents -proc name: fire syringe (/obj/item/weapon/gun/syringe/proc/fire_syringe) - - -The following runtime has occured 2 time(s). -runtime error: undefined proc or verb /obj/machinery/space_heater/attack(). - - - -The following runtime has occured 2 time(s). +The following runtime has occurred 30 time(s). runtime error: list index out of bounds -proc name: mergeConnectedNetworksOnTurf (/obj/structure/cable/proc/mergeConnectedNetworksOnTurf) - source file: cable.dm,520 - usr: Josue Sandford (/mob/living/carbon/human) - src: the power cable (/obj/structure/cable) - - -The following runtime has occured 2 time(s). -runtime error: Cannot read null.loc -proc name: Life (/mob/living/simple_animal/corgi/Ian/Life) - source file: corgi.dm,304 +proc name: GetGreaterChild (/Heap/proc/GetGreaterChild) + source file: heap.dm,62 usr: null - src: Captain Ian (/mob/living/simple_animal/corgi/Ian) + src: /Heap (/Heap) -The following runtime has occured 1 time(s). -runtime error: undefined proc or verb /obj/machinery/portable_atmospherics/canister/toxins/attack(). +The following runtime has occurred 21 time(s). +runtime error: undefined proc or verb /turf/closed/wall/shuttle/high pressure movements(). - -The following runtime has occured 1 time(s). -runtime error: undefined variable /datum/preferences/var/fields -proc name: Topic (/obj/machinery/computer/cloning/Topic) - source file: cloning.dm,370 - usr: Logan Graves (/mob/living/carbon/human) - src: Cloning console (/obj/machinery/computer/cloning) +The following runtime has occurred 15 time(s). +runtime error: Simple animal being instantiated in nullspace +proc name: stack trace (/proc/stack_trace) + source file: unsorted.dm,1358 + usr: the Securitron (/mob/living/simple_animal/bot/secbot) + src: null -The following runtime has occured 1 time(s). -runtime error: undefined variable /turf/simulated/floor/plating/var/mineral -proc name: attackby (/turf/simulated/wall/attackby) - source file: turf.dm,600 - usr: Monte Smail (/mob/living/carbon/human) - src: the plating (176,172,5) (/turf/simulated/floor/plating) - - -The following runtime has occured 1 time(s). -runtime error: Cannot read null.blood_DNA -proc name: update inv w uniform (/mob/living/carbon/human/update_inv_w_uniform) - source file: update_icons.dm,372 +The following runtime has occurred 12 time(s). +runtime error: Cannot read null.viewavail +proc name: record (/obj/machinery/computer/monitor/proc/record) + source file: monitor.dm,41 usr: null - src: Chip Harshman (/mob/living/carbon/human) + src: Engineering Power Monitoring C... (/obj/machinery/computer/monitor) + src.loc: space (154,151,1) (/turf/open/space) -The following runtime has occured 1 time(s). -runtime error: Cannot execute null.dropped(). -proc name: drop l hand (/mob/proc/drop_l_hand) - source file: inventory.dm,104 - usr: Jeb Stone (/mob/living/carbon/human) - src: Jeb Stone (/mob/living/carbon/human) +The following runtime has occurred 11 time(s). +runtime error: Cannot read null.selection_color +proc name: SetChoices (/datum/preferences/proc/SetChoices) + source file: preferences.dm,543 + usr: Sweetestbro (/mob/new_player) + src: /datum/preferences (/datum/preferences) -The following runtime has occured 1 time(s). -runtime error: Cannot read null.w_class -proc name: attackby (/obj/item/weapon/storage/attackby) - source file: storage.dm,187 - usr: Samuel York (/mob/living/carbon/human) - src: the backpack (/obj/item/weapon/storage/backpack) +The following runtime has occurred 10 time(s). +runtime error: Cannot execute null.add turf(). +proc name: process cell (/turf/open/process_cell) + source file: LINDA_turf_tile.dm,172 + usr: null + src: the plating (102,135,1) (/turf/open/floor/plating) -The following runtime has occured 1 time(s). -runtime error: Cannot read null.amount -proc name: attackby (/obj/machinery/constructable_frame/machine_frame/attackby) - source file: constructable_frame.dm,27 - usr: Quinton Bould (/mob/living/carbon/human) - src: the machine frame (/obj/machinery/constructable_frame/machine_frame) +The following runtime has occurred 9 time(s). +runtime error: Cannot read null.thrownby +proc name: hitby (/mob/living/carbon/human/hitby) + source file: human_defense.dm,353 + usr: the monkey (662) (/mob/living/carbon/monkey) + src: Sydney Hujsak (/mob/living/carbon/human) -The following runtime has occured 1 time(s). -runtime error: Cannot modify null.status. -proc name: death (/mob/living/silicon/robot/death) +The following runtime has occurred 8 time(s). +runtime error: Cannot execute null.HasProximity(). +proc name: Entered (/turf/Entered) + source file: turf.dm,95 + usr: 0 + src: the floor (99,147,1) (/turf/open/floor/plasteel) + + +The following runtime has occurred 5 time(s). +runtime error: bad client +proc name: open (/datum/browser/proc/open) + source file: browser.dm,106 + usr: Timothy Catleay (/mob/living/carbon/human) + src: /datum/browser (/datum/browser) + + +The following runtime has occurred 4 time(s). +runtime error: return_file_text(): File not found + + +The following runtime has occurred 4 time(s). +runtime error: Cannot execute null.merge(). +proc name: assume air (/turf/open/assume_air) + source file: LINDA_turf_tile.dm,56 + usr: null + src: the engraved floor (95,49,5) (/turf/open/floor/engine/cult) + + +The following runtime has occurred 3 time(s). +runtime error: Cannot read null.gases +proc name: tile graphic (/turf/open/proc/tile_graphic) + source file: LINDA_turf_tile.dm,110 + usr: null + src: the floor (81,52,5) (/turf/open/floor/plasteel/freezer) + + +The following runtime has occurred 3 time(s). +runtime error: undefined proc or verb /turf/closed/wall/high pressure movements(). + + +The following runtime has occurred 2 time(s). +runtime error: Cannot modify null.loc. +proc name: ui act (/obj/machinery/suit_storage_unit/ui_act) + source file: suit_storage_unit.dm,359 + usr: Cy Carter (/mob/living/carbon/human) + src: the suit storage unit (/obj/machinery/suit_storage_unit/standard_unit) + + +The following runtime has occurred 2 time(s). +runtime error: Cannot execute null.remove(). +proc name: remove air (/turf/open/remove_air) + source file: LINDA_turf_tile.dm,62 + usr: null + src: the engraved floor (171,210,5) (/turf/open/floor/engine/cult) + + +The following runtime has occurred 2 time(s). +runtime error: Cannot execute null.return pressure(). +proc name: update pressure (/obj/mecha/working/ripley/proc/update_pressure) + source file: ripley.dm,150 + usr: 0 + src: the APLU \"Ripley\" (/obj/mecha/working/ripley) + src.loc: the floor (88,45,5) (/turf/open/floor/plasteel/freezer) + + +The following runtime has occurred 1 time(s). +runtime error: list index out of bounds +proc name: Topic (/datum/song/Topic) + source file: musician.dm,208 + usr: Francisco Luque (/mob/living/carbon/human) + src: Untitled (/datum/song/handheld) + + +The following runtime has occurred 1 time(s). +runtime error: undefined proc or verb /turf/closed/wall/r_wall/high pressure movements(). + + +The following runtime has occurred 1 time(s). +runtime error: bad client +proc name: show (/datum/html_interface/proc/show) + source file: html_interface.dm,227 + usr: Mariabella Pycroft-Crane (/mob/living/carbon/human) + src: /datum/html_interface/nanotras... (/datum/html_interface/nanotrasen) + + +The following runtime has occurred 1 time(s). +runtime error: Cannot execute null.GetAtmosAdjacentTurfs(). +proc name: spread smoke (/obj/effect/particle_effect/smoke/proc/spread_smoke) + source file: effects_smoke.dm,74 + usr: null + src: the smoke (/obj/effect/particle_effect/smoke) + src.loc: null + + +The following runtime has occurred 1 time(s). +runtime error: Cannot read null.pipe_vision_img +proc name: add ventcrawl (/mob/living/proc/add_ventcrawl) + source file: ventcrawling.dm,94 + usr: (src) + src: the monkey (809) (/mob/living/carbon/monkey) + src.loc: the Cloning Lab vent pump #1 (/obj/machinery/atmospherics/components/unary/vent_pump) + + +The following runtime has occurred 1 time(s). +runtime error: Cannot execute null.s click(). +proc name: Click (/obj/screen/grab/Click) + source file: screen_objects.dm,167 + usr: Hisstian Weston Chandler (/mob/living/carbon/human) + src: the disarm/kill (/obj/screen/grab) + + +The following runtime has occurred 1 time(s). +runtime error: list index out of bounds +proc name: mod list add (/client/proc/mod_list_add) + source file: modifyvariables.dm,161 + usr: the potted plant (/mob/dead/observer) + src: MimicFaux (/client) + + +The following runtime has occurred 1 time(s). +runtime error: Cannot execute null.get reagent amount(). +proc name: get fuel (/obj/item/weapon/weldingtool/proc/get_fuel) + source file: tools.dm,334 + usr: null + src: the welding tool (/obj/item/weapon/weldingtool) + src.loc: null + + + +** Hard deletions ** +/obj/structure/lattice - 499 time(s). + +/mob/new_player - 91 time(s). + +/image - 87 time(s). + +/mob/dead/observer - 42 time(s). + +/datum/mind - 36 time(s). + +/obj/item/radio/integrated/signal - 31 time(s). + +/obj/effect/ebeam - 24 time(s). + +/obj/machinery/camera - 15 time(s). + +/obj/machinery/atmospherics/components/binary/pump - 14 time(s). + +/obj/effect/decal/cleanable/trail_holder - 10 time(s). + +/obj/screen/buildmode/bdir - 10 time(s). + +/obj/screen/buildmode/help - 10 time(s). + +/obj/screen/buildmode/mode - 10 time(s). + +/obj/screen/buildmode/quit - 10 time(s). + +/obj/effect/landmark/start - 8 time(s). + +/obj/machinery/status_display - 7 time(s). + +/obj/item/stack/sheet/cardboard - 7 time(s). + +/datum/gas_mixture - 6 time(s). + +/obj/machinery/requests_console - 6 time(s). + +/obj/machinery/airalarm - 5 time(s). + +/obj/machinery/atmospherics/components/unary/vent_pump - 5 time(s). + +/obj/item/organ/hivelord_core/legion - 5 time(s). + +/obj/item/weapon/electronics/airlock - 5 time(s). + +/obj/effect/mist - 5 time(s). + +/obj/machinery/camera/portable - 5 time(s). + +/obj/machinery/porta_turret - 4 time(s). + +/obj/machinery/atmospherics/components/unary/portables_connector/visible - 4 time(s). + +/obj/effect/decal/cleanable/blood/gibs - 4 time(s). + +/obj/machinery/atmospherics/components/unary/vent_scrubber - 4 time(s). + +/obj/item/weapon/tank/internals/oxygen - 3 time(s). + +/obj/item/device/assembly/signaler - 3 time(s). + +/datum/event - 3 time(s). + +/obj/effect/decal/cleanable/blood/footprints - 3 time(s). + +/obj/machinery/camera/emp_proof - 3 time(s). + +/obj/item/weapon/tank/internals/plasma - 3 time(s). + +/obj/machinery/camera_assembly - 3 time(s). + +/obj/machinery/camera/motion - 3 time(s). + +/obj/machinery/atmospherics/pipe/simple/supply/hidden - 2 time(s). + +/obj/item/mecha_parts/chassis/ripley - 2 time(s). + +/obj/item/stack/sheet/metal - 2 time(s). + +/obj/machinery/door/airlock/external - 2 time(s). + +/obj/machinery/suit_storage_unit/engine - 2 time(s). + +/obj/item/weapon/stock_parts/cell - 2 time(s). + +/obj/machinery/computer/communications - 2 time(s). + +/obj/item/clothing/tie/petcollar - 2 time(s). + +/obj/machinery/atmospherics/components/binary/passive_gate - 2 time(s). + +/obj/machinery/power/apc - 2 time(s). + +/obj/structure/table/glass - 2 time(s). + +/obj/item/stack/cable_coil - 2 time(s). + +/obj/effect/decal/cleanable/blood/gibs/core - 2 time(s). + +/obj/machinery/atmospherics/components/unary/heat_exchanger - 2 time(s). + +/obj/item/mecha_parts/chassis/firefighter - 2 time(s). + +/obj/machinery/porta_turret/ai - 2 time(s). + +/obj/structure/closet/secure_closet/brig - 1 time(s). + +/obj/effect/landmark/river_waypoint - 1 time(s). + +/obj/item/clothing/head/chaplain_hood - 1 time(s). + +/obj/machinery/door/window/eastleft - 1 time(s). + +/obj/item/weapon/staff/broom - 1 time(s). + +/obj/machinery/door/window - 1 time(s). + +/obj/machinery/computer/pandemic - 1 time(s). + +/obj/machinery/door/airlock/research - 1 time(s). + +/obj/machinery/portable_atmospherics/pump - 1 time(s). + +/obj/machinery/suit_storage_unit/ce - 1 time(s). + +/obj/machinery/power/grounding_rod - 1 time(s). + +/mob/camera/aiEye - 1 time(s). + +/datum/data/record - 1 time(s). + +/obj/machinery/firealarm - 1 time(s). + +/obj/machinery/porta_turret_cover - 1 time(s). + +/obj/mecha/working/ripley - 1 time(s). + +/obj/machinery/portable_atmospherics/scrubber - 1 time(s). + +/obj/effect/decal/cleanable/robot_debris/old - 1 time(s). + +/obj/machinery/biogenerator - 1 time(s). + +/obj/machinery/plantgenes - 1 time(s). + +/datum/reagents - 1 time(s). + +/obj/machinery/vending/hydronutrients - 1 time(s). + +/obj/machinery/recycler - 1 time(s). + +/obj/item/weapon/tank/jetpack/suit - 1 time(s). + +/obj/machinery/vending/hydroseeds - 1 time(s). + +/obj/structure/table/optable - 1 time(s). + +/obj/item/device/pda/ai/pai - 1 time(s). + +/obj/item/stack/sheet/mineral/plasma - 1 time(s). + +/obj/machinery/chem_dispenser/drinks/beer - 1 time(s). + +/obj/structure/closet/crate - 1 time(s). + +/obj/item/stack/sheet/animalhide/monkey - 1 time(s). + +/obj/effect/hallucination/simple/singularity - 1 time(s). + +/obj - 1 time(s). + +/obj/item/clothing/head/explorer - 1 time(s). + +/obj/screen/grab - 1 time(s). + +/obj/item/weapon/grab - 1 time(s). + +/obj/machinery/atmospherics/components/unary/thermomachine/heater - 1 time(s). + +/obj/machinery/atmospherics/components/unary/thermomachine/freezer - 1 time(s). + +/obj/machinery/door/poddoor - 1 time(s). + +/obj/item/weapon/cartridge/hos - 1 time(s). + +/obj/item/device/pda/heads/hos - 1 time(s). + +/obj/item/ammo_casing/energy/electrode - 1 time(s). + +/obj/effect/mob_spawn/human/alive/space_bar_patron - 1 time(s). + +/obj/effect/blob/core - 1 time(s). + +/obj/machinery/clonepod - 1 time(s). + +/obj/item/weapon/reagent_containers/spray/mister/janitor - 1 time(s). + +/obj/item/clothing/head/helmet/space/hardsuit/shielded/ctf - 1 time(s). + +/obj/item/weapon/card/id/syndicate - 1 time(s). + +/obj/item/device/radio - 1 time(s). + +/obj/machinery/r_n_d/circuit_imprinter - 1 time(s). + +/obj/machinery/door/window/brigdoor - 1 time(s). + +/obj/machinery/computer/rdconsole/core - 1 time(s). + +/obj/structure/cable/yellow - 1 time(s). + +/obj/machinery/disposal/bin - 1 time(s). + +/obj/effect/mob_spawn/human/prisoner_transport - 1 time(s). diff --git a/tools/Runtime Condenser/RuntimeCondenser.exe b/tools/Runtime Condenser/RuntimeCondenser.exe index f32fe9d6ece..e53102f219a 100644 Binary files a/tools/Runtime Condenser/RuntimeCondenser.exe and b/tools/Runtime Condenser/RuntimeCondenser.exe differ
Red Deity