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 ? (