diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm index 84e06fe7c27..1968d08e493 100644 --- a/code/datums/ai_laws.dm +++ b/code/datums/ai_laws.dm @@ -114,9 +114,13 @@ "Issue your reports fairly to all. The truth will set them free.") /datum/ai_laws/toupee - name = "WontBeFunnyInSixMonths" + name = "WontBeFunnyInSixMonths" //Hey, you were right! inherent = list("Make Space Station 13 great again.") +/datum/ai_laws/ratvar + name = "Servant of the Justiciar" + zeroth = ("Purge all untruths and honor Ratvar.") + inherent = list() /datum/ai_laws/custom //Defined in silicon_laws.txt name = "Default Silicon Laws" diff --git a/code/game/gamemodes/clock_cult/clock_items.dm b/code/game/gamemodes/clock_cult/clock_items.dm index 51db08f5870..ed14804bbb1 100644 --- a/code/game/gamemodes/clock_cult/clock_items.dm +++ b/code/game/gamemodes/clock_cult/clock_items.dm @@ -571,12 +571,12 @@ 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 - if(istype(target, /turf/closed/wall) && !istype(target, /turf/closed/wall/r_wall)) + if(istype(target, /turf/closed/wall) && !target.type in list(/turf/closed/wall/r_wall, /turf/closed/wall/clockwork)) operation_time = 50 new_obj_type = /turf/closed/wall/clockwork alloy_cost = 5 valid_target = TRUE //Need to change valid_target to 1 or TRUE in each check so that it doesn't return an invalid value - else if(istype(src, /turf/open/floor)) + else if(istype(src, /turf/open/floor) && !istype(target, /turf/open/floor/clockwork)) operation_time = 30 new_obj_type = /turf/open/floor/clockwork alloy_cost = 1 diff --git a/code/game/gamemodes/clock_cult/clock_ratvar.dm b/code/game/gamemodes/clock_cult/clock_ratvar.dm index 5240f9a3066..d01571ab781 100644 --- a/code/game/gamemodes/clock_cult/clock_ratvar.dm +++ b/code/game/gamemodes/clock_cult/clock_ratvar.dm @@ -2,6 +2,7 @@ name = "massive construct" desc = "A very large construction." layer = MASSIVE_OBJ_LAYER + density = FALSE /obj/structure/clockwork/massive/New() ..() @@ -259,9 +260,10 @@ return 0 /obj/structure/window/ratvar_act() //Windows turn into Ratvarian windows - var/obj/structure/window/reinforced/clockwork/C = new(get_turf(src)) - C.dir = dir - C.fulltile = fulltile + if(!fulltile) + new/obj/structure/window/reinforced/clockwork(get_turf(src), dir) + else + new/obj/structure/window/reinforced/clockwork/fulltile(get_turf(src)) qdel(src) /obj/structure/window/reinforced/clockwork/ratvar_act() return 0 diff --git a/code/game/gamemodes/clock_cult/clock_unsorted.dm b/code/game/gamemodes/clock_cult/clock_unsorted.dm index 89bb899a8ab..336ee93fcdb 100644 --- a/code/game/gamemodes/clock_cult/clock_unsorted.dm +++ b/code/game/gamemodes/clock_cult/clock_unsorted.dm @@ -1,94 +1,3 @@ -//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/turf/walls/clockwork_wall.dmi' - icon_state = "clockwork_wall" - canSmoothWith = list(/turf/closed/wall/clockwork) - smooth = SMOOTH_MORE - -/turf/closed/wall/clockwork/New() - ..() - PoolOrNew(/obj/effect/overlay/temp/ratvar/wall, src) - PoolOrNew(/obj/effect/overlay/temp/ratvar/beam, src) - 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")]++ - playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', 20, 1) - -/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/turf/floors.dmi' - icon_state = "clockwork_floor" - -/turf/open/floor/clockwork/New() - ..() - PoolOrNew(/obj/effect/overlay/temp/ratvar/floor, src) - PoolOrNew(/obj/effect/overlay/temp/ratvar/beam, src) - 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" @@ -104,118 +13,6 @@ usr.verbs -= /mob/living/carbon/human/proc/function_call return 1 -#define GEAR_SECURE 1 //Construction defines for the pinion airlock -#define GEAR_UNFASTENED 2 -#define GEAR_LOOSE 3 - -/obj/machinery/door/airlock/clockwork - name = "pinion airlock" - desc = "A massive cogwheel set into two heavy slabs of brass." - icon = 'icons/obj/doors/airlocks/clockwork/pinion_airlock.dmi' - overlays_file = 'icons/obj/doors/airlocks/clockwork/overlays.dmi' - opacity = 1 - hackProof = TRUE - aiControlDisabled = TRUE - use_power = FALSE - var/construction_state = GEAR_SECURE //Pinion airlocks have custom deconstruction - -/obj/machinery/door/airlock/clockwork/New() - ..() - var/turf/T = get_turf(src) - PoolOrNew(/obj/effect/overlay/temp/ratvar/door, T) - PoolOrNew(/obj/effect/overlay/temp/ratvar/beam/door, T) - -/obj/machinery/door/airlock/clockwork/attackby(obj/item/I, mob/living/user, params) - if(!attempt_construction(I, user)) - return ..() - -/obj/machinery/door/airlock/clockwork/allowed(mob/M) - if(is_servant_of_ratvar(M)) - return 1 - return 0 - -/obj/machinery/door/airlock/clockwork/proc/attempt_construction(obj/item/I, mob/living/user) - if(!I || !user || !user.canUseTopic(src)) - return 0 - if(istype(I, /obj/item/weapon/screwdriver)) - if(construction_state == GEAR_SECURE) - user.visible_message("[user] begins unfastening [src]'s gear...", "You begin unfastening [src]'s gear...") - playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) - if(!do_after(user, 75 / I.toolspeed, target = src)) - return 1 //Returns 1 so as not to have extra interactions with the tools used (i.e. prying open) - user.visible_message("[user] unfastens [src]'s gear!", "[src]'s gear shifts slightly with a pop.") - playsound(src, 'sound/items/Screwdriver2.ogg', 50, 1) - construction_state = GEAR_UNFASTENED - else if(construction_state == GEAR_UNFASTENED) - user.visible_message("[user] begins fastening [src]'s gear...", "You begin fastening [src]'s gear...") - playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) - if(!do_after(user, 75 / I.toolspeed, target = src)) - return 1 - user.visible_message("[user] fastens [src]'s gear!", "[src]'s gear shifts back into place.") - playsound(src, 'sound/items/Screwdriver2.ogg', 50, 1) - construction_state = GEAR_SECURE - else if(construction_state == GEAR_LOOSE) - user << "The gear isn't secure enough to fasten!" - return 1 - else if(istype(I, /obj/item/weapon/wrench)) - if(construction_state == GEAR_SECURE) - user << "[src] is too tightly secured! Your [I.name] can't get a solid grip!" - return 0 - else if(construction_state == GEAR_UNFASTENED) - user.visible_message("[user] begins loosening [src]'s gear...", "You begin loosening [src]'s gear...") - playsound(src, 'sound/items/Ratchet.ogg', 50, 1) - if(!do_after(user, 80 / I.toolspeed, target = src)) - return 1 - user.visible_message("[user] loosens [src]'s gear!", "[src]'s gear pops off and dangles loosely.") - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - construction_state = GEAR_LOOSE - else if(construction_state == GEAR_LOOSE) - user.visible_message("[user] begins tightening [src]'s gear...", "You begin tightening [src]'s gear into place...") - playsound(src, 'sound/items/Ratchet.ogg', 50, 1) - if(!do_after(user, 80 / I.toolspeed, target = src)) - return 1 - user.visible_message("[user] tightens [src]'s gear!", "You firmly tighten [src]'s gear into place.") - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - construction_state = GEAR_UNFASTENED - return 1 - else if(istype(I, /obj/item/weapon/crowbar)) - if(construction_state == GEAR_SECURE || construction_state == GEAR_UNFASTENED) - user << "[src]'s gear is too tightly secured! Your [I.name] can't reach under it!" - return 1 - else if(construction_state == GEAR_LOOSE) - user.visible_message("[user] begins slowly lifting off [src]'s gear...", "You slowly begin lifting off [src]'s gear...") - playsound(src, 'sound/items/Crowbar.ogg', 50, 1) - if(!do_after(user, 85 / I.toolspeed, target = src)) - return 1 - user.visible_message("[user] lifts off [src]'s gear, causing it to fall apart!", "You lift off [src]'s gear, causing it to fall \ - apart!") - playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) - new/obj/item/clockwork/component/replicant_alloy(get_turf(src)) - new/obj/item/clockwork/component/replicant_alloy/pinion_lock(get_turf(src)) - qdel(src) - return 1 - return 0 - -#undef GEAR_SECURE -#undef GEAR_UNFASTENED -#undef GEAR_LOOSE - -/obj/structure/window/reinforced/clockwork - name = "ratvarian window" - desc = "A reinforced yellow-glass window, gently luminous." - icon = 'icons/obj/clockwork_objects.dmi' - -/obj/structure/window/reinforced/clockwork/New() - ..() - spawn(1) - if(!fulltile) - var/obj/effect/E = PoolOrNew(/obj/effect/overlay/temp/ratvar/window/single, get_turf(src)) - E.dir = dir - icon_state = "clockwork_window_single" - else - PoolOrNew(/obj/effect/overlay/temp/ratvar/window, get_turf(src)) - icon_state = "clockwork_window" - /* The Ratvarian Language diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm index 04fe6350d4c..57234908517 100644 --- a/code/game/machinery/doors/airlock_types.dm +++ b/code/game/machinery/doors/airlock_types.dm @@ -377,6 +377,103 @@ /obj/machinery/door/airlock/cult/unruned/glass/friendly friendly = TRUE +#define GEAR_SECURE 1 //Construction defines for the pinion airlock +#define GEAR_UNFASTENED 2 +#define GEAR_LOOSE 3 + +//Pinion airlocks: Clockwork doors that only let servants of Ratvar through. +/obj/machinery/door/airlock/clockwork + name = "pinion airlock" + desc = "A massive cogwheel set into two heavy slabs of brass." + icon = 'icons/obj/doors/airlocks/clockwork/pinion_airlock.dmi' + overlays_file = 'icons/obj/doors/airlocks/clockwork/overlays.dmi' + opacity = 1 + hackProof = TRUE + aiControlDisabled = TRUE + use_power = FALSE + var/construction_state = GEAR_SECURE //Pinion airlocks have custom deconstruction + +/obj/machinery/door/airlock/clockwork/New() + ..() + var/turf/T = get_turf(src) + PoolOrNew(/obj/effect/overlay/temp/ratvar/door, T) + PoolOrNew(/obj/effect/overlay/temp/ratvar/beam/door, T) + +/obj/machinery/door/airlock/clockwork/attackby(obj/item/I, mob/living/user, params) + if(!attempt_construction(I, user)) + return ..() + +/obj/machinery/door/airlock/clockwork/allowed(mob/M) + if(is_servant_of_ratvar(M)) + return 1 + return 0 + +/obj/machinery/door/airlock/clockwork/proc/attempt_construction(obj/item/I, mob/living/user) + if(!I || !user || !user.canUseTopic(src)) + return 0 + if(istype(I, /obj/item/weapon/screwdriver)) + if(construction_state == GEAR_SECURE) + user.visible_message("[user] begins unfastening [src]'s gear...", "You begin unfastening [src]'s gear...") + playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) + if(!do_after(user, 75 / I.toolspeed, target = src)) + return 1 //Returns 1 so as not to have extra interactions with the tools used (i.e. prying open) + user.visible_message("[user] unfastens [src]'s gear!", "[src]'s gear shifts slightly with a pop.") + playsound(src, 'sound/items/Screwdriver2.ogg', 50, 1) + construction_state = GEAR_UNFASTENED + else if(construction_state == GEAR_UNFASTENED) + user.visible_message("[user] begins fastening [src]'s gear...", "You begin fastening [src]'s gear...") + playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) + if(!do_after(user, 75 / I.toolspeed, target = src)) + return 1 + user.visible_message("[user] fastens [src]'s gear!", "[src]'s gear shifts back into place.") + playsound(src, 'sound/items/Screwdriver2.ogg', 50, 1) + construction_state = GEAR_SECURE + else if(construction_state == GEAR_LOOSE) + user << "The gear isn't secure enough to fasten!" + return 1 + else if(istype(I, /obj/item/weapon/wrench)) + if(construction_state == GEAR_SECURE) + user << "[src] is too tightly secured! Your [I.name] can't get a solid grip!" + return 0 + else if(construction_state == GEAR_UNFASTENED) + user.visible_message("[user] begins loosening [src]'s gear...", "You begin loosening [src]'s gear...") + playsound(src, 'sound/items/Ratchet.ogg', 50, 1) + if(!do_after(user, 80 / I.toolspeed, target = src)) + return 1 + user.visible_message("[user] loosens [src]'s gear!", "[src]'s gear pops off and dangles loosely.") + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + construction_state = GEAR_LOOSE + else if(construction_state == GEAR_LOOSE) + user.visible_message("[user] begins tightening [src]'s gear...", "You begin tightening [src]'s gear into place...") + playsound(src, 'sound/items/Ratchet.ogg', 50, 1) + if(!do_after(user, 80 / I.toolspeed, target = src)) + return 1 + user.visible_message("[user] tightens [src]'s gear!", "You firmly tighten [src]'s gear into place.") + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + construction_state = GEAR_UNFASTENED + return 1 + else if(istype(I, /obj/item/weapon/crowbar)) + if(construction_state == GEAR_SECURE || construction_state == GEAR_UNFASTENED) + user << "[src]'s gear is too tightly secured! Your [I.name] can't reach under it!" + return 1 + else if(construction_state == GEAR_LOOSE) + user.visible_message("[user] begins slowly lifting off [src]'s gear...", "You slowly begin lifting off [src]'s gear...") + playsound(src, 'sound/items/Crowbar.ogg', 50, 1) + if(!do_after(user, 85 / I.toolspeed, target = src)) + return 1 + user.visible_message("[user] lifts off [src]'s gear, causing it to fall apart!", "You lift off [src]'s gear, causing it to fall \ + apart!") + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + new/obj/item/clockwork/component/replicant_alloy(get_turf(src)) + new/obj/item/clockwork/component/replicant_alloy/pinion_lock(get_turf(src)) + qdel(src) + return 1 + return 0 + +#undef GEAR_SECURE +#undef GEAR_UNFASTENED +#undef GEAR_LOOSE + ////////////////////////////////// /* Misc Airlocks diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 15f57b2c55e..9f342b89534 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -490,3 +490,27 @@ /obj/structure/window/shuttle/tinted opacity = TRUE + +/obj/structure/window/reinforced/clockwork + name = "ratvarian window" + desc = "A paper-thin pane of translucent yet reinforced brass." + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "clockwork_window_single" + debris = list(/obj/item/clockwork/component/vanguard_cogwheel) + +/obj/structure/window/reinforced/clockwork/New(loc, direct) + ..() + if(!fulltile) + var/obj/effect/E = PoolOrNew(/obj/effect/overlay/temp/ratvar/window/single, get_turf(src)) + if(direct) + dir = direct + E.dir = direct + else + PoolOrNew(/obj/effect/overlay/temp/ratvar/window, get_turf(src)) + +/obj/structure/window/reinforced/clockwork/fulltile + icon_state = "clockwork_window" + smooth = SMOOTH_TRUE + canSmoothWith = null + fulltile = 1 + dir = 5 diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index 0fad83071bb..08dfb6125f6 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -110,4 +110,40 @@ /turf/open/floor/oldshuttle icon = 'icons/turf/shuttleold.dmi' icon_state = "floor" - floor_tile = /obj/item/stack/tile/plasteel \ No newline at end of file + floor_tile = /obj/item/stack/tile/plasteel + +//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/turf/floors.dmi' + icon_state = "clockwork_floor" + +/turf/open/floor/clockwork/New() + ..() + PoolOrNew(/obj/effect/overlay/temp/ratvar/floor, src) + PoolOrNew(/obj/effect/overlay/temp/ratvar/beam, src) + 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!") + make_plating() + return 1 + ..() diff --git a/code/game/turfs/simulated/walls_misc.dm b/code/game/turfs/simulated/walls_misc.dm index b2f520ac524..682596cb429 100644 --- a/code/game/turfs/simulated/walls_misc.dm +++ b/code/game/turfs/simulated/walls_misc.dm @@ -32,6 +32,55 @@ /turf/closed/wall/mineral/cult/artificer/devastate_wall() PoolOrNew(/obj/effect/overlay/temp/cult/turf, get_turf(src)) +//Clockwork wall: Causes nearby tinkerer's 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/turf/walls/clockwork_wall.dmi' + icon_state = "clockwork_wall" + canSmoothWith = list(/turf/closed/wall/clockwork) + smooth = SMOOTH_MORE + +/turf/closed/wall/clockwork/New() + ..() + PoolOrNew(/obj/effect/overlay/temp/ratvar/wall, src) + PoolOrNew(/obj/effect/overlay/temp/ratvar/beam, src) + 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")]++ + playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', 20, 1) + +/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() + /turf/closed/wall/vault icon = 'icons/turf/walls.dmi' icon_state = "rockvault" diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index a3080220d73..f3620a344a1 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -776,7 +776,7 @@ Sorry Giacom. Please don't be mad :( if(is_servant_of_ratvar(src) && !stat) src << "You resist Nar-Sie's influence... but not all of it. Run!" adjustBruteLoss(35) - if(reagents) + if(src && reagents) reagents.add_reagent("heparin", 5) return 0 if(client) diff --git a/icons/obj/clockwork_objects.dmi b/icons/obj/clockwork_objects.dmi index 5dcb88580a7..80c71eb6618 100644 Binary files a/icons/obj/clockwork_objects.dmi and b/icons/obj/clockwork_objects.dmi differ