diff --git a/aurorastation.dme b/aurorastation.dme index 155e8b48b6a..378054b98be 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -120,6 +120,8 @@ #include "code\_onclick\hud\_defines.dm" #include "code\_onclick\hud\action.dm" #include "code\_onclick\hud\ai.dm" +#include "code\_onclick\hud\borer_hud.dm" +#include "code\_onclick\hud\captive_brain_hud.dm" #include "code\_onclick\hud\fullscreen.dm" #include "code\_onclick\hud\gun_mode.dm" #include "code\_onclick\hud\hud.dm" @@ -135,6 +137,7 @@ #include "code\_onclick\hud\screen_objects.dm" #include "code\_onclick\hud\spell_screen_objects.dm" #include "code\_onclick\hud\screen_object_types\ai_screen_objs.dm" +#include "code\_onclick\hud\screen_object_types\borer.dm" #include "code\_onclick\hud\screen_object_types\internals.dm" #include "code\_onclick\hud\screen_object_types\vampire.dm" #include "code\ATMOSPHERICS\_atmos_setup.dm" diff --git a/code/_onclick/hud/borer_hud.dm b/code/_onclick/hud/borer_hud.dm new file mode 100644 index 00000000000..407ffb559c5 --- /dev/null +++ b/code/_onclick/hud/borer_hud.dm @@ -0,0 +1,13 @@ +/mob/living/simple_animal/borer/instantiate_hud(var/datum/hud/HUD) + HUD.borer_hud() + +/datum/hud/proc/borer_hud() + src.adding = list() + + var/obj/screen/borer/chemicals/C = new /obj/screen/borer/chemicals() + adding += C + + var/mob/living/simple_animal/borer/B = mymob + B.chem_hud = C + + mymob.client.screen += src.adding \ No newline at end of file diff --git a/code/_onclick/hud/captive_brain_hud.dm b/code/_onclick/hud/captive_brain_hud.dm new file mode 100644 index 00000000000..8a25ab24dbc --- /dev/null +++ b/code/_onclick/hud/captive_brain_hud.dm @@ -0,0 +1,15 @@ +/mob/living/captive_brain/instantiate_hud(var/datum/hud/HUD) + HUD.captive_brain_hud() + +/datum/hud/proc/captive_brain_hud() + src.adding = list() + + var/obj/screen/resist = new /obj/screen() + resist.name = "resist" + resist.icon = 'icons/mob/screen/captive_brain.dmi' + resist.icon_state = "resist" + resist.screen_loc = "EAST-8,BOTTOM:8" + resist.layer = SCREEN_LAYER + adding += resist + + mymob.client.screen += src.adding \ No newline at end of file diff --git a/code/_onclick/hud/screen_object_types/borer.dm b/code/_onclick/hud/screen_object_types/borer.dm new file mode 100644 index 00000000000..73888dbc65a --- /dev/null +++ b/code/_onclick/hud/screen_object_types/borer.dm @@ -0,0 +1,14 @@ +/obj/screen/borer + icon = 'icons/mob/screen/borer.dmi' + maptext_x = 5 + maptext_y = 16 + +/obj/screen/borer/chemicals + name = "chemicals" + icon_state = "chemicals" + screen_loc = ui_alien_toxin + +/obj/screen/borer/chemicals/Click(var/location, var/control, var/params) + if(istype(usr, /mob/living/simple_animal/borer)) + var/mob/living/simple_animal/borer/B = usr + to_chat(usr, SPAN_NOTICE("You have [B.chemicals]u of chemicals to use for your abilities.")) \ No newline at end of file diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm index 1abbedec4a6..1227f6bd3c6 100644 --- a/code/datums/progressbar.dm +++ b/code/datums/progressbar.dm @@ -3,6 +3,7 @@ /datum/progressbar var/goal = 1 + var/destroy_on_full = FALSE var/last_progress = 0 var/image/bar var/shown = 0 @@ -46,6 +47,8 @@ if (!shown) user.client.images += bar shown = TRUE + if(destroy_on_full && progress == goal) + QDEL_IN(src, 5) /datum/progressbar/proc/shiftDown() --listindex @@ -76,5 +79,8 @@ client.images -= bar client = null +/datum/progressbar/autocomplete + destroy_on_full = TRUE + #undef PROGRESSBAR_ANIMATION_TIME #undef PROGRESSBAR_HEIGHT \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index e91e6ba56f6..521791d3235 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -160,7 +160,7 @@ return O -/mob/living/carbon/human/proc/awaken_psi_basic(var/source) +/mob/living/carbon/human/proc/awaken_psi_basic(var/source, var/allow_latency = TRUE) var/static/list/psi_operancy_messages = list( "There's something in your skull!", "Something is eating your thoughts!", @@ -176,11 +176,9 @@ var/list/faculties = list(PSI_COERCION, PSI_REDACTION, PSI_ENERGISTICS, PSI_PSYCHOKINESIS) for(var/i = 1 to new_latencies) custom_pain(SPAN_DANGER("[pick(psi_operancy_messages)]"), 25) - set_psi_rank(pick_n_take(faculties), 1) + set_psi_rank(pick_n_take(faculties), allow_latency ? PSI_RANK_LATENT : PSI_RANK_OPERANT) // if set to latent, it spikes anywhere from OPERANT to PARAMOUNT sleep(30) - psi.update() - sleep(45) - psi.check_latency_trigger(100, source, TRUE) + addtimer(CALLBACK(psi, /datum/psi_complexus/.proc/check_latency_trigger, 100, source, TRUE), 4.5 SECONDS) /mob/living/carbon/human/get_resist_power() return species.resist_mod diff --git a/code/modules/mob/living/simple_animal/borer/borer.dm b/code/modules/mob/living/simple_animal/borer/borer.dm index 74789a4a75f..8219b63f997 100644 --- a/code/modules/mob/living/simple_animal/borer/borer.dm +++ b/code/modules/mob/living/simple_animal/borer/borer.dm @@ -28,11 +28,15 @@ hunger_enabled = FALSE var/used_dominate + var/datum/progressbar/autocomplete/ability_bar + var/ability_start_time = 0 + var/obj/screen/borer/chemicals/chem_hud var/chemicals = 10 // Chemicals used for reproduction and spitting neurotoxin. var/mob/living/carbon/human/host // Human host for the brain worm. var/truename // Name used for brainworm-speak. var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. var/controlling // Used in human death check. + var/list/static/controlling_emotes = list("blink","blink_r","choke","drool","twitch","twitch_v","gasp") var/has_reproduced var/request_player = TRUE @@ -43,6 +47,8 @@ ..() if(mind) borers.add_antagonist(mind) + if(client && host) + client.screen += host.healths /mob/living/simple_animal/borer/Initialize() . = ..() @@ -56,6 +62,11 @@ if(request_player && !ckey && !client) SSghostroles.add_spawn_atom("borer", src) +/mob/living/simple_animal/borer/Destroy() + QDEL_NULL(ability_bar) + QDEL_NULL(host_brain) + return ..() + /mob/living/simple_animal/borer/death(gibbed, deathmessage) SSghostroles.remove_spawn_atom("borer", src) return ..(gibbed,deathmessage) @@ -64,14 +75,28 @@ return FALSE /mob/living/simple_animal/borer/Life() - ..() if(host) if(!stat && host.stat != DEAD) if(chemicals < 250) chemicals++ - if(host && !host.stat) - if(controlling && prob(host.getBrainLoss()/20)) - host.say("*[pick(list("blink","blink_r","choke","drool","twitch","twitch_s","gasp"))]") + if(!host.stat && controlling && prob(2)) + host.emote(pick(controlling_emotes)) + ..() + if(!QDELETED(ability_bar)) + ability_bar.update(world.time - ability_start_time) + +/mob/living/simple_animal/borer/proc/start_ability(var/atom/target, var/time) + if(!QDELETED(ability_bar)) + return FALSE + ability_bar = new /datum/progressbar/autocomplete(src, time, target) + ability_start_time = world.time + ability_bar.update(0) + return TRUE + +/mob/living/simple_animal/borer/handle_regular_hud_updates() + . = ..() + if(chem_hud) + chem_hud.maptext = SMALL_FONTS(7, chemicals) /mob/living/simple_animal/borer/Stat() ..() @@ -89,6 +114,9 @@ if(!host || !controlling) return + if(ability_bar) + QDEL_NULL(ability_bar) + if(istype(host,/mob/living/carbon/human)) var/mob/living/carbon/human/H = host var/obj/item/organ/external/head = H.get_organ(BP_HEAD) @@ -158,8 +186,27 @@ host.machine = null host.status_flags &= ~PASSEMOTES + if(client) + client.screen -= host.healths host = null return /mob/living/simple_animal/borer/cannot_use_vents() return + +/mob/living/simple_animal/borer/UnarmedAttack(atom/A, proximity) + if(iscarbon(A)) + var/mob/living/carbon/C = A + if(C.lying || C.weakened) + do_infest(C) + else + do_paralyze(C) + return + return ..() + +/mob/living/simple_animal/borer/RangedAttack(atom/A, params) + if(iscarbon(A) && get_dist(src, A) <= 2) + var/mob/living/carbon/C = A + do_paralyze(C) + return + return ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/borer/borer_captive.dm b/code/modules/mob/living/simple_animal/borer/borer_captive.dm index a38b3509497..26a52b799f3 100644 --- a/code/modules/mob/living/simple_animal/borer/borer_captive.dm +++ b/code/modules/mob/living/simple_animal/borer/borer_captive.dm @@ -3,6 +3,9 @@ real_name = "host brain" universal_understand = TRUE + var/datum/progressbar/resist_bar + var/resist_start_time = 0 + /mob/living/captive_brain/say(var/message) if(istype(src.loc,/mob/living/simple_animal/borer)) message = sanitize(message) @@ -22,19 +25,36 @@ else if(M.stat == DEAD && M.client.prefs.toggles & CHAT_GHOSTEARS) to_chat(M, "The captive mind of [src] whispers, \"[message]\"") +/mob/living/captive_brain/Destroy() + QDEL_NULL(resist_bar) + return ..() + /mob/living/captive_brain/emote(var/message) return +/mob/living/captive_brain/Life() + if(resist_bar) + resist_bar.update(world.time - resist_start_time) + return ..() + /mob/living/captive_brain/process_resist() //Resisting control by an alien mind. + if(resist_bar) + to_chat(src, SPAN_WARNING("You're already resisting the alien control!")) + return + if(istype(src.loc,/mob/living/simple_animal/borer)) var/mob/living/simple_animal/borer/B = src.loc var/mob/living/captive_brain/H = src - to_chat(H, SPAN_DANGER("You begin doggedly resisting the parasite's control (this will take approximately sixty seconds).")) + to_chat(H, SPAN_DANGER("You begin doggedly resisting the parasite's control.")) to_chat(B.host, SPAN_DANGER("You feel the captive mind of [src] begin to resist your control.")) - addtimer(CALLBACK(src, .proc/eject_borer, B, H), rand(200, 250) + B.host.getBrainLoss()) + var/resist_time = rand(40 SECONDS, 1 MINUTE) + addtimer(CALLBACK(src, .proc/eject_borer, B, H), resist_time) + resist_bar = new /datum/progressbar/autocomplete(src, resist_time, B.host) + resist_start_time = world.time + resist_bar.update(0) return ..() diff --git a/code/modules/mob/living/simple_animal/borer/borer_powers.dm b/code/modules/mob/living/simple_animal/borer/borer_powers.dm index 0b1f6745338..e9d27079c73 100644 --- a/code/modules/mob/living/simple_animal/borer/borer_powers.dm +++ b/code/modules/mob/living/simple_animal/borer/borer_powers.dm @@ -10,12 +10,16 @@ to_chat(src, SPAN_NOTICE("You cannot leave your host in your current state.")) return - to_chat(src, SPAN_NOTICE("You begin disconnecting from [host]'s synapses and prodding at their internal ear canal.")) + var/exit_time = 10 SECONDS + if(!start_ability(host, exit_time)) + to_chat(src, SPAN_WARNING("You're busy doing something else, complete that task first.")) + return + to_chat(src, SPAN_NOTICE("You begin disconnecting from [host]'s synapses and prodding at their internal ear canal.")) if(!host.stat) to_chat(host, SPAN_WARNING("An odd, uncomfortable pressure begins to build inside your skull, behind your ear...")) - addtimer(CALLBACK(src, .proc/exit_host), 100) + addtimer(CALLBACK(src, .proc/exit_host), exit_time) /mob/living/simple_animal/borer/proc/exit_host() if(!host || !src) @@ -48,19 +52,28 @@ return var/list/choices = list() - for(var/mob/living/carbon/C in view(1,src)) + for(var/mob/living/carbon/C in view(2, src)) if(src.Adjacent(C)) choices += C - if(!choices.len) + if(!length(choices)) to_chat(src, SPAN_NOTICE("There are no viable hosts within range.")) return var/mob/living/carbon/M = input(src,"Who do you wish to infest?") in null|choices + if(M) + do_infest(M) +/mob/living/simple_animal/borer/proc/do_infest(var/mob/living/carbon/M) + if(host) + to_chat(src, SPAN_NOTICE("You are already within a host.")) + return + if(stat) + to_chat(src, SPAN_NOTICE("You cannot infest a target in your current state.")) + return if(!M || !src) return - if(!(src.Adjacent(M))) + if(!Adjacent(M)) return if(M.has_brain_worms()) to_chat(src, SPAN_WARNING("You cannot infest someone who is already infested!")) @@ -108,6 +121,9 @@ src.host.status_flags |= PASSEMOTES src.forceMove(M) + if(client) + client.screen += host.healths + //Update their traitor status. if(host.mind) borers.add_antagonist_mind(host.mind, 1, borers.faction_role_text, borers.faction_welcome) @@ -240,12 +256,14 @@ if(C.stat != 2) choices += C + var/mob/living/carbon/M = input(src, "Who do you wish to dominate?") in null|choices + if(M) + do_paralyze(M) + +/mob/living/simple_animal/borer/proc/do_paralyze(var/mob/living/carbon/M) if(world.time - used_dominate < 150) to_chat(src, SPAN_NOTICE("You cannot use that ability again so soon.")) return - - var/mob/living/carbon/M = input(src, "Who do you wish to dominate?") in null|choices - if(!M || !src) return if(M.isSynthetic()) @@ -283,10 +301,15 @@ to_chat(src, SPAN_WARNING("\The [host]'s mind is shielded against your powers.")) return + var/takeover_time = 10 SECONDS + (host.getBrainLoss() * 5) + if(!start_ability(host, takeover_time)) + to_chat(src, SPAN_WARNING("You're busy doing something else, complete that task first.")) + return + to_chat(src, SPAN_WARNING("You begin delicately adjusting your connection to the host brain...")) to_chat(host, SPAN_WARNING("You feel a tingling sensation at the back of your head.")) - addtimer(CALLBACK(src, .proc/host_takeover), 100+(host.getBrainLoss()*5)) + addtimer(CALLBACK(src, .proc/host_takeover), takeover_time) /mob/living/simple_animal/borer/proc/host_takeover() if(!host || !src || controlling) @@ -307,8 +330,8 @@ host_brain = new(src) host_brain.ckey = host.ckey - - host_brain.name = host.name + host_brain.name = host.real_name + host_brain.real_name = "[host_brain.name] (captive host)" if(!host_brain.computer_id) host_brain.computer_id = h2b_id @@ -441,20 +464,30 @@ to_chat(src, SPAN_WARNING("\The [host]'s mind is shielded against your powers.")) return + var/jumpstart_time = 15 SECONDS + if(!start_ability(host, jumpstart_time)) + to_chat(src, SPAN_WARNING("You're busy doing something else, complete that task first.")) + return + chemicals -= 150 to_chat(src, SPAN_NOTICE("You probe your tendrils deep within your host's zona bovinae, seeking to unleash their potential.")) to_chat(host, SPAN_DANGER("You feel some tendrils probe at the back of your head...")) to_chat(host, FONT_LARGE(SPAN_WARNING("You feel something terrible coming on..."))) - addtimer(CALLBACK(src, .proc/jumpstart_psi), 150) + + addtimer(CALLBACK(src, .proc/jumpstart_psi), jumpstart_time) /mob/living/simple_animal/borer/proc/jumpstart_psi() + if(!host) + return + to_chat(src, SPAN_NOTICE("You succeed in interfacing with the host's zona bovinae, this will be a painful process for them.")) - host.awaken_psi_basic("something in your head") + host.awaken_psi_basic("something in your head", FALSE) + host.add_language(LANGUAGE_TCB) // if we don't have TCB, give them TCB | this allows monkey borers to RP /mob/living/simple_animal/borer/verb/advance_faculty() set category = "Abilities" set name = "Advance Psionic Faculty (75)" - set desc = "Advance one of your host's psionic faculties' by one step." + set desc = "Advance one of your host's psionic faculties by one step." if(!host) to_chat(src, SPAN_NOTICE("You are not inside a host body.")) @@ -479,19 +512,31 @@ if(!selected_faculty) return selected_faculty = lowertext(selected_faculty) - if(host.psi.get_rank(selected_faculty) >= PSI_RANK_GRANDMASTER) + var/max_rank = islesserform(host) ? PSI_RANK_OPERANT : PSI_RANK_MASTER + if(host.psi.get_rank(selected_faculty) >= max_rank) to_chat(src, SPAN_NOTICE("This faculty has already been pushed to the max potential you can achieve!")) return + var/faculty_time = 10 SECONDS + if(!start_ability(host, faculty_time)) + to_chat(src, SPAN_WARNING("You're busy doing something else, complete that task first.")) + return + chemicals -= 75 to_chat(src, SPAN_NOTICE("You probe your tendrils deep within your host's zona bovinae, seeking to upgrade their abilities.")) to_chat(host, SPAN_WARNING("You feel a burning, tingling sensation at the back of your head...")) - addtimer(CALLBACK(src, .proc/faculty_upgrade, selected_faculty), 100) + + addtimer(CALLBACK(src, .proc/faculty_upgrade, selected_faculty), faculty_time) /mob/living/simple_animal/borer/proc/faculty_upgrade(var/selected_faculty) - host.psi.set_rank(selected_faculty, host.psi.get_rank(selected_faculty) + 1) - host.psi.update() - to_chat(src, SPAN_NOTICE("You successfully manage to upgrade your host's [selected_faculty] faculty.")) + if(!host) + return + + var/host_psi_rank = host.psi.get_rank(selected_faculty) + var/next_rank = host_psi_rank > PSI_RANK_BLUNT ? host_psi_rank + 1 : PSI_RANK_OPERANT + host.psi.set_rank(selected_faculty, next_rank) + host.psi.update(TRUE) + to_chat(src, SPAN_NOTICE("You successfully manage to upgrade your host to [psychic_ranks_to_strings[host.psi.ranks[selected_faculty]]] [selected_faculty].")) to_chat(host, SPAN_GOOD("A breeze of fresh air washes over your mind, you feel powerful!")) to_chat(host, SPAN_NOTICE("You have been psionically enlightened. You are now a [psychic_ranks_to_strings[host.psi.ranks[selected_faculty]]] in the [selected_faculty] faculty.")) diff --git a/code/modules/mob/living/simple_animal/borer/say.dm b/code/modules/mob/living/simple_animal/borer/say.dm index cea49f8b01f..c504b285386 100644 --- a/code/modules/mob/living/simple_animal/borer/say.dm +++ b/code/modules/mob/living/simple_animal/borer/say.dm @@ -31,15 +31,14 @@ to_chat(src, SPAN_NOTICE("There are no viable hosts to speak with.")) else var/mob/living/carbon/human/chosen_sayer = pick(viable_sayers) - log_say("[key_name(src)] : ([name]) [message]", ckey=key_name(src)) + log_say("[key_name(src)] : (forcing [key_name(chosen_sayer)]) [message]", ckey=key_name(src)) chosen_sayer.say(message) return to_chat(src, "You drop words into [host]'s mind: \"[message]\"") to_chat(host, "Your own thoughts speak: \"[message]\"") + log_say("[key_name(src)] : (borer whisper -> [key_name(host)]) [message]") - for(var/mob/M in player_list) - if(istype(M, /mob/abstract/new_player)) - continue - else if(M.stat == DEAD && M.client.prefs.toggles & CHAT_GHOSTEARS) - to_chat(M, "[src.truename] whispers to [host], \"[message]\"") + for(var/mob/M in mob_list) + if(M.client && M.stat == DEAD && !isnewplayer(M) && (M.client.prefs.toggles & CHAT_GHOSTEARS)) + to_chat(M, "[src.truename] whispers to [host], \"[message]\"") \ No newline at end of file diff --git a/html/changelogs/geeves-borer_stuff.yml b/html/changelogs/geeves-borer_stuff.yml new file mode 100644 index 00000000000..27bfd3d86f1 --- /dev/null +++ b/html/changelogs/geeves-borer_stuff.yml @@ -0,0 +1,18 @@ +author: Geeves + +delete-after: True + +changes: + - rscadd: "Cortical borers now get a UI element that shows how many chemicals they have to use. They also gain their host's health doll when they infest them." + - rscadd: "Hosts that get taken over by cortical borers now get a big resist button UI element, which they can use to resist the borer's control." + - tweak: "Resisting against borer control now takes fourty seconds to a minute, up from twenty seconds to twenty-five seconds." + - rscadd: "Various cortical borer action now have a progress bar. Resisting borer control now also has a progress bar." + - tweak: "Hosts being controlled by borers now emote randomly regardless of brain damage." + - rscadd: "Borers can now paralyze and infest people by clicking on them. It will paralyze if they're not lying down/paralyzed, and will infest if they are." + - tweak: "Borers can now paralyze people from two tiles away (one tile between them)." + - rscadd: "Awakening a humanoid's psionics will now also grant them the ability to speak Basic." + - tweak: "The max rank a borer can upgrade a humanoid to has been reduced to Masterclass, down from Grandmasterclass. Lesser forms, such as monkeys, can only be upgraded to operant levels." + - bugfix: "Dead players can now properly see when a ghost is whispering to someone." + - tweak: "Borer psionic awakening no longer has a chance to give someone paramount psionics." + - tweak: "Borers upgrading psionics will now skip the Latent rank, which does nothing." + - bugfix: "Upgrading faculties will now always update the psionic powers available." \ No newline at end of file diff --git a/icons/mob/screen/borer.dmi b/icons/mob/screen/borer.dmi new file mode 100644 index 00000000000..89b09c649f0 Binary files /dev/null and b/icons/mob/screen/borer.dmi differ diff --git a/icons/mob/screen/captive_brain.dmi b/icons/mob/screen/captive_brain.dmi new file mode 100644 index 00000000000..e7aefc64494 Binary files /dev/null and b/icons/mob/screen/captive_brain.dmi differ