diff --git a/_maps/map_files/generic/City_of_Cogs.dmm b/_maps/map_files/generic/City_of_Cogs.dmm index 9ef765aa28..aba6569fd9 100644 --- a/_maps/map_files/generic/City_of_Cogs.dmm +++ b/_maps/map_files/generic/City_of_Cogs.dmm @@ -80,10 +80,6 @@ }, /turf/open/floor/clockwork/reebe, /area/reebe/city_of_cogs) -"az" = ( -/obj/structure/destructible/clockwork/eminence_spire, -/turf/open/floor/clockwork/reebe, -/area/reebe/city_of_cogs) "aA" = ( /obj/machinery/door/airlock/clockwork/brass{ name = "Infirmary" @@ -168,21 +164,6 @@ /obj/effect/clockwork/sigil/transmission, /turf/open/floor/clockwork/reebe, /area/reebe/city_of_cogs) -"ew" = ( -/obj/structure/table/reinforced/brass, -/obj/item/clockwork/construct_chassis/cogscarab{ - pixel_y = 20 - }, -/obj/item/clockwork/construct_chassis/cogscarab{ - pixel_x = -7; - pixel_y = 6 - }, -/obj/item/clockwork/construct_chassis/cogscarab{ - pixel_x = 7; - pixel_y = 6 - }, -/turf/open/floor/clockwork/reebe, -/area/reebe/city_of_cogs) "hY" = ( /obj/item/clockwork/alloy_shards/pinion_lock, /turf/open/floor/clockwork/reebe, @@ -208,10 +189,6 @@ /obj/item/storage/belt/utility/servant, /turf/open/floor/clockwork/reebe, /area/reebe/city_of_cogs) -"vx" = ( -/obj/effect/clockwork/sigil/transmission, -/turf/open/floor/clockwork/reebe, -/area/reebe/city_of_cogs) "wV" = ( /obj/item/clockwork/alloy_shards/large, /turf/open/indestructible/reebe_void/spawning/lattices, @@ -244,6 +221,10 @@ /obj/item/clockwork/alloy_shards/small, /turf/open/indestructible/reebe_void/spawning/lattices, /area/reebe) +"Ox" = ( +/obj/structure/destructible/clockwork/eminence_spire, +/turf/open/floor/clockwork/reebe, +/area/reebe/city_of_cogs) "OB" = ( /obj/item/clockwork/component/geis_capacitor/fallen_armor, /turf/open/floor/clockwork/reebe, @@ -32770,7 +32751,7 @@ ae ae ag ah -ew +aL aj aj aj @@ -33284,8 +33265,8 @@ ab ab ae ai -az -vx +Ox +aj aj aj aE diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index faffea0e6e..ea48671d82 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -59,6 +59,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( //Human sub-species #define isabductor(A) (is_species(A, /datum/species/abductor)) #define isgolem(A) (is_species(A, /datum/species/golem)) +#define isclockworkgolem(A) (is_species(A, /datum/species/golem/clockwork/no_scrap)) #define islizard(A) (is_species(A, /datum/species/lizard)) #define isplasmaman(A) (is_species(A, /datum/species/plasmaman)) #define ispodperson(A) (is_species(A, /datum/species/pod)) diff --git a/code/_onclick/hud/clockwork_marauder.dm b/code/_onclick/hud/clockwork_marauder.dm index 09f85db574..b1b24cc58b 100644 --- a/code/_onclick/hud/clockwork_marauder.dm +++ b/code/_onclick/hud/clockwork_marauder.dm @@ -3,7 +3,7 @@ var/obj/screen/blockchance var/obj/screen/counterchance -/datum/hud/marauder/New(mob/living/simple_animal/hostile/clockwork/marauder/guardian/owner) +/datum/hud/marauder/New(mob/living/simple_animal/hostile/clockwork/guardian/owner) ..() var/obj/screen/using @@ -24,7 +24,7 @@ hosthealth = null return ..() -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/create_mob_hud() +/mob/living/simple_animal/hostile/clockwork/guardian/create_mob_hud() if(client && !hud_used) hud_used = new /datum/hud/marauder(src, ui_style2icon(client.prefs.UI_style)) @@ -37,8 +37,8 @@ desc = "Emerge or Return." /obj/screen/marauder/emerge/Click() - if(istype(usr, /mob/living/simple_animal/hostile/clockwork/marauder/guardian)) - var/mob/living/simple_animal/hostile/clockwork/marauder/guardian/G = usr + if(istype(usr, /mob/living/simple_animal/hostile/clockwork/guardian)) + var/mob/living/simple_animal/hostile/clockwork/guardian/G = usr if(G.is_in_host()) G.try_emerge() else diff --git a/code/datums/skills/modifiers/job.dm b/code/datums/skills/modifiers/job.dm index e989ab11e3..e751fbf120 100644 --- a/code/datums/skills/modifiers/job.dm +++ b/code/datums/skills/modifiers/job.dm @@ -34,3 +34,6 @@ /datum/skill_modifier/job/level/wiring/basic level_mod = JOB_SKILL_BASIC + +/datum/skill_modifier/job/level/dwarfy/blacksmithing + target_skills = /datum/skill/level/dwarfy/blacksmithing diff --git a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm index b868fb54c0..34370faa0a 100644 --- a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm +++ b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm @@ -144,7 +144,7 @@ to_chat(L, "\"You belong to me now.\"") if(!GLOB.application_scripture_unlocked) GLOB.application_scripture_unlocked = TRUE - hierophant_message("With the conversion of a new servant the Ark's power grows. Application scriptures are now available.") + hierophant_message("With the conversion of a new servant the Hierophant Network's power grows. Application scriptures are now available.") if(add_servant_of_ratvar(L)) L.log_message("conversion was done with a [sigil_name]", LOG_ATTACK, color="BE8700") if(iscarbon(L)) diff --git a/code/modules/antagonists/clockcult/clock_helpers/clock_rites.dm b/code/modules/antagonists/clockcult/clock_helpers/clock_rites.dm index 58ee73ef5b..2b2720dea3 100644 --- a/code/modules/antagonists/clockcult/clock_helpers/clock_rites.dm +++ b/code/modules/antagonists/clockcult/clock_helpers/clock_rites.dm @@ -196,4 +196,68 @@ CL.visible_message("[CL] materialises out of thin air!") new /obj/effect/temp_visual/ratvar/sigil/transmission(T,2) +//summons a soul vessel, which is the clockwork cult version of a soul shard. It acts like a posibrain and, as long as the target has a brain, a soul shard. +/datum/clockwork_rite/soul_vessel + name = "Rite of the Vessel" //The name of the rite + desc = "This rite is used to summon a soul vessel, a special posibrain that makes whoever has their brain put into it loyal to the Justiciar.,\ + When put into a cyborg shell, the created cyborg will automatically be a servant of Ratvar." + required_ingredients = list(/obj/item/stack/cable_coil, /obj/item/stock_parts/cell/, /obj/item/organ/cyberimp) + power_cost = 2500 //These things are pretty strong, I won't lie + requires_full_power = TRUE + cast_time = 50 + limit = INFINITE + rite_cast_sound = 'sound/magic/summon_guns.ogg' + +/datum/clockwork_rite/soul_vessel/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target) + . = ..() + if(!.) + return FALSE + var/obj/item/mmi/posibrain/soul_vessel/SV = new /obj/item/mmi/posibrain/soul_vessel(T) + SV.visible_message("[SV] materalizes out of thin air!") + new /obj/effect/temp_visual/ratvar/sigil/transmission(T,2) + + +/datum/clockwork_rite/cyborg_transform + name = "Rite of the Divine Form" + desc = "This rite is used to ascend into a cyborg, gaining unique scripture and a loadout that depends on which module is chosen. Consult the wiki for details on each cyborg module's loadout. Mutually exclusive to Enhanced Form." + required_ingredients = list(/obj/item/mmi/posibrain, /obj/item/stack/cable_coil, /obj/item/stock_parts/cell/super, /obj/item/bodypart/l_arm/robot, /obj/item/bodypart/r_arm/robot, /obj/item/bodypart/chest/robot, /obj/item/bodypart/head/robot, /obj/item/bodypart/r_leg/robot, /obj/item/bodypart/l_leg/robot) + power_cost = 20000 + requires_human = TRUE + requires_full_power = FALSE + cast_time = 100 + limit = INFINITE + rite_cast_sound = 'sound/magic/disable_tech.ogg' + +/datum/clockwork_rite/cyborg_transform/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target) + . = ..() + if(!.) + return FALSE + if(isclockworkgolem(target)) + return FALSE + target.visible_message("The robotic parts magnetize to [target], the new frame's eyes glowing in a brilliant yellow!") + var/mob/living/silicon/robot/R = target.Robotize() + R.cell = new /obj/item/stock_parts/cell/super(R)//takes one to use the rite to begin with + new /obj/effect/temp_visual/ratvar/sigil/transmission(T,2) + +/datum/clockwork_rite/golem_transform + name = "Rite of the Enhanced Form" + desc = "This rite is used to shed one's flesh to become a clockwork automaton, becoming immune to many environmental hazards as well as being more resilient to incoming damage. Mutually exclusive to Divine Form." + required_ingredients = list(/obj/item/mmi/posibrain, /obj/item/stock_parts/cell/super, /obj/item/bodypart/l_arm/robot, /obj/item/bodypart/r_arm/robot, /obj/item/bodypart/chest/robot, /obj/item/bodypart/head/robot, /obj/item/bodypart/r_leg/robot, /obj/item/bodypart/l_leg/robot) + power_cost = 20000 + requires_human = TRUE + requires_full_power = FALSE + cast_time = 100 + limit = INFINITE + rite_cast_sound = 'sound/magic/disable_tech.ogg' + + +/datum/clockwork_rite/golem_transform/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target) + . = ..() + if(!.) + return FALSE + target.visible_message("The robotic parts magnetize to [target], the humanoid shape's eye glowing with an inner flame!") + to_chat(target, "The rite's power warps your body into a clockwork form! You are now immune to many hazards, and your body is more robust against damage!") + target.set_species(/datum/species/golem/clockwork/no_scrap) + new /obj/effect/temp_visual/ratvar/sigil/transmission(T,2) + #undef INFINITE diff --git a/code/modules/antagonists/clockcult/clock_helpers/fabrication_helpers.dm b/code/modules/antagonists/clockcult/clock_helpers/fabrication_helpers.dm index b0259c460e..99d1142990 100644 --- a/code/modules/antagonists/clockcult/clock_helpers/fabrication_helpers.dm +++ b/code/modules/antagonists/clockcult/clock_helpers/fabrication_helpers.dm @@ -350,3 +350,22 @@ // Winter coat /obj/item/clothing/suit/hooded/wintercoat/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) //four sheets of metal return list("operation_time" = 30, "new_obj_type" = /obj/item/clothing/suit/hooded/wintercoat/ratvar, "power_cost" = POWER_METAL * 4, "spawn_dir" = SOUTH) + +//tools +/obj/item/crowbar/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) //1 metal rod each + return list("operation_time" = 2, "new_obj_type" = /obj/item/crowbar/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH) + +/obj/item/screwdriver/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) + return list("operation_time" = 2, "new_obj_type" = /obj/item/screwdriver/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH) + +/obj/item/weldingtool/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) + return list("operation_time" = 2, "new_obj_type" = /obj/item/weldingtool/experimental/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH) + +/obj/item/wirecutters/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) + return list("operation_time" = 2, "new_obj_type" = /obj/item/wirecutters/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH) + +/obj/item/wrench/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) + return list("operation_time" = 2, "new_obj_type" = /obj/item/wrench/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH) + +/obj/item/multitool/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) + return list("operation_time" = 2, "new_obj_type" = /obj/item/multitool/advanced/brass, "power_cost" = POWER_ROD * 1, "spawn_dir" = SOUTH) diff --git a/code/modules/antagonists/clockcult/clock_items/clock_augments.dm b/code/modules/antagonists/clockcult/clock_items/clock_augments.dm index 2131aa7160..ff19e8891b 100644 --- a/code/modules/antagonists/clockcult/clock_items/clock_augments.dm +++ b/code/modules/antagonists/clockcult/clock_items/clock_augments.dm @@ -5,7 +5,7 @@ name = "clock-themed arm-mounted implant" var/clockwork_desc = "According to Ratvar, this really shouldn't exist. Tell Him about this immediately." syndicate_implant = TRUE - icon_state = "clock_arm_implant" + icon_state = "toolkit_implant" /obj/item/organ/cyberimp/arm/clockwork/ui_action_click() if(is_servant_of_ratvar(owner) || (obj_flags & EMAGGED)) //If you somehow manage to steal a clockie's implant AND have an emag AND manage to get it implanted for yourself, good on ya! diff --git a/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm b/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm index dfda2bf152..d9938316b6 100644 --- a/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm +++ b/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm @@ -251,7 +251,9 @@ var/mob/camera/eminence/E = owner E.eminence_help() + /* + //Returns to the Ark - Commented out and replaced with obelisk_jump /datum/action/innate/eminence/ark_jump name = "Return to Ark" @@ -265,7 +267,7 @@ owner.playsound_local(owner, 'sound/magic/magic_missile.ogg', 50, TRUE) flash_color(owner, flash_color = "#AF0AAF", flash_time = 25) else - to_chat(owner, "There is no Ark!") + to_chat(owner, "There is no Ark!") */ //Warps to a chosen Obelisk diff --git a/code/modules/antagonists/clockcult/clock_mobs/clockwork_guardian.dm b/code/modules/antagonists/clockcult/clock_mobs/clockwork_guardian.dm new file mode 100644 index 0000000000..5060f516c7 --- /dev/null +++ b/code/modules/antagonists/clockcult/clock_mobs/clockwork_guardian.dm @@ -0,0 +1,432 @@ +//Clockwork guardian: Slow but with high damage, resides inside of a servant. Created via the Memory Allocation scripture. +/mob/living/simple_animal/hostile/clockwork/guardian + name = "clockwork guardian" + desc = "A slow, armored clockwork machine, blazing with magenta flames. It's armed with a gladius and shield, and stands ready by its master." + icon_state = "clockwork_guardian" + health = 300 + maxHealth = 300 + speed = 1 + obj_damage = 40 + melee_damage_lower = 20//ranged attacks are the way to go for fighting these + melee_damage_upper = 20 + attack_verb_continuous = "slashes" + attack_verb_simple = "slash" + attack_sound = 'sound/weapons/bladeslice.ogg' + weather_immunities = list("lava") + movement_type = FLYING + AIStatus = AI_OFF //this has to be manually set so that the guardian doesn't start bashing the host, how annoying -_- + loot = list(/obj/structure/destructible/clockwork/taunting_trail) + var/true_name = "Meme Master 69" //Required to call forth the guardian + var/global/list/possible_true_names = list("Servant", "Warden", "Serf", "Page", "Usher", "Knave", "Vassal", "Escort") + var/mob/living/host //The mob that the guardian is living inside of + var/recovering = FALSE //If the guardian is recovering from recalling + var/blockchance = 17 //chance to block attacks entirely + var/counterchance = 30 //chance to counterattack after blocking + var/static/list/damage_heal_order = list(OXY, BURN, BRUTE, TOX) //we heal our host's damage in this order + light_color = "#AF0AAF" + light_range = 2 + light_power = 1.1 + playstyle_string = "You are a clockwork guardian, a living extension of Sevtug's will. As a guardian, you are somewhat slow, but may block attacks, \ + and have a chance to also counter blocked melee attacks for extra damage, in addition to being immune to extreme temperatures and pressures. \ + Your primary goal is to serve the creature that you are now a part of, as well as The Clockwork Justiciar, Ratvar. You can use The Hierophant Network to communicate silently with your master and their allies, \ + but can only exit if your master calls your true name or if they are exceptionally damaged. \ + \n\n\ + Stay near your host to protect and heal them; being too far from your host will rapidly cause you massive damage. Recall to your host if you are too weak and believe you cannot continue \ + fighting safely. As a final note, you should probably avoid harming any fellow servants of Ratvar." + +/mob/living/simple_animal/hostile/clockwork/guardian/Initialize() + . = ..() + true_name = pick(possible_true_names) + +/mob/living/simple_animal/hostile/clockwork/guardian/BiologicalLife(seconds, times_fired) + ..() + if(is_in_host()) + if(!is_servant_of_ratvar(host)) + emerge_from_host(FALSE, TRUE) + unbind_from_host() + return + if(!GLOB.ratvar_awakens && host.stat == DEAD) + death() + return + if(GLOB.ratvar_awakens) + adjustHealth(-50) + else + adjustHealth(-10) + if(!recovering) + heal_host() //also heal our host if inside of them and we aren't recovering + else if(health == maxHealth) + to_chat(src, "Your strength has returned. You can once again come forward!") + to_chat(host, "Your guardian is now strong enough to come forward again!") + recovering = FALSE + else + if(GLOB.ratvar_awakens) //If Ratvar is alive, guardians don't need a host and are downright impossible to kill + adjustHealth(-5) + heal_host() + else if(host) + if(!is_servant_of_ratvar(host)) + unbind_from_host() + return + if(host.stat == DEAD) + adjustHealth(50) + to_chat(src, "Your host is dead!") + return + if(z && host.z && z == host.z) + switch(get_dist(get_turf(src), get_turf(host))) + if(2) + adjustHealth(-1) + if(3) + //EQUILIBRIUM + if(4) + adjustHealth(1) + if(5) + adjustHealth(3) + if(6) + adjustHealth(6) + if(7) + adjustHealth(9) + if(8 to INFINITY) + adjustHealth(15) + to_chat(src, "You're too far from your host and rapidly taking damage!") + else //right next to or on top of host + adjustHealth(-2) + heal_host() //gradually heal host if nearby and host is very weak + else //well then, you're not even in the same zlevel + adjustHealth(15) + to_chat(src, "You're too far from your host and rapidly taking damage!") + +/mob/living/simple_animal/hostile/clockwork/guardian/death(gibbed) + emerge_from_host(FALSE, TRUE) + unbind_from_host() + visible_message("[src]'s equipment fades into a purple mist as the flames within sputter and dissipate.", \ + "Your equipment fades away. You feel a moment of confusion before your fragile form is annihilated.") + . = ..() + +/mob/living/simple_animal/hostile/clockwork/guardian/Stat() + ..() + if(statpanel("Status")) + stat(null, "Current True Name: [true_name]") + stat(null, "Host: [host ? host : "NONE"]") + if(host) + var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5) + if(iscarbon(host)) + resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100) + stat(null, "Host Health: [resulthealth]%") + if(GLOB.ratvar_awakens) + stat(null, "You are [recovering ? "un" : ""]able to deploy!") + else + if(resulthealth > GUARDIAN_EMERGE_THRESHOLD) + stat(null, "You are [recovering ? "unable to deploy" : "able to deploy on hearing your True Name"]!") + else + stat(null, "You are [recovering ? "unable to deploy" : "able to deploy to protect your host"]!") + stat(null, "You do [melee_damage_upper] damage on melee attacks.") + +/mob/living/simple_animal/hostile/clockwork/guardian/Process_Spacemove(movement_dir = 0) + return 1 + +/mob/living/simple_animal/hostile/clockwork/guardian/proc/bind_to_host(mob/living/new_host) + if(!new_host) + return FALSE + host = new_host + var/datum/action/innate/summon_guardian/SG = new() + SG.linked_guardian = src + SG.Grant(host) + var/datum/action/innate/linked_minds/LM = new() + LM.linked_guardian = src + LM.Grant(host) + return TRUE + +/mob/living/simple_animal/hostile/clockwork/guardian/proc/unbind_from_host() + if(host) + for(var/datum/action/innate/summon_guardian/SG in host.actions) + qdel(SG) + for(var/datum/action/innate/linked_minds/LM in host.actions) + qdel(LM) + host = null + return TRUE + return FALSE + +//DAMAGE and FATIGUE +/mob/living/simple_animal/hostile/clockwork/guardian/proc/heal_host() + if(!host) + return + var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5) + if(iscarbon(host)) + resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100) + if(GLOB.ratvar_awakens || resulthealth <= GUARDIAN_EMERGE_THRESHOLD) + new /obj/effect/temp_visual/heal(host.loc, "#AF0AAF") + host.heal_ordered_damage(4, damage_heal_order) + +/mob/living/simple_animal/hostile/clockwork/guardian/adjustHealth(amount, updating_health = TRUE, forced = FALSE) + if(amount > 0) + for(var/mob/living/L in view(2, src)) + if(L.is_holding_item_of_type(/obj/item/nullrod)) + to_chat(src, "The presence of a brandished holy artifact weakens your armor!") + amount *= 4 //if a wielded null rod is nearby, it takes four times the health damage + break + . = ..() + if(src && updating_health) + update_health_hud() + update_stats() + +/mob/living/simple_animal/hostile/clockwork/guardian/update_health_hud() + if(hud_used && hud_used.healths) + if(istype(hud_used, /datum/hud)) + var/datum/hud/marauder/G = hud_used + var/resulthealth + if(host) + if(iscarbon(host)) + resulthealth = "[round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)]%" + else + resulthealth = "[round((host.health / host.maxHealth) * 100, 0.5)]%" + else + resulthealth = "NONE" + G.hosthealth.maptext = "
HOST
[resulthealth]
" + hud_used.healths.maptext = "
[round((health / maxHealth) * 100, 0.5)]%" + +/mob/living/simple_animal/hostile/clockwork/guardian/proc/update_stats() + if(GLOB.ratvar_awakens) + speed = 0 + melee_damage_lower = 20 + melee_damage_upper = 20 + attack_verb_continuous = "devastates" + else + var/healthpercent = (health/maxHealth) * 100 + switch(healthpercent) + if(100 to 70) //Bonuses to speed and damage at high health + speed = 0 + melee_damage_lower = 16 + melee_damage_upper = 16 + attack_verb_continuous = "viciously slashes" + if(70 to 40) + speed = initial(speed) + melee_damage_lower = initial(melee_damage_lower) + melee_damage_upper = initial(melee_damage_upper) + attack_verb_continuous = initial(attack_verb_continuous) + if(40 to 30) //Damage decrease, but not speed + speed = initial(speed) + melee_damage_lower = 10 + melee_damage_upper = 10 + attack_verb_continuous = "lightly slashes" + if(30 to 20) //Speed decrease + speed = 2 + melee_damage_lower = 8 + melee_damage_upper = 8 + attack_verb_continuous = "lightly slashes" + if(20 to 10) //Massive speed decrease and weak melee attacks + speed = 3 + melee_damage_lower = 6 + melee_damage_upper = 6 + attack_verb_continuous = "weakly slashes" + if(10 to 0) //We are super weak and going to die + speed = 4 + melee_damage_lower = 4 + melee_damage_upper = 4 + attack_verb_continuous = "taps" + +//ATTACKING, BLOCKING, and COUNTERING + +/mob/living/simple_animal/hostile/clockwork/guardian/AttackingTarget() + if(is_in_host()) + return FALSE + return ..() + +/mob/living/simple_animal/hostile/clockwork/guardian/bullet_act(obj/item/projectile/Proj) + if(blockOrCounter(null, Proj)) + return + return ..() + +/mob/living/simple_animal/hostile/clockwork/guardian/hitby(atom/movable/AM, skipcatch, hitpush, blocked, atom/movable/AM, datum/thrownthing/throwingdatum) + if(blockOrCounter(null, AM)) + return + return ..() + +/mob/living/simple_animal/hostile/clockwork/guardian/attack_animal(mob/living/simple_animal/M) + if(istype(M, /mob/living/simple_animal/hostile/clockwork/guardian) || !blockOrCounter(M, M)) //we don't want infinite blockcounter loops if fighting another guardian + return ..() + +/mob/living/simple_animal/hostile/clockwork/guardian/attack_paw(mob/living/carbon/monkey/M) + if(!blockOrCounter(M, M)) + return ..() + +/mob/living/simple_animal/hostile/clockwork/guardian/attack_alien(mob/living/carbon/alien/humanoid/M) + if(!blockOrCounter(M, M)) + return ..() + +/mob/living/simple_animal/hostile/clockwork/guardian/attack_slime(mob/living/simple_animal/slime/M) + if(!blockOrCounter(M, M)) + return ..() + +/mob/living/simple_animal/hostile/clockwork/guardian/attack_hand(mob/living/carbon/human/M) + if(!blockOrCounter(M, M)) + return ..() + +/mob/living/simple_animal/hostile/clockwork/guardian/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/nullrod) || !blockOrCounter(user, I)) + return ..() + +/mob/living/simple_animal/hostile/clockwork/guardian/proc/blockOrCounter(mob/target, atom/textobject) + if(GLOB.ratvar_awakens) //if ratvar has woken, we block nearly everything at a very high chance + blockchance = 90 + counterchance = 90 + if(prob(blockchance)) + . = TRUE + if(target) + target.do_attack_animation(src) + target.DelayNextAction(CLICK_CD_MELEE) + blockchance = initial(blockchance) + playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', 30, 1, 0, 1) //clang + visible_message("[src] blocks [target && isitem(textobject) ? "[target]'s [textobject.name]":"\the [textobject]"]!", \ + "You block [target && isitem(textobject) ? "[target]'s [textobject.name]":"\the [textobject]"]!") + if(target && Adjacent(target)) + if(prob(counterchance)) + counterchance = initial(counterchance) + var/previousattack_verb_continuous = attack_verb_continuous + attack_verb_continuous = "counters" + UnarmedAttack(target) + attack_verb_continuous = previousattack_verb_continuous + else + counterchance = min(counterchance + initial(counterchance), 100) + else + blockchance = min(blockchance + initial(blockchance), 100) + if(GLOB.ratvar_awakens) + blockchance = 90 + counterchance = 90 + +//COMMUNICATION and EMERGENCE +/* +/mob/living/simple_animal/hostile/clockwork/guardian/handle_inherent_channels(message, message_mode) + if(host && (is_in_host() || message_mode == MODE_BINARY)) + guardian_comms(message) + return TRUE + return ..() +*/ +/mob/living/simple_animal/hostile/clockwork/guardian/proc/guardian_comms(message) + var/name_part = "[src] ([true_name])" + message = "\"[message]\"" //Processed output + to_chat(src, "[name_part]: [message]") + to_chat(host, "[name_part]: [message]") + for(var/M in GLOB.mob_list) + if(isobserver(M)) + var/link = FOLLOW_LINK(M, src) + to_chat(M, "[link] [name_part] (to [findtextEx(host.name, host.real_name) ? "[host.name]" : "[host.real_name] (as [host.name])"]): [message] ") + return TRUE + +/mob/living/simple_animal/hostile/clockwork/guardian/proc/return_to_host() + if(is_in_host()) + return FALSE + if(!host) + to_chat(src, "You don't have a host!") + return FALSE + var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5) + if(iscarbon(host)) + resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100) + host.visible_message("[host]'s skin flashes magenta!", "You feel [true_name]'s consciousness settle in your mind.") + visible_message("[src] suddenly disappears!", "You return to [host].") + forceMove(host) + if(resulthealth > GUARDIAN_EMERGE_THRESHOLD && health != maxHealth) + recovering = TRUE + to_chat(src, "You have weakened and will need to recover before manifesting again!") + to_chat(host, "[true_name] has weakened and will need to recover before manifesting again!") + return TRUE + +/mob/living/simple_animal/hostile/clockwork/guardian/proc/try_emerge() + if(!host) + to_chat(src, "You don't have a host!") + return FALSE + if(!GLOB.ratvar_awakens) + var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5) + if(iscarbon(host)) + resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100) + if(host.stat != DEAD && resulthealth > GUARDIAN_EMERGE_THRESHOLD) //if above 20 health, fails + to_chat(src, "Your host must be at [GUARDIAN_EMERGE_THRESHOLD]% or less health to emerge like this!") + return FALSE + return emerge_from_host(FALSE) + +/mob/living/simple_animal/hostile/clockwork/guardian/proc/emerge_from_host(hostchosen, force) //Notice that this is a proc rather than a verb - guardians can NOT exit at will, but they CAN return + if(!is_in_host()) + return FALSE + if(!force && recovering) + if(hostchosen) + to_chat(host, "[true_name] is too weak to come forth!") + else + to_chat(host, "[true_name] tries to emerge to protect you, but it's too weak!") + to_chat(src, "You try to come forth, but you're too weak!") + return FALSE + if(!force) + if(hostchosen) //guardian approved + to_chat(host, "Your words echo with power as [true_name] emerges from your body!") + else + to_chat(host, "[true_name] emerges from your body to protect you!") + forceMove(host.loc) + visible_message("[host]'s skin glows red as [name] emerges from their body!", "You exit the safety of [host]'s body!") + return TRUE + +/mob/living/simple_animal/hostile/clockwork/guardian/get_alt_name() + return " ([text2ratvar(true_name)])" + +/mob/living/simple_animal/hostile/clockwork/guardian/proc/is_in_host() //Checks if the guardian is inside of their host + return host && loc == host + +//HOST ACTIONS + +//Summon guardian action: Calls forth or recalls your guardian +/datum/action/innate/summon_guardian + name = "Force Guardian to Emerge/Recall" + desc = "Allows you to force your clockwork guardian to emerge or recall as required." + button_icon_state = "clockwork_marauder" + background_icon_state = "bg_clock" + check_flags = AB_CHECK_CONSCIOUS + buttontooltipstyle = "clockcult" + var/mob/living/simple_animal/hostile/clockwork/guardian/linked_guardian + var/list/defend_phrases = list("Defend me", "Come forth", "Assist me", "Protect me", "Give aid", "Help me") + var/list/return_phrases = list("Return", "Return to me", "Your job is done", "You have served", "Come back", "Retreat") + +/datum/action/innate/summon_guardian/IsAvailable() + if(!linked_guardian) + return FALSE + if(isliving(owner)) + var/mob/living/L = owner + if(!L.can_speak_vocal() || L.stat) + return FALSE + return ..() + +/datum/action/innate/summon_guardian/Activate() + if(linked_guardian.is_in_host()) + clockwork_say(owner, text2ratvar("[pick(defend_phrases)], [linked_guardian.true_name]!")) + linked_guardian.emerge_from_host(TRUE) + else + clockwork_say(owner, text2ratvar("[pick(return_phrases)], [linked_guardian.true_name]!")) + linked_guardian.return_to_host() + return TRUE + +//Linked Minds action: talks to your guardian +/datum/action/innate/linked_minds + name = "Linked Minds" + desc = "Allows you to silently communicate with your guardian." + button_icon_state = "linked_minds" + background_icon_state = "bg_clock" + check_flags = AB_CHECK_CONSCIOUS + buttontooltipstyle = "clockcult" + var/mob/living/simple_animal/hostile/clockwork/guardian/linked_guardian + +/datum/action/innate/linked_minds/IsAvailable() + if(!linked_guardian) + return FALSE + return ..() + +/datum/action/innate/linked_minds/Activate() + var/message = stripped_input(owner, "Enter a message to tell your guardian.", "Telepathy") + if(!owner || !message) + return FALSE + if(!linked_guardian) + to_chat(owner, "Your guardian seems to have been destroyed!") + return FALSE + var/name_part = "Servant [findtextEx(owner.name, owner.real_name) ? "[owner.name]" : "[owner.real_name] (as [owner.name])"]" + message = "\"[message]\"" //Processed output + to_chat(owner, "[name_part]: [message]") + to_chat(linked_guardian, "[name_part]: [message]") + for(var/M in GLOB.mob_list) + if(isobserver(M)) + var/link = FOLLOW_LINK(M, src) + to_chat(M, "[link] [name_part] (to [linked_guardian] ([linked_guardian.true_name])): [message]") + return TRUE diff --git a/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm b/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm index 76c9db7231..d58608652c 100644 --- a/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm +++ b/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm @@ -123,437 +123,3 @@ #undef MARAUDER_SLOWDOWN_PERCENTAGE #undef MARAUDER_SHIELD_REGEN_TIME - -//Clockwork guardian: Slow but with high damage, resides inside of a servant. Created via the Memory Allocation scripture. -/mob/living/simple_animal/hostile/clockwork/marauder/guardian - name = "clockwork guardian" - desc = "A stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield and stands ready by its master." - icon_state = "clockwork_marauder" - health = 300 - maxHealth = 300 - speed = 1 - obj_damage = 40 - melee_damage_lower = 12 - melee_damage_upper = 12 - attack_verb_continuous = "slashes" - attack_verb_simple = "slash" - attack_sound = 'sound/weapons/bladeslice.ogg' - weather_immunities = list("lava") - movement_type = FLYING - AIStatus = AI_OFF //this has to be manually set so that the guardian doesn't start bashing the host, how annoying -_- - loot = list(/obj/item/clockwork/component/geis_capacitor/fallen_armor) - max_shield_health = 0 - shield_health = 0 - var/true_name = "Meme Master 69" //Required to call forth the guardian - var/global/list/possible_true_names = list("Servant", "Warden", "Serf", "Page", "Usher", "Knave", "Vassal", "Escort") - var/mob/living/host //The mob that the guardian is living inside of - var/recovering = FALSE //If the guardian is recovering from recalling - var/blockchance = 17 //chance to block attacks entirely - var/counterchance = 30 //chance to counterattack after blocking - var/static/list/damage_heal_order = list(OXY, BURN, BRUTE, TOX) //we heal our host's damage in this order - light_range = 2 - light_power = 1.1 - playstyle_string = "You are a clockwork guardian, a living extension of Sevtug's will. As a guardian, you are somewhat slow, but may block attacks, \ - and have a chance to also counter blocked melee attacks for extra damage, in addition to being immune to extreme temperatures and pressures. \ - Your primary goal is to serve the creature that you are now a part of, as well as The Clockwork Justiciar, Ratvar. You can use The Hierophant Network to communicate silently with your master and their allies, \ - but can only exit if your master calls your true name or if they are exceptionally damaged. \ - \n\n\ - Stay near your host to protect and heal them; being too far from your host will rapidly cause you massive damage. Recall to your host if you are too weak and believe you cannot continue \ - fighting safely. As a final note, you should probably avoid harming any fellow servants of Ratvar." - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/Initialize() - . = ..() - true_name = pick(possible_true_names) - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/BiologicalLife(seconds, times_fired) - ..() - if(is_in_host()) - if(!is_servant_of_ratvar(host)) - emerge_from_host(FALSE, TRUE) - unbind_from_host() - return - if(!GLOB.ratvar_awakens && host.stat == DEAD) - death() - return - if(GLOB.ratvar_awakens) - adjustHealth(-50) - else - adjustHealth(-10) - if(!recovering) - heal_host() //also heal our host if inside of them and we aren't recovering - else if(health == maxHealth) - to_chat(src, "Your strength has returned. You can once again come forward!") - to_chat(host, "Your guardian is now strong enough to come forward again!") - recovering = FALSE - else - if(GLOB.ratvar_awakens) //If Ratvar is alive, guardians don't need a host and are downright impossible to kill - adjustHealth(-5) - heal_host() - else if(host) - if(!is_servant_of_ratvar(host)) - unbind_from_host() - return - if(host.stat == DEAD) - adjustHealth(50) - to_chat(src, "Your host is dead!") - return - if(z && host.z && z == host.z) - switch(get_dist(get_turf(src), get_turf(host))) - if(2) - adjustHealth(-1) - if(3) - //EQUILIBRIUM - if(4) - adjustHealth(1) - if(5) - adjustHealth(3) - if(6) - adjustHealth(6) - if(7) - adjustHealth(9) - if(8 to INFINITY) - adjustHealth(15) - to_chat(src, "You're too far from your host and rapidly taking damage!") - else //right next to or on top of host - adjustHealth(-2) - heal_host() //gradually heal host if nearby and host is very weak - else //well then, you're not even in the same zlevel - adjustHealth(15) - to_chat(src, "You're too far from your host and rapidly taking damage!") - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/death(gibbed) - emerge_from_host(FALSE, TRUE) - unbind_from_host() - visible_message("[src]'s equipment clatters lifelessly to the ground as the red flames within dissipate.", \ - "Your equipment falls away. You feel a moment of confusion before your fragile form is annihilated.") - . = ..() - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/Stat() - ..() - if(statpanel("Status")) - stat(null, "Current True Name: [true_name]") - stat(null, "Host: [host ? host : "NONE"]") - if(host) - var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5) - if(iscarbon(host)) - resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100) - stat(null, "Host Health: [resulthealth]%") - if(GLOB.ratvar_awakens) - stat(null, "You are [recovering ? "un" : ""]able to deploy!") - else - if(resulthealth > GUARDIAN_EMERGE_THRESHOLD) - stat(null, "You are [recovering ? "unable to deploy" : "able to deploy on hearing your True Name"]!") - else - stat(null, "You are [recovering ? "unable to deploy" : "able to deploy to protect your host"]!") - stat(null, "You do [melee_damage_upper] damage on melee attacks.") - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/Process_Spacemove(movement_dir = 0) - return 1 - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/bind_to_host(mob/living/new_host) - if(!new_host) - return FALSE - host = new_host - var/datum/action/innate/summon_guardian/SG = new() - SG.linked_guardian = src - SG.Grant(host) - var/datum/action/innate/linked_minds/LM = new() - LM.linked_guardian = src - LM.Grant(host) - return TRUE - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/unbind_from_host() - if(host) - for(var/datum/action/innate/summon_guardian/SG in host.actions) - qdel(SG) - for(var/datum/action/innate/linked_minds/LM in host.actions) - qdel(LM) - host = null - return TRUE - return FALSE - -//DAMAGE and FATIGUE -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/heal_host() - if(!host) - return - var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5) - if(iscarbon(host)) - resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100) - if(GLOB.ratvar_awakens || resulthealth <= GUARDIAN_EMERGE_THRESHOLD) - new /obj/effect/temp_visual/heal(host.loc, "#AF0AAF") - host.heal_ordered_damage(4, damage_heal_order) - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - if(amount > 0) - for(var/mob/living/L in view(2, src)) - if(L.is_holding_item_of_type(/obj/item/nullrod)) - to_chat(src, "The presence of a brandished holy artifact weakens your armor!") - amount *= 4 //if a wielded null rod is nearby, it takes four times the health damage - break - . = ..() - if(src && updating_health) - update_health_hud() - update_stats() - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/update_health_hud() - if(hud_used && hud_used.healths) - if(istype(hud_used, /datum/hud/marauder)) - var/datum/hud/marauder/G = hud_used - var/resulthealth - if(host) - if(iscarbon(host)) - resulthealth = "[round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100)]%" - else - resulthealth = "[round((host.health / host.maxHealth) * 100, 0.5)]%" - else - resulthealth = "NONE" - G.hosthealth.maptext = "
HOST
[resulthealth]
" - hud_used.healths.maptext = "
[round((health / maxHealth) * 100, 0.5)]%" - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/update_stats() - if(GLOB.ratvar_awakens) - speed = 0 - melee_damage_lower = 20 - melee_damage_upper = 20 - attack_verb_continuous = "devastates" - else - var/healthpercent = (health/maxHealth) * 100 - switch(healthpercent) - if(100 to 70) //Bonuses to speed and damage at high health - speed = 0 - melee_damage_lower = 16 - melee_damage_upper = 16 - attack_verb_continuous = "viciously slashes" - if(70 to 40) - speed = initial(speed) - melee_damage_lower = initial(melee_damage_lower) - melee_damage_upper = initial(melee_damage_upper) - attack_verb_continuous = initial(attack_verb_continuous) - if(40 to 30) //Damage decrease, but not speed - speed = initial(speed) - melee_damage_lower = 10 - melee_damage_upper = 10 - attack_verb_continuous = "lightly slashes" - if(30 to 20) //Speed decrease - speed = 2 - melee_damage_lower = 8 - melee_damage_upper = 8 - attack_verb_continuous = "lightly slashes" - if(20 to 10) //Massive speed decrease and weak melee attacks - speed = 3 - melee_damage_lower = 6 - melee_damage_upper = 6 - attack_verb_continuous = "weakly slashes" - if(10 to 0) //We are super weak and going to die - speed = 4 - melee_damage_lower = 4 - melee_damage_upper = 4 - attack_verb_continuous = "taps" - -//ATTACKING, BLOCKING, and COUNTERING - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/AttackingTarget() - if(is_in_host()) - return FALSE - return ..() - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/bullet_act(obj/item/projectile/Proj) - if(blockOrCounter(null, Proj)) - return - return ..() - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/hitby(atom/movable/AM, skipcatch, hitpush, blocked, atom/movable/AM, datum/thrownthing/throwingdatum) - if(blockOrCounter(null, AM)) - return - return ..() - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_animal(mob/living/simple_animal/M) - if(istype(M, /mob/living/simple_animal/hostile/clockwork/marauder/guardian) || !blockOrCounter(M, M)) //we don't want infinite blockcounter loops if fighting another guardian - return ..() - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_paw(mob/living/carbon/monkey/M) - if(!blockOrCounter(M, M)) - return ..() - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_alien(mob/living/carbon/alien/humanoid/M) - if(!blockOrCounter(M, M)) - return ..() - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_slime(mob/living/simple_animal/slime/M) - if(!blockOrCounter(M, M)) - return ..() - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attack_hand(mob/living/carbon/human/M) - if(!blockOrCounter(M, M)) - return ..() - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/nullrod) || !blockOrCounter(user, I)) - return ..() - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/blockOrCounter(mob/target, atom/textobject) - if(GLOB.ratvar_awakens) //if ratvar has woken, we block nearly everything at a very high chance - blockchance = 90 - counterchance = 90 - if(prob(blockchance)) - . = TRUE - if(target) - target.do_attack_animation(src) - target.DelayNextAction(CLICK_CD_MELEE) - blockchance = initial(blockchance) - playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', 30, 1, 0, 1) //clang - visible_message("[src] blocks [target && isitem(textobject) ? "[target]'s [textobject.name]":"\the [textobject]"]!", \ - "You block [target && isitem(textobject) ? "[target]'s [textobject.name]":"\the [textobject]"]!") - if(target && Adjacent(target)) - if(prob(counterchance)) - counterchance = initial(counterchance) - var/previousattack_verb_continuous = attack_verb_continuous - attack_verb_continuous = "counters" - UnarmedAttack(target) - attack_verb_continuous = previousattack_verb_continuous - else - counterchance = min(counterchance + initial(counterchance), 100) - else - blockchance = min(blockchance + initial(blockchance), 100) - if(GLOB.ratvar_awakens) - blockchance = 90 - counterchance = 90 - -//COMMUNICATION and EMERGENCE -/* -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/handle_inherent_channels(message, message_mode) - if(host && (is_in_host() || message_mode == MODE_BINARY)) - guardian_comms(message) - return TRUE - return ..() -*/ -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/guardian_comms(message) - var/name_part = "[src] ([true_name])" - message = "\"[message]\"" //Processed output - to_chat(src, "[name_part]: [message]") - to_chat(host, "[name_part]: [message]") - for(var/M in GLOB.mob_list) - if(isobserver(M)) - var/link = FOLLOW_LINK(M, src) - to_chat(M, "[link] [name_part] (to [findtextEx(host.name, host.real_name) ? "[host.name]" : "[host.real_name] (as [host.name])"]): [message] ") - return TRUE - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/return_to_host() - if(is_in_host()) - return FALSE - if(!host) - to_chat(src, "You don't have a host!") - return FALSE - var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5) - if(iscarbon(host)) - resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100) - host.visible_message("[host]'s skin flashes crimson!", "You feel [true_name]'s consciousness settle in your mind.") - visible_message("[src] suddenly disappears!", "You return to [host].") - forceMove(host) - if(resulthealth > GUARDIAN_EMERGE_THRESHOLD && health != maxHealth) - recovering = TRUE - to_chat(src, "You have weakened and will need to recover before manifesting again!") - to_chat(host, "[true_name] has weakened and will need to recover before manifesting again!") - return TRUE - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/try_emerge() - if(!host) - to_chat(src, "You don't have a host!") - return FALSE - if(!GLOB.ratvar_awakens) - var/resulthealth = round((host.health / host.maxHealth) * 100, 0.5) - if(iscarbon(host)) - resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - host.health) / abs(HEALTH_THRESHOLD_DEAD - host.maxHealth)) * 100) - if(host.stat != DEAD && resulthealth > GUARDIAN_EMERGE_THRESHOLD) //if above 20 health, fails - to_chat(src, "Your host must be at [GUARDIAN_EMERGE_THRESHOLD]% or less health to emerge like this!") - return FALSE - return emerge_from_host(FALSE) - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/emerge_from_host(hostchosen, force) //Notice that this is a proc rather than a verb - guardians can NOT exit at will, but they CAN return - if(!is_in_host()) - return FALSE - if(!force && recovering) - if(hostchosen) - to_chat(host, "[true_name] is too weak to come forth!") - else - to_chat(host, "[true_name] tries to emerge to protect you, but it's too weak!") - to_chat(src, "You try to come forth, but you're too weak!") - return FALSE - if(!force) - if(hostchosen) //guardian approved - to_chat(host, "Your words echo with power as [true_name] emerges from your body!") - else - to_chat(host, "[true_name] emerges from your body to protect you!") - forceMove(host.loc) - visible_message("[host]'s skin glows red as [name] emerges from their body!", "You exit the safety of [host]'s body!") - return TRUE - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/get_alt_name() - return " ([text2ratvar(true_name)])" - -/mob/living/simple_animal/hostile/clockwork/marauder/guardian/proc/is_in_host() //Checks if the guardian is inside of their host - return host && loc == host - -//HOST ACTIONS - -//Summon guardian action: Calls forth or recalls your guardian -/datum/action/innate/summon_guardian - name = "Force Guardian to Emerge/Recall" - desc = "Allows you to force your clockwork guardian to emerge or recall as required." - button_icon_state = "clockwork_marauder" - background_icon_state = "bg_clock" - check_flags = AB_CHECK_CONSCIOUS - buttontooltipstyle = "clockcult" - var/mob/living/simple_animal/hostile/clockwork/marauder/guardian/linked_guardian - var/list/defend_phrases = list("Defend me", "Come forth", "Assist me", "Protect me", "Give aid", "Help me") - var/list/return_phrases = list("Return", "Return to me", "Your job is done", "You have served", "Come back", "Retreat") - -/datum/action/innate/summon_guardian/IsAvailable() - if(!linked_guardian) - return FALSE - if(isliving(owner)) - var/mob/living/L = owner - if(!L.can_speak_vocal() || L.stat) - return FALSE - return ..() - -/datum/action/innate/summon_guardian/Activate() - if(linked_guardian.is_in_host()) - clockwork_say(owner, text2ratvar("[pick(defend_phrases)], [linked_guardian.true_name]!")) - linked_guardian.emerge_from_host(TRUE) - else - clockwork_say(owner, text2ratvar("[pick(return_phrases)], [linked_guardian.true_name]!")) - linked_guardian.return_to_host() - return TRUE - -//Linked Minds action: talks to your guardian -/datum/action/innate/linked_minds - name = "Linked Minds" - desc = "Allows you to silently communicate with your guardian." - button_icon_state = "linked_minds" - background_icon_state = "bg_clock" - check_flags = AB_CHECK_CONSCIOUS - buttontooltipstyle = "clockcult" - var/mob/living/simple_animal/hostile/clockwork/marauder/guardian/linked_guardian - -/datum/action/innate/linked_minds/IsAvailable() - if(!linked_guardian) - return FALSE - return ..() - -/datum/action/innate/linked_minds/Activate() - var/message = stripped_input(owner, "Enter a message to tell your guardian.", "Telepathy") - if(!owner || !message) - return FALSE - if(!linked_guardian) - to_chat(owner, "Your guardian seems to have been destroyed!") - return FALSE - var/name_part = "Servant [findtextEx(owner.name, owner.real_name) ? "[owner.name]" : "[owner.real_name] (as [owner.name])"]" - message = "\"[message]\"" //Processed output - to_chat(owner, "[name_part]: [message]") - to_chat(linked_guardian, "[name_part]: [message]") - for(var/M in GLOB.mob_list) - if(isobserver(M)) - var/link = FOLLOW_LINK(M, src) - to_chat(M, "[link] [name_part] (to [linked_guardian] ([linked_guardian.true_name])): [message]") - return TRUE diff --git a/code/modules/antagonists/clockcult/clock_scripture.dm b/code/modules/antagonists/clockcult/clock_scripture.dm index aa0f7f03fa..6922c7cd81 100644 --- a/code/modules/antagonists/clockcult/clock_scripture.dm +++ b/code/modules/antagonists/clockcult/clock_scripture.dm @@ -5,7 +5,7 @@ Pieces of scripture require certain follower counts, contruction value, and acti Drivers: Unlocked by default Scripts: 35k power or one convert Applications: 50k or three converts -Judgement 5 converts +Judgement 80k power or nine converts */ /datum/clockwork_scripture diff --git a/code/modules/antagonists/clockcult/clock_scriptures/scripture_applications.dm b/code/modules/antagonists/clockcult/clock_scriptures/scripture_applications.dm index 77dc174238..85450dc152 100644 --- a/code/modules/antagonists/clockcult/clock_scriptures/scripture_applications.dm +++ b/code/modules/antagonists/clockcult/clock_scriptures/scripture_applications.dm @@ -18,7 +18,7 @@ tier = SCRIPTURE_APPLICATION one_per_tile = TRUE primary_component = HIEROPHANT_ANSIBLE - sort_priority = 1 + sort_priority = 2 important = TRUE quickbind = TRUE quickbind_desc = "Creates a Sigil of Transmission, which can drain and will store power for clockwork structures." @@ -72,7 +72,7 @@ tier = SCRIPTURE_APPLICATION one_per_tile = TRUE primary_component = HIEROPHANT_ANSIBLE - sort_priority = 2 + sort_priority = 5 quickbind = TRUE quickbind_desc = "Creates a Mania Motor, which causes minor damage and negative mental effects in non-Servants." requires_full_power = TRUE @@ -101,7 +101,7 @@ //Memory Allocation: Finds a willing ghost and makes them into a clockwork guardian for the invoker. /datum/clockwork_scripture/memory_allocation - descname = "Personal Guardian, A Peice Of Your Mind." + descname = "Personal Guardian housed in the brain." name = "Memory Allocation" desc = "Allocates part of your consciousness to a Clockwork Guardian, a variant of Marauder that lives within you, able to be \ called forth by Speaking its True Name or if you become exceptionally low on health.
\ @@ -109,13 +109,13 @@ invocations = list("Fright's will...", "...call forth...") channel_time = 100 power_cost = 8000 - usage_tip = "guardians are useful as personal bodyguards and frontline warriors." + usage_tip = "Guardians are useful as personal bodyguards and frontline warriors." tier = SCRIPTURE_APPLICATION primary_component = GEIS_CAPACITOR - sort_priority = 5 + sort_priority = 6 /datum/clockwork_scripture/memory_allocation/check_special_requirements() - for(var/mob/living/simple_animal/hostile/clockwork/marauder/guardian/M in GLOB.all_clockwork_mobs) + for(var/mob/living/simple_animal/hostile/clockwork/guardian/M in GLOB.all_clockwork_mobs) if(M.host == invoker) to_chat(invoker, "You can only house one guardian at a time!") return FALSE @@ -151,7 +151,7 @@ return FALSE clockwork_say(invoker, text2ratvar("...sword and shield!")) var/mob/dead/observer/theghost = pick(marauder_candidates) - var/mob/living/simple_animal/hostile/clockwork/marauder/guardian/M = new(invoker) + var/mob/living/simple_animal/hostile/clockwork/guardian/M = new(invoker) M.key = theghost.key M.bind_to_host(invoker) invoker.visible_message("The tendril retracts from [invoker]'s head, sealing the entry wound as it does so!", \ @@ -171,7 +171,7 @@ tier = SCRIPTURE_APPLICATION one_per_tile = TRUE primary_component = BELLIGERENT_EYE - sort_priority = 6 + sort_priority = 7 quickbind = TRUE quickbind_desc = "Creates a clockwork marauder, used for frontline combat." object_path = /obj/item/clockwork/construct_chassis/clockwork_marauder @@ -223,7 +223,7 @@ object_path = /obj/mecha/combat/neovgre tier = SCRIPTURE_APPLICATION primary_component = BELLIGERENT_EYE - sort_priority = 7 + sort_priority = 8 creator_message = "Neovgre, the Anima Bulwark towers over you... your enemies reckoning has come." /datum/clockwork_scripture/create_object/summon_arbiter/check_special_requirements() diff --git a/code/modules/antagonists/clockcult/clock_scriptures/scripture_judgement.dm b/code/modules/antagonists/clockcult/clock_scriptures/scripture_judgement.dm index 5075840e76..ea10725489 100644 --- a/code/modules/antagonists/clockcult/clock_scriptures/scripture_judgement.dm +++ b/code/modules/antagonists/clockcult/clock_scriptures/scripture_judgement.dm @@ -20,7 +20,7 @@ usage_tip = "The gateway is completely vulnerable to attack during its five-minute duration. It will periodically give indication of its general position to everyone on the station \ as well as being loud enough to be heard throughout the entire sector. Defend it with your life!" tier = SCRIPTURE_APPLICATION - sort_priority = 8 + sort_priority = 1 requires_full_power = TRUE /datum/clockwork_scripture/create_object/ark_of_the_clockwork_justiciar/check_special_requirements() diff --git a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm index 3b507fb57c..a481415cf5 100644 --- a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm +++ b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm @@ -81,7 +81,7 @@ priority_announce("Massive [Gibberish("bluespace", 100)] anomaly detected on all frequencies. All crew are directed to \ @!$, [text2ratvar("PURGE ALL UNTRUTHS")] <&. the anomalies and destroy their source to prevent further damage to corporate property. This is \ not a drill.", "Central Command Higher Dimensional Affairs", 'sound/magic/clockwork/ark_activation_sequence.ogg') - set_security_level("delta") + set_security_level("Delta") for(var/V in SSticker.mode.servants_of_ratvar) var/datum/mind/M = V if(!M || !M.current) diff --git a/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm b/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm index 28901ea0b2..24ad1af88a 100644 --- a/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm +++ b/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm @@ -29,8 +29,9 @@ sound_to_playing_players('sound/effects/ratvar_reveal.ogg') var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/clockwork_effects.dmi', "ratvar_alert") notify_ghosts("The Justiciar's light calls to you! Reach out to Ratvar in [get_area_name(src)] to be granted a shell to spread his glory!", null, source = src, alert_overlay = alert_overlay) - INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 10, null, FALSE, 0) SSpersistence.station_was_destroyed = TRUE + INVOKE_ASYNC(src, .proc/purge_the_heresy) + /obj/structure/destructible/clockwork/massive/ratvar/Destroy() GLOB.ratvar_awakens-- @@ -151,3 +152,34 @@ sound_to_playing_players('sound/machines/clockcult/ratvar_scream.ogg', 80) narsie.clashing = FALSE qdel(src) + + +/obj/structure/destructible/clockwork/massive/ratvar/proc/purge_the_heresy() + sleep(50) + priority_announce("Massive energy surge detected. Closest matching threat: Incoming supernova. All crew are advised to evacuate NAN lightyears away from blast zone","Central Command Higher Dimensional Affairs", 'sound/misc/airraid.ogg') + sleep(300) + priority_announce("Gravitational anomalies detected on the station. [Gibberish("There is no additional dat", 100)]-BZZZZZT.","Central Command Higher Dimensional Affairs", 'sound/magic/clockwork/ratvar_announce1.ogg') + sleep(80) + sound_to_playing_players('sound/magic/clockwork/ratvar_announce2.ogg', 70) + send_to_playing_players("\"COME, ALL THOSE FAITHFUL! WITNESS THE RAYS OF JUSTICE CAST UPON THE HERETICS!\"") + sleep(50) + SSshuttle.registerHostileEnvironment(src) + SSshuttle.lockdown = TRUE + sleep(250) + if(QDELETED(src)) + priority_announce("Energy signal no longer detected.","Central Command Higher Dimensional Affairs") + return + sound_to_playing_players('sound/magic/clockwork/ark_activation_sequence.ogg', 80) //if this isn't lessened in volume it peaks for some reason + addtimer(CALLBACK(GLOBAL_PROC, /proc/clockcult_ending_helper), 300) + +/proc/clockcult_ending_helper() + for(var/mob/M in GLOB.mob_list) + if(M.client) + SEND_SOUND(M, sound('sound/magic/clockwork/ratvar_attack.ogg')) + SEND_SOUND(M, sound('sound/magic/clockwork/ratvarfire.ogg')) + if(!is_servant_of_ratvar(M) && isliving(M)) + var/mob/living/L = M + L.fire_stacks = INFINITY + L.IgniteMob() + sleep(50) + SSticker.force_ending = 1 diff --git a/code/modules/antagonists/clockcult/clock_structures/reflector.dm b/code/modules/antagonists/clockcult/clock_structures/reflector.dm index c91e9dd918..e734429524 100644 --- a/code/modules/antagonists/clockcult/clock_structures/reflector.dm +++ b/code/modules/antagonists/clockcult/clock_structures/reflector.dm @@ -1,7 +1,7 @@ /obj/structure/destructible/clockwork/reflector name = "reflector" - desc = "A large lantern-shaped machine made of thin brass. It looks fragile." - clockwork_desc = "A lantern-shaped generator that produces power when near starlight." + desc = "A large mirror-like structure made of thin brass on one side. It looks fragile." + clockwork_desc = "A large mirror-like structure made of thin brass on one side. It can redirect laser fire on one side" icon_state = "reflector" unanchored_icon = "reflector_unwrenched" max_integrity = 40 diff --git a/code/modules/antagonists/clockcult/clockcult.dm b/code/modules/antagonists/clockcult/clockcult.dm index b935258c27..efea8ed771 100644 --- a/code/modules/antagonists/clockcult/clockcult.dm +++ b/code/modules/antagonists/clockcult/clockcult.dm @@ -5,7 +5,7 @@ antagpanel_category = "Clockcult" job_rank = ROLE_SERVANT_OF_RATVAR antag_moodlet = /datum/mood_event/cult - skill_modifiers = list(/datum/skill_modifier/job/level/wiring) + skill_modifiers = list(/datum/skill_modifier/job/level/wiring, /datum/skill_modifier/job/level/dwarfy/blacksmithing) var/datum/action/innate/hierophant/hierophant_network = new threat = 3 var/datum/team/clockcult/clock_team diff --git a/code/modules/antagonists/cult/cult_comms.dm b/code/modules/antagonists/cult/cult_comms.dm index 77893225fa..bbdf41ff48 100644 --- a/code/modules/antagonists/cult/cult_comms.dm +++ b/code/modules/antagonists/cult/cult_comms.dm @@ -83,7 +83,7 @@ if(choice == "Yes" && IsAvailable()) var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE) if(!C.cult_team) - to_chat(owner, "Do you not alreaady lead yourself?") + to_chat(owner, "Do you not already lead yourself?") return pollCultists(owner,C.cult_team) diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 75aff774ce..fba6ecd40c 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -625,7 +625,7 @@ say_mod = "clicks" limbs_id = "clockgolem" info_text = "As a Clockwork Golem, you are faster than other types of golems, and are capable of using guns. On death, you will break down into scrap." - species_traits = list(NOBLOOD,NO_UNDERWEAR,NOEYES,NOGENITALS,NOAROUSAL) + species_traits = list(NOBLOOD,NO_UNDERWEAR,NOEYES,NOGENITALS,NOAROUSAL,ROBOTIC_LIMBS) inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOFIRE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER) inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID @@ -667,6 +667,10 @@ /datum/species/golem/clockwork/no_scrap //These golems are created through the herald's beacon and leave normal corpses on death. id = "clockwork golem servant" + species_traits = list(NOBLOOD,NO_UNDERWEAR,NOEYES,NOSTOMACH,NOLIVER,NOGENITALS,NOAROUSAL,NOTRANSSTING,ROBOTIC_LIMBS,HAS_BONE) + inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID + inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_NOHUNGER,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOFIRE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER) + inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID armor = 15 //Balance reasons make this armor weak no_equip = list() nojumpsuit = FALSE @@ -675,6 +679,8 @@ dangerous_existence = TRUE random_eligible = FALSE info_text = "As a Clockwork Golem Servant, you are faster than other types of golems, and are capable of using guns." //warcult golems leave a corpse + gib_types = list(/obj/effect/gibspawner/ipc, /obj/effect/gibspawner/ipc/bodypartless) + /datum/species/golem/cloth name = "Cloth Golem" diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 8e39dc5234..486a0acf4e 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -514,10 +514,21 @@ /mob/living/ratvar_act() if(status_flags & GODMODE) return - if(stat != DEAD && !is_servant_of_ratvar(src)) + if(stat == DEAD || is_servant_of_ratvar(src)) + return + if(is_eligible_servant(src)) + add_servant_of_ratvar(src) + to_chat(src, "Ratvar's influence invades your mind, praise the Justiciar!") + else to_chat(src, "A blinding light boils you alive! Run!") adjust_fire_stacks(20) + adjustFireLoss(35) IgniteMob() + if(iscultist(src)) + to_chat(src, "You resist Ratvar's influence... but not all of it! Run!") + adjustFireLoss(35) + if(src && reagents) + reagents.add_reagent(/datum/reagent/fuel/holyoil, 5) return FALSE diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 5b5292a020..0ebbce53cf 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -80,6 +80,9 @@ SSshuttle.lockdown = TRUE SSpersistence.station_was_destroyed = TRUE sleep(600) + if(QDELETED(src)) + priority_announce("Accausal event alert rescinded. Eldritch presence no longer reading on sensors. Solution package disarmed. Reccomend immediate evacuation","Central Command Higher Dimensional Affairs") + return if(resolved == FALSE) resolved = TRUE sound_to_playing_players('sound/machines/alarm.ogg') diff --git a/icons/mob/actions/actions_clockcult.dmi b/icons/mob/actions/actions_clockcult.dmi index 92d6f98e73..d4f5c4ed6f 100644 Binary files a/icons/mob/actions/actions_clockcult.dmi and b/icons/mob/actions/actions_clockcult.dmi differ diff --git a/icons/mob/clockwork_mobs.dmi b/icons/mob/clockwork_mobs.dmi index 54690f6cac..b43f7f958e 100644 Binary files a/icons/mob/clockwork_mobs.dmi and b/icons/mob/clockwork_mobs.dmi differ diff --git a/icons/mob/inhands/antag/clockwork_righthand.dmi b/icons/mob/inhands/antag/clockwork_righthand.dmi index 64ee6199a7..c813c83634 100644 Binary files a/icons/mob/inhands/antag/clockwork_righthand.dmi and b/icons/mob/inhands/antag/clockwork_righthand.dmi differ diff --git a/sound/magic/clockwork/ratvar_announce.ogg b/sound/magic/clockwork/ratvar_announce.ogg new file mode 100644 index 0000000000..6f997108c9 Binary files /dev/null and b/sound/magic/clockwork/ratvar_announce.ogg differ diff --git a/sound/magic/clockwork/ratvar_announce1.ogg b/sound/magic/clockwork/ratvar_announce1.ogg new file mode 100644 index 0000000000..08308cb1cd Binary files /dev/null and b/sound/magic/clockwork/ratvar_announce1.ogg differ diff --git a/sound/magic/clockwork/ratvar_announce2.ogg b/sound/magic/clockwork/ratvar_announce2.ogg new file mode 100644 index 0000000000..54a49c75c7 Binary files /dev/null and b/sound/magic/clockwork/ratvar_announce2.ogg differ diff --git a/sound/magic/clockwork/ratvarfire.ogg b/sound/magic/clockwork/ratvarfire.ogg new file mode 100644 index 0000000000..4b8a9be10a Binary files /dev/null and b/sound/magic/clockwork/ratvarfire.ogg differ diff --git a/sound/weapons/neovgre_laser.ogg b/sound/weapons/neovgre_laser.ogg index da97117ae8..c36e54fe87 100644 Binary files a/sound/weapons/neovgre_laser.ogg and b/sound/weapons/neovgre_laser.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 7879a87e32..b15633366e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1617,6 +1617,7 @@ #include "code\modules\antagonists\clockcult\clock_items\clock_weapons\ratvarian_shield.dm" #include "code\modules\antagonists\clockcult\clock_items\clock_weapons\ratvarian_spear.dm" #include "code\modules\antagonists\clockcult\clock_mobs\_eminence.dm" +#include "code\modules\antagonists\clockcult\clock_mobs\clockwork_guardian.dm" #include "code\modules\antagonists\clockcult\clock_mobs\clockwork_marauder.dm" #include "code\modules\antagonists\clockcult\clock_scriptures\scripture_applications.dm" #include "code\modules\antagonists\clockcult\clock_scriptures\scripture_cyborg.dm"