diff --git a/code/__DEFINES/~monkestation/clock_cult.dm b/code/__DEFINES/~monkestation/clock_cult.dm
index e9c9424b50dd..2d765bf45a70 100644
--- a/code/__DEFINES/~monkestation/clock_cult.dm
+++ b/code/__DEFINES/~monkestation/clock_cult.dm
@@ -5,16 +5,32 @@
#define SIGIL_TRANSMISSION_RANGE 4
+#define MAX_CLOCK_VITALITY 400
+
///base state the ark is created in, any state besides this will be a hostile environment
#define ARK_STATE_BASE 0
///state for the grace period after the cult has reached its member count max and have enough activing anchoring crystals to summon
#define ARK_STATE_CHARGING 1
-///state for after the cult has been annouced as well as the first half of the assault
-#define ARK_STATE_ACTIVE 2
+///state for after the cult has been annouced and are preparing for the portals to open
+#define ARK_STATE_GRACE 2
+///state for the first half of the assault
+#define ARK_STATE_ACTIVE 3
///state for the halfway point of ark activation
-#define ARK_STATE_SUMMONING 3
+#define ARK_STATE_SUMMONING 4
///the ark has either finished opening or been destroyed in this state
-#define ARK_STATE_FINAL 4
+#define ARK_STATE_FINAL 5
///max damage taken per hit by "important" clock structures
#define MAX_IMPORTANT_CLOCK_DAMAGE 30
+
+///how many anchoring crystals need to be active before the ark can open
+#define ANCHORING_CRYSTALS_TO_SUMMON 2
+
+///the map path of the reebe map
+#define REEBE_MAP_PATH "_maps/~monkestation/templates/reebe.dmm"
+
+///how long between uses of the anchoring crystal scripture, also how long the hostile environment lasts if the crystal is not destroyed
+#define ANCHORING_CRYSTAL_COOLDOWN 7 MINUTES
+
+///up to how many tiles away will the ark stop certain things from breaking turfs
+#define ARK_TURF_DESTRUCTION_BLOCK_RANGE 9
diff --git a/code/__DEFINES/~monkestation/components.dm b/code/__DEFINES/~monkestation/components.dm
new file mode 100644
index 000000000000..8d7aa1d49798
--- /dev/null
+++ b/code/__DEFINES/~monkestation/components.dm
@@ -0,0 +1,7 @@
+//for the wound converter component, higher values will take priority over lower ones
+#define WOUND_CONVERSION_PRIORITY_ITEM 1
+#define WOUND_CONVERSION_PRIORITY_ITEM_HIGH 2
+#define WOUND_CONVERSION_PRIORITY_MOB 3
+#define WOUND_CONVERSION_PRIORITY_HIGH 4
+///NEVER USE THIS PRIORITY LEVEL OR HAVE ANYTHING HIGHER, this is used to make sure we dont try and act on wounds we have already handled
+#define WOUND_CONVERSION_PRIORITY_CONVERTED 5
diff --git a/code/__DEFINES/~monkestation/dcs/signals/signals_atom.dm b/code/__DEFINES/~monkestation/dcs/signals/signals_atom.dm
index 4e3566280e4f..e73c3218e22c 100644
--- a/code/__DEFINES/~monkestation/dcs/signals/signals_atom.dm
+++ b/code/__DEFINES/~monkestation/dcs/signals/signals_atom.dm
@@ -4,9 +4,6 @@
/// /datum/component/clockwork_trap signals: ()
#define COMSIG_CLOCKWORK_SIGNAL_RECEIVED "clock_received"
-/// Called when a clock cultist uses a clockwork slab: (obj/item/clockwork/clockwork_slab/slab)
-#define COMSIG_CLOCKWORK_SLAB_USED "clockwork_slab_used"
-
/// from base of atom/eminence_act() : (mob/living/eminence/user)
#define COMSIG_ATOM_EMINENCE_ACT "atom_eminence_act"
@@ -17,3 +14,6 @@
/// Mob is trying to open the hacking menu of a target [/atom], from /datum/hacking/interactable(): (mob/user)
#define COMSIG_TRY_HACKING_INTERACT "try_hacking_interact"
#define COMPONENT_CANT_INTERACT_HACKING (1<<0)
+
+/// generic turf checker signal
+#define COMSIG_CHECK_TURF_GENERIC "check_turf_generic"
diff --git a/code/__DEFINES/~monkestation/dcs/signals/signals_element.dm b/code/__DEFINES/~monkestation/dcs/signals/signals_element.dm
new file mode 100644
index 000000000000..0c2bfa082ff2
--- /dev/null
+++ b/code/__DEFINES/~monkestation/dcs/signals/signals_element.dm
@@ -0,0 +1,4 @@
+///called when /datum/element/turf_checker detects a new state on constant checking (new_state) TRUE for a valid turf FALSE for an invalid
+#define COMSIG_TURF_CHECKER_UPDATE_STATE "turf_checker_update_state"
+#define COMPONENT_CHECKER_VALID_TURF (1<<0)
+#define COMPONENT_CHECKER_INVALID_TURF (2<<0)
diff --git a/code/__DEFINES/~monkestation/dcs/signals/signals_item.dm b/code/__DEFINES/~monkestation/dcs/signals/signals_item.dm
index b3fec9b1d9c1..798e7a94443b 100644
--- a/code/__DEFINES/~monkestation/dcs/signals/signals_item.dm
+++ b/code/__DEFINES/~monkestation/dcs/signals/signals_item.dm
@@ -4,3 +4,9 @@
#define COMPONENT_HANDLED_MESSAGE (1 << 1)
/// Called after an item is compressed by a bluespace compression kit: (mob/user, obj/item/compression_kit/kit)
#define COMSIG_ITEM_COMPRESSED "item_compressed"
+
+/// Called when a clock cultist uses a clockwork slab: (obj/item/clockwork/clockwork_slab/slab)
+#define COMSIG_CLOCKWORK_SLAB_USED "clockwork_slab_used"
+
+/// the comsig for clockwork items checking turf
+#define COMSIG_CHECK_TURF_CLOCKWORK "check_turf_clockwork"
diff --git a/code/__DEFINES/~monkestation/dcs/signals/signals_mob.dm b/code/__DEFINES/~monkestation/dcs/signals/signals_mob/signals_mob_main.dm
similarity index 100%
rename from code/__DEFINES/~monkestation/dcs/signals/signals_mob.dm
rename to code/__DEFINES/~monkestation/dcs/signals/signals_mob/signals_mob_main.dm
diff --git a/code/__DEFINES/~monkestation/market.dm b/code/__DEFINES/~monkestation/market.dm
index 899af43fb164..dc46b8fc52c5 100644
--- a/code/__DEFINES/~monkestation/market.dm
+++ b/code/__DEFINES/~monkestation/market.dm
@@ -1,2 +1,5 @@
#define MARKET_PROCESS (1<<0)
#define MARKET_AUCTION (1<<1)
+
+///How much does it cost to buy a lootbox
+#define LOOTBOX_COST 5000
diff --git a/code/__HELPERS/~monkestation-helpers/atoms.dm b/code/__HELPERS/~monkestation-helpers/atoms.dm
index d25f0e33b555..67b81a50dafb 100644
--- a/code/__HELPERS/~monkestation-helpers/atoms.dm
+++ b/code/__HELPERS/~monkestation-helpers/atoms.dm
@@ -1,3 +1,25 @@
+///Returns all the objects that we are nested within the contents of starting with ourself then adding our previous loc's loc, does not include our turf
+/atom/movable/proc/get_locs_recursive()
+ var/atom/movable/current_loc = loc
+ var/list/locs_list = list(src)
+ if(!current_loc)
+ return locs_list
+
+ var/sanity = 0 //you REALLY should not have something nested in 500 layers of contents
+ while(!isturf(current_loc))
+ sanity++
+ locs_list += current_loc
+ if(sanity > 500 || !ismovable(current_loc))
+ return locs_list
+
+ current_loc = current_loc.loc
+ return locs_list //if sanity is 0 then that means our loc is a turf and we are the highest level loc
+
+///Returns our highest level loc that is not a turf or ourselves if we are directly in a turf, if we were in a box in the backpack of a mob we would return that mob
+/atom/movable/proc/get_highest_non_turf_loc()
+ var/list/locs_list = get_locs_recursive()
+ return locs_list[length(locs_list)]
+
/atom/proc/effective_contents(list/typecache = null)
var/static/list/default_typecache
if(!typecache)
diff --git a/code/controllers/subsystem/economy.dm b/code/controllers/subsystem/economy.dm
index 23a4ea42e0df..f0628eba1d98 100644
--- a/code/controllers/subsystem/economy.dm
+++ b/code/controllers/subsystem/economy.dm
@@ -67,8 +67,6 @@ SUBSYSTEM_DEF(economy)
/// Tracks a temporary sum of all money in the system
/// We need this on the subsystem because of yielding and such
var/temporary_total = 0
- ///the mail crate we last generated
- var/obj/structure/closet/crate/mail/economy/mail_crate
/datum/controller/subsystem/economy/Initialize()
//removes cargo from the split
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index a047ea743033..4499e7804260 100644
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -287,8 +287,13 @@
state = STATE_MAIN
if ("recallShuttle")
// AIs cannot recall the shuttle
- if (!authenticated(usr) || issilicon(usr) || syndicate)
+ var/clock_user = IS_CLOCK(usr) //monkestation edit
+ if (!authenticated(usr) || issilicon(usr) || syndicate || (clock_user && GLOB.main_clock_cult?.member_recalled)) //monkestation edit: adds the CWC check
return
+//monkestation edit start
+ if(clock_user)
+ GLOB.main_clock_cult?.member_recalled = TRUE
+//monkestation edit end
SSshuttle.cancelEvac(usr)
if ("requestNukeCodes")
if (!authenticated_as_non_silicon_captain(usr))
diff --git a/code/game/objects/effects/spiderwebs.dm b/code/game/objects/effects/spiderwebs.dm
index e0e541b9857d..f067f0c17bf8 100644
--- a/code/game/objects/effects/spiderwebs.dm
+++ b/code/game/objects/effects/spiderwebs.dm
@@ -178,7 +178,7 @@
return TRUE
if(!isliving(mover))
return
- if(isspider(mover.pulledby))
+ if(!isnull(mover.pulledby) && isspider(mover.pulledby))
return TRUE
balloon_alert(mover, "stuck in web!")
return FALSE
diff --git a/code/game/objects/items/melee/baton.dm b/code/game/objects/items/melee/baton.dm
index 65525a2f8612..6e7bef5883e3 100644
--- a/code/game/objects/items/melee/baton.dm
+++ b/code/game/objects/items/melee/baton.dm
@@ -376,7 +376,7 @@
playsound(src, on_sound, 50, TRUE)
return COMPONENT_NO_DEFAULT_MESSAGE
-//monkestation removal start
+//monkestation removal start, moved to \antagonists\contractor\items in the modular folder
/*/obj/item/melee/baton/telescopic/contractor_baton
name = "contractor baton"
desc = "A compact, specialised baton assigned to Syndicate contractors. Applies light electrical shocks to targets."
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 078f4d2a5ae5..7360393a0f60 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -54,7 +54,8 @@ GLOBAL_LIST_EMPTY(antagonists)
/// The typepath for the outfit to show in the preview for the preferences menu.
var/preview_outfit
/// Flags for antags to turn on or off and check!
- var/antag_flags = NONE /// If true, this antagonist can assign themself a new objective
+ var/antag_flags = NONE
+ /// If true, this antagonist can assign themself a new objective
var/can_assign_self_objectives = FALSE
/// Default to fill in when entering a custom objective.
var/default_custom_objective = "Cause chaos on the space station."
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index 8dde896803b4..3f32f3af6e23 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -49,7 +49,7 @@
return ..()
/datum/antagonist/brother/proc/on_mob_successful_flashed_carbon(mob/living/source, mob/living/carbon/flashed, obj/item/assembly/flash/flash)
- /* SIGNAL_HANDLER */ // monkestation edit: allow used of is_banned_from
+ SIGNAL_HANDLER
if (flashed.stat == DEAD)
return
@@ -63,12 +63,6 @@
flashed.balloon_alert(source, "[flashed.p_their()] mind is vacant!")
return
- // monkestation edit: allow people to opt-out of BB
- if(!(ROLE_BROTHER in flashed.client?.prefs?.be_special) || is_banned_from(flashed.ckey, list(ROLE_BROTHER, ROLE_SYNDICATE)))
- flashed.balloon_alert(source, "unwilling to play role!")
- return
- // monkestation end
-
for(var/datum/objective/brother_objective as anything in source.mind.get_all_objectives())
// If the objective has a target, are we flashing them?
if(flashed == brother_objective.target?.current)
diff --git a/code/modules/antagonists/changeling/headslug_eggs.dm b/code/modules/antagonists/changeling/headslug_eggs.dm
index 08733a7e6af0..7f999139d89b 100644
--- a/code/modules/antagonists/changeling/headslug_eggs.dm
+++ b/code/modules/antagonists/changeling/headslug_eggs.dm
@@ -6,13 +6,21 @@
desc = "Twitching and disgusting."
/// The mind of the original changeling that gave forth to the headslug mob.
var/datum/mind/origin
- /// Tracks how long the egg has been growing.
- var/time = 0
+ /// When we're expected to hatch.
+ var/hatch_time = 0
+ /// When this egg last got removed from a body. If -1, the egg hasn't been removed from a body.
+ var/removal_time = -1
+
+/obj/item/organ/internal/body_egg/changeling_egg/Insert(mob/living/carbon/egg_owner, special = FALSE, drop_if_replaced = TRUE) //monkestation edit: movement_flags = DELETE_IF_REPLACED)
+ . = ..()
+ hatch_time = world.time + (removal_time == -1 ? EGG_INCUBATION_TIME : (hatch_time - removal_time))
+
+/obj/item/organ/internal/body_egg/changeling_egg/Remove(mob/living/carbon/egg_owner, special, movement_flags)
+ . = ..()
+ removal_time = world.time
/obj/item/organ/internal/body_egg/changeling_egg/egg_process(seconds_per_tick, times_fired)
- // Changeling eggs grow in dead people
- time += seconds_per_tick * 10
- if(time >= EGG_INCUBATION_TIME)
+ if(owner && hatch_time <= world.time)
pop()
/// Once the egg is fully grown, we gib the host and spawn a monkey (with the changeling's player controlling it). Very descriptive proc name.
diff --git a/code/modules/antagonists/spiders/spiders.dm b/code/modules/antagonists/spiders/spiders.dm
index 34811e04dbbc..52941d43615a 100644
--- a/code/modules/antagonists/spiders/spiders.dm
+++ b/code/modules/antagonists/spiders/spiders.dm
@@ -14,7 +14,7 @@
/datum/antagonist/spider/on_gain()
forge_objectives(directive)
- . = ..()
+ return ..()
/datum/antagonist/spider/greet()
. = ..()
@@ -35,3 +35,23 @@
var/datum/objective/spider/objective = new(directive)
objective.owner = owner
objectives += objective
+
+/// Subtype for flesh spiders who don't have a queen
+/datum/antagonist/spider/flesh
+ name = "Flesh Spider"
+
+/datum/antagonist/spider/flesh/forge_objectives()
+ var/datum/objective/custom/destroy = new()
+ destroy.owner = owner
+ destroy.explanation_text = "Wreak havoc and consume living flesh."
+ objectives += destroy
+
+ var/datum/objective/survive/dont_die = new()
+ dont_die.owner = owner
+ objectives += dont_die
+
+/datum/antagonist/spider/flesh/greet()
+ . = ..()
+ to_chat(owner, span_boldwarning("An abomination of flesh set upon the station by changelings, \
+ you are aggressive to all living beings outside of your species and know no loyalties... even to your creator. \
+
Your malleable flesh quickly regenerates if you can avoid taking damage for a few seconds."))
diff --git a/code/modules/mob/living/basic/space_fauna/changeling/flesh_spider.dm b/code/modules/mob/living/basic/space_fauna/changeling/flesh_spider.dm
new file mode 100644
index 000000000000..febba8130541
--- /dev/null
+++ b/code/modules/mob/living/basic/space_fauna/changeling/flesh_spider.dm
@@ -0,0 +1,75 @@
+/**
+ * Spider-esque mob summoned by changelings. Exclusively player-controlled.
+ * An independent hit-and-run antagonist which can make webs and heals itself if left undamaged for a few seconds.
+ * Not a spider subtype because it keeps getting hit by unrelated balance changes intended for the Giant Spiders gamemode.
+ */
+/mob/living/basic/flesh_spider
+ name = "flesh spider"
+ desc = "A odd fleshy creature in the shape of a spider. Its eyes are pitch black and soulless."
+ icon = 'icons/mob/simple/arachnoid.dmi'
+ icon_state = "flesh"
+ icon_living = "flesh"
+ icon_dead = "flesh_dead"
+ mob_biotypes = MOB_ORGANIC|MOB_BUG
+ speak_emote = list("chitters")
+ response_help_continuous = "pets"
+ response_help_simple = "pet"
+ response_disarm_continuous = "gently pushes aside"
+ response_disarm_simple = "gently push aside"
+ damage_coeff = list(BRUTE = 1, BURN = 1.25, TOX = 1, CLONE = 1, STAMINA = 1, OXY = 1)
+ basic_mob_flags = FLAMMABLE_MOB
+ status_flags = NONE
+ speed = -0.1
+ maxHealth = 90
+ health = 90
+ melee_damage_lower = 15
+ melee_damage_upper = 20
+ obj_damage = 30
+ melee_attack_cooldown = CLICK_CD_MELEE
+ attack_verb_continuous = "bites"
+ attack_verb_simple = "bite"
+ attack_sound = 'sound/weapons/bite.ogg'
+ attack_vis_effect = ATTACK_EFFECT_BITE
+ unsuitable_cold_damage = 4
+ unsuitable_heat_damage = 4
+// combat_mode = TRUE //monkestation removal
+ faction = list() // No allies but yourself
+ pass_flags = PASSTABLE
+ unique_name = TRUE
+ lighting_cutoff_red = 22
+ lighting_cutoff_green = 5
+ lighting_cutoff_blue = 5
+ butcher_results = list(/obj/item/food/meat/slab/spider = 2, /obj/item/food/spiderleg = 8)
+ ai_controller = /datum/ai_controller/basic_controller/giant_spider
+
+/mob/living/basic/flesh_spider/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_WEB_SURFER, INNATE_TRAIT)
+ AddElement(/datum/element/cliff_walking)
+ AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW)
+ AddElement(/datum/element/venomous, /datum/reagent/toxin/hunterspider, 5)
+ AddElement(/datum/element/web_walker, /datum/movespeed_modifier/fast_web)
+ AddElement(/datum/element/nerfed_pulling, GLOB.typecache_general_bad_things_to_easily_move)
+ AddElement(/datum/element/prevent_attacking_of_types, GLOB.typecache_general_bad_hostile_attack_targets, "this tastes awful!")
+ AddComponent(\
+ /datum/component/blood_walk,\
+ blood_type = /obj/effect/decal/cleanable/blood/bubblegum,\
+ blood_spawn_chance = 5,\
+ )
+ AddComponent(\
+ /datum/component/regenerator,\
+ regeneration_delay = 4 SECONDS,\
+ health_per_second = maxHealth / 6,\
+ outline_colour = COLOR_PINK,\
+ )
+
+ var/datum/action/cooldown/mob_cooldown/lay_web/webbing = new(src)
+ webbing.webbing_time *= 0.7
+ webbing.Grant(src)
+ ai_controller?.set_blackboard_key(BB_SPIDER_WEB_ACTION, webbing)
+
+ var/datum/action/cooldown/mob_cooldown/lay_web/web_spikes/spikes_web = new(src)
+ spikes_web.Grant(src)
+
+ var/datum/action/cooldown/mob_cooldown/lay_web/sticky_web/web_sticky = new(src)
+ web_sticky.Grant(src)
diff --git a/code/modules/mob/living/basic/space_fauna/headslug.dm b/code/modules/mob/living/basic/space_fauna/changeling/headslug.dm
similarity index 99%
rename from code/modules/mob/living/basic/space_fauna/headslug.dm
rename to code/modules/mob/living/basic/space_fauna/changeling/headslug.dm
index 2e66b34642aa..f1e21ba55e55 100644
--- a/code/modules/mob/living/basic/space_fauna/headslug.dm
+++ b/code/modules/mob/living/basic/space_fauna/changeling/headslug.dm
@@ -71,7 +71,6 @@
/// Just to be super-duper safe to the player, we do return TRUE if all goes well and read that value in check_and_implant() to be nice to the player.
/mob/living/basic/headslug/proc/infect(mob/living/carbon/victim)
var/obj/item/organ/internal/body_egg/changeling_egg/egg = new(victim)
- egg.Insert(victim)
egg.origin = mind
diff --git a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spiders.dm b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spiders.dm
index 3d179e4f7332..92e27c4e3415 100644
--- a/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spiders.dm
+++ b/code/modules/mob/living/basic/space_fauna/spider/giant_spider/giant_spiders.dm
@@ -468,54 +468,6 @@
menu_description = "Weaker version of the nurse spider, specializing in healing their brethren and placing webbings very swiftly, but has very low amount of health and deals low damage."
ai_controller = /datum/ai_controller/basic_controller/giant_spider/weak
-/**
- * ### Flesh Spider
- *
- * A subtype of giant spider which only occurs from changelings.
- * Has the base stats of a hunter, but they can heal themselves and spin webs faster.
- * They also occasionally leave puddles of blood when they walk around. Flavorful!
- */
-/mob/living/basic/spider/giant/hunter/flesh
- name = "flesh spider"
- desc = "A odd fleshy creature in the shape of a spider. Its eyes are pitch black and soulless."
- icon = 'icons/mob/simple/arachnoid.dmi'
- icon_state = "flesh"
- icon_living = "flesh"
- icon_dead = "flesh_dead"
- web_speed = 0.7
- maxHealth = 90
- health = 90
- menu_description = "Self-sufficient spider variant capable of healing themselves and producing webbbing fast."
-
-/mob/living/basic/spider/giant/hunter/flesh/Initialize(mapload)
- . = ..()
- AddComponent(/datum/component/blood_walk, \
- blood_type = /obj/effect/decal/cleanable/blood/bubblegum, \
- blood_spawn_chance = 5)
- // It might be easier and more fitting to just replace this with Regenerator
- AddComponent(/datum/component/healing_touch,\
- heal_brute = 45,\
- heal_burn = 45,\
- interaction_key = DOAFTER_SOURCE_SPIDER,\
- valid_targets_typecache = typecacheof(list(/mob/living/basic/spider/giant/hunter/flesh)),\
- extra_checks = CALLBACK(src, PROC_REF(can_mend)),\
- action_text = "%SOURCE% begins mending themselves...",\
- complete_text = "%SOURCE%'s wounds mend together.",\
- )
-
- var/datum/action/cooldown/mob_cooldown/lay_web/web_spikes/spikes_web = new(src)
- spikes_web.Grant(src)
-
- var/datum/action/cooldown/mob_cooldown/lay_web/sticky_web/web_sticky = new(src)
- web_sticky.Grant(src)
-
-/// Prevent you from healing other flesh spiders, or healing when on fire
-/mob/living/basic/spider/giant/hunter/flesh/proc/can_mend(mob/living/source, mob/living/target)
- if (on_fire)
- balloon_alert(src, "on fire!")
- return FALSE
- return TRUE
-
/**
* ### Viper Spider (Wizard)
*
diff --git a/code/modules/mob_spawn/ghost_roles/spider_roles.dm b/code/modules/mob_spawn/ghost_roles/spider_roles.dm
index e68cd593b478..37e35a31d33a 100644
--- a/code/modules/mob_spawn/ghost_roles/spider_roles.dm
+++ b/code/modules/mob_spawn/ghost_roles/spider_roles.dm
@@ -41,8 +41,9 @@
color = rgb(148, 0, 211)
/obj/structure/spider/eggcluster/bloody
+ icon = 'icons/mob/simple/meteor_heart.dmi'
+ icon_state = "eggs"
name = "bloody egg cluster"
- color = rgb(255, 0, 0)
/obj/structure/spider/eggcluster/midwife
name = "midwife egg cluster"
@@ -72,6 +73,8 @@
var/cluster_type = /obj/structure/spider/eggcluster
/// Physical structure housing the spawner
var/obj/structure/spider/eggcluster/egg
+ /// Which antag datum do we grant?
+ var/granted_datum = /datum/antagonist/spider
/// The types of spiders that the spawner can produce
var/list/potentialspawns = list(
/mob/living/basic/spider/growing/spiderling/nurse,
@@ -122,10 +125,11 @@
/obj/effect/mob_spawn/ghost_role/spider/special(mob/living/basic/spider/spawned_mob, mob/mob_possessor)
. = ..()
- spawned_mob.directive = directive
+ if (isspider(spawned_mob))
+ spawned_mob.directive = directive
egg.spawner = null
QDEL_NULL(egg)
- var/datum/antagonist/spider/spider_antag = new(directive)
+ var/datum/antagonist/spider/spider_antag = new granted_datum(directive)
spawned_mob.mind.add_antag_datum(spider_antag)
/obj/effect/mob_spawn/ghost_role/spider/enriched
@@ -141,14 +145,17 @@
/obj/effect/mob_spawn/ghost_role/spider/bloody
name = "bloody egg cluster"
- color = rgb(255, 0, 0)
- you_are_text = "You are a bloody spider."
+ icon = 'icons/mob/simple/meteor_heart.dmi'
+ icon_state = "eggs"
+ you_are_text = "You are a flesh spider."
flavour_text = "An abomination of nature set upon the station by changelings. Your only goal is to kill, terrorize, and survive."
- directive = "You are the spawn of a vicious changeling. You have no ambitions except to wreak havoc and ensure your own survival. You are aggressive to all living beings outside of your species, including changelings."
+ faction = list()
+ directive = null
cluster_type = /obj/structure/spider/eggcluster/bloody
potentialspawns = list(
- /mob/living/basic/spider/giant/hunter/flesh,
+ /mob/living/basic/flesh_spider,
)
+ granted_datum = /datum/antagonist/spider/flesh
/obj/effect/mob_spawn/ghost_role/spider/midwife
name = "midwife egg cluster"
@@ -170,6 +177,14 @@
* * newname - If set, renames the mob to this name
*/
/obj/effect/mob_spawn/ghost_role/spider/create(mob/user, newname)
+ var/chosen_spider = length(potentialspawns) > 1 ? get_radial_choice(user) : potentialspawns[1]
+ if(QDELETED(src) || QDELETED(user) || isnull(chosen_spider))
+ return FALSE
+ mob_type = chosen_spider
+ return ..()
+
+/// Pick a spider type from a radial menu
+/obj/effect/mob_spawn/ghost_role/spider/proc/get_radial_choice(mob/user)
var/list/spider_list = list()
var/list/display_spiders = list()
for(var/choice in potentialspawns)
@@ -191,9 +206,6 @@
display_spiders[initial(spider.name)] = option
sort_list(display_spiders)
+
var/chosen_spider = show_radial_menu(user, egg, display_spiders, radius = 38)
- chosen_spider = spider_list[chosen_spider]
- if(QDELETED(src) || QDELETED(user) || !chosen_spider)
- return FALSE
- mob_type = chosen_spider
- return ..()
+ return spider_list[chosen_spider]
diff --git a/code/modules/research/xenobiology/crossbreeding/burning.dm b/code/modules/research/xenobiology/crossbreeding/burning.dm
index 1b14d8221c47..e29856e5b326 100644
--- a/code/modules/research/xenobiology/crossbreeding/burning.dm
+++ b/code/modules/research/xenobiology/crossbreeding/burning.dm
@@ -80,7 +80,13 @@ Burning extracts:
effect_desc = "Instantly destroys walls around you."
/obj/item/slimecross/burning/metal/do_effect(mob/user)
- for(var/turf/closed/wall/W in range(1,get_turf(user)))
+//monkestation edit start
+ var/turf/our_turf = get_turf(src)
+ if(GLOB.clock_ark && on_reebe(our_turf) && get_dist(our_turf, GLOB.clock_ark) <= ARK_TURF_DESTRUCTION_BLOCK_RANGE)
+ balloon_alert(user, "a near by energy source is stopping \the [src] from activating!")
+ return FALSE
+//monkestation edit end
+ for(var/turf/closed/wall/W in range(1, our_turf)) //monkestation edit: replaces get_turf(src) with our_turf
W.dismantle_wall(1)
playsound(W, 'sound/effects/break_stone.ogg', 50, TRUE)
user.visible_message(span_danger("[src] pulses violently, and shatters the walls around it!"))
diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm
index 683bdc2d647c..90b64bf26a1c 100644
--- a/code/modules/shuttle/supply.dm
+++ b/code/modules/shuttle/supply.dm
@@ -27,6 +27,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
/obj/machinery/teleport/station,
/obj/narsie,
/obj/projectile/beam/wormhole,
+ /obj/ratvar, //monkestation edit
/obj/structure/blob,
/obj/structure/checkoutmachine,
/obj/structure/disposalpipe,
diff --git a/monkestation/code/controllers/subsystem/economy.dm b/monkestation/code/controllers/subsystem/economy.dm
new file mode 100644
index 000000000000..c01d3e1abdd7
--- /dev/null
+++ b/monkestation/code/controllers/subsystem/economy.dm
@@ -0,0 +1,3 @@
+/datum/controller/subsystem/economy
+ ///the mail crate we last generated
+ var/obj/structure/closet/crate/mail/economy/mail_crate
diff --git a/monkestation/code/datums/components/multi_hit.dm b/monkestation/code/datums/components/multi_hit.dm
index 7c439ebf6631..8e7a2a8f6e97 100644
--- a/monkestation/code/datums/components/multi_hit.dm
+++ b/monkestation/code/datums/components/multi_hit.dm
@@ -23,6 +23,9 @@
///this is incredibly cursed i should probably move the default defines into this to make it not have a ton of if statements but that feels wrong aswell.
/datum/component/multi_hit/Initialize(continues_travel, icon_state, icon, width, height, center_offset, attacking_direction, after_hit_callback, pre_hit_callback, stamina_cost)
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+
if(continues_travel)
src.continues_travel = continues_travel
if(icon_state)
diff --git a/monkestation/code/datums/components/throw_bounce.dm b/monkestation/code/datums/components/throw_bounce.dm
index 771340281d07..b51f3594a011 100644
--- a/monkestation/code/datums/components/throw_bounce.dm
+++ b/monkestation/code/datums/components/throw_bounce.dm
@@ -22,7 +22,6 @@
/datum/component/throw_bounce/Initialize(bounce_cooldown, bounce_charge_max, bounce_recharge_rate, targeting_range)
- . = ..()
if(!isitem(parent)) //cant throw non-items
return COMPONENT_INCOMPATIBLE
diff --git a/monkestation/code/datums/components/turf_checker_complex.dm b/monkestation/code/datums/components/turf_checker_complex.dm
new file mode 100644
index 000000000000..9f6ac5197c90
--- /dev/null
+++ b/monkestation/code/datums/components/turf_checker_complex.dm
@@ -0,0 +1,167 @@
+//a simple element that listens for passed signals and then returns based on if get_turf's type is within valid_turfs, if you need more then use the /complex subtype
+/datum/component/turf_checker
+ dupe_mode = COMPONENT_DUPE_ALLOWED
+ ///list of turf types that are valid for us
+ var/list/valid_turfs
+ ///the signal we listen for
+ var/registered_signal
+ ///do we listen for COMSIG_MOVABLE_MOVED
+ var/check_on_move
+ ///our last validity state, used to save on checks
+ var/last_validity_state = FALSE
+ ///a ref to the loc that we listen to the movement of to send our signals
+ var/atom/watched_holder
+ ///our parent's recursive locs minus it and watched_holder
+ var/list/trimmed_recursive_locs
+
+/**
+ * valid_turfs - the list of turf types that are valid for when we check
+ * registered_signal - the signal from parent we listen for to call on_signal_recieved()
+ * check_on_move - should we check turf every time our parent calls Moved()
+ * update_state_proc - proc for parent to call when we send COMSIG_TURF_CHECKER_UPDATE_STATE
+ * register_loc - should we listen to Moved() on locs of our parent as well
+ */
+/datum/component/turf_checker/Initialize(list/valid_turfs, registered_signal, check_on_move = FALSE, update_state_proc, register_loc = TRUE)
+ if(!ismovable(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.valid_turfs = valid_turfs
+ src.registered_signal = registered_signal
+ src.check_on_move = check_on_move
+ if(update_state_proc && check_on_move)
+ parent.RegisterSignal(src, COMSIG_TURF_CHECKER_UPDATE_STATE, update_state_proc)
+
+/datum/component/turf_checker/RegisterWithParent()
+ if(registered_signal)
+ RegisterSignal(parent, registered_signal, PROC_REF(on_signal_recieved))
+
+ if(check_on_move)
+ RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_attached_moved))
+ trimmed_recursive_locs = list()
+ get_new_locs(parent)
+ check_turf(parent)
+
+/datum/component/turf_checker/UnregisterFromParent()
+ if(registered_signal)
+ UnregisterSignal(parent, registered_signal)
+
+ if(check_on_move)
+ parent.UnregisterSignal(src, COMSIG_TURF_CHECKER_UPDATE_STATE)
+ UnregisterSignal(parent, COMSIG_MOVABLE_MOVED)
+
+ if(watched_holder != parent)
+ UnregisterSignal(watched_holder, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING, COMSIG_ATOM_ABSTRACT_EXITED))
+
+ for(var/atom/recursive_loc in trimmed_recursive_locs)
+ UnregisterSignal(recursive_loc, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING))
+
+ parent = null
+ watched_holder = null
+ trimmed_recursive_locs = null
+
+/datum/component/turf_checker/proc/on_signal_recieved(atom/movable/checked_atom, atom/movable/check_override, do_check_turf = TRUE, register_to, unregister_from)
+ SIGNAL_HANDLER
+ if(register_to) //keeping these here in case your use case can handle this on the attached atom in a cheaper way than the /complex subtype
+ RegisterSignal(register_to, COMSIG_MOVABLE_MOVED, PROC_REF(check_turf_parent_only))
+
+ if(unregister_from)
+ UnregisterSignal(unregister_from, COMSIG_MOVABLE_MOVED)
+
+ if(do_check_turf)
+ return check_turf(checked_atom, check_override)
+
+//so we dont override checked_atom with old_loc
+/datum/component/turf_checker/proc/check_turf_parent_only(atom/movable/checked_atom)
+ SIGNAL_HANDLER
+ check_turf(checked_atom)
+
+/datum/component/turf_checker/proc/check_turf(atom/movable/checked_atom, atom/movable/check_override)
+ SIGNAL_HANDLER
+ if(check_override)
+ checked_atom = check_override
+
+ var/turf/checked_turf_type = get_turf(checked_atom)
+ if(!checked_turf_type)
+ return COMPONENT_CHECKER_INVALID_TURF
+
+ checked_turf_type = checked_turf_type.type
+ if(!(checked_turf_type in valid_turfs))
+ if(check_on_move && last_validity_state)
+ last_validity_state = FALSE
+ SEND_SIGNAL(src, COMSIG_TURF_CHECKER_UPDATE_STATE, FALSE, checked_atom)
+ return COMPONENT_CHECKER_INVALID_TURF
+
+ if(check_on_move && !last_validity_state)
+ last_validity_state = TRUE
+ SEND_SIGNAL(src, COMSIG_TURF_CHECKER_UPDATE_STATE, TRUE, checked_atom)
+ return COMPONENT_CHECKER_VALID_TURF
+
+/datum/component/turf_checker/proc/get_new_locs()
+ if(QDELETED(parent))
+ return
+
+ var/atom/movable/movable_parent = parent
+ var/list/attached_locs = movable_parent.get_locs_recursive()
+ var/atom/highest_holder = attached_locs[length(attached_locs)]
+ if(highest_holder == parent && watched_holder == parent)
+ return
+
+ var/list/old_recursive_locs = trimmed_recursive_locs
+ trimmed_recursive_locs = list()
+ if(watched_holder != highest_holder)
+ if(watched_holder != parent)
+ UnregisterSignal(watched_holder, list(COMSIG_MOVABLE_MOVED, COMSIG_ATOM_ABSTRACT_EXITED, COMSIG_QDELETING))
+
+ if(highest_holder != parent)
+ watched_holder = highest_holder
+ RegisterSignal(highest_holder, COMSIG_MOVABLE_MOVED, PROC_REF(check_turf_parent_only))
+ RegisterSignal(highest_holder, COMSIG_ATOM_ABSTRACT_EXITED, PROC_REF(on_holder_exited))
+ RegisterSignal(highest_holder, COMSIG_QDELETING, PROC_REF(on_loc_qdeleted))
+ if(length(attached_locs) > 2)
+ trimmed_recursive_locs = attached_locs - list(attached_locs[1], highest_holder)
+ for(var/atom/recursive_loc in trimmed_recursive_locs)
+ if(!QDELETED(recursive_loc))
+ if(recursive_loc in old_recursive_locs)
+ old_recursive_locs -= recursive_loc
+ else
+ RegisterSignal(recursive_loc, COMSIG_MOVABLE_MOVED, PROC_REF(check_holder))
+ RegisterSignal(recursive_loc, COMSIG_QDELETING, PROC_REF(on_loc_qdeleted))
+ else
+ watched_holder = parent
+
+ for(var/atom/old_recursive_loc in old_recursive_locs)
+ UnregisterSignal(old_recursive_loc, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING))
+
+/datum/component/turf_checker/proc/check_holder(atom/movable/moved)
+ SIGNAL_HANDLER
+ var/atom/movable/movable_parent = parent
+ if(movable_parent.get_highest_non_turf_loc() != watched_holder)
+ get_new_locs()
+
+/datum/component/turf_checker/proc/on_attached_moved(atom/movable/moved, atom/old_loc)
+ SIGNAL_HANDLER
+ if(watched_holder == parent)
+ var/atom/movable/movable_parent = parent
+ if(movable_parent.get_highest_non_turf_loc() == parent)
+ check_turf(moved)
+ return
+
+ get_new_locs()
+ check_turf(moved)
+
+/datum/component/turf_checker/proc/on_holder_exited(atom/exited, atom/movable/gone)
+ SIGNAL_HANDLER
+ var/atom/movable/movable_parent = parent
+ if(gone == parent || movable_parent.get_highest_non_turf_loc() != watched_holder)
+ get_new_locs()
+ check_turf(parent)
+
+/datum/component/turf_checker/proc/on_loc_qdeleted(atom/destroyed, forced)
+ SIGNAL_HANDLER
+ UnregisterSignal(destroyed, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING))
+ if(destroyed == watched_holder)
+ UnregisterSignal(destroyed, COMSIG_ATOM_ABSTRACT_EXITED)
+ watched_holder = null
+ else
+ trimmed_recursive_locs -= destroyed
+ get_new_locs()
diff --git a/monkestation/code/datums/components/wound_converter.dm b/monkestation/code/datums/components/wound_converter.dm
new file mode 100644
index 000000000000..08fcbc7ef371
--- /dev/null
+++ b/monkestation/code/datums/components/wound_converter.dm
@@ -0,0 +1,32 @@
+/datum/component/wound_converter
+ ///What priority does this converter have, higher priority will override lower priority converters if they both try and act on the same wound
+ var/priority = 0
+
+ var/list/exact_type_conversions = list()
+
+ var/list/general_type_conversions = list()
+ ///Assoc list of wounds with values of their highest handled priority
+ var/static/list/wounds_to_convert = list()
+
+/datum/component/wound_converter/Initialize(priority = 0, exact_type_conversions = list(), general_type_conversions = list())
+ if(!iscarbon(parent) && !isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.priority = priority
+ src.exact_type_conversions = exact_type_conversions
+ src.general_type_conversions = general_type_conversions
+
+/datum/component/wound_converter/RegisterWithParent()
+ if(iscarbon(parent))
+// RegisterSignal(parent, COMSIG_PRE_CARBON_GAIN_WOUND, PROC_REF(on_pre_wound))
+ return
+ if(istype(parent, /obj/item/bodypart))
+ return
+
+/datum/component/wound_converter/Destroy(force, silent)
+ . = ..()
+ //var/eee = exact_type_conversions[item] || general_type_conversions[item]
+
+/datum/component/wound_converter/proc/on_pre_wound()
+ SIGNAL_HANDLER
+ return
diff --git a/monkestation/code/datums/random_engines/boxstation.dm b/monkestation/code/datums/random_engines/boxstation.dm
deleted file mode 100644
index 546248c4887e..000000000000
--- a/monkestation/code/datums/random_engines/boxstation.dm
+++ /dev/null
@@ -1,20 +0,0 @@
-// *!! BOXSTATION ENGINES !!*
-/datum/map_template/random_room/random_engines/box_supermatter
- name = "Box Supermatter"
- room_id = "box_supermatter"
- mappath = "monkestation/_maps/RandomEngines/BoxStation/supermatter.dmm"
- centerspawner = FALSE
- template_height = 26
- template_width = 29
- weight = 8
- station_name = "BoxStation"
-
-/datum/map_template/random_room/random_engines/box_singularity
- name = "Box Singularity"
- room_id = "box_singularity"
- mappath = "monkestation/_maps/RandomEngines/BoxStation/singularity.dmm"
- centerspawner = FALSE
- template_height = 26
- template_width = 29
- weight = 8
- station_name = "BoxStation"
diff --git a/monkestation/code/game/objects/items/stickers.dm b/monkestation/code/game/objects/items/stickers.dm
index 8d5f07fd9436..a388dbc7387b 100644
--- a/monkestation/code/game/objects/items/stickers.dm
+++ b/monkestation/code/game/objects/items/stickers.dm
@@ -75,10 +75,18 @@
/obj/item/storage/box/monkestation_stickers
name = "Box of Ook Certified Stickers"
desc = "Signed off on by the duke himself!"
-
+ ///How many stickers do we fill this box with
var/sticker_count = 30
+ ///List of sticker types we are restricted to picking from, if any
+ var/list/allowed_sticker_types
/obj/item/storage/box/monkestation_stickers/PopulateContents()
+ var/list/subtypes_list = subtypesof(/obj/item/sticker/monkestation)
for(var/i = 1 to sticker_count)
- var/obj/item/sticker/sticker_type = pick(subtypesof(/obj/item/sticker/monkestation))
+ var/obj/item/sticker/sticker_type = pick(allowed_sticker_types || subtypes_list)
new sticker_type(src)
+
+/obj/item/storage/box/monkestation_stickers/bad_time
+ name = "Box of Uncertified Leak Stickers"
+ desc = "NOT signed off on by NT."
+ allowed_sticker_types = list(/obj/item/sticker/monkestation/bad_times)
diff --git a/monkestation/code/game/objects/items/turf_demolisher.dm b/monkestation/code/game/objects/items/turf_demolisher.dm
new file mode 100644
index 000000000000..f4f0a9d3eb07
--- /dev/null
+++ b/monkestation/code/game/objects/items/turf_demolisher.dm
@@ -0,0 +1,78 @@
+//simply an item that breaks turfs down
+/obj/item/turf_demolisher
+ name = "\improper Exprimental Demolisher"
+ desc = "An exprimental able to quickly deconstruct any surface."
+ icon = 'icons/obj/mining.dmi'
+ lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi'
+ icon_state = "jackhammer"
+ inhand_icon_state = "jackhammer"
+ ///The balloon_alert() to send when we cannot demolish a turf
+ var/unbreakable_alert = "Unable to demolish that."
+ ///List of turf types we are allowed to break, if unset then we can break any turfs that dont have the INDESTRUCTIBLE resistance flag
+ var/list/allowed_types = list(/turf/closed/wall)
+ ///List of turf types we are NOT allowed to break
+ var/list/blacklisted_types
+ ///How long is the do_after() to break a turf
+ var/break_time = 8 SECONDS
+ ///Do we devastate broken walls, because of quality 7 year old code this always makes iron no matter the wall type
+ var/devastate = TRUE
+ ///How long is our recharge time between uses
+ var/recharge_time = 0
+ COOLDOWN_DECLARE(recharge)
+
+/obj/item/turf_demolisher/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ if(!proximity_flag || !isturf(target) || (user.istate & ISTATE_HARM))
+ return
+
+ if(!check_breakble(target, user, click_parameters))
+ return
+
+ if(try_demolish(target, user))
+ return
+
+/obj/item/turf_demolisher/proc/check_breakble(turf/attacked_turf, mob/living/user, params)
+ if(recharge_time && !COOLDOWN_FINISHED(src, recharge))
+ balloon_alert(user, "\The [src] is still recharging.")
+ return FALSE
+
+ if((allowed_types && !is_type_in_list(attacked_turf, allowed_types)) || is_type_in_list(attacked_turf, blacklisted_types) || (attacked_turf.resistance_flags & INDESTRUCTIBLE))
+ if(unbreakable_alert)
+ balloon_alert(user, unbreakable_alert)
+ return FALSE
+ return TRUE
+
+/obj/item/turf_demolisher/proc/try_demolish(turf/attacked_turf, mob/living/user)
+ if(!do_after(user, break_time, attacked_turf))
+ return FALSE
+
+ playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 80, channel = CHANNEL_SOUND_EFFECTS, mixer_channel = CHANNEL_SOUND_EFFECTS)
+ if(iswallturf(attacked_turf))
+ var/turf/closed/wall/wall_turf = attacked_turf
+ wall_turf.dismantle_wall(devastate)
+ else
+ attacked_turf.ScrapeAway()
+
+ if(recharge_time)
+ COOLDOWN_START(src, recharge, recharge_time)
+ return TRUE
+
+/obj/item/turf_demolisher/reebe
+ desc = "An exprimental able to quickly deconstruct any surface. This one seems to be calibrated to only work on reebe."
+ break_time = 5 SECONDS
+ recharge_time = 5 SECONDS
+
+/obj/item/turf_demolisher/reebe/check_breakble(turf/attacked_turf, mob/living/user, params)
+ . = ..()
+ if(!.)
+ return
+
+ var/turf/our_turf = get_turf(src)
+ if(!on_reebe(our_turf))
+ balloon_alert(user, "\The [src] is specially calibrated to be used on reebe and will not work here!")
+ return FALSE
+
+ if(GLOB.clock_ark && get_dist(our_turf, get_turf(GLOB.clock_ark)) <= ARK_TURF_DESTRUCTION_BLOCK_RANGE)
+ balloon_alert(user, "A near by energy source is interfering \the [src]!")
+ return FALSE
diff --git a/monkestation/code/modules/antagonists/clock_cult/actions/add_warp_area.dm b/monkestation/code/modules/antagonists/clock_cult/actions/add_warp_area.dm
new file mode 100644
index 000000000000..b5587606ece8
--- /dev/null
+++ b/monkestation/code/modules/antagonists/clock_cult/actions/add_warp_area.dm
@@ -0,0 +1,94 @@
+///How much do we subtract from the base cose of adding a new area
+#define AREAS_TO_IGNORE_FOR_COST 10
+///How many areas are observation consoles able to warp to at the start
+#define STARTING_WARP_AREAS 8
+
+/datum/action/innate/clockcult/add_warp_area
+ name = "Add Warp Area"
+ desc = "Add an additional area observation consoles can warp to."
+ button_icon_state = "Spatial Warp"
+ ///a cache of areas we can are to the warpable list
+ var/static/list/cached_addable_areas
+ ///what area types are we blocked from warping to
+ var/static/list/blocked_areas = typecacheof(list(/area/station/service/chapel, /area/station/ai_monitored))
+ ///what area types cost double
+ var/static/list/costly_areas = typecacheof(list(/area/station/command, /area/station/security))
+
+/datum/action/innate/clockcult/add_warp_area/New(Target)
+ . = ..()
+ if(!cached_addable_areas)
+ build_addable_areas()
+
+/datum/action/innate/clockcult/add_warp_area/IsAvailable(feedback)
+ if(!IS_CLOCK(owner))
+ return FALSE
+ return ..()
+
+/datum/action/innate/clockcult/add_warp_area/Activate()
+ if(!cached_addable_areas || !length(cached_addable_areas))
+ return
+
+ var/area/input_area = tgui_input_list(owner, "Select an area to add.", "Add Area", cached_addable_areas)
+ if(!input_area)
+ return
+
+ var/cost = max((length(GLOB.clock_warp_areas) * 3) - (STARTING_WARP_AREAS * 3), 0)
+ if(is_type_in_typecache(input_area.type, costly_areas))
+ cost *= 2
+
+ if(tgui_alert(owner, "Are you sure you want to add [input_area]? It will cost [cost] vitality.", "Add Area", list("Yes", "No")) == "Yes")
+ if(GLOB.clock_vitality < cost)
+ to_chat(span_brass("Not enough vitality."))
+ return
+
+ if(input_area in GLOB.clock_warp_areas)
+ return
+
+ GLOB.clock_warp_areas += input_area
+ cached_addable_areas -= input_area
+ send_clock_message(null, "[input_area] added to warpable areas.")
+
+/datum/action/innate/clockcult/add_warp_area/proc/choose_starting_warp_areas()
+ if(!cached_addable_areas || !length(cached_addable_areas))
+ return
+
+ //shuffle_inplace(cached_addable_areas) //this is so our picked maint areas are random without needing to do anything weird
+ var/sanity = 0
+ var/added_areas = 0
+ var/list/temp_list = cached_addable_areas.Copy()
+ while(added_areas < STARTING_WARP_AREAS && sanity < 100 && length(temp_list))
+ sanity++
+ /*if(i <= 2) //always give them 2 maint areas to hopefully be easy to warp from
+ var/area/station/maintenance/maint_area = locate() in cached_addable_areas
+ if(maint_area)
+ cached_addable_areas -= maint_area
+ GLOB.clock_warp_areas += maint_area
+ continue*/ //for if I implement abscond restrictions
+ var/area/picked_area = pick(temp_list)
+ temp_list -= picked_area
+ if(is_type_in_typecache(picked_area.type, costly_areas))
+ continue
+
+ added_areas++
+ GLOB.clock_warp_areas += picked_area
+ cached_addable_areas -= picked_area
+
+/datum/action/innate/clockcult/add_warp_area/proc/build_addable_areas()
+ cached_addable_areas = list()
+ for(var/area/station_area as anything in GLOB.the_station_areas)
+ station_area = GLOB.areas_by_type[station_area]
+ if(station_area.outdoors || (station_area.area_flags & ABDUCTOR_PROOF) || is_type_in_typecache(station_area, blocked_areas) || (station_area in GLOB.clock_warp_areas))
+ continue
+ cached_addable_areas += station_area
+
+/datum/action/innate/clockcult/show_warpable_areas
+ name = "Warpable Areas"
+ desc = "Display what areas are currently warpable to by observation consoles."
+ button_icon_state = "console_info"
+
+/datum/action/innate/clockcult/show_warpable_areas/Activate()
+ to_chat(owner, examine_block(span_brass("Current areas observation consoles can warp to: [english_list(GLOB.clock_warp_areas)]
\
+ You can add additional areas with the \"Add Warp Area\" action."))) //anyone who has this action should also have add warp area
+
+#undef AREAS_TO_IGNORE_FOR_COST
+#undef STARTING_WARP_AREAS
diff --git a/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cult_team.dm b/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cult_team.dm
index 2e664728243e..50563e408926 100644
--- a/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cult_team.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cult_team.dm
@@ -16,6 +16,8 @@ GLOBAL_DATUM(main_clock_cult, /datum/team/clock_cult)
var/list/non_human_servants = list()
/// what warning stage are we at
var/warning_stage = CONVERSION_WARNING_NONE
+ /// have we used our recall
+ var/member_recalled = FALSE
/datum/team/clock_cult/add_member(datum/mind/new_member)
. = ..()
@@ -67,10 +69,11 @@ GLOBAL_DATUM(main_clock_cult, /datum/team/clock_cult)
///check how many human members we have and anything that goes with that
/datum/team/clock_cult/proc/check_member_count()
check_member_distribution()
- max_human_servants = round(max((get_active_player_count() / 8) + 6, max_human_servants))
+ max_human_servants = round(max((get_active_player_count() / 8), max_human_servants))
var/human_servant_count = length(human_servants)
var/main_message = "The Ark will be torn open if [max_human_servants - human_servant_count] more minds are converted to the faith of Rat'var\
- [get_charged_anchor_crystals() >= 2 ? "." : "and two Anchoring Crystals are summoned and protected on the station."]"
+ [get_charged_anchor_crystals() >= ANCHORING_CRYSTALS_TO_SUMMON ? "." : " and \
+ [ANCHORING_CRYSTALS_TO_SUMMON] Anchoring Crystal[ANCHORING_CRYSTALS_TO_SUMMON > 1 ? "s are" : " is"] summoned and protected on the station."]"
if((human_servant_count * 2) > max_human_servants && warning_stage < CONVERSION_WARNING_HALFWAY)
send_clock_message(null, span_bigbrass("Rat'var's influence is growing. [main_message]"), sent_sound = 'sound/magic/clockwork/scripture_tier_up.ogg')
@@ -87,7 +90,7 @@ GLOBAL_DATUM(main_clock_cult, /datum/team/clock_cult)
sent_sound = 'sound/magic/clockwork/scripture_tier_up.ogg')
warning_stage = CONVERSION_WARNING_CRITIAL
- else if((human_servant_count >= max_human_servants) && get_charged_anchor_crystals() >= 2)
+ else if((human_servant_count >= max_human_servants) && get_charged_anchor_crystals() >= ANCHORING_CRYSTALS_TO_SUMMON)
GLOB.clock_ark?.prepare_ark()
///check that our human_servants and non_human_servants lists are correct and if not then set them to be correct
@@ -142,11 +145,13 @@ GLOBAL_DATUM(main_clock_cult, /datum/team/clock_cult)
update_explanation_text()
/datum/objective/anchoring_crystals/update_explanation_text()
- explanation_text = "Summon two anchorings crystals on the station and protect them for 5 minutes to allow the ark to open. \
- Up to 2 crystals can be created for extra power, however, the crew will be alerted and they must be summoned in [english_list(valid_areas)]."
+ var/plural = ANCHORING_CRYSTALS_TO_SUMMON > 1
+ explanation_text = "Summon [ANCHORING_CRYSTALS_TO_SUMMON] anchoring crystal[plural ? "s" : ""] on the station and protect [plural ? "them" : "it"] for 5 \
+ minutes to allow the ark to open. Crystals after the first one must be summoned in [english_list(valid_areas)]. \
+ Up to 2 additional crystals can be created for extra power."
/datum/objective/anchoring_crystals/check_completion()
- return get_charged_anchor_crystals() >= 2 || completed
+ return get_charged_anchor_crystals() >= ANCHORING_CRYSTALS_TO_SUMMON || completed
/datum/objective/ratvar
explanation_text = "Protect The Ark so that Rat'var may enlighten this world!"
diff --git a/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cultist.dm b/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cultist.dm
index 0cf8285fe20a..3c4ff85f2aa8 100644
--- a/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cultist.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/antag_datums/clock_cultist.dm
@@ -16,8 +16,6 @@
var/datum/team/clock_cult/clock_team
///should we directly give them a slab or not
var/give_slab = TRUE
- ///our overlay for after the assault begins
- var/mutable_appearance/forbearance
///ref to our turf_healing component, used for deletion when deconverted
var/datum/component/turf_healing/owner_turf_healing
///used for holy water deconversion, slightly easier to have this here then on the team, might want to refactor this to an assoc global list
@@ -80,13 +78,10 @@
recall.Grant(current)
owner_turf_healing = current.AddComponent(/datum/component/turf_healing, healing_types = list(TOX = 4), \
- healing_turfs = list(/turf/open/floor/bronze, /turf/open/indestructible/reebe_flooring))
+ healing_turfs = GLOB.clock_turf_types)
RegisterSignal(current, COMSIG_CLOCKWORK_SLAB_USED, PROC_REF(switch_recall_slab))
handle_clown_mutation(current, mob_override ? null : "The light of Rat'var allows you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
- ADD_TRAIT(current, TRAIT_KNOW_ENGI_WIRES, CULT_TRAIT)
- if(ishuman(current) && GLOB.clock_ark?.current_state >= 2) //active state value
- forbearance = mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER)
- current.add_overlay(forbearance)
+ add_forbearance(current)
/datum/antagonist/clock_cultist/remove_innate_effects(mob/living/mob_override)
. = ..()
@@ -94,15 +89,13 @@
current.faction -= FACTION_CLOCK
current.remove_language(/datum/language/ratvar, TRUE, TRUE, LANGUAGE_CULTIST)
current.clear_alert("clockinfo")
+ current.remove_filter("forbearance")
if(!iseminence(current))
communicate.Remove(current)
recall.Remove(current)
UnregisterSignal(current, COMSIG_CLOCKWORK_SLAB_USED)
QDEL_NULL(owner_turf_healing)
handle_clown_mutation(current, removing = FALSE)
- REMOVE_TRAIT(current, TRAIT_KNOW_ENGI_WIRES, CULT_TRAIT)
- if(forbearance)
- current.cut_overlay(list(forbearance))
/datum/antagonist/clock_cultist/can_be_owned(datum/mind/new_owner)
. = ..()
@@ -204,55 +197,55 @@
if(object.type in GLOB.types_to_drop_on_clock_deonversion)
current.dropItemToGround(object, TRUE, TRUE)
+/datum/antagonist/clock_cultist/proc/add_forbearance(mob/apply_to)
+ if(GLOB.clock_ark?.current_state >= ARK_STATE_ACTIVE)
+ apply_to.add_filter("forbearance", 3, list("type" = "outline", "color" = "#FAE48E", "size" = 2, "alpha" = 100))
+
/datum/antagonist/clock_cultist/eminence
name = "Eminence"
give_slab = FALSE
antag_moodlet = null
- show_to_ghosts = TRUE
communicate = null
recall = null
- //all our innate actions
- var/datum/action/innate/clockcult/space_fold/trigger_events = new
- var/datum/action/cooldown/eminence/purge_reagents/remove_water = new
- var/datum/action/cooldown/eminence/linked_abscond/recall_servant = new
- var/datum/action/innate/clockcult/teleport_to_servant/find_servant = new
- var/datum/action/innate/clockcult/teleport_to_station/to_station = new
- var/datum/action/innate/clockcult/eminence_abscond/return_home = new
+ ///The list of our actions
+ var/list/action_list = list(
+ /datum/action/innate/clockcult/space_fold,
+ /datum/action/cooldown/eminence/purge_reagents,
+ /datum/action/cooldown/eminence/linked_abscond,
+ /datum/action/innate/clockcult/teleport_to_servant,
+ /datum/action/innate/clockcult/teleport_to_station,
+ /datum/action/innate/clockcult/eminence_abscond,
+ /datum/action/innate/clockcult/show_warpable_areas,
+ /datum/action/innate/clockcult/add_warp_area,
+ )
/datum/antagonist/clock_cultist/eminence/Destroy()
- QDEL_NULL(trigger_events)
+ QDEL_LIST(action_list)
return ..()
/datum/antagonist/clock_cultist/eminence/greet()
- to_chat(owner.current, span_bigbrass("You are the Eminence, a being bound to Rat'var. By his light you are able to influence nearby space and time."))
- to_chat(owner.current, span_brass("Your abilities: As the Eminence you have access to various abilities, they are as follows. \
- You may click on various machines to interface with them or a servant to mark them."))
- to_chat(owner.current, span_brass("Purge Reagents: Remove all reagents from the bloodstream of a marked servant, this is useful for a servant who is being deconverted by holy water."))
- to_chat(owner.current, span_brass("Linked Abscond: Return a marked servant and anything they are pulling to reebe, this has a lengthy cooldown and they must remain still for 7 seconds."))
- to_chat(owner.current, span_brass("Space Fold: Fold local spacetime to ensure certain \"events\" are inflicted upon the station, while doing this will cost cogs, \
- these cogs are not taken from the cult itself. The cooldown is based on the cog cost of the event."))
- to_chat(owner.current, span_brass("You can also teleport yourself to any other servant, useful for servants who need to be absconded like those which are dead or being deconverted."))
+ to_chat(owner.current, examine_block("[span_bigbrass("You are the Eminence, a being bound to Rat'var. By his light you are able to influence nearby space and time.")]
\
+ [span_brass("As the Eminence you have access to various abilities, they are as follows.
\
+ You may click on various machines to interface with them or a servant to mark them.
\
+ Purge Reagents: Remove all reagents from the bloodstream of a marked servant, this is useful for a servant who is being deconverted by holy water.
\
+ Linked Abscond: Return a marked servant and anything they are pulling to reebe, this has a lengthy cooldown and they must remain still for 7 seconds.
\
+ Space Fold: Fold local spacetime to ensure certain \"events\" are inflicted upon the station, while doing this will cost cogs, \
+ these cogs are not taken from the cult itself. The cooldown is based on the cog cost of the event.
\
+ You can also teleport yourself to any other servant, useful for servants who need to be absconded like those which are dead or being deconverted.")]"))
/datum/antagonist/clock_cultist/eminence/apply_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
add_team_hud(current, /datum/antagonist/clock_cultist)
- trigger_events.Grant(current)
- remove_water.Grant(current)
- recall_servant.Grant(current)
- find_servant.Grant(current)
- to_station.Grant(current)
- return_home.Grant(current)
+ for(var/datum/action/our_action as anything in action_list)
+ if(ispath(our_action))
+ our_action = new our_action()
+ our_action.Grant(current)
/datum/antagonist/clock_cultist/eminence/remove_innate_effects(mob/living/mob_override)
. = ..()
- var/mob/living/current = owner.current
- trigger_events.Remove(current)
- remove_water.Remove(current)
- recall_servant.Remove(current)
- find_servant.Remove(current)
- to_station.Remove(current)
- return_home.Remove(current)
+ for(var/datum/action/removed_action in action_list)
+ removed_action.Remove(owner.current)
/datum/antagonist/clock_cultist/eminence/on_removal() //this should never happen without an admin being involved, something has gone wrong
to_chat(owner.current, span_userdanger("You lost your eminence antagonist status! This should not happen and you should ahelp(f1) unless you are already talking to an admin."))
diff --git a/monkestation/code/modules/antagonists/clock_cult/enchantments/_enchantment.dm b/monkestation/code/modules/antagonists/clock_cult/enchantments/_enchantment.dm
index 2a0c9de6a45c..7a94e9d598e6 100644
--- a/monkestation/code/modules/antagonists/clock_cult/enchantments/_enchantment.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/enchantments/_enchantment.dm
@@ -9,6 +9,9 @@
/datum/component/enchantment/Initialize()
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
+
+ if(on_reebe(parent)) //currently this is only added by stargazers so this should work fine
+ max_level = 1
//Get random level
level = rand(1, max_level)
//Apply effect
diff --git a/monkestation/code/modules/antagonists/clock_cult/enchantments/haste.dm b/monkestation/code/modules/antagonists/clock_cult/enchantments/haste.dm
index 5dc6732fdf97..d6e36aeaa653 100644
--- a/monkestation/code/modules/antagonists/clock_cult/enchantments/haste.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/enchantments/haste.dm
@@ -3,4 +3,4 @@
max_level = 1
/datum/component/enchantment/haste/apply_effect(obj/item/target)
- target.attack_speed = min(CLICK_CD_FAST_MELEE, target.attack_speed)
+ target.attack_speed = max(1, target.attack_speed - 2)
diff --git a/monkestation/code/modules/antagonists/clock_cult/enchantments/penetration.dm b/monkestation/code/modules/antagonists/clock_cult/enchantments/penetration.dm
index 76286176ce52..2ed66718131d 100644
--- a/monkestation/code/modules/antagonists/clock_cult/enchantments/penetration.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/enchantments/penetration.dm
@@ -1,5 +1,5 @@
/datum/component/enchantment/penetration
- examine_description = "It has been blessed with the gift of armour penetration, allowing it to cut through targets with ease."
+ examine_description = "It has been blessed with the gift of armor penetration, allowing it to cut through targets with ease."
max_level = 5
/datum/component/enchantment/penetration/apply_effect(obj/item/target)
diff --git a/monkestation/code/modules/antagonists/clock_cult/globals.dm b/monkestation/code/modules/antagonists/clock_cult/globals.dm
index 566a7ef39a72..b3839ff9f8ff 100644
--- a/monkestation/code/modules/antagonists/clock_cult/globals.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/globals.dm
@@ -1,7 +1,8 @@
GLOBAL_VAR_INIT(clock_power, 2500)
GLOBAL_VAR_INIT(max_clock_power, 2500) // Increases with every APC cogged
-GLOBAL_VAR_INIT(clock_vitality, 50) //start with only a bit of vitality
-GLOBAL_VAR_INIT(max_clock_vitality, 400) // This one however is constant
+GLOBAL_VAR_INIT(clock_vitality, 100) //start with only a bit of vitality
GLOBAL_VAR_INIT(clock_installed_cogs, 0)
+GLOBAL_LIST_INIT(clock_turf_types, typesof(/turf/open/floor/bronze, /turf/open/indestructible/reebe_flooring, /turf/closed/wall/clockwork))
GLOBAL_LIST_EMPTY(types_to_drop_on_clock_deonversion) //list of types to check for dropping on deconversion
+//CONVERT TO GLOBAL DATUM
GLOBAL_VAR_INIT(narsie_breaching_rune, FALSE) //rune where nar'sie is trying to summon from, if it gets destroyed somehow then just summon her on a random station turf
diff --git a/monkestation/code/modules/antagonists/clock_cult/hint_element.dm b/monkestation/code/modules/antagonists/clock_cult/hint_element.dm
index 4d9979b0ad78..2af060a06072 100644
--- a/monkestation/code/modules/antagonists/clock_cult/hint_element.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/hint_element.dm
@@ -4,9 +4,10 @@
/// Text to add to the description of the parent
var/text_to_add = ""
+ /// Text to add if we are on reebe
+ var/reebe_desc = ""
-
-/datum/element/clockwork_description/Attach(datum/target, parent_text)
+/datum/element/clockwork_description/Attach(datum/target, parent_text, reebe_text)
. = ..()
if(!isatom(target))
@@ -17,6 +18,8 @@
if(parent_text && !text_to_add)
text_to_add = parent_text
+ if(reebe_text && !reebe_desc)
+ reebe_desc = reebe_text
/datum/element/clockwork_description/Detach(datum/target)
. = ..()
@@ -37,3 +40,5 @@
if(IS_CLOCK(user) || isobserver(user))
examine_texts += span_brass(text_to_add)
+ if(reebe_desc && on_reebe(source))
+ examine_texts += span_brass(reebe_desc)
diff --git a/monkestation/code/modules/antagonists/clock_cult/items/clockwork_slab.dm b/monkestation/code/modules/antagonists/clock_cult/items/clockwork_slab.dm
index 615af05401e4..50aee2692681 100644
--- a/monkestation/code/modules/antagonists/clock_cult/items/clockwork_slab.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/items/clockwork_slab.dm
@@ -162,7 +162,7 @@ GLOBAL_LIST_INIT(clockwork_slabs, list())
data["cogs"] = cogs
data["vitality"] = GLOB.clock_vitality
- data["max_vitality"] = GLOB.max_clock_vitality
+ data["max_vitality"] = MAX_CLOCK_VITALITY
data["power"] = GLOB.clock_power
data["max_power"] = GLOB.max_clock_power
data["scriptures"] = list()
diff --git a/monkestation/code/modules/antagonists/clock_cult/items/clothing.dm b/monkestation/code/modules/antagonists/clock_cult/items/clothing.dm
index 731d74ed07bb..e6e14a66d5e8 100644
--- a/monkestation/code/modules/antagonists/clock_cult/items/clothing.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/items/clothing.dm
@@ -1,3 +1,4 @@
+#define CLOAK_DODGE_CHANCE 20
/obj/item/clothing/suit/clockwork
name = "bronze armor"
desc = "A strong, bronze suit worn by the soldiers of the Ratvarian armies."
@@ -5,6 +6,7 @@
worn_icon = 'monkestation/icons/mob/clock_cult/clockwork_garb_worn.dmi'
icon_state = "clockwork_cuirass"
armor_type = /datum/armor/suit_clockwork
+ slowdown = 0.2
resistance_flags = FIRE_PROOF | ACID_PROOF
w_class = WEIGHT_CLASS_BULKY
body_parts_covered = CHEST|GROIN|LEGS|ARMS
@@ -13,35 +15,68 @@
/obj/item/stack/tile/bronze,
/obj/item/gun/ballistic/bow/clockwork,
)
+ ///what is the value of our slowdown while empowered
+ var/empowered_slowdown = 0
+ ///what armor type do we use while empowered
+ var/datum/armor/empowered_armor = /datum/armor/suit_clockwork_empowered
/datum/armor/suit_clockwork
+ melee = 25
+ bullet = 30
+ laser = 15
+ energy = 30
+ bomb = 80
+ bio = 100
+ fire = 100
+ acid = 100
+
+/datum/armor/suit_clockwork_empowered
melee = 50
- bullet = 60
- laser = 30
+ bullet = 50
+ laser = 40
energy = 60
bomb = 80
bio = 100
fire = 100
acid = 100
-
/obj/item/clothing/suit/clockwork/Initialize(mapload)
. = ..()
AddElement(/datum/element/clockwork_pickup, ~(ITEM_SLOT_HANDS))
+ AddComponent(/datum/component/turf_checker, GLOB.clock_turf_types, null, TRUE, PROC_REF(set_empowered_state))
+/obj/item/clothing/suit/clockwork/proc/set_empowered_state(datum/component/turf_checker/checker, empowered)
+ if(empowered)
+ set_armor(empowered_armor)
+ slowdown = empowered_slowdown
+ return
+
+ set_armor(initial(armor_type))
+ slowdown = initial(slowdown)
/obj/item/clothing/suit/clockwork/speed
name = "robes of divinity"
desc = "A shiny suit, glowing with a vibrant energy. The wearer will be able to move quickly across battlefields, but will be able to withstand less damage before falling."
icon_state = "clockwork_cuirass_speed"
- slowdown = -0.5
- resistance_flags = FIRE_PROOF | ACID_PROOF
+ slowdown = -0.2
armor_type = /datum/armor/clockwork_speed
+ empowered_armor = /datum/armor/clockwork_speed_empowered
+ empowered_slowdown = -0.6
/datum/armor/clockwork_speed
- melee = 40
+ melee = 20
+ bullet = 0
+ laser = 0
+ energy = 0
+ bomb = 60
+ bio = 100
+ fire = 100
+ acid = 100
+
+/datum/armor/clockwork_speed_empowered
+ melee = 30
bullet = 40
- laser = 20
+ laser = -20
energy = -20
bomb = 60
bio = 100
@@ -50,43 +85,53 @@
/obj/item/clothing/suit/clockwork/cloak
name = "shrouding cloak"
- desc = "A faltering cloak that bends light around it, distorting the user's appearance, making it hard to see them with the naked eye. However, it provides very little protection."
+ desc = "A faltering cloak that bends light around it, distorting the user's appearance, making it hard to see them with the naked eye and be harder to hit. \
+ However, it provides very little physical protection."
icon_state = "clockwork_cloak"
armor_type = /datum/armor/clockwork_cloak
actions_types = list(/datum/action/item_action/toggle/clock)
- slowdown = -0.1
- resistance_flags = FIRE_PROOF | ACID_PROOF
+ w_class = WEIGHT_CLASS_NORMAL
+ empowered_armor = /datum/armor/clockwork_cloak
+ empowered_slowdown = -0.1
/// Is the shroud itself active or not
var/shroud_active = FALSE
/// Previous alpha value of the user when removing/disabling the jacket
var/previous_alpha = 255
/// Ref to who is wearing this
var/mob/living/wearer
+ /// Are we currently empowered
+ var/is_empowered = FALSE
/datum/armor/clockwork_cloak
- melee = 30
- bullet = 60
- laser = 40
- energy = 30
- bomb = 40
+ melee = 15
+ bullet = 30
+ laser = 20
+ energy = 15
+ bomb = 50
bio = 100
fire = 100
acid = 100
+/obj/item/clothing/suit/clockwork/cloak/set_empowered_state(datum/component/turf_checker/checker, empowered)
+ . = ..()
+ is_empowered = empowered
+ if(shroud_active && !empowered)
+ disable()
+
/obj/item/clothing/suit/clockwork/cloak/Destroy()
+ if(shroud_active)
+ disable()
wearer = null
-
return ..()
-
/obj/item/clothing/suit/clockwork/cloak/attack_self(mob/user, modifiers)
. = ..()
if(shroud_active)
disable()
-
- else
+ else if(is_empowered)
enable()
-
+ else
+ balloon_alert(user, "Must be standing on brass!")
/obj/item/clothing/suit/clockwork/cloak/equipped(mob/user, slot)
. = ..()
@@ -94,18 +139,30 @@
return
wearer = user
-
- if(shroud_active)
+ if(shroud_active && is_empowered)
enable()
-
/obj/item/clothing/suit/clockwork/cloak/dropped(mob/user)
. = ..()
if(shroud_active)
disable()
-
wearer = null
+/obj/item/clothing/suit/clockwork/cloak/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type)
+ if(is_empowered && shroud_active && prob(CLOAK_DODGE_CHANCE)) //we handle this just a biiiiit too different from parent to make simply using the vars be viable
+ owner.visible_message(span_danger("[owner]'s [src] makes them phase out of the way of [attack_text]!"))
+ owner.add_filter("clock_cloak", 3, motion_blur_filter(0, 0))
+ addtimer(CALLBACK(src, PROC_REF(remove_phase_filter), owner), (0.6 SECONDS) + 1)
+ ASYNC
+ animate(owner.get_filter("clock_cloak"), 0.3 SECONDS, x = prob(50) ? rand(4, 5) : rand(-4, -5), y = prob(50) ? rand(4, 5) : rand(-4, -5), flags = ANIMATION_PARALLEL)
+ animate(time = 0.3 SECONDS, x = 0, y = 0)
+ playsound(src, 'sound/weapons/etherealmiss.ogg', BLOCK_SOUND_VOLUME, vary = TRUE, mixer_channel = CHANNEL_SOUND_EFFECTS)
+ return TRUE
+
+/obj/item/clothing/suit/clockwork/cloak/proc/remove_phase_filter(mob/living/remove_from)
+ if(QDELETED(remove_from))
+ return
+ remove_from.remove_filter("clock_cloak")
/// Apply the effects to the wearer, making them pretty hard to see
/obj/item/clothing/suit/clockwork/cloak/proc/enable()
@@ -118,7 +175,6 @@
apply_wibbly_filters(wearer)
ADD_TRAIT(wearer, TRAIT_UNKNOWN, CLOTHING_TRAIT)
-
/// Un-apply the effects of the cloak, returning the wearer to normal
/obj/item/clothing/suit/clockwork/cloak/proc/disable()
shroud_active = FALSE
@@ -130,7 +186,6 @@
animate(wearer, alpha = previous_alpha, time = 3 SECONDS)
REMOVE_TRAIT(wearer, TRAIT_UNKNOWN, CLOTHING_TRAIT)
-
/obj/item/clothing/glasses/clockwork
name = "base clock glasses"
icon = 'monkestation/icons/obj/clock_cult/clockwork_garb.dmi'
@@ -144,7 +199,6 @@
AddElement(/datum/element/clockwork_description, clock_desc)
AddElement(/datum/element/clockwork_pickup, ~(ITEM_SLOT_HANDS))
-
#define SECONDS_FOR_EYE_HEAL 60
// Thermal goggles, no protection from eye stuff
/obj/item/clothing/glasses/clockwork/wraith_spectacles
@@ -346,8 +400,6 @@
//THIS IS MOST LIKELY BREAKING
/// Applies the actual effects to the wearer, giving them flash protection and a variety of sight/info bonuses
/obj/item/clothing/glasses/clockwork/judicial_visor/proc/apply_to_wearer()
- ADD_TRAIT(wearer, TRAIT_NOFLASH, CLOTHING_TRAIT)
-
ADD_TRAIT(wearer, TRAIT_MEDICAL_HUD, CLOTHING_TRAIT)
var/datum/atom_hud/med_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
med_hud.show_to(wearer)
@@ -356,16 +408,12 @@
var/datum/atom_hud/sec_hud = GLOB.huds[DATA_HUD_SECURITY_ADVANCED]
sec_hud.show_to(wearer)
- ADD_TRAIT(wearer, TRAIT_MADNESS_IMMUNE, CLOTHING_TRAIT)
- ADD_TRAIT(wearer, TRAIT_MESON_VISION, CLOTHING_TRAIT)
- ADD_TRAIT(wearer, TRAIT_KNOW_CYBORG_WIRES, CLOTHING_TRAIT)
+ add_traits(list(TRAIT_KNOW_ENGI_WIRES, TRAIT_MADNESS_IMMUNE, TRAIT_MESON_VISION, TRAIT_KNOW_CYBORG_WIRES, TRAIT_NOFLASH), CLOTHING_TRAIT)
color_cutoffs = list(50, 10, 30)
wearer.update_sight()
/// Removes the effects to the wearer, removing the flash protection and similar
/obj/item/clothing/glasses/clockwork/judicial_visor/proc/unapply_to_wearer()
- REMOVE_TRAIT(wearer, TRAIT_NOFLASH, CLOTHING_TRAIT)
-
REMOVE_TRAIT(wearer, TRAIT_MEDICAL_HUD, CLOTHING_TRAIT)
var/datum/atom_hud/med_hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
med_hud.hide_from(wearer)
@@ -374,13 +422,10 @@
var/datum/atom_hud/sec_hud = GLOB.huds[DATA_HUD_SECURITY_ADVANCED]
sec_hud.hide_from(wearer)
- REMOVE_TRAIT(wearer, TRAIT_MADNESS_IMMUNE, CLOTHING_TRAIT)
- REMOVE_TRAIT(wearer, TRAIT_MESON_VISION, CLOTHING_TRAIT)
- REMOVE_TRAIT(wearer, TRAIT_KNOW_CYBORG_WIRES, CLOTHING_TRAIT)
+ remove_traits(list(TRAIT_KNOW_ENGI_WIRES, TRAIT_MADNESS_IMMUNE, TRAIT_MESON_VISION, TRAIT_KNOW_CYBORG_WIRES, TRAIT_NOFLASH), CLOTHING_TRAIT)
color_cutoffs = null
wearer.update_sight()
-
/obj/item/clothing/glasses/clockwork/judicial_visor/equipped(mob/living/user, slot)
. = ..()
if(!isliving(user))
@@ -399,7 +444,8 @@
/obj/item/clothing/head/helmet/clockwork
name = "brass helmet"
- desc = "A strong, brass helmet worn by the soldiers of the Ratvarian armies. Includes an integrated light-dimmer for flash protection, as well as occult-grade muffling for factory based environments."
+ desc = "A strong, brass helmet worn by the soldiers of the Ratvarian armies. Includes an integrated light-dimmer for flash protection, \
+ as well as occult-grade muffling for factory based environments."
icon = 'monkestation/icons/obj/clock_cult/clockwork_garb.dmi'
worn_icon = 'monkestation/icons/mob/clock_cult/clockwork_garb_worn.dmi'
icon_state = "clockwork_helmet"
@@ -409,10 +455,20 @@
flash_protect = FLASH_PROTECTION_FLASH
/datum/armor/helmet_clockwork
+ melee = 25
+ bullet = 30
+ laser = 15
+ energy = 40
+ bomb = 80
+ bio = 100
+ fire = 100
+ acid = 100
+
+/datum/armor/helmet_clockwork_empowered
melee = 50
- bullet = 65
+ bullet = 55
laser = 35
- energy = 75
+ energy = 70
bomb = 80
bio = 100
fire = 100
@@ -422,7 +478,10 @@
. = ..()
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_HEAD))
AddElement(/datum/element/clockwork_pickup, ~(ITEM_SLOT_HANDS))
+ AddComponent(/datum/component/turf_checker, GLOB.clock_turf_types, null, TRUE, PROC_REF(set_empowered_state))
+/obj/item/clothing/head/helmet/clockwork/proc/set_empowered_state(datum/component/turf_checker/checker, empowered)
+ empowered ? set_armor(/datum/armor/helmet_clockwork_empowered) : initial(armor_type)
/obj/item/clothing/shoes/clockwork
name = "brass treads"
@@ -439,8 +498,8 @@
energy = 0
bomb = 0
bio = 100
- fire = 0
- acid = 0
+ fire = 80
+ acid = 100
/obj/item/clothing/shoes/clockwork/Initialize(mapload)
. = ..()
@@ -470,8 +529,10 @@
bomb = 10
bio = 80
fire = 80
- acid = 50
+ acid = 100
/obj/item/clothing/gloves/clockwork/Initialize(mapload)
. = ..()
AddElement(/datum/element/clockwork_pickup, ~(ITEM_SLOT_HANDS))
+
+#undef CLOAK_DODGE_CHANCE
diff --git a/monkestation/code/modules/antagonists/clock_cult/items/integration_cog.dm b/monkestation/code/modules/antagonists/clock_cult/items/integration_cog.dm
index acd42582baf4..70ba4f780d84 100644
--- a/monkestation/code/modules/antagonists/clock_cult/items/integration_cog.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/items/integration_cog.dm
@@ -45,7 +45,7 @@
GLOB.clock_installed_cogs++
GLOB.max_clock_power += MAX_POWER_PER_COG
cogger_apc.clock_cog_rewarded = TRUE
- send_clock_message(null, span_brass(span_bold("[user] has installed an integration cog into [cogger_apc].")), msg_ghosts = TRUE)
+ send_clock_message(null, span_brass(span_bold("[user.real_name] has installed an integration cog into [cogger_apc].")), msg_ghosts = TRUE)
//Update the cog counts
for(var/obj/item/clockwork/clockwork_slab/slab as anything in GLOB.clockwork_slabs)
slab.cogs++
diff --git a/monkestation/code/modules/antagonists/clock_cult/items/replica_fabricator.dm b/monkestation/code/modules/antagonists/clock_cult/items/replica_fabricator.dm
index b587b80a7d19..ab31f805f4ec 100644
--- a/monkestation/code/modules/antagonists/clock_cult/items/replica_fabricator.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/items/replica_fabricator.dm
@@ -63,7 +63,6 @@
var/turf/creation_turf = get_turf(target)
var/atom/movable/possible_replaced
-
if(locate(selected_output.to_create_path) in creation_turf)
to_chat(user, span_clockyellow("There is already one of these on this tile!"))
return
@@ -87,10 +86,11 @@
calculated_creation_delay = selected_output.reebe_mult
if(!get_charged_anchor_crystals())
calculated_creation_delay += SLOWDOWN_FROM_NO_ANCHOR_CRYSTAL
+ else if(GLOB.clock_ark?.current_state >= ARK_STATE_ACTIVE)
+ calculated_creation_delay += (iscogscarab(user) ? 2.5 : 5)
calculated_creation_delay = selected_output.creation_delay * calculated_creation_delay
var/obj/effect/temp_visual/ratvar/constructing_effect/effect = new(creation_turf, calculated_creation_delay)
-
if(!do_after(user, calculated_creation_delay, target))
qdel(effect)
return
@@ -99,9 +99,7 @@
return
GLOB.clock_power -= selected_output.cost
-
var/atom/created
-
if(!istype(selected_output, /datum/replica_fabricator_output/turf_output))
if(possible_replaced)
qdel(possible_replaced)
@@ -248,28 +246,22 @@
cost = BRASS_POWER_COST * 0.25 // 1/4th the cost, since one sheet = 4 floor tiles
to_create_path = /turf/open/floor/bronze
-
/datum/replica_fabricator_output/turf_output/brass_floor/on_create(obj/created_object, turf/creation_turf, mob/creator)
. = ..()
new /obj/effect/temp_visual/ratvar/floor(creation_turf)
new /obj/effect/temp_visual/ratvar/beam(creation_turf)
-
/datum/replica_fabricator_output/turf_output/brass_wall
name = "wall"
cost = BRASS_POWER_COST * 4
to_create_path = /turf/closed/wall/clockwork
- creation_delay = 7 SECONDS
+ creation_delay = 14 SECONDS
replace_types_of = list(/turf/closed/wall)
- reebe_mult = 1.5
-
/datum/replica_fabricator_output/turf_output/brass_wall/on_create(obj/created_object, turf/creation_turf, mob/creator)
. = ..()
-
new /obj/effect/temp_visual/ratvar/wall(creation_turf)
new /obj/effect/temp_visual/ratvar/beam(creation_turf)
-
/datum/replica_fabricator_output/wall_gear
name = "wall gear"
cost = BRASS_POWER_COST * 2
@@ -277,46 +269,39 @@
creation_delay = 5 SECONDS
replace_types_of = list(/obj/structure/girder)
-
/datum/replica_fabricator_output/wall_gear/on_create(obj/created_object, turf/creation_turf, mob/creator)
new /obj/effect/temp_visual/ratvar/gear(creation_turf)
new /obj/effect/temp_visual/ratvar/beam(creation_turf)
return ..()
-
/datum/replica_fabricator_output/brass_window
name = "window"
cost = BRASS_POWER_COST * 2
to_create_path = /obj/structure/window/reinforced/clockwork/fulltile
- creation_delay = 6 SECONDS
+ creation_delay = 10 SECONDS
replace_types_of = list(/obj/structure/window)
reebe_mult = 1.2
-
/datum/replica_fabricator_output/brass_window/on_create(obj/created_object, turf/creation_turf, mob/creator)
new /obj/effect/temp_visual/ratvar/window(creation_turf)
new /obj/effect/temp_visual/ratvar/beam(creation_turf)
return ..()
-
/datum/replica_fabricator_output/pinion_airlock
name = "airlock"
cost = BRASS_POWER_COST * 5 // Breaking it only gets 2 but this is the exception to the rule of equivalent exchange, due to all the small parts inside
to_create_path = /obj/machinery/door/airlock/bronze/clock
- creation_delay = 6 SECONDS
-
+ creation_delay = 10 SECONDS
/datum/replica_fabricator_output/pinion_airlock/on_create(obj/created_object, turf/creation_turf, mob/creator)
new /obj/effect/temp_visual/ratvar/door(creation_turf)
new /obj/effect/temp_visual/ratvar/beam(creation_turf)
return ..()
-
/datum/replica_fabricator_output/pinion_airlock/glass
name = "glass airlock"
to_create_path = /obj/machinery/door/airlock/bronze/clock/glass
-
#undef BRASS_POWER_COST
#undef REGULAR_POWER_COST
#undef SLOWDOWN_FROM_NO_ANCHOR_CRYSTAL
diff --git a/monkestation/code/modules/antagonists/clock_cult/items/weaponry.dm b/monkestation/code/modules/antagonists/clock_cult/items/weaponry.dm
index 7997a1ae6a4a..46272ff1f3c5 100644
--- a/monkestation/code/modules/antagonists/clock_cult/items/weaponry.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/items/weaponry.dm
@@ -1,5 +1,6 @@
#define HAMMER_FLING_DISTANCE 2
#define HAMMER_THROW_FLING_DISTANCE 3
+#define COGSCARAB_BOW_DRAW_TIME_MULT 20
/obj/item/clockwork/weapon
name = "clockwork weapon"
@@ -17,21 +18,29 @@
attack_verb_continuous = list("attacks", "pokes", "jabs", "tears", "gores")
attack_verb_simple = list("attack", "poke", "jab", "tear", "gore")
sharpness = SHARP_EDGED
- wound_bonus = -10 //wounds are really strong for clock cult, so im making their weapons slightly worse then normal at wounding
- /// Typecache of valid turfs to have the weapon's special effect on
- var/static/list/effect_turf_typecache = typecacheof(list(/turf/open/floor/bronze, /turf/open/indestructible/reebe_flooring))
+ wound_bonus = -15 //wounds are really strong for clock cult, so im making their weapons slightly worse then normal at wounding
-/obj/item/clockwork/weapon/afterattack(mob/living/target, mob/living/user)
+/obj/item/clockwork/weapon/Initialize(mapload)
. = ..()
- if(!.)
+ AddComponent(/datum/component/turf_checker, GLOB.clock_turf_types, COMSIG_CHECK_TURF_CLOCKWORK)
+
+/obj/item/clockwork/weapon/afterattack(mob/living/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
+ if(!proximity_flag)
+ return
+
+ if(!check_turf())
+ return
+
+ if(QDELETED(target))
return
- var/turf/gotten_turf = get_turf(user)
- if(!is_type_in_typecache(gotten_turf, effect_turf_typecache))
+ if(ismob(target))
+ if(target.stat != DEAD && !IS_CLOCK(target) && !target.can_block_magic(MAGIC_RESISTANCE_HOLY))
+ mob_hit_effect(target, user)
return
- if((!QDELETED(target) && (!ismob(target) || (ismob(target) && target.stat != DEAD && !IS_CLOCK(target) && !target.can_block_magic(MAGIC_RESISTANCE_HOLY)))))
- hit_effect(target, user)
+ atom_hit_effect(target, user)
/obj/item/clockwork/weapon/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
@@ -42,23 +51,30 @@
return
var/mob/living/target = hit_atom
-
if(!target.can_block_magic(MAGIC_RESISTANCE_HOLY) && !IS_CLOCK(target))
- hit_effect(target, throwingdatum.thrower, TRUE)
+ mob_hit_effect(target, throwingdatum.thrower, TRUE)
+
+/// What occurs to non-holy mobs when attacked from brass tiles
+/obj/item/clockwork/weapon/proc/mob_hit_effect(mob/living/target, mob/living/user, thrown = FALSE)
+ return
-/// What occurs to non-holy people when attacked from brass tiles
-/obj/item/clockwork/weapon/proc/hit_effect(mob/living/target, mob/living/user, thrown = FALSE)
+/// What occurs to non-mob atoms when attacked from brass tiles
+/obj/item/clockwork/weapon/proc/atom_hit_effect(mob/living/target, mob/living/user, thrown = FALSE)
return
+/obj/item/clockwork/weapon/proc/check_turf(check_override)
+ return (SEND_SIGNAL(src, COMSIG_CHECK_TURF_CLOCKWORK, check_override) & COMPONENT_CHECKER_VALID_TURF)
+
/obj/item/clockwork/weapon/brass_spear
name = "brass spear"
desc = "A razor-sharp spear made of brass. It thrums with barely-contained energy."
- icon_state = "ratvarian_spear"
+ base_icon_state = "ratvarian_spear"
+ icon_state = "ratvarian_spear0"
embedding = list("max_damage_mult" = 15, "armour_block" = 80)
- throwforce = 36
- force = 26
- armour_penetration = 24
- block_chance = 40
+ throwforce = 40
+ force = 7
+ armour_penetration = 40
+ block_chance = 15
clockwork_desc = "Can be summoned back to its last holder every 10 seconds if they are standing on bronze."
///ref to our recall spell
var/datum/action/cooldown/spell/summon_spear/our_summon = new
@@ -67,7 +83,12 @@
/obj/item/clockwork/weapon/brass_spear/Initialize(mapload)
. = ..()
-
+ AddComponent(/datum/component/two_handed, \
+ force_multiplier = 2, \
+ icon_wielded = "[base_icon_state]1", \
+ wield_callback = CALLBACK(src, PROC_REF(on_wield)), \
+ unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \
+ )
RegisterSignal(src, COMSIG_ITEM_PICKUP, PROC_REF(on_pickup))
our_summon.recalled_spear = src
@@ -76,6 +97,10 @@
QDEL_NULL(our_summon)
return ..()
+/obj/item/clockwork/weapon/brass_spear/update_icon_state()
+ icon_state = "[base_icon_state]0"
+ return ..()
+
/obj/item/clockwork/weapon/brass_spear/proc/on_pickup(picked_up, mob/taker)
SIGNAL_HANDLER
@@ -90,11 +115,22 @@
if(!(locate(our_summon) in taker.actions)) //dont let them have multiple summons
our_summon.Grant(taker)
+/obj/item/clockwork/weapon/brass_spear/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type)
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
+ final_block_chance += 30
+ return ..()
+
+/obj/item/clockwork/weapon/brass_spear/proc/on_wield()
+ attack_speed = max(attack_speed - 3, 1)
+
+/obj/item/clockwork/weapon/brass_spear/proc/on_unwield()
+ attack_speed += 3 //yes technically this could break with the max() in on_wield() but you should not be getting attack speed that low anyway so its only there for sanity
+
/datum/action/cooldown/spell/summon_spear
name = "Summon Brass Spear"
desc = "Summons the last brass spear you picked up if you are currently standing on bronze."
button_icon = 'monkestation/icons/obj/clock_cult/clockwork_weapons.dmi'
- button_icon_state = "ratvarian_spear"
+ button_icon_state = "ratvarian_spear0"
background_icon = 'monkestation/icons/mob/clock_cult/background_clock.dmi'
background_icon_state = "bg_clock"
overlay_icon_state = ""
@@ -121,8 +157,7 @@
if(!IS_CLOCK(owner))
return FALSE
- var/turf/summoner_turf = get_turf(owner)
- if(!is_type_in_typecache(summoner_turf, recalled_spear?.effect_turf_typecache))
+ if(!recalled_spear.check_turf(owner))
if(feedback)
to_chat(owner, span_brass("You need to be standing on bronze to do this."))
return FALSE
@@ -148,6 +183,7 @@
armour_penetration = 6
attack_verb_simple = list("bash", "hammer", "attack", "smash")
attack_verb_continuous = list("bashes", "hammers", "attacks", "smashes")
+ attack_speed = CLICK_CD_LARGE_WEAPON
clockwork_desc = "Enemies hit by this will be flung back while you are on bronze tiles."
sharpness = FALSE
hitsound = 'sound/weapons/smash.ogg'
@@ -161,7 +197,7 @@
force_wielded = 30, \
)
-/obj/item/clockwork/weapon/brass_battlehammer/hit_effect(mob/living/target, mob/living/user, thrown = FALSE)
+/obj/item/clockwork/weapon/brass_battlehammer/mob_hit_effect(mob/living/target, mob/living/user, thrown = FALSE)
if((!thrown && !HAS_TRAIT(src, TRAIT_WIELDED)) || !istype(target))
return
@@ -171,50 +207,46 @@
/obj/item/clockwork/weapon/brass_battlehammer/update_icon_state()
icon_state = "[base_icon_state]0"
return ..()
+
/obj/item/clockwork/weapon/brass_sword
name = "brass longsword"
desc = "A large sword made of brass."
icon_state = "ratvarian_sword"
- force = 26
+ force = 20
throwforce = 20
- armour_penetration = 12
+ armour_penetration = 15
attack_verb_simple = list("attack", "slash", "cut", "tear", "gore")
attack_verb_continuous = list("attacks", "slashes", "cuts", "tears", "gores")
- clockwork_desc = "Enemies and mechs will be struck with a powerful electromagnetic pulse while you are on bronze tiles, with a cooldown."
- block_chance = 35
+ clockwork_desc = "Enemies and mechs will be struck with a powerful electromagnetic pulse while you are on bronze tiles, with a cooldown. It seems to only be able to parry melee attacks."
+ block_chance = 20
COOLDOWN_DECLARE(emp_cooldown)
-/obj/item/clockwork/weapon/brass_sword/hit_effect(mob/living/target, mob/living/user, thrown)
+/obj/item/clockwork/weapon/brass_sword/mob_hit_effect(mob/living/target, mob/living/user, thrown)
if(!COOLDOWN_FINISHED(src, emp_cooldown))
return
COOLDOWN_START(src, emp_cooldown, 30 SECONDS)
target.emp_act(EMP_LIGHT)
- new /obj/effect/temp_visual/emp/pulse(target.loc)
+ new /obj/effect/temp_visual/emp/pulse(get_turf(target))
addtimer(CALLBACK(src, PROC_REF(send_message), user), 30 SECONDS)
to_chat(user, span_brass("You strike [target] with an electromagnetic pulse!"))
playsound(user, 'sound/magic/lightningshock.ogg', 40)
-/obj/item/clockwork/weapon/brass_sword/attack_atom(obj/attacked_obj, mob/living/user, params)
- . = ..()
- var/turf/gotten_turf = get_turf(user)
-
- if(!ismecha(attacked_obj) || !is_type_in_typecache(gotten_turf, effect_turf_typecache))
- return
-
- if(!COOLDOWN_FINISHED(src, emp_cooldown))
+/obj/item/clockwork/weapon/brass_sword/atom_hit_effect(obj/vehicle/sealed/mecha/target, mob/living/user, thrown)
+ if(!istype(target) || !COOLDOWN_FINISHED(src, emp_cooldown))
return
COOLDOWN_START(src, emp_cooldown, 20 SECONDS)
-
- var/obj/vehicle/sealed/mecha/target = attacked_obj
target.emp_act(EMP_HEAVY)
- new /obj/effect/temp_visual/emp/pulse(target.loc)
+ new /obj/effect/temp_visual/emp/pulse(get_turf(target))
addtimer(CALLBACK(src, PROC_REF(send_message), user), 20 SECONDS)
to_chat(user, span_brass("You strike [target] with an electromagnetic pulse!"))
playsound(user, 'sound/magic/lightningshock.ogg', 40)
+/obj/item/clockwork/weapon/brass_sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text, final_block_chance, damage, attack_type)
+ return attack_type == MELEE_ATTACK && ..()
+
/obj/item/clockwork/weapon/brass_sword/proc/send_message(mob/living/target)
to_chat(target, span_brass("[src] glows, indicating the next attack will disrupt electronics of the target."))
@@ -232,14 +264,13 @@
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/bow/clockwork
/// Time between bolt recharges
var/recharge_time = 1.5 SECONDS
- /// Typecache of valid turfs to have the weapon's special effect on
- var/static/list/effect_turf_typecache = typecacheof(list(/turf/open/floor/bronze, /turf/open/indestructible/reebe_flooring))
/obj/item/gun/ballistic/bow/clockwork/Initialize(mapload)
. = ..()
update_icon_state()
AddElement(/datum/element/clockwork_description, "Firing from brass tiles will halve the time that it takes to recharge a bolt.")
AddElement(/datum/element/clockwork_pickup)
+ AddComponent(/datum/component/turf_checker, GLOB.clock_turf_types, COMSIG_CHECK_TURF_CLOCKWORK)
/obj/item/gun/ballistic/bow/clockwork/afterattack(atom/target, mob/living/user, flag, params, passthrough)
if(!drawn || !chambered)
@@ -253,9 +284,7 @@
/obj/item/gun/ballistic/bow/clockwork/shoot_live_shot(mob/living/user, pointblank, atom/pbtarget, message)
. = ..()
- var/turf/user_turf = get_turf(user)
-
- if(is_type_in_typecache(user_turf, effect_turf_typecache))
+ if((SEND_SIGNAL(src, COMSIG_CHECK_TURF_CLOCKWORK) & COMPONENT_CHECKER_VALID_TURF))
recharge_time = 0.75 SECONDS
addtimer(CALLBACK(src, PROC_REF(recharge_bolt)), recharge_time)
@@ -265,7 +294,7 @@
if(drawn || !chambered)
return
- if(!do_after(user, 0.5 SECONDS, src))
+ if(!do_after(user, 0.5 SECONDS * (iscogscarab(user) ? COGSCARAB_BOW_DRAW_TIME_MULT : 1), src))
return
to_chat(user, span_notice("You draw back the bowstring."))
@@ -305,7 +334,7 @@
damage = 25
damage_type = BURN
-//double damage to non clockwork structures and machines
+//double damage to non clockwork structures and machines(if we rework reebe itself this will no longer be needed)
/obj/projectile/energy/clockbolt/on_hit(atom/target, blocked, pierce_hit)
if(ismob(target))
var/mob/mob_target = target
@@ -321,3 +350,4 @@
#undef HAMMER_FLING_DISTANCE
#undef HAMMER_THROW_FLING_DISTANCE
+#undef COGSCARAB_BOW_DRAW_TIME_MULT
diff --git a/monkestation/code/modules/antagonists/clock_cult/machines/observation_console.dm b/monkestation/code/modules/antagonists/clock_cult/machines/observation_console.dm
index 42b7795e2773..072a13c80225 100644
--- a/monkestation/code/modules/antagonists/clock_cult/machines/observation_console.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/machines/observation_console.dm
@@ -1,3 +1,6 @@
+//if its not enough I could also make it so it takes longer to abscond from other areas
+GLOBAL_LIST_EMPTY(clock_warp_areas)
+
/obj/machinery/computer/camera_advanced/ratvar
name = "Ratvarian Observation Console"
desc = "Used by the servants of Rat'var to conduct operations on Nanotrasen property."
@@ -8,15 +11,16 @@
clockwork = TRUE
lock_override = TRUE
circuit = /obj/item/circuitboard/machine/camera_console_ratvar
- ///List of areas we are allowed to warp to
- var/static/list/allowed_areas = list()
/obj/machinery/computer/camera_advanced/ratvar/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
actions += new /datum/action/innate/clockcult/warp(src)
- actions += new /datum/action/innate/clockcult/console_info(src)
- actions += new /datum/action/innate/clockcult/add_warp_area(src, src)
+ actions += new /datum/action/innate/clockcult/show_warpable_areas(src)
+ var/datum/action/innate/clockcult/add_warp_area/add_area = new(src)
+ actions += add_area
+ if(!length(GLOB.clock_warp_areas))
+ add_area.choose_starting_warp_areas() //slightly hacky but handling it on the action is cheaper as it lets us just build our warpable areas once without needing globals
/obj/machinery/computer/camera_advanced/ratvar/Destroy()
STOP_PROCESSING(SSobj, src)
@@ -57,9 +61,11 @@
/datum/action/innate/clockcult/warp/Activate()
if(!isliving(owner))
return
+
if(GLOB.clock_ark && GLOB.clock_ark.current_state >= ARK_STATE_ACTIVE)
to_chat(owner, span_brass("You cannot warp while the gateway is opening!"))
return
+
if(warping)
button_icon_state = "warp_down"
build_all_button_icons(UPDATE_BUTTON_ICON)
@@ -70,12 +76,13 @@
var/mob/camera/ai_eye/remote/cam = cam_user.remote_control
var/turf/target_loc = get_turf(cam)
var/area/target_area = get_area(target_loc)
+ if(!(target_area in GLOB.clock_warp_areas))
+ to_chat(owner, span_brass("This area is not currently warpable."))
+ return
+
if(isclosedturf(target_loc))
to_chat(owner, span_brass("You cannot warp into dense objects."))
return
- if((target_area.area_flags & ABDUCTOR_PROOF) || is_type_in_typecache(target_area, blocked_areas))
- to_chat(owner, span_brass("A strange force blocks you from warping here"))
- return
do_sparks(5, TRUE, get_turf(cam))
warping = TRUE
@@ -83,6 +90,7 @@
build_all_button_icons(UPDATE_BUTTON_ICON)
if(do_after(cam_user, 5 SECONDS, target = target_loc, extra_checks = CALLBACK(src, PROC_REF(warping_check))))
try_servant_warp(cam_user, target_loc)
+
button_icon_state = "warp_down"
build_all_button_icons(UPDATE_BUTTON_ICON)
warping = FALSE
@@ -90,25 +98,5 @@
/datum/action/innate/clockcult/warp/proc/warping_check()
return warping
-/datum/action/innate/clockcult/console_info
- name = "Console info"
- desc = "Get info on this console."
- button_icon_state = "console_info"
-
-/datum/action/innate/clockcult/add_warp_area
- name = "Add Warp Area"
- desc = "Add an additional area you can warp to."
- button_icon_state = "Spatial Warp"
- ///Ref to the console we are linked to
- var/obj/machinery/computer/camera_advanced/ratvar/linked_console
-
-/datum/action/innate/clockcult/add_warp_area/New(Target, console)
- . = ..()
- linked_console = console
-
-/datum/action/innate/clockcult/add_warp_area/Destroy()
- linked_console = null
- return ..()
-
/obj/item/circuitboard/machine/camera_console_ratvar
build_path = /obj/machinery/computer/camera_advanced/ratvar
diff --git a/monkestation/code/modules/antagonists/clock_cult/mobs/cogscarab.dm b/monkestation/code/modules/antagonists/clock_cult/mobs/cogscarab.dm
index c2c18e57e116..54d9d8c89249 100644
--- a/monkestation/code/modules/antagonists/clock_cult/mobs/cogscarab.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/mobs/cogscarab.dm
@@ -1,7 +1,7 @@
-//#define CLOCKDRONE "drone_clock"
-
GLOBAL_LIST_EMPTY(cogscarabs)
+#define CLOCK_DRONE_MAX_ITEM_FORCE 15
+
//====Cogscarab====
/mob/living/basic/drone/cogscarab
@@ -42,7 +42,7 @@ GLOBAL_LIST_EMPTY(cogscarabs)
/mob/living/basic/drone/cogscarab/death(gibbed)
GLOB.cogscarabs -= src
- . = ..()
+ return ..()
/mob/living/basic/drone/cogscarab/Life(seconds, times_fired)
if(!on_reebe(src) && !GLOB.ratvar_risen && length(GLOB.abscond_markers) && stay_on_reebe)
@@ -53,6 +53,9 @@ GLOBAL_LIST_EMPTY(cogscarabs)
GLOB.cogscarabs -= src
return ..()
+/mob/living/basic/drone/cogscarab/transferItemToLoc(obj/item/item, newloc, force, silent) //ideally I would handle this on attacking instead
+ return (item.force <= CLOCK_DRONE_MAX_ITEM_FORCE) && ..()
+
//====Shell====
/obj/effect/mob_spawn/ghost_role/drone/cogscarab
@@ -79,3 +82,5 @@ GLOBAL_LIST_EMPTY(cogscarabs)
to_chat(user, span_notice("The cult currently has its maximum amount of cogscarabs."))
return FALSE
return TRUE
+
+#undef CLOCK_DRONE_MAX_ITEM_FORCE
diff --git a/monkestation/code/modules/antagonists/clock_cult/portal.dm b/monkestation/code/modules/antagonists/clock_cult/portal.dm
index 204fd85cb90d..e0fa4f0bc932 100644
--- a/monkestation/code/modules/antagonists/clock_cult/portal.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/portal.dm
@@ -17,7 +17,7 @@
possible_targets += portal_mark
var/static/times_warned_admins //we spawn a massive amount of these normally so we dont want to warn admins for every single one if something breaks
- if(possible_targets.len)
+ if(length(possible_targets))
hard_target = get_turf(pick(possible_targets))
return
else if(!times_warned_admins)
@@ -29,18 +29,24 @@
. = ..()
teleport(bumper)
-/obj/effect/portal/clockcult/teleport(atom/movable/teleported_atom)
+/obj/effect/portal/clockcult/teleport(atom/movable/teleported_atom, pull_loop = FALSE)
if(isliving(teleported_atom))
+ if(pull_loop)
+ return
+
to_chat(teleported_atom, span_notice("You begin climbing into the rift."))
if(!do_after(teleported_atom, 5 SECONDS, src))
return
var/mob/living/teleported_living = teleported_atom
+ if(teleported_living.pulling)
+ teleport(teleported_living.pulling, TRUE)
+
if(teleported_living.client)
var/client_color = teleported_living.client.color
teleported_living.client.color = "#BE8700"
- animate(teleported_living.client, color = client_color, time = 25)
+ animate(teleported_living.client, color = client_color, time = 2.5 SECONDS)
var/prev_alpha = teleported_atom.alpha
teleported_atom.alpha = 0
- animate(teleported_atom, alpha=prev_alpha, time=10)
+ animate(teleported_atom, alpha = prev_alpha, time = 1 SECONDS)
. = ..()
diff --git a/monkestation/code/modules/antagonists/clock_cult/ratvar.dm b/monkestation/code/modules/antagonists/clock_cult/ratvar.dm
index 22450051d7dc..2a44308af888 100644
--- a/monkestation/code/modules/antagonists/clock_cult/ratvar.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/ratvar.dm
@@ -106,12 +106,16 @@ GLOBAL_DATUM(cult_ratvar, /obj/ratvar)
forceMove(the_turf)
/obj/ratvar/attack_ghost(mob/user)
- if(!user.mind) //this should not happen but just to be safe
- return
. = ..()
var/mob/living/basic/drone/created_drone = new /mob/living/basic/drone/cogscarab(get_turf(src))
created_drone.flags_1 |= (flags_1 & ADMIN_SPAWNED_1)
- user.mind.transfer_to(created_drone, TRUE)
+ if(user.mind)
+ user.mind.transfer_to(created_drone, TRUE)
+ else if(isobserver(user))
+ created_drone.key = user.key
+ else
+ return
+ created_drone.mind.add_antag_datum(/datum/antagonist/clock_cultist)
/obj/ratvar/proc/consume(atom/consumed)
consumed.ratvar_act()
diff --git a/monkestation/code/modules/antagonists/clock_cult/reebe_modules.dm b/monkestation/code/modules/antagonists/clock_cult/reebe_modules.dm
index 364dd95cbb4f..4853ed0e2f6a 100644
--- a/monkestation/code/modules/antagonists/clock_cult/reebe_modules.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/reebe_modules.dm
@@ -24,7 +24,7 @@ GLOBAL_LIST_EMPTY(abscond_markers)
reebe_loaded = FALSE
CRASH("Failed to reserve a block for Reebe.")
- var/datum/map_template/reebe_template = new(path = "_maps/~monkestation/templates/reebe.dmm", cache = TRUE)
+ var/datum/map_template/reebe_template = new(path = REEBE_MAP_PATH, cache = TRUE)
if(!reebe_template.cached_map) //might not be needed, im just copying lazy template code and I cant figure out what cached maps are for in this case
reebe_loaded = FALSE
CRASH("Failed to cache template for loading Reebe.")
@@ -34,6 +34,38 @@ GLOBAL_LIST_EMPTY(abscond_markers)
CRASH("Failed to load the Reebe template.")
return TRUE
+///Send a pod full of helpful items to the station's bridge
+/proc/send_station_support_package(list/additional_items, sent_message = "We are sending a support package to the bridge to help deal with the threats to the station.")
+ var/turf/bridge_turf = pick(GLOB.areas_by_type[/area/station/command/bridge].contained_turfs)
+ if(!bridge_turf)
+ return
+
+ var/list/spawned_list = list(
+ /obj/item/storage/medkit/advanced,
+ /obj/item/storage/medkit/brute,
+ /obj/item/storage/medkit/fire,
+ /obj/item/storage/medkit/regular,
+ /obj/item/gun/medbeam,
+ /obj/item/storage/part_replacer/cargo,
+ /obj/item/storage/box/recharger_parts,
+ )
+
+ if(additional_items)
+ spawned_list += additional_items
+
+ priority_announce(sent_message, has_important_message = TRUE)
+ podspawn(list("target" = bridge_turf, "spawn" = spawned_list))
+
+/obj/item/storage/box/recharger_parts
+ name = "Recharger Parts"
+
+/obj/item/storage/box/recharger_parts/PopulateContents()
+ . = ..() //there is actually a helper for this but I cant remember the name
+ var/list/spawned_list = list(/obj/item/circuitboard/machine/recharger = 5, /obj/item/stack/cable_coil = 1, /obj/item/stack/sheet/iron/fifty = 1)
+ for(var/type in spawned_list)
+ for(var/i in 1 to spawned_list[type])
+ new type(src)
+
/obj/effect/mob_spawn/corpse/human/blood_cultist
name = "Blood Cultist"
outfit = /datum/outfit/blood_cultist
diff --git a/monkestation/code/modules/antagonists/clock_cult/scriptures/_scripture.dm b/monkestation/code/modules/antagonists/clock_cult/scriptures/_scripture.dm
index bd8f5cc8220f..a24b76ee0bf9 100644
--- a/monkestation/code/modules/antagonists/clock_cult/scriptures/_scripture.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/scriptures/_scripture.dm
@@ -200,6 +200,9 @@ GLOBAL_LIST_EMPTY(clock_scriptures_by_type)
if(fast_invoke_mult && HAS_TRAIT(invoker, TRAIT_FASTER_SLAB_INVOKE))
true_invocation_time = invocation_time * fast_invoke_mult
+ if(istype(src, /datum/scripture/create_structure) && GLOB.clock_ark?.current_state >= ARK_STATE_ACTIVE)
+ true_invocation_time *= (iscogscarab(invoking_mob) ? 2.5 : 5)
+
if(do_after(invoking_mob, true_invocation_time, target = invoking_mob, extra_checks = CALLBACK(src, PROC_REF(check_special_requirements), invoking_mob)))
invoke()
diff --git a/monkestation/code/modules/antagonists/clock_cult/scriptures/preservation/dimensional_breach.dm b/monkestation/code/modules/antagonists/clock_cult/scriptures/preservation/dimensional_breach.dm
index 6b7a94fe489d..d3a9ad10d82b 100644
--- a/monkestation/code/modules/antagonists/clock_cult/scriptures/preservation/dimensional_breach.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/scriptures/preservation/dimensional_breach.dm
@@ -25,7 +25,7 @@
to_chat(invoker, span_userdanger("No ark located, contact the admins with an ahelp(f1)."))
return FALSE
- if(!get_charged_anchor_crystals() >= 2)
+ if(!(get_charged_anchor_crystals() >= ANCHORING_CRYSTALS_TO_SUMMON))
to_chat(invoker, span_brass("Reebe is not yet anchored enough to this realm, the ark cannot open until enough anchoring crystals are summoned and protected."))
return FALSE
diff --git a/monkestation/code/modules/antagonists/clock_cult/scriptures/preservation/summon_cogscarab.dm b/monkestation/code/modules/antagonists/clock_cult/scriptures/preservation/summon_cogscarab.dm
index 7de48952d5f1..879aae5dd667 100644
--- a/monkestation/code/modules/antagonists/clock_cult/scriptures/preservation/summon_cogscarab.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/scriptures/preservation/summon_cogscarab.dm
@@ -29,7 +29,7 @@
to_chat(invoker, span_warning("You can't summon anymore cogscarabs."))
return FALSE
- if(GLOB.clock_ark?.current_state >= 2) //active state
+ if(GLOB.clock_ark?.current_state >= ARK_STATE_ACTIVE)
to_chat(invoker, span_warning("It is too late to summon cogscarabs now, Rat'var is coming!"))
return FALSE
return TRUE
diff --git a/monkestation/code/modules/antagonists/clock_cult/scriptures/servitude/kindle.dm b/monkestation/code/modules/antagonists/clock_cult/scriptures/servitude/kindle.dm
index 9668b6cf6ed1..1a06de3c9086 100644
--- a/monkestation/code/modules/antagonists/clock_cult/scriptures/servitude/kindle.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/scriptures/servitude/kindle.dm
@@ -8,7 +8,7 @@
tip = "Best paired with hateful manacels for conversion, they are stunned for 6.5 seconds and muted for 13."
button_icon_state = "Kindle"
power_cost = 125
- invocation_time = 1 SECONDS
+ invocation_time = 2 SECONDS
invocation_text = list("Divinity, show them your light!")
after_use_text = "Let the power flow through you!"
slab_overlay = "volt"
@@ -76,11 +76,11 @@
if(issilicon(hit_mob))
var/mob/living/silicon/borgo = hit_mob
-
borgo.emp_act(EMP_HEAVY)
else if(iscarbon(hit_mob))
var/mob/living/carbon/carbon_hit = hit_mob
+ var/has_mindshield = HAS_TRAIT(hit_mob, TRAIT_MINDSHIELD)
carbon_hit.adjust_stutter(15 SECONDS)
carbon_hit.adjust_jitter(15 SECONDS)
@@ -88,9 +88,9 @@
carbon_hit.adjust_timed_status_effect(26 SECONDS, /datum/status_effect/speech/slurring/cult)
carbon_hit.adjust_silence(EFFECT_TIME * 2) //enough time to cuff and remove their radio, or just go back to reebe where their comms wont work
- carbon_hit.AdjustKnockdown(EFFECT_TIME * 1.5)
-
- carbon_hit.Stun(EFFECT_TIME * ((on_reebe(carbon_hit) && GLOB.clock_ark?.current_state) ? 0.1 : 1)) //pretty much 0 stun if your on reebe, still good for knockdown though
+ carbon_hit.AdjustKnockdown(EFFECT_TIME * (has_mindshield ? 1 : 1.5))
+ //pretty much 0 stun if your on reebe, still good for knockdown though, also only a 1 second stun on mindshielded people
+ carbon_hit.Stun((has_mindshield ? 1 SECONDS : EFFECT_TIME) * ((on_reebe(carbon_hit) && GLOB.clock_ark?.current_state) ? 0.1 : 1))
if(hit_mob.client)
var/client_color = hit_mob.client.color
diff --git a/monkestation/code/modules/antagonists/clock_cult/scriptures/servitude/sentinels_compromise.dm b/monkestation/code/modules/antagonists/clock_cult/scriptures/servitude/sentinels_compromise.dm
index eb897dff7586..257ad4a8e6cb 100644
--- a/monkestation/code/modules/antagonists/clock_cult/scriptures/servitude/sentinels_compromise.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/scriptures/servitude/sentinels_compromise.dm
@@ -13,6 +13,13 @@
recital_sound = 'sound/magic/magic_missile.ogg'
fast_invoke_mult = 0.8
+/datum/scripture/slab/sentinels_compromise/check_special_requirements(mob/user)
+ if(issilicon(user))
+ invocation_time = 10 * initial(invocation_time)
+ else
+ invocation_time = initial(invocation_time) //might be worth making a silicon_invoke() proc or something
+ return ..()
+
/datum/scripture/slab/sentinels_compromise/apply_effects(mob/living/healed_mob)
if(!istype(healed_mob) || !IS_CLOCK(invoker) || !IS_CLOCK(healed_mob))
return FALSE
diff --git a/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/anchoring_crystal.dm b/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/anchoring_crystal.dm
index edf32557c0d9..e36effbac16f 100644
--- a/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/anchoring_crystal.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/anchoring_crystal.dm
@@ -1,4 +1,3 @@
-#define TIME_BETWEEN_INVOKES 660 //7 minutes
/datum/scripture/create_structure/anchoring_crystal
name = "Anchoring Crystal"
desc = "Summon an anchoring crystal to the station."
@@ -19,7 +18,7 @@
. = ..()
/datum/scripture/create_structure/anchoring_crystal/process(seconds_per_tick)
- time_until_invokable = time_until_invokable - seconds_per_tick
+ time_until_invokable = time_until_invokable - (seconds_per_tick SECONDS)
if(time_until_invokable <= 0)
var/datum/scripture/create_structure/anchoring_crystal/global_datum = GLOB.clock_scriptures_by_type[/datum/scripture/create_structure/anchoring_crystal]
STOP_PROCESSING(SSprocessing, global_datum)
@@ -35,7 +34,7 @@
return FALSE
var/datum/objective/anchoring_crystals/crystals_objective = locate() in GLOB.main_clock_cult?.objectives
- if(!crystals_objective || !crystals_objective?.valid_areas.len)
+ if(!length(crystals_objective?.valid_areas))
return FALSE
if(get_charged_anchor_crystals() && !(get_area(invoker) in crystals_objective.valid_areas))
@@ -61,7 +60,7 @@
/datum/scripture/create_structure/anchoring_crystal/invoke_success()
. = ..()
- time_until_invokable = TIME_BETWEEN_INVOKES
+ time_until_invokable = ANCHORING_CRYSTAL_COOLDOWN
var/datum/scripture/create_structure/anchoring_crystal/scripture = GLOB.clock_scriptures_by_type[/datum/scripture/create_structure/anchoring_crystal]
START_PROCESSING(SSprocessing, scripture)
@@ -73,5 +72,3 @@
area_list += added_area.get_original_area_name()
desc = "Summon an anchoring crystal to the station, it can be summoned in [english_list(area_list)]."
tip = "With this crystal [anchoring_crystal_charge_message()]"
-
-#undef TIME_BETWEEN_INVOKES
diff --git a/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/stargazer.dm b/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/stargazer.dm
index f3f4786f1a39..e15b446271e0 100644
--- a/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/stargazer.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/stargazer.dm
@@ -4,7 +4,7 @@
tip = "Make your weapons more powerful by enchanting them with stargazers."
button_icon_state = "Stargazer"
power_cost = 300
- invocation_time = 8 SECONDS
+ invocation_time = 1 MINUTES
invocation_text = list("A light of Eng'ine shall empower my armaments!")
summoned_structure = /obj/structure/destructible/clockwork/gear_base/stargazer
cogs_required = 2
diff --git a/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/tinkerers_cache.dm b/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/tinkerers_cache.dm
index e96177f586ef..adaf72dec91b 100644
--- a/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/tinkerers_cache.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/scriptures/structures/tinkerers_cache.dm
@@ -4,7 +4,7 @@
tip = "Use the cache to create more powerful equipment at the cost of power and time."
button_icon_state = "Tinkerer's Cache"
power_cost = 500
- invocation_time = 5 SECONDS
+ invocation_time = 1 MINUTES
invocation_text = list("Guide my hand and we shall create greatness.")
summoned_structure = /obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache
cogs_required = 4
diff --git a/monkestation/code/modules/antagonists/clock_cult/structures/_structure.dm b/monkestation/code/modules/antagonists/clock_cult/structures/_structure.dm
index 06f95d5730fe..dcaf1679400d 100644
--- a/monkestation/code/modules/antagonists/clock_cult/structures/_structure.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/structures/_structure.dm
@@ -20,14 +20,18 @@
var/datum/mind/owner = null
///Shown to servants when they examine
var/clockwork_desc = ""
+ ///Shown to servants when they examine and are on reebe
+ var/reebe_desc = ""
///can this structure be rotated with a crowbar
var/can_rotate = TRUE
///if set, then the maximum amount of damage this structure can take from take_damage()
var/damage_cap
+ ///a basic cooldown declare for anything that will use it
+ COOLDOWN_DECLARE(use_cooldown)
/obj/structure/destructible/clockwork/Initialize(mapload)
. = ..()
- AddElement(/datum/element/clockwork_description, clockwork_desc)
+ AddElement(/datum/element/clockwork_description, clockwork_desc, reebe_desc)
/obj/structure/destructible/clockwork/Destroy()
owner = null
diff --git a/monkestation/code/modules/antagonists/clock_cult/structures/airlock.dm b/monkestation/code/modules/antagonists/clock_cult/structures/airlock.dm
index c14bf31356c6..03f2b7979419 100644
--- a/monkestation/code/modules/antagonists/clock_cult/structures/airlock.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/structures/airlock.dm
@@ -38,11 +38,11 @@
if(!density || IS_CLOCK(user))
return TRUE
- else
+ else if(!on_reebe(src))
user.Paralyze(2 SECONDS)
- user.electrocute_act(25, src, 1, SHOCK_NOGLOVES|SHOCK_SUPPRESS_MESSAGE)
+ user.electrocute_act(20, src, 1, SHOCK_NOGLOVES|SHOCK_SUPPRESS_MESSAGE)
to_chat(user, span_warning("You feel a sudden jolt as you touch [src]!"))
- return FALSE
+ return FALSE
/obj/machinery/door/airlock/bronze/clock/emp_act(severity)
return
diff --git a/monkestation/code/modules/antagonists/clock_cult/structures/anchor_crystal.dm b/monkestation/code/modules/antagonists/clock_cult/structures/anchor_crystal.dm
index 9b5a84aa5504..914d70b456b4 100644
--- a/monkestation/code/modules/antagonists/clock_cult/structures/anchor_crystal.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/structures/anchor_crystal.dm
@@ -56,23 +56,23 @@ GLOBAL_LIST_EMPTY(anchoring_crystals) //list of all anchoring crystals
clock_theme = new /datum/dimension_theme/clockwork(is_cult = TRUE)
start_turf_conversion()
-
send_clock_message(null, span_bigbrass(span_bold("An Anchoring Crystal has been created at [crystal_area], defend it!")))
- if(GLOB.anchoring_crystals.len >= 2)
+ if(length(GLOB.anchoring_crystals) >= 2)
priority_announce("Reality warping object detected aboard the station.", "Higher Dimensional Affairs", ANNOUNCER_SPANOMALIES, has_important_message = TRUE)
START_PROCESSING(SSprocessing, src)
-
RegisterSignal(src, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays))
-
update_icon()
var/datum/objective/anchoring_crystals/crystals_objective = locate() in GLOB.main_clock_cult?.objectives
if(crystal_area in crystals_objective?.valid_areas) //if a crystal gets destroyed you cant use that area again
crystals_objective.valid_areas -= crystal_area
- SSshuttle.registerHostileEnvironment(src) //removed on destruction on on completetion of charging
+ SSshuttle.registerHostileEnvironment(src) //removed on destruction or once the scripture is off cooldown
+ var/datum/scripture/create_structure/anchoring_crystal/crystal_scripture
+ addtimer(CALLBACK(src, PROC_REF(clear_hostile_environment)), ANCHORING_CRYSTAL_COOLDOWN + initial(crystal_scripture.invocation_time)) //also give them time to invoke
+ GLOB.clock_warp_areas |= crystal_area
/obj/structure/destructible/clockwork/anchoring_crystal/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
COOLDOWN_START(src, recently_hit_cd, CRYSTAL_SHIELD_DELAY)
@@ -96,7 +96,7 @@ GLOBAL_LIST_EMPTY(anchoring_crystals) //list of all anchoring crystals
continue
affected_mob.adjust_silence_up_to(5 SECONDS * seconds_per_tick, 2 MINUTES)
- if(charge_state == FULLY_CHARGED) //if fully charged then add the power and return as we are done here
+ if(charge_state == FULLY_CHARGED) //if fully charged then add the power and return
GLOB.clock_power = min(GLOB.clock_power + (10 * seconds_per_tick), GLOB.max_clock_power)
return
@@ -112,9 +112,9 @@ GLOBAL_LIST_EMPTY(anchoring_crystals) //list of all anchoring crystals
finish_charging()
return
- if(charge_state < CRYSTAL_LOCATION_ANNOUNCED && charging_for >= (CRYSTAL_CHARGE_TIMER * 0.5))
+ if(charge_state < CRYSTAL_LOCATION_ANNOUNCED && charging_for >= (CRYSTAL_CHARGE_TIMER * 0.4))
charge_state = CRYSTAL_LOCATION_ANNOUNCED
- if(GLOB.anchoring_crystals.len >= 2)
+ if(length(GLOB.anchoring_crystals) >= 2)
priority_announce("Reality warping object located in [crystal_area].", "Central Command Higher Dimensional Affairs", ANNOUNCER_SPANOMALIES, has_important_message = TRUE)
/obj/structure/destructible/clockwork/anchoring_crystal/Destroy()
@@ -128,22 +128,21 @@ GLOBAL_LIST_EMPTY(anchoring_crystals) //list of all anchoring crystals
/obj/structure/destructible/clockwork/anchoring_crystal/examine(mob/user) //needs to be here as it has updating information
. = ..()
if(IS_CLOCK(user) || isobserver(user))
- . += span_brass("[charge_state == FULLY_CHARGED ? "It is fully charged and indestructable." : "It will be fully charged in [(CRYSTAL_CHARGE_TIMER - charging_for)] seconds."]")
+ . += span_brass("[charge_state == FULLY_CHARGED ? "It is fully charged and is indestructable." : "It will be fully charged in [(CRYSTAL_CHARGE_TIMER - charging_for)] seconds."]")
//called on init, transforms the turfs and objs in the area of the crystal to clockwork versions
/obj/structure/destructible/clockwork/anchoring_crystal/proc/start_turf_conversion()
var/timer_counter = 1 //used by the addtimer()
for(var/turf/turf_to_transform in crystal_area)
- if(istype(turf_to_transform, /turf/closed) && !(GLOB.anchoring_crystals.len >= 2)) //dont transform walls if its our first crystal, so we can hopefully maintain some stealth
- continue
if(!clock_theme.can_convert(turf_to_transform))
continue
addtimer(CALLBACK(src, PROC_REF(do_turf_conversion), turf_to_transform), 3 * timer_counter)
timer_counter++
/obj/structure/destructible/clockwork/anchoring_crystal/proc/do_turf_conversion(turf/converted_turf)
- if (!clock_theme.can_convert(converted_turf))
+ if(QDELETED(src) || !clock_theme.can_convert(converted_turf))
return
+
clock_theme.apply_theme(converted_turf)
new /obj/effect/temp_visual/ratvar/beam(converted_turf)
if(istype(converted_turf, /turf/closed/wall))
@@ -158,7 +157,7 @@ GLOBAL_LIST_EMPTY(anchoring_crystals) //list of all anchoring crystals
resistance_flags += INDESTRUCTIBLE
atom_integrity = INFINITY
set_armor(/datum/armor/immune)
- if(GLOB.anchoring_crystals.len >= 2)
+ if(length(GLOB.anchoring_crystals) >= 2)
priority_announce("Reality in [crystal_area] has been destabilized, all personnel are advised to avoid the area.", \
"Central Command Higher Dimensional Affairs", ANNOUNCER_SPANOMALIES, has_important_message = TRUE)
@@ -172,8 +171,8 @@ GLOBAL_LIST_EMPTY(anchoring_crystals) //list of all anchoring crystals
switch(get_charged_anchor_crystals())
if(1) //add 2 more max servants and increase replica fabricator build speed
GLOB.main_clock_cult.max_human_servants += SERVANT_CAPACITY_TO_GIVE
- if(3) //create a steam helios on reebe
- if(GLOB.abscond_markers.len)
+ if(ANCHORING_CRYSTALS_TO_SUMMON + 1) //create a steam helios on reebe
+ if(length(GLOB.abscond_markers))
var/turf/created_at = get_turf(pick(GLOB.abscond_markers))
new /obj/vehicle/sealed/mecha/steam_helios(created_at)
new /obj/effect/temp_visual/steam(created_at)
@@ -181,7 +180,7 @@ GLOBAL_LIST_EMPTY(anchoring_crystals) //list of all anchoring crystals
new /obj/vehicle/sealed/mecha/steam_helios(get_turf(GLOB.clock_ark))
else
message_admins("No valid location for Steam Helios creation.")
- if(4) //lock the anchoring crystal scripture and unlock the golem scripture
+ if(ANCHORING_CRYSTALS_TO_SUMMON + 2) //lock the anchoring crystal scripture and unlock the golem scripture
var/datum/scripture/create_structure/anchoring_crystal/crystal_scripture = GLOB.clock_scriptures_by_type[/datum/scripture/create_structure/anchoring_crystal]
crystal_scripture.unique_lock()
@@ -204,17 +203,23 @@ GLOBAL_LIST_EMPTY(anchoring_crystals) //list of all anchoring crystals
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon)), 2)
overlays += shield_appearance
+/obj/structure/destructible/clockwork/anchoring_crystal/proc/clear_hostile_environment()
+ if(QDELETED(src))
+ return
+
+ SSshuttle.clearHostileEnvironment(src)
+
///return a message based off of what this anchoring crystal did/will do for the cult
/proc/anchoring_crystal_charge_message(completed = FALSE)
var/message = ""
switch(get_charged_anchor_crystals())
if(0)
message = "[completed ? "We can now" : "We will be able to"] support 2 more servants and gain faster build speed with replica fabricators on reebe."
- if(1)
+ if(ANCHORING_CRYSTALS_TO_SUMMON - 1)
message = "[completed ? "We can now" : "We will be able to"] open the ark."
- if(2)
+ if(ANCHORING_CRYSTALS_TO_SUMMON)
message = "The Steam Helios, a strong 2 pilot mech, [completed ? "has been" : "will be"] summoned to reebe."
- if(3)
+ if(ANCHORING_CRYSTALS_TO_SUMMON + 1)
message = "Humaniod servants [completed ? "may now" : "will be able to"] ascend their form to that of a clockwork golem, giving them innate armor, environmental immunity, \
and faster invoking for most scriptures."
return message
diff --git a/monkestation/code/modules/antagonists/clock_cult/structures/brass_window.dm b/monkestation/code/modules/antagonists/clock_cult/structures/brass_window.dm
index e9f25209cae7..da3c1e68c84e 100644
--- a/monkestation/code/modules/antagonists/clock_cult/structures/brass_window.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/structures/brass_window.dm
@@ -6,16 +6,22 @@
resistance_flags = FIRE_PROOF | ACID_PROOF
max_integrity = 80
explosion_block = 2
- decon_speed = 40
+ decon_speed = 4 SECONDS
glass_type = /obj/item/stack/sheet/bronze
glass_amount = 1
+/obj/structure/window/reinforced/clockwork/Initialize(mapload, direct)
+ if(on_reebe(src))
+ decon_speed = 1 SECONDS
+ max_integrity = round(max_integrity * 0.5) //I would like to make it take double damage instead but this about works for now
+ . = ..()
+
/obj/structure/window/reinforced/clockwork/attackby_secondary(obj/item/tool, mob/user, params)
if(state == RWINDOW_SECURE)
- if(tool.tool_behaviour == TOOL_WIRECUTTER)
+ if(tool.tool_behaviour == TOOL_WIRECUTTER && IS_CLOCK(user))
user.visible_message(span_notice("[user] starts cutting the pane of \the [src] away..."),
span_notice("You start cutting away the pane of \the [src]."))
- if(tool.use_tool(src, user, 20, volume = 50))
+ if(tool.use_tool(src, user, 2 SECONDS, volume = 50))
state = RWINDOW_BARS_CUT
to_chat(user, span_notice("The window pane falls out of the way exposing the frame bolts."))
return ..()
@@ -52,7 +58,7 @@
fulltile = TRUE
flags_1 = PREVENT_CLICK_UNDER_1
obj_flags = CAN_BE_HIT
- max_integrity = 110
+ max_integrity = 100
glass_amount = 2
/obj/structure/window/reinforced/clockwork/Initialize(mapload, direct)
diff --git a/monkestation/code/modules/antagonists/clock_cult/structures/eminence_beacon.dm b/monkestation/code/modules/antagonists/clock_cult/structures/eminence_beacon.dm
index b6676c23bb29..5fda471f004d 100644
--- a/monkestation/code/modules/antagonists/clock_cult/structures/eminence_beacon.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/structures/eminence_beacon.dm
@@ -35,7 +35,7 @@
return
vote_active = TRUE
-/obj/structure/destructible/clockwork/eminence_beacon/proc/vote_succeed(mob/eminence)
+/obj/structure/destructible/clockwork/eminence_beacon/proc/vote_succeed(mob/living/eminence) //if we select a ghost then we dont call any living procs so this is fine
vote_active = FALSE
if(GLOB.current_eminence)
message_admins("[type] calling vote_succeed() with a set GLOB.current_eminence, this should not be happening.")
@@ -50,22 +50,22 @@
pic_source = /mob/living/eminence,
role_name_text = "eminence"
)
- if(LAZYLEN(candidates))
+ if(length(candidates))
eminence = pick(candidates)
- if(!(eminence?.client) || !(eminence?.mind))
+ if(!(eminence?.client))
send_clock_message(null, "The Eminence remains in slumber, for now, try waking it again soon.")
return
var/mob/living/eminence/new_mob = new /mob/living/eminence(get_turf(src))
- var/datum/antagonist/clock_cultist/servant_datum = eminence.mind.has_antag_datum(/datum/antagonist/clock_cultist)
- if(servant_datum)
- servant_datum.silent = TRUE
- servant_datum.on_removal()
- eminence.mind.transfer_to(new_mob, TRUE)
+ if(isobserver(eminence))
+ new_mob.key = eminence.key
+ else
+ var/datum/antagonist/clock_cultist/servant_datum = eminence.mind.has_antag_datum(/datum/antagonist/clock_cultist)
+ if(servant_datum)
+ servant_datum.silent = TRUE
+ servant_datum.on_removal()
+ eminence.mind.transfer_to(new_mob, TRUE)
+ eminence.dust(TRUE, TRUE)
new_mob.mind.add_antag_datum(/datum/antagonist/clock_cultist/eminence)
send_clock_message(null, span_bigbrass("The Eminence has risen!"))
-
- if(isliving(eminence))
- var/mob/living/living_eminence = eminence
- living_eminence.dust(TRUE, TRUE)
diff --git a/monkestation/code/modules/antagonists/clock_cult/structures/sigil/_sigil.dm b/monkestation/code/modules/antagonists/clock_cult/structures/sigil/_sigil.dm
index 9469b4d5a83d..1fb54cdee3e3 100644
--- a/monkestation/code/modules/antagonists/clock_cult/structures/sigil/_sigil.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/structures/sigil/_sigil.dm
@@ -7,6 +7,9 @@
name = "sigil"
desc = "It's a sigil that does something."
max_integrity = 10
+ break_sound = null
+ debris = null
+ break_message = "The sigil is dispelled."
icon = 'monkestation/icons/obj/clock_cult/clockwork_effects.dmi'
icon_state = "sigilvitality"
density = FALSE
diff --git a/monkestation/code/modules/antagonists/clock_cult/structures/sigil/sigil_vitality.dm b/monkestation/code/modules/antagonists/clock_cult/structures/sigil/sigil_vitality.dm
index b75111bd7ab4..7ef2cad0599d 100644
--- a/monkestation/code/modules/antagonists/clock_cult/structures/sigil/sigil_vitality.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/structures/sigil/sigil_vitality.dm
@@ -1,5 +1,7 @@
-///how damage damage do we heal when reviving someone before costing vitality
+///how much damage do we heal when reviving someone before costing vitality
#define FREE_DAMAGE_HEALED 20
+///how much do we reduce drained mobs health health by each siphon
+#define HEALTH_DRAINED 20
/obj/structure/destructible/clockwork/sigil/vitality
name = "vitality matrix"
desc = "A twisting, confusing artifact that drains the unenlightended on contact."
@@ -36,13 +38,13 @@
active_timer = null
var/revived = FALSE
if(affected_mob.stat == DEAD)
- var/damage_healed = FREE_DAMAGE_HEALED + ((affected_mob.maxHealth - affected_mob.health) * 0.6)
+ var/damage_healed = FREE_DAMAGE_HEALED + ((affected_mob.getMaxHealth() - affected_mob.health) * 0.6)
if(GLOB.clock_vitality >= damage_healed)
GLOB.clock_vitality -= damage_healed
affected_mob.revive(ADMIN_HEAL_ALL)
revived = TRUE
- if(!affected_mob.client || affected_mob.client.is_afk())
+ if(affected_mob.stat != DEAD && (!affected_mob.client || affected_mob.client.is_afk()))
set waitfor = FALSE
var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob(
"Do you want to play as a [affected_mob.real_name], an inactive clock cultist?",
@@ -71,45 +73,42 @@
return
affected_mob.Paralyze(1 SECONDS)
+ var/before_drain = affected_mob.getMaxHealth()
+ affected_mob.setMaxHealth(before_drain - HEALTH_DRAINED)
+ var/after_drain = affected_mob.getMaxHealth()
- var/before_cloneloss = affected_mob.getCloneLoss()
- affected_mob.adjustCloneLoss(19, TRUE, TRUE)
- var/after_cloneloss = affected_mob.getCloneLoss()
-
- if(before_cloneloss == after_cloneloss)
+ if(before_drain == after_drain)
visible_message(span_clockred("[src] fails to siphon [affected_mob]'s spirit!"))
return
playsound(loc, 'sound/magic/clockwork/ratvar_attack.ogg', 40)
- if((affected_mob.stat == DEAD) || (affected_mob.getCloneLoss() >= affected_mob.maxHealth))
- affected_mob.do_jitter_animation()
- affected_mob.become_husk(src)
- affected_mob.death()
+ if((affected_mob.stat == DEAD) || affected_mob.getMaxHealth() <= 0)
playsound(loc, 'sound/magic/exit_blood.ogg', 60)
to_chat(affected_mob, span_clockred("The last of your life is drained away..."))
check_special_role(affected_mob)
- GLOB.clock_vitality = min(GLOB.clock_vitality + 40, GLOB.max_clock_vitality) // 100 (for clients) total in the ideal situation, since it'll take 6 pulses to go from full to crit
- ADD_TRAIT(affected_mob, TRAIT_NO_SOUL, CULT_TRAIT)
+ GLOB.clock_vitality = min(GLOB.clock_vitality + 40, MAX_CLOCK_VITALITY) // 100 (for clients) total in the ideal situation, since it'll take 6 pulses to go from full to crit
if(affected_mob.client)
new /obj/item/robot_suit/prebuilt/clockwork(get_turf(src))
var/obj/item/mmi/posibrain/soul_vessel/new_vessel = new(get_turf(src))
if(!is_banned_from(affected_mob.ckey, list(JOB_CYBORG, ROLE_CLOCK_CULTIST)))
new_vessel.transfer_personality(affected_mob)
+ affected_mob.dust(TRUE, TRUE)
return
affected_mob.visible_message(span_clockred("[affected_mob] looks weak as the color fades from their body."), span_clockred("You feel your soul faltering..."))
- GLOB.clock_vitality = min(GLOB.clock_vitality + (affected_mob.client ? 10 : 1), GLOB.max_clock_vitality) // Monkey or whatever? You get jackshit
+ GLOB.clock_vitality = min(GLOB.clock_vitality + (affected_mob.client ? 10 : 1), MAX_CLOCK_VITALITY) // Monkey or whatever? You get jackshit
/// Checks the role of whoever was killed by the vitality sigil, and does any special code if needed.
/obj/structure/destructible/clockwork/sigil/vitality/proc/check_special_role(mob/living/affected_mob)
if(IS_CULTIST(affected_mob)) //for now these just give extra vitality, but at some point I need to make them give something unique, maybe the gun?
send_clock_message(null, span_clockred("The dog of Nar'sie, [affected_mob] has had their vitality drained, rejoice!"))
- GLOB.clock_vitality = min(GLOB.clock_vitality + 20, GLOB.max_clock_vitality)
+ GLOB.clock_vitality = min(GLOB.clock_vitality + 20, MAX_CLOCK_VITALITY)
else if(IS_HERETIC(affected_mob))
send_clock_message(null, span_clockred("The heretic, [affected_mob] has had their vitality drained, rejoice!"))
- GLOB.clock_vitality = min(GLOB.clock_vitality + 30, GLOB.max_clock_vitality)
+ GLOB.clock_vitality = min(GLOB.clock_vitality + 30, MAX_CLOCK_VITALITY)
else
send_clock_message(null, span_clockred("[affected_mob] has had their vitality drained by [src], rejoice!"))
#undef FREE_DAMAGE_HEALED
+#undef HEALTH_DRAINED
diff --git a/monkestation/code/modules/antagonists/clock_cult/structures/stargazer.dm b/monkestation/code/modules/antagonists/clock_cult/structures/stargazer.dm
index cbbfb333abe2..db898e74c02d 100644
--- a/monkestation/code/modules/antagonists/clock_cult/structures/stargazer.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/structures/stargazer.dm
@@ -1,18 +1,17 @@
-#define STARGAZER_COOLDOWN 180 SECONDS
-
//Stargazer structure
/obj/structure/destructible/clockwork/gear_base/stargazer
name = "stargazer"
desc = "A small pedestal, glowing with a divine energy."
clockwork_desc = "Place a weapon upon it to provide special powers and abilities to the weapon."
+ reebe_desc = "The energies of reebe are interfering with it's abilites, making it only be able to to enchant things at the lowest level."
icon_state = "stargazer"
base_icon_state = "stargazer"
anchored = TRUE
break_message = span_warning("The stargazer collapses.")
///ref to our visual effect, migtht be able to make this just be an overlay
var/obj/effect/stargazer_light/light_effect
- ///cooldown for enchanting items
- var/cooldown_time = 0
+ ///how long is our use cooldown
+ var/stargazer_cooldown = 3 MINUTES
/obj/structure/destructible/clockwork/gear_base/stargazer/Initialize(mapload)
. = ..()
@@ -65,13 +64,13 @@
if(istype(attacking_item, /obj/item) && !istype(attacking_item, /obj/item/clothing) && attacking_item.force)
upgrade_weapon(attacking_item, user)
- cooldown_time = world.time + STARGAZER_COOLDOWN
+ COOLDOWN_START(src, use_cooldown, stargazer_cooldown)
return
to_chat(user, span_brass("You cannot upgrade \the [attacking_item]."))
/obj/structure/destructible/clockwork/gear_base/stargazer/proc/enchanting_checks(obj/item/checked_item, mob/living/user)
- if(cooldown_time > world.time)
- to_chat(user, span_brass("\The [src] is still warming up, it will be ready in [DisplayTimeText(cooldown_time - world.time)]."))
+ if(!COOLDOWN_FINISHED(src, use_cooldown))
+ to_chat(user, span_brass("\The [src] is still warming up, it will be ready in [DisplayTimeText(COOLDOWN_TIMELEFT(src, use_cooldown))]."))
return FALSE
if(HAS_TRAIT(checked_item, TRAIT_STARGAZED))
@@ -135,5 +134,3 @@
/obj/effect/stargazer_light/proc/cancel_timer()
if(active_timer)
deltimer(active_timer)
-
-#undef STARGAZER_COOLDOWN
diff --git a/monkestation/code/modules/antagonists/clock_cult/structures/the_ark.dm b/monkestation/code/modules/antagonists/clock_cult/structures/the_ark.dm
index a10d48c3f560..39b9377c3f22 100644
--- a/monkestation/code/modules/antagonists/clock_cult/structures/the_ark.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/structures/the_ark.dm
@@ -135,9 +135,9 @@ GLOBAL_VAR_INIT(ratvar_risen, FALSE)
addtimer(CALLBACK(src, PROC_REF(open_gateway)), ARK_READY_PERIOD)
/obj/structure/destructible/clockwork/the_ark/proc/open_gateway()
- if(current_state >= ARK_STATE_ACTIVE)
+ if(current_state >= ARK_STATE_GRACE)
return
- current_state = ARK_STATE_ACTIVE
+ current_state = ARK_STATE_GRACE
SSshuttle.registerHostileEnvironment(src)
icon_state = "clockwork_gateway_active"
send_clock_message(null, span_bigbrass("The Ark has been activated, you will be transported soon! Dont forget to gather weapons with your \"Clockwork Armaments\" scripture."), \
@@ -146,24 +146,24 @@ GLOBAL_VAR_INIT(ratvar_risen, FALSE)
/obj/structure/destructible/clockwork/the_ark/proc/announce_gateway()
send_clock_message(null, span_ratvar("DESTROY THE HERETICS."), sent_sound = 'monkestation/sound/machines/clockcult/ark_recall.ogg')
-
sleep(3 SECONDS)
+ current_state = ARK_STATE_ACTIVE
for(var/datum/mind/servant_mind in GLOB.main_clock_cult.members)
var/mob/living/servant_mob = servant_mind.current
- if(!servant_mob || QDELETED(servant_mob))
+ if(QDELETED(servant_mob))
continue
+
if(GLOB.abscond_markers)
try_servant_warp(servant_mob, get_turf(pick(GLOB.abscond_markers)))
- if(ishuman(servant_mob))
- var/datum/antagonist/clock_cultist/servant_antag = servant_mind.has_antag_datum(/datum/antagonist/clock_cultist)
- if(servant_antag)
- servant_antag.forbearance = mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER)
- servant_mob.add_overlay(servant_antag.forbearance)
+
+ var/datum/antagonist/clock_cultist/servant_antag = servant_mind.has_antag_datum(/datum/antagonist/clock_cultist)
+ servant_antag?.add_forbearance(servant_mob)
sound_to_playing_players('sound/magic/clockwork/invoke_general.ogg', 50)
SSsecurity_level.set_level(3)
addtimer(CALLBACK(src, PROC_REF(begin_assault)), ARK_GRACE_PERIOD)
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(send_station_support_package), /obj/item/turf_demolisher/reebe), 10 SECONDS)
priority_announce("Massive [Gibberish("bluespace", 100)] anomaly detected on all frequencies. All crew are directed to \
@!$, [text2ratvar("PURGE ALL UNTRUTHS")] <&. the anomalies and destroy their source to prevent further damage to corporate property. This is \
@@ -226,11 +226,11 @@ GLOBAL_VAR_INIT(ratvar_risen, FALSE)
/proc/explode_reebe()
var/list/reebe_area_list = get_area_turfs(/area/ruin/powered/reebe/city)
- if(reebe_area_list.len)
+ if(length(reebe_area_list))
for(var/i in 1 to 30)
explosion(pick(reebe_area_list), 0, 2, 4, 4, FALSE)
sleep(5)
- if(GLOB.abscond_markers.len)
+ if(length(GLOB.abscond_markers))
explosion(pick(GLOB.abscond_markers), 50, 40, 30, 30, FALSE, TRUE)
SSticker.force_ending = TRUE
diff --git a/monkestation/code/modules/antagonists/clock_cult/structures/tinkerers_cache.dm b/monkestation/code/modules/antagonists/clock_cult/structures/tinkerers_cache.dm
index 1483e6964cdf..e26694e97a8c 100644
--- a/monkestation/code/modules/antagonists/clock_cult/structures/tinkerers_cache.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/structures/tinkerers_cache.dm
@@ -4,22 +4,22 @@
icon_state = "tinkerers_cache"
base_icon_state = "tinkerers_cache"
clockwork_desc = "Can be used to forge powerful Ratvarian items and traps at the cost of power and time."
+ reebe_desc = "It's connection to the physical realm is weakened from being on reebe, restricting its ability to make certain items."
anchored = TRUE
break_message = span_warning("The tinkerer's cache melts into a pile of brass.")
has_on_icon = FALSE
has_off_icon = FALSE
has_power_toggle = FALSE
- COOLDOWN_DECLARE(use_cooldown)
/// Assoc list of the names of all the craftable items to their path
- var/static/list/craft_possibilities
-
+ var/static/list/station_craftable
+ /// Assoc list of items craftable on reebe
+ var/static/list/reebe_craftable
/obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache/Initialize(mapload)
. = ..()
- if(!length(craft_possibilities))
+ if(!length(station_craftable) || !length(reebe_craftable))
assemble_datum_list()
-
/obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache/attack_hand(mob/living/user)
. = ..()
if(.)
@@ -41,46 +41,45 @@
to_chat(user, span_brass("[src] is still warming up, it will be ready in [DisplayTimeText(COOLDOWN_TIMELEFT(src, use_cooldown))]."))
return
- var/selection = tgui_input_list(user, "Select an item to create at the forge.", "Forging", craft_possibilities)
-
- if(!selection)
+ var/datum/tinker_cache_item/chosen_item = tgui_input_list(user, "Select an item to create at the forge.", "Forging", \
+ (on_reebe(src) ? reebe_craftable : reebe_craftable + station_craftable))
+ if(!chosen_item)
return
- var/datum/tinker_cache_item/chosen_item = craft_possibilities[selection]
-
+ chosen_item = station_craftable[chosen_item]
if(!can_interact(user) || !anchored || depowered || !chosen_item || !COOLDOWN_FINISHED(src, use_cooldown))
return
- if(!LAZYLEN(transmission_sigils))
+ if(!length(transmission_sigils))
to_chat(user, span_brass("This needs to be connected to a transmission sigil!"))
return
var/amount_to_create = 1
- if(!initial(chosen_item.time_delay_mult))
+ if(!chosen_item.time_delay_mult)
amount_to_create = tgui_input_number(user, "How many would you like to create?", "Tinkerers Cache", max_value = 10, min_value = 1)
- if(!use_power(initial(chosen_item.power_use) * amount_to_create))
+ if(!use_power(chosen_item.power_use * amount_to_create))
to_chat(user, span_brass("You need more power to forge this item."))
return
- COOLDOWN_START(src, use_cooldown, 4 MINUTES * initial(chosen_item.time_delay_mult))
+ if(chosen_item.time_delay_mult)
+ COOLDOWN_START(src, use_cooldown, 4 MINUTES * chosen_item.time_delay_mult)
- var/crafting_item = initial(chosen_item.item_path)
+ var/crafting_item = chosen_item.item_path
for(var/i in 1 to amount_to_create)
new crafting_item(get_turf(src))
playsound(src, 'sound/machines/clockcult/steam_whoosh.ogg', 50)
- to_chat(user, span_brass("You craft [initial(chosen_item.name)] to near perfection, \the [src] cooling down. \
- [initial(chosen_item.time_delay_mult) ? "It will be available in [DisplayTimeText(COOLDOWN_TIMELEFT(src, use_cooldown))]." : "It is ready to use again."]"))
-
+ to_chat(user, span_brass("You craft [chosen_item.name] to near perfection, \the [src] cooling down. \
+ [chosen_item.time_delay_mult ? "It will be available in [DisplayTimeText(COOLDOWN_TIMELEFT(src, use_cooldown))]." : "It is ready to use again."]"))
// Assemble a list of subtype tinker cache datums
/obj/structure/destructible/clockwork/gear_base/powered/tinkerers_cache/proc/assemble_datum_list()
- craft_possibilities = list()
- for(var/type in subtypesof(/datum/tinker_cache_item))
- var/datum/tinker_cache_item/initial_item = type
- craft_possibilities["[initial(initial_item.name)] ([initial(initial_item.power_use)] W)"] = type
-
+ station_craftable = list()
+ reebe_craftable = list()
+ for(var/datum/tinker_cache_item/initial_item as anything in subtypesof(/datum/tinker_cache_item))
+ initial_item = new initial_item
+ (!initial_item.allowed_on_reebe ? station_craftable : reebe_craftable)["[initial_item.name] ([initial_item.power_use] W)"] = initial_item
// This used to be a hardcoded list
/datum/tinker_cache_item
@@ -92,26 +91,32 @@
var/power_use = 0
/// Multiplier for time delay (default 4m) after producing this item
var/time_delay_mult = 1
+ /// Is this item able to be fabricated on reebe
+ var/allowed_on_reebe = TRUE
/datum/tinker_cache_item/speed_robes
name = "Robes Of Divinity"
item_path = /obj/item/clothing/suit/clockwork/speed
power_use = 200
+ allowed_on_reebe = FALSE
/datum/tinker_cache_item/invis_cloak
name = "Shrouding Cloak"
item_path = /obj/item/clothing/suit/clockwork/cloak
power_use = 200
+ allowed_on_reebe = FALSE
/datum/tinker_cache_item/sight_goggles
name = "Wraith Spectacles"
item_path = /obj/item/clothing/glasses/clockwork/wraith_spectacles
power_use = 500
+ allowed_on_reebe = FALSE
/datum/tinker_cache_item/hud_visor
name = "Judicial Visor"
item_path = /obj/item/clothing/glasses/clockwork/judicial_visor
power_use = 400
+ allowed_on_reebe = FALSE
/datum/tinker_cache_item/replica_fabricator
name = "Replica Fabricator"
diff --git a/monkestation/code/modules/antagonists/clock_cult/structures/traps/recievers/skewer.dm b/monkestation/code/modules/antagonists/clock_cult/structures/traps/recievers/skewer.dm
index 795d68c668a2..6d1aee126bcc 100644
--- a/monkestation/code/modules/antagonists/clock_cult/structures/traps/recievers/skewer.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/structures/traps/recievers/skewer.dm
@@ -16,7 +16,7 @@
component_datum = /datum/component/clockwork_trap/skewer
unwrench_path = /obj/item/clockwork/trap_placer/skewer
buckle_lying = FALSE
- max_integrity = 40
+ max_integrity = 20
clockwork_desc = "A skewer that can pierce through a target, activated by a linked trigger."
COOLDOWN_DECLARE(stab_cooldown)
/// If the spear is currently extended
@@ -24,6 +24,11 @@
/// Mutable appearance stab overlay
var/mutable_appearance/stab_overlay
+/datum/armor/raised_clock_skewer
+ laser = 30
+ melee = 50
+ bullet = 40
+ energy = 30
/// Stab any non-clock mobs who stood on the tile
/obj/structure/destructible/clockwork/trap/skewer/proc/stab()
@@ -35,11 +40,11 @@
return
COOLDOWN_START(src, stab_cooldown, 10 SECONDS)
-
extended = TRUE
icon_state = "[initial(icon_state)]_extended"
var/target_stabbed = FALSE
density = TRUE
+ set_armor(/datum/armor/raised_clock_skewer)
for(var/mob/living/stabbed_mob in get_turf(src))
if(stabbed_mob.incorporeal_move || (stabbed_mob.movement_type & (FLOATING|FLYING)))
@@ -95,6 +100,7 @@
icon_state = initial(icon_state)
density = FALSE
cut_overlay(stab_overlay)
+ set_armor(null)
for(var/mob/living/stabbed_mob as anything in buckled_mobs)
unbuckle_mob(stabbed_mob, TRUE)
diff --git a/monkestation/code/modules/antagonists/clock_cult/turf.dm b/monkestation/code/modules/antagonists/clock_cult/turf.dm
index 8bd077ff4d42..41c3e5cf1c7a 100644
--- a/monkestation/code/modules/antagonists/clock_cult/turf.dm
+++ b/monkestation/code/modules/antagonists/clock_cult/turf.dm
@@ -196,7 +196,7 @@
//do the deconstruction stuff, this really should be a proc on Rwalls as well
/turf/closed/wall/clockwork/proc/next_decon_state(obj/item/used_tool, mob/user, current_state, set_state, sent_message, use_time = 4 SECONDS)
if(on_reebe(src))
- use_time = round(use_time / 2, 0.1) //each stage takes half as long on reebe
+ use_time = round(use_time * 0.2, 0.1) //it takes much less time to deconstruct walls on reebe
if(used_tool.use_tool(src, user, use_time, volume=100))
if(!istype(src, /turf/closed/wall/clockwork) || d_state != current_state)
diff --git a/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm b/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm
index c36be4bbb47c..6d984316c65d 100644
--- a/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm
+++ b/monkestation/code/modules/antagonists/wizard/equipment/artefact.dm
@@ -28,7 +28,7 @@
return
//reactive talisman
-#define REACTION_COOLDOWN_DURATION 15 SECONDS
+#define REACTION_COOLDOWN_DURATION 10 SECONDS
/obj/item/clothing/neck/neckless/wizard_reactive //reactive armor for wizards that casts a spell when it reacts
name = "reactive talisman"
desc = "A reactive talisman for the reactive mage."
diff --git a/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm b/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
index 27e95736d103..4199e85c6e73 100644
--- a/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
+++ b/monkestation/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
@@ -10,4 +10,4 @@
desc = "An enchanted talisman that has a chance to cast a spell if it's wearer is hit."
item_path = /obj/item/clothing/neck/neckless/wizard_reactive
category = "Defensive"
- cost = 2
+ cost = 1
diff --git a/monkestation/code/modules/store/atm/_atm.dm b/monkestation/code/modules/store/atm/_atm.dm
index 87aeeff4e101..9df9e2097a30 100644
--- a/monkestation/code/modules/store/atm/_atm.dm
+++ b/monkestation/code/modules/store/atm/_atm.dm
@@ -26,7 +26,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/atm, 30)
REGISTER_REQUIRED_MAP_ITEM(1, INFINITY)
if(!lottery_running)
lottery_running = TRUE
- addtimer(CALLBACK(src, PROC_REF(pull_lottery_winner)), 20 MINUTES)
+ addtimer(CALLBACK(src, PROC_REF(poll_lottery_winner)), 20 MINUTES)
/obj/machinery/atm/ui_interact(mob/user, datum/tgui/ui)
if(!is_operational)
@@ -43,19 +43,21 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/atm, 30)
/obj/machinery/atm/ui_data(mob/user)
var/list/data = list()
-
if(!user.client)
return
+
var/cash_balance = 0
- var/obj/item/user_id = user.get_item_by_slot(ITEM_SLOT_ID)
- if(user_id && istype(user_id, /obj/item/card/id))
- var/obj/item/card/id/id_card = user_id.GetID()
- cash_balance = id_card.registered_account.account_balance
+ var/obj/item/card/id/user_id = user.get_item_by_slot(ITEM_SLOT_ID)
+ if(user_id)
+ user_id = user_id.GetID()
+ if(istype(user_id))
+ cash_balance = user_id.registered_account.account_balance
else
if(ishuman(user))
var/mob/living/carbon/human/human_user = user
var/datum/bank_account/user_account = SSeconomy.bank_accounts_by_id["[human_user.account_id]"]
- cash_balance = user_account.account_balance
+ if(user_account)
+ cash_balance = user_account.account_balance
data["meta_balance"] = user.client.prefs.metacoins
data["cash_balance"] = cash_balance
@@ -93,9 +95,12 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/atm, 30)
if("buy_flash")
buy_flash_sale()
return TRUE
+ if("buy_lootbox")
+ buy_lootbox()
+ return TRUE
return TRUE
-/obj/machinery/atm/proc/pull_lottery_winner()
+/obj/machinery/atm/proc/poll_lottery_winner()
if(length(ticket_owners))
var/datum/bank_account/winning_account = pick_weight(ticket_owners)
winning_account.account_balance += lottery_pool
@@ -108,7 +113,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/atm, 30)
lottery_running = FALSE
if(!lottery_running)
lottery_running = TRUE
- addtimer(CALLBACK(src, PROC_REF(pull_lottery_winner)), 20 MINUTES)
+ addtimer(CALLBACK(src, PROC_REF(poll_lottery_winner)), 20 MINUTES)
/obj/machinery/atm/proc/buy_lottery()
if(!iscarbon(usr))
@@ -143,8 +148,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/atm, 30)
/obj/machinery/atm/proc/attempt_withdraw()
var/mob/living/living_user = usr
- var/current_balance = living_user.client.prefs.metacoins
+ if(!living_user)
+ return
+ var/current_balance = living_user.client.prefs.metacoins
var/withdraw_amount = tgui_input_number(living_user, "How many Monkecoins would you like to withdraw?", "ATM", 0 , current_balance, 0)
if(!withdraw_amount)
@@ -159,6 +166,24 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/atm, 30)
living_user.put_in_hands(coin_stack)
+/obj/machinery/atm/proc/buy_lootbox()
+ var/mob/living/living_user = usr
+ if(!living_user)
+ return
+
+ var/current_balance = living_user.client.prefs.metacoins
+ if(tgui_alert(living_user, "Are you sure you would like to purchase a lootbox for [LOOTBOX_COST] monkecoins?", "Balance: [current_balance]", list("Yes", "No")) != "Yes")
+ return
+
+ if(!living_user.client.prefs.has_coins(LOOTBOX_COST))
+ to_chat(living_user, span_warning("Not enough monkecoins."))
+ return
+
+ if(!living_user.client.prefs.adjust_metacoins(living_user.client.ckey, -LOOTBOX_COST, donator_multipler = FALSE))
+ return
+
+ var/obj/item/lootbox/box = new(get_turf(living_user))
+ living_user.put_in_hands(box)
/obj/machinery/atm/attacked_by(obj/item/attacking_item, mob/living/user)
@@ -168,6 +193,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/atm, 30)
var/obj/item/stack/monkecoin/attacked_coins = attacking_item
if(!user.client.prefs.adjust_metacoins(user.client.ckey, attacked_coins.amount, donator_multipler = FALSE))
say("Error acceptings coins, please try again later.")
+ return
qdel(attacked_coins)
say("Coins deposited to your account, have a nice day.")
diff --git a/monkestation/code/modules/storytellers/converted_events/solo/clockwork_cult.dm b/monkestation/code/modules/storytellers/converted_events/solo/clockwork_cult.dm
index 0ebe10a6fd18..3f7475364bc1 100644
--- a/monkestation/code/modules/storytellers/converted_events/solo/clockwork_cult.dm
+++ b/monkestation/code/modules/storytellers/converted_events/solo/clockwork_cult.dm
@@ -31,11 +31,11 @@
base_antags = 4
maximum_antags = 4
// I give up, just there should be enough heads with 35 players...
- min_players = 30
+ min_players = 35
roundstart = TRUE
earliest_start = 0 SECONDS
weight = 4
- max_occurrences = 1
+ max_occurrences = 0
/datum/round_event/antagonist/solo/clockcult
end_when = 60000
diff --git a/monkestation/code/modules/trading/lootbox_buying.dm b/monkestation/code/modules/trading/lootbox_buying.dm
index bc66145a268d..1e41f79ea68c 100644
--- a/monkestation/code/modules/trading/lootbox_buying.dm
+++ b/monkestation/code/modules/trading/lootbox_buying.dm
@@ -14,7 +14,7 @@
if(!prefs)
lootbox_prompt = FALSE
return
- if(!prefs.has_coins(5000))
+ if(!prefs.has_coins(LOOTBOX_COST))
to_chat(src, span_warning("You do not have enough Monkecoins to buy a lootbox"))
lootbox_prompt = FALSE
return
@@ -27,11 +27,11 @@
return
/client/proc/attempt_lootbox_buy()
- if(!prefs.has_coins(5000))
+ if(!prefs.has_coins(LOOTBOX_COST))
to_chat(src, span_warning("You do not have enough Monkecoins to buy a lootbox"))
lootbox_prompt = FALSE
return
- if(!prefs.adjust_metacoins(ckey, -5000, donator_multipler = FALSE))
+ if(!prefs.adjust_metacoins(ckey, -LOOTBOX_COST, donator_multipler = FALSE))
return
prefs.lootboxes_owned++
prefs.save_preferences()
diff --git a/monkestation/code/modules/uplink/uplink_items/badass.dm b/monkestation/code/modules/uplink/uplink_items/badass.dm
index bf735ae2ba18..4dbca914bf05 100644
--- a/monkestation/code/modules/uplink/uplink_items/badass.dm
+++ b/monkestation/code/modules/uplink/uplink_items/badass.dm
@@ -15,3 +15,11 @@
purchasable_from = ALL
limited_stock = 1
cant_discount = TRUE
+
+/datum/uplink_item/badass/bad_time_stickers
+ name = "Leaked Sticker Pack"
+ desc = "A box of leaked stickers from the NT production line."
+ item = /obj/item/storage/box/monkestation_stickers/bad_time
+ cost = 1
+ purchasable_from = ALL
+ cant_discount = TRUE
diff --git a/monkestation/icons/mob/clock_cult/clockwork_garb_worn.dmi b/monkestation/icons/mob/clock_cult/clockwork_garb_worn.dmi
index 4bfa69d6dd41..4709b9468721 100644
Binary files a/monkestation/icons/mob/clock_cult/clockwork_garb_worn.dmi and b/monkestation/icons/mob/clock_cult/clockwork_garb_worn.dmi differ
diff --git a/monkestation/icons/mob/clock_cult/clockwork_lefthand.dmi b/monkestation/icons/mob/clock_cult/clockwork_lefthand.dmi
index ca0e053ef610..8e8bb58de2b0 100644
Binary files a/monkestation/icons/mob/clock_cult/clockwork_lefthand.dmi and b/monkestation/icons/mob/clock_cult/clockwork_lefthand.dmi differ
diff --git a/monkestation/icons/mob/clock_cult/clockwork_righthand.dmi b/monkestation/icons/mob/clock_cult/clockwork_righthand.dmi
index b7cd85785bb8..b951aa8ece05 100644
Binary files a/monkestation/icons/mob/clock_cult/clockwork_righthand.dmi and b/monkestation/icons/mob/clock_cult/clockwork_righthand.dmi differ
diff --git a/monkestation/icons/obj/clock_cult/clockwork_weapons.dmi b/monkestation/icons/obj/clock_cult/clockwork_weapons.dmi
index 1919027144cb..d4a82b6797cb 100644
Binary files a/monkestation/icons/obj/clock_cult/clockwork_weapons.dmi and b/monkestation/icons/obj/clock_cult/clockwork_weapons.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 5a26bb276b5f..4fd36bb85a04 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -398,6 +398,7 @@
#include "code\__DEFINES\~monkestation\colors.dm"
#include "code\__DEFINES\~monkestation\combat.dm"
#include "code\__DEFINES\~monkestation\company.dm"
+#include "code\__DEFINES\~monkestation\components.dm"
#include "code\__DEFINES\~monkestation\construction.dm"
#include "code\__DEFINES\~monkestation\cooldowns.dm"
#include "code\__DEFINES\~monkestation\cybernetics.dm"
@@ -441,10 +442,11 @@
#include "code\__DEFINES\~monkestation\dcs\signals\signals_atom.dm"
#include "code\__DEFINES\~monkestation\dcs\signals\signals_blueshift.dm"
#include "code\__DEFINES\~monkestation\dcs\signals\signals_carbon.dm"
+#include "code\__DEFINES\~monkestation\dcs\signals\signals_element.dm"
#include "code\__DEFINES\~monkestation\dcs\signals\signals_guns.dm"
#include "code\__DEFINES\~monkestation\dcs\signals\signals_item.dm"
-#include "code\__DEFINES\~monkestation\dcs\signals\signals_mob.dm"
#include "code\__DEFINES\~monkestation\dcs\signals\signals_traitor.dm"
+#include "code\__DEFINES\~monkestation\dcs\signals\signals_mob\signals_mob_main.dm"
#include "code\__HELPERS\_auxtools_api.dm"
#include "code\__HELPERS\_lists.dm"
#include "code\__HELPERS\_planes.dm"
@@ -4420,7 +4422,6 @@
#include "code\modules\mob\living\basic\space_fauna\faithless.dm"
#include "code\modules\mob\living\basic\space_fauna\garden_gnome.dm"
#include "code\modules\mob\living\basic\space_fauna\ghost.dm"
-#include "code\modules\mob\living\basic\space_fauna\headslug.dm"
#include "code\modules\mob\living\basic\space_fauna\killer_tomato.dm"
#include "code\modules\mob\living\basic\space_fauna\lightgeist.dm"
#include "code\modules\mob\living\basic\space_fauna\morph.dm"
@@ -4439,6 +4440,8 @@
#include "code\modules\mob\living\basic\space_fauna\carp\carp_controllers.dm"
#include "code\modules\mob\living\basic\space_fauna\carp\magicarp.dm"
#include "code\modules\mob\living\basic\space_fauna\carp\megacarp.dm"
+#include "code\modules\mob\living\basic\space_fauna\changeling\flesh_spider.dm"
+#include "code\modules\mob\living\basic\space_fauna\changeling\headslug.dm"
#include "code\modules\mob\living\basic\space_fauna\demon\demon.dm"
#include "code\modules\mob\living\basic\space_fauna\demon\demon_items.dm"
#include "code\modules\mob\living\basic\space_fauna\demon\demon_subtypes.dm"
@@ -5736,6 +5739,7 @@
#include "monkestation\code\_onclick\hud\alert.dm"
#include "monkestation\code\area\areas\direction_names.dm"
#include "monkestation\code\area\areas\station.dm"
+#include "monkestation\code\controllers\subsystem\economy.dm"
#include "monkestation\code\controllers\subsystem\job.dm"
#include "monkestation\code\datums\action.dm"
#include "monkestation\code\datums\dna.dm"
@@ -5762,8 +5766,10 @@
#include "monkestation\code\datums\components\irradiated.dm"
#include "monkestation\code\datums\components\multi_hit.dm"
#include "monkestation\code\datums\components\throw_bounce.dm"
+#include "monkestation\code\datums\components\turf_checker_complex.dm"
#include "monkestation\code\datums\components\turf_healing.dm"
#include "monkestation\code\datums\components\uplink.dm"
+#include "monkestation\code\datums\components\wound_converter.dm"
#include "monkestation\code\datums\diseases\advance\symptoms\clockwork.dm"
#include "monkestation\code\datums\elements\area_locked.dm"
#include "monkestation\code\datums\elements\uncompressed_storage.dm"
@@ -5823,6 +5829,7 @@
#include "monkestation\code\game\objects\items\spraycan_gun.dm"
#include "monkestation\code\game\objects\items\stickers.dm"
#include "monkestation\code\game\objects\items\superglue.dm"
+#include "monkestation\code\game\objects\items\turf_demolisher.dm"
#include "monkestation\code\game\objects\items\venom_knife.dm"
#include "monkestation\code\game\objects\items\AI_modules\monke_lawsets.dm"
#include "monkestation\code\game\objects\items\circuitboards\computer_circuitboards.dm"
@@ -5958,6 +5965,7 @@
#include "monkestation\code\modules\antagonists\clock_cult\temp_visual.dm"
#include "monkestation\code\modules\antagonists\clock_cult\turf.dm"
#include "monkestation\code\modules\antagonists\clock_cult\actions\_action.dm"
+#include "monkestation\code\modules\antagonists\clock_cult\actions\add_warp_area.dm"
#include "monkestation\code\modules\antagonists\clock_cult\actions\purge_reagents.dm"
#include "monkestation\code\modules\antagonists\clock_cult\actions\recall_slab.dm"
#include "monkestation\code\modules\antagonists\clock_cult\actions\space_fold.dm"
diff --git a/tgui/packages/tgui/interfaces/ATM.jsx b/tgui/packages/tgui/interfaces/ATM.jsx
index 8f8bb5240b4a..4983b2b03d46 100644
--- a/tgui/packages/tgui/interfaces/ATM.jsx
+++ b/tgui/packages/tgui/interfaces/ATM.jsx
@@ -49,6 +49,11 @@ export const ATM = (props) => {
Purchase Lottery Tickets
+
+
+
{flash_sale_present ? (