diff --git a/code/__defines/borer.dm b/code/__defines/borer.dm
new file mode 100644
index 0000000000..3cf697c672
--- /dev/null
+++ b/code/__defines/borer.dm
@@ -0,0 +1,6 @@
+#define BORER_MAX_CHEMS 80
+#define BORER_POWER_COST_TORTURE 5
+#define BORER_POWER_COST_SECRETE 25
+#define BORER_POWER_COST_REPRODUCE 50
+
+#define BORER_PSYCHIC_SAY_MINIMUM_CHEMS 20
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 546b6d3979..a21cc66c28 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -410,6 +410,13 @@
name = "chemical storage"
icon_state = "power_display"
+/atom/movable/screen/borer
+ invisibility = INVISIBILITY_ABSTRACT
+
+/atom/movable/screen/borer/chems
+ name = "chemical storage"
+ icon_state = "power_display"
+
/atom/movable/screen/wizard
invisibility = INVISIBILITY_ABSTRACT
diff --git a/code/game/antagonist/alien/borer.dm b/code/game/antagonist/alien/borer.dm
index 04edda17e2..872581998e 100644
--- a/code/game/antagonist/alien/borer.dm
+++ b/code/game/antagonist/alien/borer.dm
@@ -5,7 +5,7 @@ var/datum/antagonist/borer/borers
role_type = BE_ALIEN
role_text = "Cortical Borer"
role_text_plural = "Cortical Borers"
- mob_path = /mob/living/simple_mob/animal/borer
+ mob_path = /mob/living/simple_mob/animal/borer/roundstart // Use roundstart borer, or ghostcheck makes it take forever to enter mob when assigned
bantype = "Borer"
welcome_text = "Use your Infest power to crawl into the ear of a host and fuse with their brain. You can only take control temporarily, and at risk of hurting your host, so be clever and careful; your host is encouraged to help you however they can. Talk to your fellow borers with :x."
antag_indicator = "brainworm"
diff --git a/code/game/antagonist/antagonist_create.dm b/code/game/antagonist/antagonist_create.dm
index 22462d1704..e2c4f9aef0 100644
--- a/code/game/antagonist/antagonist_create.dm
+++ b/code/game/antagonist/antagonist_create.dm
@@ -26,6 +26,8 @@
M = new /mob/living/carbon/human(get_turf(source))
M.real_name = source.real_name
M.name = M.real_name
+ if(!isnull(source.mind))
+ source.mind.transfer_to(M)
M.ckey = source.ckey
add_antagonist(M.mind, 1, 0, 1) // Equip them and move them to spawn.
return M
diff --git a/code/game/machinery/virtual_reality/vr_console.dm b/code/game/machinery/virtual_reality/vr_console.dm
index 1c095b75b7..50ee9d1fa4 100644
--- a/code/game/machinery/virtual_reality/vr_console.dm
+++ b/code/game/machinery/virtual_reality/vr_console.dm
@@ -192,11 +192,12 @@
update_icon()
- if(!M.has_brain_worms())
- update_use_power(USE_POWER_ACTIVE)
- enter_vr()
- else
+ if(M.has_brain_worms())
to_chat(user, span_warning("\The [src] rejects [M] with a sharp beep."))
+ return
+
+ update_use_power(USE_POWER_ACTIVE)
+ enter_vr()
return
/obj/machinery/vr_sleeper/proc/go_out()
diff --git a/code/game/objects/items/devices/scanners/health.dm b/code/game/objects/items/devices/scanners/health.dm
index ef4793d002..43c3000867 100644
--- a/code/game/objects/items/devices/scanners/health.dm
+++ b/code/game/objects/items/devices/scanners/health.dm
@@ -268,7 +268,7 @@
dat += "
"
// if (M.reagents && M.reagents.get_reagent_amount(REAGENT_ID_INAPROVALINE))
// user.show_message(span_notice("Bloodstream Analysis located [M.reagents:get_reagent_amount(REAGENT_ID_INAPROVALINE)] units of rejuvenation chemicals."))
- if (M.has_brain_worms())
+ if (advscan >= 2 && M.has_brain_worms()) // Borers need to hide
dat += span_warning("Subject suffering from aberrant brain activity. Recommend further scanning.")
dat += "
"
else if (M.getBrainLoss() >= 60 || !M.has_brain())
diff --git a/code/game/objects/items/devices/scanners/motion_tracker.dm b/code/game/objects/items/devices/scanners/motion_tracker.dm
index b2e38c665c..27ae896214 100644
--- a/code/game/objects/items/devices/scanners/motion_tracker.dm
+++ b/code/game/objects/items/devices/scanners/motion_tracker.dm
@@ -19,11 +19,14 @@
/obj/item/motiontracker/Initialize(mapload)
RegisterSignal(SSmotiontracker, COMSIG_MOVABLE_MOTIONTRACKER, PROC_REF(handle_motion_tracking))
. = ..()
+ if(ismob(loc))
+ var/mob/M = loc
+ M.motiontracker_subscribe()
/obj/item/motiontracker/Destroy(force, ...)
if(ismob(loc))
var/mob/M = loc
- M.motiontracker_subscribe()
+ M.motiontracker_unsubscribe()
UnregisterSignal(SSmotiontracker, COMSIG_MOVABLE_MOTIONTRACKER)
. = ..()
diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm
index c7acb4e306..ce8a69e516 100644
--- a/code/modules/mob/death.dm
+++ b/code/modules/mob/death.dm
@@ -71,6 +71,11 @@
if(stat == DEAD)
return 0
+
+ var/mob/living/simple_mob/animal/borer/has_worm = has_brain_worms()
+ if(has_worm) // This is our host's problem to deal with
+ has_worm.detatch()
+
SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed)
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_MOB_DEATH, src, gibbed)
diff --git a/code/modules/mob/living/carbon/carbon_powers.dm b/code/modules/mob/living/carbon/carbon_powers.dm
index e49aa30f10..f4fdf84f6c 100644
--- a/code/modules/mob/living/carbon/carbon_powers.dm
+++ b/code/modules/mob/living/carbon/carbon_powers.dm
@@ -1,66 +1,3 @@
-//Brain slug proc for voluntary removal of control.
-/mob/living/carbon/proc/release_control()
-
- set category = "Abilities.Brainslug"
- set name = "Release Control"
- set desc = "Release control of your host's body."
-
- var/mob/living/simple_mob/animal/borer/B = has_brain_worms()
-
- if(B && B.host_brain)
- to_chat(src, span_danger("You withdraw your probosci, releasing control of [B.host_brain]"))
-
- B.detatch()
-
- remove_verb(src, /mob/living/carbon/proc/release_control)
- remove_verb(src, /mob/living/carbon/proc/punish_host)
- remove_verb(src, /mob/living/carbon/proc/spawn_larvae)
-
- else
- to_chat(src, span_danger("ERROR NO BORER OR BRAINMOB DETECTED IN THIS MOB, THIS IS A BUG !"))
-
-//Brain slug proc for tormenting the host.
-/mob/living/carbon/proc/punish_host()
- set category = "Abilities.Brainslug"
- set name = "Torment host"
- set desc = "Punish your host with agony."
-
- var/mob/living/simple_mob/animal/borer/B = has_brain_worms()
-
- if(!B)
- return
-
- if(B.host_brain.ckey)
- to_chat(src, span_danger("You send a punishing spike of psychic agony lancing into your host's brain."))
- if (!can_feel_pain())
- to_chat(B.host_brain, span_warning("You feel a strange sensation as a foreign influence prods your mind."))
- to_chat(src, span_danger("It doesn't seem to be as effective as you hoped."))
- else
- to_chat(B.host_brain, span_danger(span_large("Horrific, burning agony lances through you, ripping a soundless scream from your trapped mind!")))
-
-/mob/living/carbon/proc/spawn_larvae()
- set category = "Abilities.Brainslug"
- set name = "Reproduce"
- set desc = "Spawn several young."
-
- var/mob/living/simple_mob/animal/borer/B = has_brain_worms()
-
- if(!B)
- return
-
- if(B.chemicals >= 100)
- to_chat(src, span_danger("Your host twitches and quivers as you rapidly excrete a larva from your sluglike body."))
- visible_message(span_danger("\The [src] heaves violently, expelling a rush of vomit and a wriggling, sluglike creature!"))
- B.chemicals -= 100
- B.has_reproduced = 1
-
- vomit(1)
- new /mob/living/simple_mob/animal/borer(get_turf(src))
-
- else
- to_chat(src, span_warning("You do not have enough chemicals stored to reproduce."))
- return
-
/mob/living/proc/toggle_active_cloaking() // Borrowed from Rogue Star, thanks guys!
set category = "Abilities.General"
set name = "Toggle Active Cloaking"
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm
index 28a71d1338..759fb0c797 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm
@@ -21,6 +21,7 @@
status_flags = CANPUSH
pass_flags = PASSTABLE
movement_cooldown = 1.5
+ mob_size = MOB_TINY // no landmines for you
universal_understand = TRUE
can_be_antagged = TRUE
@@ -35,16 +36,37 @@
var/antag = TRUE // If false, will avoid setting up objectives and events
var/chemicals = 10 // A resource used for reproduction and powers.
- var/max_chemicals = 250 // Max of said resource.
var/true_name = null // String used when speaking among other worms.
var/controlling = FALSE // Used in human death ceck.
var/docile = FALSE // Sugar can stop borers from acting.
+ var/docile_counter = 0 // How long we are docile for
var/has_reproduced = FALSE
var/used_dominate // world.time when the dominate power was last used.
- var/datum/ghost_query/Q // Used to unregister our signal
can_be_drop_prey = FALSE
+ vent_crawl_time = 30 // faster vent crawler
+
+ var/static/borer_chem_list = list(
+ // Antag utlity
+ "Repair Brain Tissue" = list(REAGENT_ID_ALKYSINE, 5),
+ "Repair Brute" = list(REAGENT_ID_BICARIDINE, 10),
+ "Repair Burn" = list(REAGENT_ID_KELOTANE, 10),
+ "Enhance Speed" = list(REAGENT_ID_HYPERZINE, 10),
+ // Stablization
+ "Stablize Mind" = list(REAGENT_ID_CITALOPRAM, 10),
+ "Stablize Nerves" = list(REAGENT_ID_ADRANOL, 10),
+ "Stablize Bleeding" = list(REAGENT_ID_INAPROVALINE, 5),
+ "Stablize Infection" = list(REAGENT_ID_SPACEACILLIN, 15),
+ "Stablize Pain" = list(REAGENT_ID_TRAMADOL, 5),
+ // Scene tools
+ "Make Drunk" = list(REAGENT_ID_ETHANOL, 5),
+ "Cure Drunk" = list(REAGENT_ID_ETHYLREDOXRAZINE, 10),
+ "Euphoric High" = list(REAGENT_ID_BLISS, 5),
+ "Mood Stimulant" = list(REAGENT_ID_APHRODISIAC, 5),
+ // Borer last resport
+ "Revive Dead Host" = null
+ )
/mob/living/simple_mob/animal/borer/roundstart
roundstart = TRUE
@@ -53,18 +75,19 @@
antag = FALSE
/mob/living/simple_mob/animal/borer/Login()
- ..()
+ . = ..()
if(antag && mind)
borers.add_antagonist(mind)
/mob/living/simple_mob/animal/borer/Initialize(mapload)
add_language("Cortical Link")
-
add_verb(src, /mob/living/proc/ventcrawl)
add_verb(src, /mob/living/proc/hide)
+ motiontracker_subscribe()
true_name = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]"
- ..()
+
+ . = ..()
if(!roundstart && antag)
return INITIALIZE_HINT_LATELOAD
@@ -72,52 +95,145 @@
/mob/living/simple_mob/animal/borer/LateInitialize()
request_player()
+/mob/living/simple_mob/animal/borer/Destroy()
+ . = ..()
+ motiontracker_unsubscribe()
+ QDEL_NULL(ghost_check)
+ if(host)
+ detatch()
+ leave_host()
+
/mob/living/simple_mob/animal/borer/handle_special()
- if(host && !stat && !host.stat)
- // Handle docility.
- if(host.reagents.has_reagent(REAGENT_ID_SUGAR) && !docile)
- var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility."
- var/target = controlling ? host : src
- to_chat(target, span_warning(message))
- docile = TRUE
-
- else if(docile)
- var/message = "You shake off your lethargy as the sugar leaves your host's blood."
- var/target = controlling ? host : src
- to_chat(target, span_notice(message))
- docile = FALSE
-
- // Chem regen.
- if(chemicals < max_chemicals)
- chemicals++
-
- // Control stuff.
- if(controlling)
- if(docile)
- to_chat(host, span_warning("You are feeling far too docile to continue controlling your host..."))
- host.release_control()
- return
-
- if(prob(5))
- host.adjustBrainLoss(0.1)
-
- if(prob(host.brainloss/20))
- host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]")
+ handle_chemicals()
+ handle_docile()
+ handle_braindamage()
/mob/living/simple_mob/animal/borer/get_status_tab_items()
. = ..()
- . += "Chemicals: [chemicals]"
+ . += "Chemicals: [FLOOR(chemicals,1)]"
+
+/mob/living/simple_mob/animal/borer/proc/handle_chemicals()
+ if(stat == DEAD || !host || host.stat == DEAD)
+ return
+ if(chemicals >= BORER_MAX_CHEMS || docile)
+ return
+
+ var/chem_before = chemicals
+ if(controlling)
+ chemicals += 0.25
+ else
+ chemicals += 0.1
+ var/new_chem = FLOOR(chemicals/10,1)
+ if(new_chem > FLOOR(chem_before/10,1))
+ to_chat(host, span_alien("Your chemicals have increased to [new_chem * 10]"))
+
+/mob/living/simple_mob/animal/borer/proc/handle_docile()
+ if(stat == DEAD)
+ docile_counter = 0
+ return
+
+ // Start docile
+ if(host && (host.reagents.has_reagent(REAGENT_ID_SUGAR) || host.ingested.has_reagent(REAGENT_ID_SUGAR)))
+ docile_counter = 5 SECONDS
+ if(!docile)
+ var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility."
+ var/target = controlling ? host : src
+ to_chat(target, span_danger( message))
+ docile = TRUE
+
+ // Drop control if docile
+ if(controlling && docile)
+ to_chat(host, span_vdanger("You are feeling far too docile to continue controlling your host..."))
+ host.release_control()
+ return
+
+ // wear it off
+ docile_counter--
+ if(docile_counter > 0)
+ return
+
+ // End docile
+ if(docile)
+ to_chat(controlling ? host : src, span_notice("You shake off your lethargy as the sugar leaves your host's blood."))
+ docile = FALSE
+ docile_counter = 0
+
+/mob/living/simple_mob/animal/borer/proc/handle_braindamage()
+ if(QDELETED(src) || !host || QDELETED(host) || !controlling)
+ return
+ if(prob(2))
+ host.adjustBrainLoss(0.1)
+ if(prob(host.brainloss/20))
+ host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]")
+
+/mob/living/simple_mob/animal/borer/proc/can_use_power_in_host()
+ if(QDELETED(src))
+ return FALSE
+ if(!host || QDELETED(host))
+ to_chat(src, span_warning("You are not inside a host body."))
+ return FALSE
+ if(stat)
+ to_chat(controlling ? host : src, span_warning("You cannot that that in your current state."))
+ return FALSE
+ return TRUE
+
+/mob/living/simple_mob/animal/borer/proc/can_use_power_controlling_host()
+ if(!can_use_power_in_host())
+ return FALSE
+ if(!controlling)
+ to_chat(controlling ? host : src, span_warning("You need to be in complete control to do this."))
+ return FALSE
+ if(host.stat)
+ to_chat(controlling ? host : src, span_warning("Your host is in no condition to do that."))
+ return FALSE
+ return TRUE
+
+/mob/living/simple_mob/animal/borer/proc/can_use_power_docile()
+ if(docile)
+ to_chat(controlling ? host : src, span_info("You are feeling far too docile to do that."))
+ return !docile
+
+/mob/living/simple_mob/animal/borer/proc/use_chems(amount)
+ if(chemicals < amount)
+ to_chat(controlling ? host : src, span_warning("You don't have enough chemicals, requires [amount]! Currently you have [FLOOR(chemicals,1)]."))
+ return FALSE
+ chemicals -= amount
+ to_chat(controlling ? host : src, span_info("You use [amount] chemicals, [FLOOR(chemicals,1)] remain."))
+ return TRUE
+
+/mob/living/simple_mob/animal/borer/handle_regular_hud_updates()
+ . = ..()
+ if(!.)
+ return
+ if(borer_chem_display)
+ borer_chem_display.invisibility = INVISIBILITY_NONE
+ switch(chemicals)
+ if(0 to 9)
+ borer_chem_display.icon_state = "ling_chems0e"
+ if(10 to 19)
+ borer_chem_display.icon_state = "ling_chems10e"
+ if(20 to 29)
+ borer_chem_display.icon_state = "ling_chems20e"
+ if(30 to 39)
+ borer_chem_display.icon_state = "ling_chems30e"
+ if(40 to 49)
+ borer_chem_display.icon_state = "ling_chems40e"
+ if(50 to 59)
+ borer_chem_display.icon_state = "ling_chems50e"
+ if(60 to 69)
+ borer_chem_display.icon_state = "ling_chems60e"
+ if(70 to 79)
+ borer_chem_display.icon_state = "ling_chems70e"
+ if(80 to INFINITY)
+ borer_chem_display.icon_state = "ling_chems80e"
/mob/living/simple_mob/animal/borer/proc/detatch()
if(!host || !controlling)
return
- if(ishuman(host))
- var/mob/living/carbon/human/H = host
- var/obj/item/organ/external/head = H.get_organ(BP_HEAD)
- if(head)
- head.implants -= src
-
+ var/obj/item/organ/external/head = host.get_organ(BP_HEAD)
+ if(head)
+ head.implants -= src
controlling = FALSE
host.remove_language("Cortical Link")
@@ -125,6 +241,7 @@
remove_verb(host, /mob/living/carbon/proc/punish_host)
remove_verb(host, /mob/living/carbon/proc/spawn_larvae)
+ // This entire section is awful and a relic of ancient times. It needs to be replaced
if(host_brain)
// these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed.
// computer_id and IP are not updated magically on their own in offline mobs -walter0o
@@ -160,7 +277,7 @@
host.lastKnownIP = b2h_ip
qdel(host_brain)
-
+ // End horrible ip swapping code for bans
/mob/living/simple_mob/animal/borer/proc/leave_host()
if(!host)
@@ -169,32 +286,16 @@
if(host.mind)
borers.remove_antagonist(host.mind)
- forceMove(get_turf(host))
-
+ if(!QDELETED(src))
+ forceMove(get_turf(host.loc))
unset_machine()
- if(ishuman(host))
- var/mob/living/carbon/human/H = host
- var/obj/item/organ/external/head = H.get_organ(BP_HEAD)
- if(head)
- head.implants -= src
-
+ var/obj/item/organ/external/head = host.get_organ(BP_HEAD)
+ if(head)
+ head.implants -= src
host.unset_machine()
host = null
-/mob/living/simple_mob/animal/borer/proc/request_player()
- Q = new /datum/ghost_query/borer()
- RegisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE, PROC_REF(get_winner))
- Q.query() // This will sleep the proc for awhile.
-
-/mob/living/simple_mob/animal/borer/proc/get_winner()
- SIGNAL_HANDLER
- if(Q && Q.candidates.len) //Q should NEVER get deleted but...whatever, sanity.
- var/mob/observer/dead/D = Q.candidates[1]
- transfer_personality(D)
- UnregisterSignal(Q, COMSIG_GHOST_QUERY_COMPLETE)
- QDEL_NULL(Q) //get rid of the query
-
/mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate)
if(!candidate)
return
@@ -206,17 +307,25 @@
mind.assigned_role = JOB_CORTICAL_BORER
mind.special_role = JOB_CORTICAL_BORER
+ // TODO - This needs to be made into something more pref friendly if it's ever going to be used on virgo.
to_chat(src, span_notice("You are a cortical borer! You are a brain slug that worms its way \
into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \
your host and your eventual spawn safe and warm."))
to_chat(src, "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers.")
/mob/living/simple_mob/animal/borer/cannot_use_vents()
- return
+ return host || stat
+
+/mob/living/simple_mob/animal/borer/extra_huds(var/datum/hud/hud,var/icon/ui_style,var/list/hud_elements)
+ // Chem hud
+ borer_chem_display = new /atom/movable/screen/borer/chems()
+ borer_chem_display.screen_loc = ui_ling_chemical_display
+ borer_chem_display.icon_state = "ling_chems"
+ hud_elements |= borer_chem_display
/mob/living/simple_mob/animal/borer/UnarmedAttack(var/atom/A, var/proximity)
if(ismob(loc))
- to_chat(src, span_warning("You cannot interact with that from inside a host!"))
+ to_chat(src, span_notice("You cannot interact with that from inside a host!"))
return
. = ..()
@@ -230,7 +339,7 @@
if(stat >= DEAD)
return say_dead(message)
- else if(stat)
+ if(stat)
return
if(client && client.prefs.muted & MUTE_IC)
@@ -247,10 +356,10 @@
return
if(!host)
- if(chemicals >= 30)
+ if(chemicals >= BORER_PSYCHIC_SAY_MINIMUM_CHEMS)
to_chat(src, span_alien("..You emit a psionic pulse with an encoded message.."))
var/list/nearby_mobs = list()
- for(var/mob/living/LM in view(src, 1 + round(6 * (chemicals / max_chemicals))))
+ for(var/mob/living/LM in view(src, 1 + round(6 * (chemicals / BORER_MAX_CHEMS))))
if(LM == src)
continue
if(!LM.stat)
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm
index ca5feabcfa..b25d6175ad 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm
@@ -7,12 +7,12 @@
/mob/living/captive_brain/say(var/message, var/datum/language/speaking = null, var/whispering = 0)
- if (src.client)
+ if(client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, span_red("You cannot speak in IC (muted)."))
return
- if(istype(src.loc, /mob/living/simple_mob/animal/borer))
+ if(istype(loc, /mob/living/simple_mob/animal/borer))
message = sanitize(message)
if (!message)
@@ -21,7 +21,7 @@
if (stat == 2)
return say_dead(message)
- var/mob/living/simple_mob/animal/borer/B = src.loc
+ var/mob/living/simple_mob/animal/borer/B = loc
to_chat(src, "You whisper silently, \"[message]\"")
to_chat(B.host, "The captive mind of [src] whispers, \"[message]\"")
@@ -41,24 +41,19 @@
/mob/living/captive_brain/process_resist()
//Resisting control by an alien mind.
- if(istype(src.loc, /mob/living/simple_mob/animal/borer))
- var/mob/living/simple_mob/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)."))
+ if(istype(loc, /mob/living/simple_mob/animal/borer))
+ var/mob/living/simple_mob/animal/borer/B = loc
+ to_chat(src, span_danger("You begin doggedly resisting the parasite's control (this will take approximately sixty seconds)."))
to_chat(B.host, span_danger("You feel the captive mind of [src] begin to resist your control."))
-
- spawn(rand(200,250)+B.host.brainloss)
- if(!B || !B.controlling) return
-
- B.host.adjustBrainLoss(rand(0.1,0.5))
- to_chat(H, span_danger("With an immense exertion of will, you regain control of your body!"))
- to_chat(B.host, span_danger("You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you."))
- B.detatch()
- remove_verb(src, /mob/living/carbon/proc/release_control)
- remove_verb(src, /mob/living/carbon/proc/punish_host)
- remove_verb(src, /mob/living/carbon/proc/spawn_larvae)
-
+ addtimer(CALLBACK(src, PROC_REF(break_control_of_borer)), (rand(20,25) + (B.host.brainloss/10)) SECONDS, TIMER_DELETE_ME)
return
+ . = ..()
- ..()
+/mob/living/captive_brain/proc/break_control_of_borer()
+ var/mob/living/simple_mob/animal/borer/B = loc
+ if(!B || !B.controlling)
+ return
+ to_chat(src, span_danger("With an immense exertion of will, you regain control of your body!"))
+ to_chat(B.host, span_danger("You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you."))
+ B.host.adjustBrainLoss(rand(0.1,0.5))
+ B.detatch()
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_control.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_control.dm
new file mode 100644
index 0000000000..3a7b28e97c
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_control.dm
@@ -0,0 +1,112 @@
+/**
+ * Assume control of a borer's host, putting the borer's client in control, and putting the other into a dominated brain.
+ */
+/mob/living/simple_mob/animal/borer/verb/bond_brain()
+ set category = "Abilities.Borer"
+ set name = "Assume Control"
+ set desc = "Fully connect to the brain of your host."
+
+ if(!can_use_power_in_host())
+ return
+ if(!can_use_power_docile())
+ return
+ if(host.stat == DEAD)
+ to_chat(src, span_warning("Your host is in no condition to do that."))
+ return
+
+ to_chat(src, span_alien("You begin delicately adjusting your connection to the host brain..."))
+ addtimer(CALLBACK(src, PROC_REF(finish_bond_brain)), 100 + (host.brainloss * 5), TIMER_DELETE_ME)
+
+// This entire section is awful and a relic of ancient times. It needs to be replaced
+/mob/living/simple_mob/animal/borer/proc/finish_bond_brain()
+ PRIVATE_PROC(TRUE)
+ RETURN_TYPE(null)
+
+ if(!host || QDELETED(src) || controlling)
+ return
+ if(host.stat == DEAD)
+ to_chat(src, span_warning("Your host is in no condition to do that."))
+ return
+
+ to_chat(src, span_alien("You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system."))
+ to_chat(host, span_danger("You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours."))
+ host.add_language("Cortical Link")
+
+ // host -> brain
+ var/h2b_id = host.computer_id
+ var/h2b_ip= host.lastKnownIP
+ host.computer_id = null
+ host.lastKnownIP = null
+
+ qdel(host_brain)
+ host_brain = new(src)
+ host_brain.ckey = host.ckey
+ host_brain.name = host.name
+
+ if(!host_brain.computer_id)
+ host_brain.computer_id = h2b_id
+
+ if(!host_brain.lastKnownIP)
+ host_brain.lastKnownIP = h2b_ip
+
+ // self -> host
+ var/s2h_id = src.computer_id
+ var/s2h_ip= src.lastKnownIP
+ src.computer_id = null
+ src.lastKnownIP = null
+
+ host.ckey = src.ckey
+
+ if(!host.computer_id)
+ host.computer_id = s2h_id
+ if(!host.lastKnownIP)
+ host.lastKnownIP = s2h_ip
+ controlling = TRUE
+
+ add_verb(host, /mob/living/carbon/proc/release_control)
+ add_verb(host, /mob/living/carbon/proc/punish_host)
+ if(antag)
+ add_verb(host, /mob/living/carbon/proc/spawn_larvae)
+// End horrible ip swapping code for bans
+
+/**
+ * Releases manual control of the borer from a mind-dominated host. Returning control to the original mind.
+ */
+/mob/living/simple_mob/animal/borer/verb/release_host()
+ set category = "Abilities.Borer"
+ set name = "Release Host"
+ set desc = "Slither out of your host."
+
+ if(!host)
+ to_chat(src, span_warning("You are not inside a host body."))
+ return
+ if(stat)
+ to_chat(src, span_warning("You cannot leave your host in your current state."))
+ return
+ if(!can_use_power_docile())
+ return
+
+ to_chat(src, span_alien("You begin disconnecting from [host]'s synapses and prodding at their internal ear canal."))
+ if(!host.stat)
+ to_chat(host, span_danger("An odd, uncomfortable pressure begins to build inside your skull, behind your ear..."))
+
+ addtimer(CALLBACK(src, PROC_REF(finish_release_host)), 10 SECONDS, TIMER_DELETE_ME)
+
+/mob/living/simple_mob/animal/borer/proc/finish_release_host()
+ PRIVATE_PROC(TRUE)
+
+ if(!host || QDELETED(src))
+ return
+
+ if(stat)
+ to_chat(src, span_warning("You cannot release your host in your current state."))
+ return
+
+ to_chat(src, span_alien("You wiggle out of [host]'s ear and plop to the ground."))
+ if(host.mind)
+ if(!host.stat)
+ to_chat(host, span_danger("Something slimy wiggles out of your ear and plops to the ground!"))
+ to_chat(host, span_danger("As though waking from a dream, you shake off the insidious mind control of the brain worm. Your thoughts are your own again."))
+
+ detatch()
+ leave_host()
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_host_powers.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_host_powers.dm
new file mode 100644
index 0000000000..13457329ed
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_host_powers.dm
@@ -0,0 +1,68 @@
+/**
+ * Brain slug proc for voluntary removal of control.
+ */
+/mob/living/carbon/proc/release_control()
+
+ set category = "Abilities.Brainslug"
+ set name = "Release Control"
+ set desc = "Release control of your host's body."
+
+ var/mob/living/simple_mob/animal/borer/B = has_brain_worms()
+ if(!B)
+ return
+ if(!B.host_brain)
+ return
+
+ to_chat(src, span_alien("You withdraw your probosci, releasing control of [B.host_brain]"))
+ B.detatch()
+
+/**
+ * Brain slug proc for tormenting the host.
+ */
+/mob/living/carbon/proc/punish_host()
+ set category = "Abilities.Brainslug"
+ set name = "Torment host"
+ set desc = "Punish your host with agony."
+
+ var/mob/living/simple_mob/animal/borer/B = has_brain_worms()
+ if(!B)
+ return
+ if(!B.host_brain)
+ return
+ if(!B.can_use_power_docile())
+ return
+ if(!B.use_chems(BORER_POWER_COST_TORTURE))
+ return
+
+ to_chat(src, span_alien("You send a punishing spike of psychic agony lancing into your host's brain."))
+ if(!B.host_brain.client || !can_feel_pain())
+ to_chat(B.host_brain, span_warning("You feel a strange sensation as a foreign influence prods your mind."))
+ to_chat(src, span_danger("It doesn't seem to be as effective as you hoped."))
+ return
+ to_chat(B.host_brain, span_danger(span_large("Horrific, burning agony lances through you, ripping a soundless scream from your trapped mind!")))
+
+/**
+ * Brain slug proc for spitting up new borers. They are ghostjoin by default, but are infertile.
+ */
+/mob/living/carbon/proc/spawn_larvae()
+ set category = "Abilities.Brainslug"
+ set name = "Reproduce"
+ set desc = "Spawn several young."
+
+ var/mob/living/simple_mob/animal/borer/B = has_brain_worms()
+ if(!B || !B.can_use_power_controlling_host())
+ return
+ if(!B.can_use_power_docile())
+ return
+ if(!B.antag)
+ to_chat(src, span_warning("You cannot reproduce, you are infertile!"))
+ return
+ if(!B.use_chems(BORER_POWER_COST_REPRODUCE))
+ return
+
+ to_chat(src, span_alien("Your host twitches and quivers as you rapidly excrete a larva from your sluglike body."))
+ visible_message(span_danger("\The [src] heaves violently, expelling a rush of vomit and a wriggling, sluglike creature!"))
+
+ do_vomit(lost_nutrition = 1) // Needs to be instant
+ new /mob/living/simple_mob/animal/borer/non_antag(get_turf(src))
+ B.has_reproduced = TRUE
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm
index ad11e46ba9..d5f6108037 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm
@@ -1,43 +1,55 @@
-/mob/living/simple_mob/animal/borer/verb/release_host()
+/**
+ * Put a nearby target to sleep to allow for infestation.
+ */
+/mob/living/simple_mob/animal/borer/verb/knockout_victim()
set category = "Abilities.Borer"
- set name = "Release Host"
- set desc = "Slither out of your host."
+ set name = "Knockout Victim"
+ set desc = "Use your psychic influence to put a target into a temporary catatonic state."
- if(!host)
- to_chat(src, span_warning("You are not inside a host body."))
+ if(world.time - used_dominate < 150)
+ to_chat(src, span_warning("You cannot use that ability again so soon."))
+ return
+ if(host)
+ to_chat(src, span_warning("You cannot do that from within a host body."))
return
-
if(stat)
- to_chat(src, span_warning("You cannot leave your host in your current state."))
-
- if(docile)
- to_chat(src, span_blue("You are feeling far too docile to do that."))
+ to_chat(src, span_warning("You cannot do that in your current state."))
return
- if(!host || !src) return
+ var/attack_range = 5
+ var/list/choices = list()
+ for(var/mob/living/carbon/human/attackable in view(attack_range,src))
+ if(attackable.stat != DEAD && !attackable.has_brain_worms())
+ choices += attackable
+ if(world.time - used_dominate < 150)
+ to_chat(src, span_warning("You cannot use that ability again so soon."))
+ return
- to_chat(src, span_warning("You begin disconnecting from [host]'s synapses and prodding at their internal ear canal."))
+ if(!choices.len)
+ to_chat(src, span_notice("There are no viable targets within range..."))
+ return
+ var/mob/living/carbon/human/attack_target = choices[1]
+ if(choices.len > 1)
+ tgui_input_list(src, "Who do you wish to dominate?", "Target Choice", choices)
- if(!host.stat)
- to_chat(host, span_warning("An odd, uncomfortable pressure begins to build inside your skull, behind your ear..."))
+ if(!attack_target || QDELETED(src))
+ return
+ if(!(attack_target in view(attack_range,src)))
+ to_chat(src, span_warning("\The [attack_target] escaped your influence..."))
+ return
+ if(attack_target.has_brain_worms())
+ to_chat(src, span_warning("You cannot influence someone who is already infested!"))
+ return
- spawn(100)
-
- if(!host || !src) return
-
- if(src.stat)
- to_chat(src, span_warning("You cannot release your host in your current state."))
- return
-
- to_chat(src, span_warning("You wiggle out of [host]'s ear and plop to the ground."))
- if(host.mind)
- if(!host.stat)
- to_chat(host, span_danger("Something slimy wiggles out of your ear and plops to the ground!"))
- to_chat(host, span_danger("As though waking from a dream, you shake off the insidious mind control of the brain worm. Your thoughts are your own again."))
-
- detatch()
- leave_host()
+ to_chat(src, span_alien("You focus your psychic lance on [attack_target] and freeze their limbs with a wave of terrible dread."))
+ to_chat(attack_target, span_vdanger("You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing."))
+ attack_target.Sleeping(10) // This was paralyze, but it resulted in players instantly screaming over radio. So get slept nerds.
+ used_dominate = world.time
+ add_attack_logs(src, attack_target, "psychic knockout (borer)")
+/**
+ * Crawls inside of a target mob, and adds the borer as an implant to the mob's brain. If no brain exists the body will become a borer husk zombie.
+ */
/mob/living/simple_mob/animal/borer/verb/infest()
set category = "Abilities.Borer"
set name = "Infest"
@@ -46,311 +58,190 @@
if(host)
to_chat(src, span_warning("You are already within a host."))
return
-
if(stat)
to_chat(src, span_warning("You cannot infest a target in your current state."))
return
var/list/choices = list()
- for(var/mob/living/carbon/C in view(1,src))
- if(src.Adjacent(C))
- choices += C
-
+ for(var/mob/living/carbon/human/check_target in view(1,src))
+ if(Adjacent(check_target) && !check_target.has_brain_worms())
+ choices += check_target
if(!choices.len)
to_chat(src, span_warning("There are no viable hosts within range..."))
return
- var/mob/living/carbon/M = tgui_input_list(src, "Who do you wish to infest?", "Target Choice", choices)
+ var/mob/living/carbon/human/infest_target = choices[1]
+ if(choices.len > 1)
+ infest_target = tgui_input_list(src, "Who do you wish to infest?", "Target Choice", choices)
+ if(QDELETED(infest_target) || QDELETED(src))
+ return
+ infest_target(infest_target)
- if(!M || !src) return
-
- if(!(src.Adjacent(M))) return
-
- if(M.has_brain_worms())
+/// Infests mob with borer.
+/mob/living/simple_mob/animal/borer/proc/infest_target(mob/living/carbon/human/infest_target)
+ if(!istype(infest_target))
+ to_chat(src, span_warning("\The [infest_target] is not suitable for infestation..."))
+ return
+ if(!(Adjacent(infest_target)))
+ to_chat(src, span_warning("\The [infest_target] has escaped your range..."))
+ return
+ if(infest_target.has_brain_worms())
to_chat(src, span_warning("You cannot infest someone who is already infested!"))
return
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
+ var/entering_timer = 30
+ var/protected = FALSE
+ var/obj/item/organ/external/E = infest_target.organs_by_name[BP_HEAD]
+ if(!E || E.is_stump())
+ to_chat(src, span_warning("\The [infest_target] does not have a head!"))
- var/obj/item/organ/external/E = H.organs_by_name[BP_HEAD]
- if(!E || E.is_stump())
- to_chat(src, span_warning("\The [H] does not have a head!"))
-
- if(!H.should_have_organ(O_BRAIN))
- to_chat(src, span_warning("\The [H] does not seem to have an ear canal to breach."))
- return
-
- if(H.check_head_coverage())
- to_chat(src, span_warning("You cannot get through that host's protective gear."))
- return
-
- to_chat(M, "Something slimy begins probing at the opening of your ear canal...")
- to_chat(src, span_warning("You slither up [M] and begin probing at their ear canal..."))
-
- if(!do_after(src, 3 SECONDS, target = M))
- to_chat(src, span_warning("As [M] moves away, you are dislodged and fall to the ground."))
+ if(!infest_target.should_have_organ(O_BRAIN))
+ to_chat(src, span_warning("\The [infest_target] does not seem to have an ear canal to breach."))
return
+ if(infest_target.check_head_coverage())
+ to_chat(src, span_alien("You begin to flatten and squirm into \the [infest_target]'s helmet to find a way inside them."))
+ entering_timer = 55
+ protected = TRUE
- if(!M || !src) return
-
- if(src.stat)
- to_chat(src, span_warning("You cannot infest a target in your current state."))
- return
-
- if(M in view(1, src))
- to_chat(src, span_warning("You wiggle into [M]'s ear."))
- if(!M.stat)
- to_chat(M, "Something disgusting and slimy wiggles into your ear!")
-
- src.host = M
- src.forceMove(M)
-
- //Update their traitor status.
- if(host.mind)
- borers.add_antagonist_mind(host.mind, 1, borers.faction_role_text, borers.faction_welcome)
-
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- var/obj/item/organ/I = H.internal_organs_by_name[O_BRAIN]
- if(!I) // No brain organ, so the borer moves in and replaces it permanently.
- replace_brain()
- else
- // If they're in normally, implant removal can get them out.
- var/obj/item/organ/external/head = H.get_organ(BP_HEAD)
- head.implants += src
-
- return
+ if(!protected)
+ to_chat(infest_target, span_vdanger("Something slimy begins probing at the opening of your ear canal..."))
else
+ to_chat(infest_target, span_vdanger("Something slimy begins trying to find a way past your helmet..."))
+ to_chat(src, span_alien("You slither up to \the [infest_target] and begin probing at their ear canal..."))
+
+ if(!do_after(src, entering_timer, target = infest_target))
+ to_chat(src, span_danger("As [infest_target] moves away, you are dislodged and fall to the ground."))
+ return
+ if(!infest_target || QDELETED(src))
+ return
+ if(stat)
+ to_chat(src, span_warning("You cannot infest \the [infest_target] in your current state."))
+ return
+ if(!(Adjacent(infest_target)))
to_chat(src, span_warning("They are no longer in range!"))
return
-
-/*
-/mob/living/simple_mob/animal/borer/verb/devour_brain()
- set category = "Abilities.Borer"
- set name = "Devour Brain"
- set desc = "Take permanent control of a dead host."
-
- if(!host)
- to_chat(src, span_warning("You are not inside a host body."))
+ if(!infest_target.internal_organs_by_name[O_BRAIN]) // See section below about replace_brain() being disabled
+ to_chat(src, span_danger("\The [infest_target] has no brain to bond to!"))
return
- if(host.stat != 2)
- to_chat(src, span_warning("Your host is still alive."))
+ to_chat(src, span_alien("You wiggle into [infest_target]'s ear."))
+ if(!infest_target.stat)
+ to_chat(infest_target, span_vdanger("Something disgusting and slimy wiggles into your ear!"))
+ host = infest_target
+ forceMove(host)
+
+ //Update their traitor status.
+ if(host.mind)
+ borers.add_antagonist_mind(host.mind, 1, borers.faction_role_text, borers.faction_welcome)
+
+ /* This is likely not desired, and has some major issues with ghost behavior. Disabling for now
+ // No brain organ, so the borer moves in and replaces it permanently.
+ if(!host.internal_organs_by_name[O_BRAIN])
+ add_attack_logs(src, host, "merged with brainless body (borer)")
+ replace_brain()
return
+ */
- if(stat)
- to_chat(src, span_warning("You cannot do that in your current state."))
-
- if(docile)
- to_chat(src, span_warning(span_blue("You are feeling far too docile to do that.")))
- return
-
-
- to_chat(src, span_danger("It only takes a few moments to render the dead host brain down into a nutrient-rich slurry..."))
- replace_brain()
-*/
-
-// BRAIN WORM ZOMBIES AAAAH.
-/mob/living/simple_mob/animal/borer/proc/replace_brain()
-
- var/mob/living/carbon/human/H = host
-
- if(!istype(host))
- to_chat(src, span_warning("This host does not have a suitable brain."))
- return
-
- to_chat(src, span_danger("You settle into the empty brainpan and begin to expand, fusing inextricably with the dead flesh of [H]."))
-
- H.add_language("Cortical Link")
-
- if(host.stat == 2)
- add_verb(H, /mob/living/carbon/human/proc/jumpstart)
-
- add_verb(H, /mob/living/carbon/human/proc/psychic_whisper)
- add_verb(H, /mob/living/carbon/human/proc/tackle)
- if(antag)
- add_verb(H, /mob/living/carbon/proc/spawn_larvae)
-
- if(H.client)
- H.ghostize(0)
-
- if(src.mind)
- src.mind.special_role = "Borer Husk"
- src.mind.transfer_to(host)
-
- H.ChangeToHusk()
-
- var/obj/item/organ/internal/borer/B = new(H)
- H.internal_organs_by_name[O_BRAIN] = B
- H.internal_organs |= B
-
- var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD)
- affecting.implants -= src
-
- var/s2h_id = src.computer_id
- var/s2h_ip= src.lastKnownIP
- src.computer_id = null
- src.lastKnownIP = null
-
- if(!H.computer_id)
- H.computer_id = s2h_id
-
- if(!H.lastKnownIP)
- H.lastKnownIP = s2h_ip
+ // If they're in normally, implant removal can get them out.
+ var/obj/item/organ/external/head = host.get_organ(BP_HEAD)
+ head.implants += src
+ add_attack_logs(src, host, "infested target (borer)")
+/**
+ * Releases chemicals from the borer into their host. Can be used as a standalone chemist in your head for an antag cooperating with their borer.
+ */
/mob/living/simple_mob/animal/borer/verb/secrete_chemicals()
set category = "Abilities.Borer"
set name = "Secrete Chemicals"
- set desc = "Push some chemicals into your host's bloodstream."
+ set desc = "Drain some chemicals into your host's bloodstream."
- if(!host)
- to_chat(src, span_warning("You are not inside a host body."))
+ if(!can_use_power_in_host())
+ return
+ if(!can_use_power_docile())
+ return
+ if(controlling)
+ to_chat(src, span_warning("You cannot do that while in full control of a host."))
return
- if(stat)
- to_chat(src, span_warning("You cannot secrete chemicals in your current state."))
-
- if(docile)
- to_chat(src, span_warning(span_blue("You are feeling far too docile to do that.")))
- return
-
- if(chemicals < 50)
- to_chat(src, span_warning("You don't have enough chemicals!"))
-
- var/chem = tgui_input_list(src, "Select a chemical to secrete.", "Chemicals", list(REAGENT_ID_ALKYSINE,REAGENT_ID_BICARIDINE,REAGENT_ID_HYPERZINE,REAGENT_ID_TRAMADOL))
-
- if(!chem || chemicals < 50 || !host || controlling || !src || stat) //Sanity check.
- return
-
- to_chat(src, span_bolddanger("You squirt a measure of [chem] from your reservoirs into [host]'s bloodstream."))
- host.reagents.add_reagent(chem, 10)
- chemicals -= 50
-
-/mob/living/simple_mob/animal/borer/verb/dominate_victim()
- set category = "Abilities.Borer"
- set name = "Paralyze Victim"
- set desc = "Freeze the limbs of a potential host with supernatural fear."
-
- if(world.time - used_dominate < 150)
- to_chat(src, span_warning("You cannot use that ability again so soon."))
- return
-
- if(host)
- to_chat(src, span_warning("You cannot do that from within a host body."))
- return
-
- if(src.stat)
- to_chat(src, span_warning("You cannot do that in your current state."))
- return
-
- var/list/choices = list()
- for(var/mob/living/carbon/C in view(3,src))
- if(C.stat != 2)
- choices += C
-
- if(world.time - used_dominate < 150)
- to_chat(src, span_warning("You cannot use that ability again so soon."))
- return
-
- var/mob/living/carbon/M = tgui_input_list(src, "Who do you wish to dominate?", "Target Choice", choices)
-
- if(!M || !src) return
-
- if(M.has_brain_worms())
- to_chat(src, span_warning("You cannot infest someone who is already infested!"))
- return
-
- to_chat(src, span_red("You focus your psychic lance on [M] and freeze their limbs with a wave of terrible dread."))
- to_chat(M, span_red("You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing."))
- M.Weaken(10)
-
- used_dominate = world.time
-
-/mob/living/simple_mob/animal/borer/verb/bond_brain()
- set category = "Abilities.Borer"
- set name = "Assume Control"
- set desc = "Fully connect to the brain of your host."
-
- if(!host)
- to_chat(src, span_warning("You are not inside a host body."))
- return
-
- if(src.stat)
- to_chat(src, span_warning("You cannot do that in your current state."))
- return
-
- if(docile)
- to_chat(src, span_blue("You are feeling far too docile to do that."))
- return
-
- to_chat(src, span_warning("You begin delicately adjusting your connection to the host brain..."))
-
- spawn(100+(host.brainloss*5))
-
- if(!host || !src || controlling)
+ var/injection_choice = tgui_input_list(src, "Select a chemical to secrete.", "Chemicals", borer_chem_list)
+ if(injection_choice == "Revive Dead Host")
+ if(!can_use_power_in_host())
return
- else
-
- to_chat(src, span_bolddanger("You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system."))
- to_chat(host, span_bolddanger("You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours."))
- host.add_language("Cortical Link")
-
- // host -> brain
- var/h2b_id = host.computer_id
- var/h2b_ip= host.lastKnownIP
- host.computer_id = null
- host.lastKnownIP = null
-
- qdel(host_brain)
- host_brain = new(src)
-
- host_brain.ckey = host.ckey
-
- host_brain.name = host.name
-
- if(!host_brain.computer_id)
- host_brain.computer_id = h2b_id
-
- if(!host_brain.lastKnownIP)
- host_brain.lastKnownIP = h2b_ip
-
- // self -> host
- var/s2h_id = src.computer_id
- var/s2h_ip= src.lastKnownIP
- src.computer_id = null
- src.lastKnownIP = null
-
- host.ckey = src.ckey
-
- if(!host.computer_id)
- host.computer_id = s2h_id
-
- if(!host.lastKnownIP)
- host.lastKnownIP = s2h_ip
-
- controlling = 1
-
- add_verb(host, /mob/living/carbon/proc/release_control)
- add_verb(host, /mob/living/carbon/proc/punish_host)
- if(antag)
- add_verb(host, /mob/living/carbon/proc/spawn_larvae)
-
+ if(controlling)
+ to_chat(src, span_warning("You cannot do that while in full control of a host."))
return
+ if(!can_use_power_docile())
+ return
+ if(host.stat != DEAD)
+ to_chat(src, span_danger("Your host must be dead!"))
+ return
+ if(HUSK in host.mutations)
+ to_chat(src, span_danger("Your host is too destroyed to revive."))
+ return
+ if(!host.can_defib)
+ to_chat(src, span_vdanger("Your host's brain is not connected to its body!"))
+ return
+ if(!use_chems(BORER_POWER_COST_SECRETE))
+ return
+ to_chat(src, span_alien("You squirt an intense mix of chemicals from your reservoirs into [host]'s bloodstream."))
+ add_attack_logs(src, host, "jumpstart host (borer)")
+ host.jumpstart()
+ return
+ // Is this even legal to do still? We had to wait for a selection...
+ if(!can_use_power_in_host())
+ return
+ if(!can_use_power_docile())
+ return
+ if(controlling)
+ to_chat(src, span_warning("You cannot do that while in full control of a host."))
+ return
+
+ // Get the chem we're injecting
+ var/list/injection_data = borer_chem_list[injection_choice]
+ if(!injection_data) //Sanity check.
+ return
+ var/injecting_chem = injection_data[1]
+ var/injectsize = injection_data[2]
+ if(!injecting_chem || !injectsize)
+ return
+
+ // Finally get to injecting
+ if(!use_chems(BORER_POWER_COST_SECRETE))
+ return
+ var/datum/reagent/inject_reagent = SSchemistry.chemical_reagents[injecting_chem]
+ if(!inject_reagent)
+ CRASH("Invalid chem reagent [injecting_chem], in borer chemical injection.")
+
+ add_attack_logs(src, host, "chemical injection [inject_reagent] (borer)")
+ to_chat(src, span_alien("You squirt a measure of [inject_reagent] from your reservoirs into [host]'s bloodstream."))
+ host.bloodstr.add_reagent(injecting_chem, injectsize)
+
+/// Does some basic regeneration on a host
/mob/living/carbon/human/proc/jumpstart()
set category = "Abilities.Borer"
set name = "Revive Host"
set desc = "Send a jolt of electricity through your host, reviving them."
- if(stat != 2)
- to_chat(src, "Your host is already alive.")
- return
-
+ // This is meant to be a bit silly, cause borers don't have much options otherwise
remove_verb(src, /mob/living/carbon/human/proc/jumpstart)
- visible_message(span_warning("With a hideous, rattling moan, [src] shudders back to life!"))
+ visible_message(span_danger("With a hideous, rattling moan, [src] shudders back to life!"))
+ // Dump damage or we won't be able to revive properly
+ setHalLoss(0)
+ setOxyLoss(0)
+ adjustBruteLoss(-20)
+ adjustFireLoss(-20)
+ adjust_nutrition(-200)
+
+ // Boilerplate revivial
rejuvenate()
restore_blood()
fixblood()
update_canmove()
+
+ // Get some healing in us
+ bloodstr.add_reagent(REAGENT_BICARIDINE, 5)
+ bloodstr.add_reagent(REAGENT_KELOTANE, 5)
+ bloodstr.add_reagent(REAGENT_ID_TRAMADOL, 5)
+ bloodstr.add_reagent(REAGENT_ID_ALKYSINE, 5)
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_query.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_query.dm
new file mode 100644
index 0000000000..0502f7a967
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_query.dm
@@ -0,0 +1,15 @@
+/mob/living/simple_mob/animal/borer
+ var/datum/ghost_query/ghost_check // Used to unregister our signal
+
+/mob/living/simple_mob/animal/borer/proc/request_player()
+ ghost_check = new /datum/ghost_query/borer()
+ RegisterSignal(ghost_check, COMSIG_GHOST_QUERY_COMPLETE, PROC_REF(get_winner))
+ ghost_check.query() // This will sleep the proc for awhile.
+
+/mob/living/simple_mob/animal/borer/proc/get_winner()
+ SIGNAL_HANDLER
+ if(ghost_check && ghost_check.candidates.len) //ghost_check should NEVER get deleted but...whatever, sanity.
+ var/mob/observer/dead/D = ghost_check.candidates[1]
+ transfer_personality(D)
+ UnregisterSignal(ghost_check, COMSIG_GHOST_QUERY_COMPLETE)
+ QDEL_NULL(ghost_check) //get rid of the query
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_zombie.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_zombie.dm
new file mode 100644
index 0000000000..d30589417d
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_zombie.dm
@@ -0,0 +1,66 @@
+/**
+ * Consumes the brain of a dead host, turning it into a husk zombie.
+ *
+ * Disabled currently due to ghosting behavior, allows you to enter a dead body ghost around for meta knowledge, then return and jumpstart the host.
+ * Ideally a borer is about cooperating with your host in some way, and not just killing and eating their brain to go full single player antag anyway...
+ */
+/mob/living/simple_mob/animal/borer/verb/devour_brain()
+ set category = "Abilities.Borer"
+ set name = "Devour Brain"
+ set desc = "Take permanent control of a dead host."
+
+ if(!can_use_power_in_host())
+ return
+ if(!can_use_power_docile())
+ return
+ if(host.stat != DEAD)
+ to_chat(src, span_warning("Your host is still alive."))
+ return
+
+ to_chat(src, span_alien("It only takes a few moments to render the dead host brain down into a nutrient-rich slurry..."))
+ add_attack_logs(src, host, "devour host brain (borer)")
+ replace_brain()
+
+/// BRAIN WORM ZOMBIES AAAAH. Husks mob and turns them into a borer zombie.
+/mob/living/simple_mob/animal/borer/proc/replace_brain()
+ if(!istype(host))
+ to_chat(src, span_warning("This host does not have a suitable brain."))
+ return
+ to_chat(src, span_alien("You settle into the empty brainpan and begin to expand, fusing inextricably with the dead flesh of [host]."))
+
+ host.add_language("Cortical Link")
+
+ if(host.stat == DEAD)
+ add_verb(host, /mob/living/carbon/human/proc/jumpstart)
+ add_verb(host, /mob/living/carbon/human/proc/psychic_whisper)
+ add_verb(host, /mob/living/carbon/human/proc/tackle)
+ if(antag)
+ add_verb(host, /mob/living/carbon/proc/spawn_larvae)
+
+ // Zombify
+ if(host.client)
+ host.ghostize(FALSE)
+ if(mind)
+ mind.special_role = "Borer Husk"
+ mind.transfer_to(host)
+ host.ChangeToHusk()
+
+ // Convert into borer brain
+ var/obj/item/organ/internal/borer/borer_organ = new(host)
+ host.internal_organs_by_name[O_BRAIN] = borer_organ
+ host.internal_organs |= borer_organ
+ var/obj/item/organ/external/affecting = host.get_organ(BP_HEAD)
+ affecting.implants -= src
+
+ // This section is awful and a relic of ancient times. It needs to be replaced
+ var/s2h_id = computer_id
+ var/s2h_ip= lastKnownIP
+ computer_id = null
+ lastKnownIP = null
+
+ if(!host.computer_id)
+ host.computer_id = s2h_id
+
+ if(!host.lastKnownIP)
+ host.lastKnownIP = s2h_ip
+ // End ip trickery
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index d84be90d5d..3031cbe2ad 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -42,6 +42,7 @@
var/atom/movable/screen/gun/run/gun_run_icon = null
var/atom/movable/screen/gun/mode/gun_setting_icon = null
var/atom/movable/screen/ling/chems/ling_chem_display = null
+ var/atom/movable/screen/borer/chems/borer_chem_display = null
var/atom/movable/screen/wizard/energy/wiz_energy_display = null
var/atom/movable/screen/wizard/instability/wiz_instability_display = null
var/atom/movable/screen/autowhisper_display = null
diff --git a/vorestation.dme b/vorestation.dme
index 75fbdf6c18..277ecd5695 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -61,6 +61,7 @@
#include "code\__defines\belly_messages.dm"
#include "code\__defines\belly_modes_vr.dm"
#include "code\__defines\blueprints.dm"
+#include "code\__defines\borer.dm"
#include "code\__defines\borg_overlays.dm"
#include "code\__defines\callbacks.dm"
#include "code\__defines\changeling.dm"
@@ -3659,7 +3660,10 @@
#include "code\modules\mob\living\simple_mob\subtypes\animal\alien animals\teppi.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\borer\borer.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\borer\borer_captive.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\animal\borer\borer_control.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\animal\borer\borer_host_powers.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\borer\borer_powers.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\animal\borer\borer_query.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\farm animals\chicken.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\farm animals\cow.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\farm animals\goat.dm"