diff --git a/baystation12.dme b/baystation12.dme index 9348f106ce0..b82ce97a578 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -1005,6 +1005,7 @@ #include "code\modules\mob\living\silicon\robot\robot_movement.dm" #include "code\modules\mob\living\silicon\robot\say.dm" #include "code\modules\mob\living\silicon\robot\wires.dm" +#include "code\modules\mob\living\simple_animal\borer.dm" #include "code\modules\mob\living\simple_animal\constructs.dm" #include "code\modules\mob\living\simple_animal\corpse.dm" #include "code\modules\mob\living\simple_animal\parrot.dm" diff --git a/code/game/gamemodes/heist/heist.dm b/code/game/gamemodes/heist/heist.dm index 935e0974c75..5a1e12a191b 100644 --- a/code/game/gamemodes/heist/heist.dm +++ b/code/game/gamemodes/heist/heist.dm @@ -7,6 +7,9 @@ VOX HEIST ROUNDTYPE var/global/vox_kills = 0 //Used to check the Inviolate. +/datum/game_mode/ + var/list/datum/mind/raiders = list() //Antags. + /datum/game_mode/heist name = "heist" config_tag = "heist" @@ -19,7 +22,6 @@ var/global/vox_kills = 0 //Used to check the Inviolate. var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) var/list/raid_objectives = list() //Raid objectives. - var/list/datum/mind/raiders = list() //Antags. var/list/obj/cortical_stacks = list() //Stacks for 'leave nobody behind' objective. /datum/game_mode/heist/announce() @@ -102,6 +104,9 @@ var/global/vox_kills = 0 //Used to check the Inviolate. vox.set_species("Vox") vox.add_language("Vox-pidgin") vox.h_style = "Short Vox Quills" + vox.flavor_text = "" + for(var/datum/organ/external/limb in vox.organs) + limb.status &= ~(ORGAN_DESTROYED | ORGAN_ROBOT) vox.equip_vox_raider() vox.regenerate_icons() diff --git a/code/game/machinery/podmen.dm b/code/game/machinery/podmen.dm index 2fe409dcfb5..1e8eb4e5368 100644 --- a/code/game/machinery/podmen.dm +++ b/code/game/machinery/podmen.dm @@ -12,7 +12,7 @@ Growing it to term with nothing injected will grab a ghost from the observers. * productname = "/mob/living/carbon/human" //verrry special -- Urist lifespan = 50 //no idea what those do endurance = 8 - maturation = 10 + maturation = 5 production = 10 yield = 1 //seeds if there isn't a dna inside oneharvest = 1 diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index e932ab856eb..44f1cfdd820 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -469,6 +469,13 @@ dat += "Wizard not found!" dat += "" + if(ticker.mode.raiders.len > 0) + for(var/datum/mind/raider in ticker.mode.raiders) + var/mob/M = raider.current + if(M) + dat += "[M.real_name][M.client ? "" : " (logged out)"][M.stat == 2 ? " (DEAD)" : ""]" + dat += "PM" + dat += "Show Objective" /* if(ticker.mode.ninjas.len > 0) dat += "
" diff --git a/code/modules/admin/verbs/vox_raiders.dm b/code/modules/admin/verbs/vox_raiders.dm index 65a0f65ad2c..4b6e2271599 100644 --- a/code/modules/admin/verbs/vox_raiders.dm +++ b/code/modules/admin/verbs/vox_raiders.dm @@ -32,7 +32,7 @@ var/global/vox_tick = 1 equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/vox/pressure(src), slot_head) equip_to_slot_or_del(new /obj/item/weapon/storage/belt/utility/full(src), slot_belt) equip_to_slot_or_del(new /obj/item/clothing/glasses/meson(src), slot_glasses) // REPLACE WITH CODED VOX ALTERNATIVE. - equip_to_slot_or_del(new /obj/item/weapon/storage/box/emps(src), slot_l_store) + equip_to_slot_or_del(new /obj/item/weapon/storage/box/emps(src), slot_r_hand) equip_to_slot_or_del(new /obj/item/device/multitool(src), slot_l_hand) diff --git a/code/modules/clothing/spacesuits/alien.dm b/code/modules/clothing/spacesuits/alien.dm index 0115eef3113..250935d61ad 100644 --- a/code/modules/clothing/spacesuits/alien.dm +++ b/code/modules/clothing/spacesuits/alien.dm @@ -92,6 +92,9 @@ return ..() +/obj/item/clothing/under/vox + has_sensor = 0 + /obj/item/clothing/under/vox/vox_casual name = "alien clothing" desc = "This doesn't look very comfortable." diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index e20829aab85..095695e23d8 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -54,6 +54,25 @@ dizziness = 0 jitteriness = 0 + //Handle brain slugs. + var/datum/organ/external/head = get_organ("head") + var/mob/living/simple_animal/borer/B + + for(var/I in head.implants) + if(istype(I,/mob/living/simple_animal/borer)) + B = I + if(B) + if(!B.ckey && ckey && B.controlling) + B.ckey = ckey + B.controlling = 0 + if(B.host_brain.ckey) + ckey = B.host_brain.ckey + B.host_brain.ckey = null + B.host_brain.name = "host brain" + B.host_brain.real_name = "host brain" + + verbs -= /mob/living/carbon/human/proc/release_control + //Check for heist mode kill count. if(ticker.mode && ( istype( ticker.mode,/datum/game_mode/heist) ) ) //Check for last assailant's mutantrace. diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index df8dec56156..f70d3956b03 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1274,3 +1274,83 @@ mob/living/carbon/human/yank_out_object() return 1 else return 0 + +//Brain slug proc for voluntary removal of control. +/mob/living/carbon/human/proc/release_control() + + set category = "Alien" + set name = "Release Control" + set desc = "Release control of your host's body." + + var/datum/organ/external/head = get_organ("head") + var/mob/living/simple_animal/borer/B + + for(var/I in head.implants) + if(istype(I,/mob/living/simple_animal/borer)) + B = I + if(!B) + return + + if(B.controlling) + src << "\red You withdraw your probosci, releasing control of [B.host_brain]" + B.host_brain << "\red Your vision swims as the alien parasite releases control of your body." + B.ckey = ckey + B.controlling = 0 + if(B.host_brain.ckey) + ckey = B.host_brain.ckey + B.host_brain.ckey = null + B.host_brain.name = "host brain" + B.host_brain.real_name = "host brain" + + verbs -= /mob/living/carbon/human/proc/release_control + verbs -= /mob/living/carbon/human/proc/punish_host + verbs -= /mob/living/carbon/human/proc/spawn_larvae + +//Brain slug proc for tormenting the host. +/mob/living/carbon/human/proc/punish_host() + set category = "Alien" + set name = "Torment host" + set desc = "Punish your host with agony." + + var/mob/living/simple_animal/borer/B = has_brain_worms() + + if(!B) + return + + if(B.host_brain.ckey) + src << "\red You send a punishing spike of psychic agony lancing into your host's brain." + B.host_brain << "\red Horrific, burning agony lances through you, ripping a soundless scream from your trapped mind!" + +//Check for brain worms in head. +/mob/living/carbon/human/proc/has_brain_worms() + + var/datum/organ/external/head = get_organ("head") + + for(var/I in head.implants) + if(istype(I,/mob/living/simple_animal/borer)) + return I + + return 0 + +/mob/living/carbon/human/proc/spawn_larvae() + set category = "Alien" + set name = "Reproduce" + set desc = "Spawn several young." + + var/mob/living/simple_animal/borer/B = has_brain_worms() + + if(!B) + return + + if(B.chemicals >= 100) + src << "\red Your host twitches and quivers as you rapdly excrete several larvae from your sluglike body." + visible_message("\red [src] heaves violently, expelling a rush of vomit and a wriggling, sluglike creature!") + B.chemicals -= 100 + + new /obj/effect/decal/cleanable/vomit(get_turf(src)) + playsound(loc, 'sound/effects/splat.ogg', 50, 1) + new /mob/living/simple_animal/borer(get_turf(src)) + + else + src << "You do not have enough chemicals stored to reproduce." + return \ No newline at end of file diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index b7ac6233556..a73e7230bf1 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -384,6 +384,12 @@ var/list/department_radio_keys = list( if(understood || P.universal_speak) //world << "[M] understood [src] (1)." heard_a += M + else if(istype(P,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = P + if(H.has_brain_worms()) // Brain worms act like Babelfish. + heard_a += M + else + heard_b += M else //world << "[M] didn't understand [src]." heard_b += M diff --git a/code/modules/mob/living/simple_animal/borer.dm b/code/modules/mob/living/simple_animal/borer.dm new file mode 100644 index 00000000000..46ff70493c9 --- /dev/null +++ b/code/modules/mob/living/simple_animal/borer.dm @@ -0,0 +1,268 @@ +/mob/living/captive_brain + name = "host brain" + real_name = "host brain" + +/mob/living/captive_brain/say(var/message) + + if (src.client) + if(client.prefs.muted & MUTE_IC) + src << "\red You cannot speak in IC (muted)." + return + if (src.client.handle_spam_prevention(message,MUTE_IC)) + return + + if(istype(src.loc,/mob/living/simple_animal/borer)) + var/mob/living/simple_animal/borer/B = src.loc + src << "You whisper silently, \"[message]\"" + B.host << "The captive mind of [src] whispers, \"[message]\"" + +/mob/living/captive_brain/emote(var/message) + return + +/mob/living/simple_animal/borer + name = "cortical borer" + real_name = "cortical borer" + desc = "A small, quivering sluglike creature." + speak_emote = list("chirrups") + emote_hear = list("chirrups") + response_help = "pokes the" + response_disarm = "prods the" + response_harm = "stomps on the" + icon_state = "brainslug" + icon_living = "brainslug" + icon_dead = "brainslug_dead" + speed = 5 + a_intent = "harm" + stop_automated_movement = 1 + status_flags = CANPUSH + attacktext = "nips" + friendly = "prods" + wander = 0 + + 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/bonded // Var for full bonding with the host brain. + var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. + var/controlling // Used in human death check. + +/mob/living/simple_animal/borer/Life() + + ..() + if(host) + if(!stat && !host.stat) + if(chemicals < 250) + chemicals++ + if(controlling) + if(prob(5)) + host.adjustBrainLoss(rand(1,2)) + + if(prob(host.brainloss/20)) + host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_s","gasp"))]") + + //if(host.brainloss > 100) + +/mob/living/simple_animal/borer/New() + ..() + truename = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]" + host_brain = new/mob/living/captive_brain(src) + + for(var/mob/M in player_list) + if(istype(M,/mob/dead/observer)) + var/mob/dead/observer/O = M + if(O.client) + if(O.client.prefs.be_special & BE_ALIEN) + src.ckey = O.ckey + break + + +/mob/living/simple_animal/borer/say(var/message) + + message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) + message = capitalize(message) + + if(!message) + return + + if (stat == 2) + return say_dead(message) + + if (stat) + return + + if (src.client) + if(client.prefs.muted & MUTE_IC) + src << "\red You cannot speak in IC (muted)." + return + if (src.client.handle_spam_prevention(message,MUTE_IC)) + return + + if (copytext(message, 1, 2) == "*") + return emote(copytext(message, 2)) + + if (copytext(message, 1, 2) == ";") //Brain borer hivemind. + return borer_speak(message) + + if(!host) + src << "You have no host to speak to." + return //No host, no audible speech. + + src << "You drop words into [host]'s mind: \"[message]\"" + host << "Your own thoughts speak: \"[message]\"" + +/mob/living/simple_animal/borer/proc/borer_speak(var/message) + if(!message) + return + + for(var/mob/M in mob_list) + if(M.mind && (istype(M, /mob/living/simple_animal/borer) || istype(M, /mob/dead/observer))) + M << "Cortical link, [truename]: [copytext(message, 2)]" + +/mob/living/simple_animal/borer/verb/bond_brain() + set category = "Alien" + set name = "Assume Control" + set desc = "Fully connect to the brain of your host." + + if(!host) + src << "You are not inside a host body." + return + + if(src.stat) + src << "You cannot do that in your current state." + return + + src << "You begin delicately adjusting your connection to the host brain..." + + spawn(300+(host.brainloss*5)) + + if(!host || !src) return + + else + src << "\red You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system." + host << "\red You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours." + + host_brain.ckey = host.ckey + host.ckey = src.ckey + controlling = 1 + + host.verbs += /mob/living/carbon/human/proc/release_control + host.verbs += /mob/living/carbon/human/proc/punish_host + host.verbs += /mob/living/carbon/human/proc/spawn_larvae + +mob/living/simple_animal/borer/verb/release_host() + set category = "Alien" + set name = "Release Host" + set desc = "Slither out of your host." + + if(!host) + src << "You are not inside a host body." + return + + if(stat) + src << "You cannot leave your host in your current state." + + + if(!host || !src) return + + src << "You begin disconnecting from [host]'s synapses and prodding at their internal ear canal." + bonded = 0 + + if(!host.stat) + host << "An odd, uncomfortable pressure begins to build inside your skull, behind your ear..." + + spawn(200) + + if(!host || !src) return + + if(src.stat) + src << "You cannot infest a target in your current state." + return + + src << "You wiggle out of [host]'s ear and plop to the ground." + if(!host.stat) + host << "Something slimy wiggles out of your ear and plops to the ground!" + + detatch() + +mob/living/simple_animal/borer/proc/detatch() + + if(!host) return + + var/datum/organ/external/head = host.get_organ("head") + head.implants -= src + src.loc = get_turf(host) + controlling = 0 + + host.verbs -= /mob/living/carbon/human/proc/release_control + host.verbs -= /mob/living/carbon/human/proc/punish_host + host.verbs -= /mob/living/carbon/human/proc/spawn_larvae + + if(host_brain.ckey) + src.ckey = host.ckey + host.ckey = host_brain.ckey + host_brain.ckey = null + host_brain.name = "host brain" + host_brain.real_name = "host brain" + + host = null + +/mob/living/simple_animal/borer/verb/infest(var/mob/living/carbon/human/H) + set category = "Alien" + set name = "Infest" + set desc = "Infest a suitable humanoid host." + + if(host) + src << "You are already within a host." + return + + if(stat) + src << "You cannot infest a target in your current state." + return + + var/list/choices = list() + for(var/mob/living/C in view(1,src)) + if(istype(C,/mob/living/carbon/human) && C.stat != 2) + choices += C + + var/mob/living/carbon/human/M = input(src,"Who do you wish to infest?") in null|choices + + if(!M || !src) return + + if(M.has_brain_worms()) + src << "You cannot infest someone who is already infested!" + + M << "Something slimy begins probing at the opening of your ear canal..." + src << "You slither up [M] and begin probing at their ear canal..." + + if(!do_after(src,50)) + src << "As [M] moves away, you are dislodged and fall to the ground." + return + + if(!M || !src) return + + if(src.stat) + src << "You cannot infest a target in your current state." + return + + if(M.stat == 2) + src << "That is not an appropriate target." + return + + if(M in view(1, src)) + src << "You wiggle into [M]'s ear." + if(!M.stat) + M << "Something disgusting and slimy wiggles into your ear!" + + src.host = M + var/datum/organ/external/head = M.get_organ("head") + head.implants += src + src.loc = M + bonded = 0 + + host_brain.name = M.name + host_brain.real_name = M.real_name + + return + else + src << "They are no longer in range!" + return \ No newline at end of file diff --git a/code/modules/mob/screen.dm b/code/modules/mob/screen.dm index 514872db379..c83ba829a9d 100644 --- a/code/modules/mob/screen.dm +++ b/code/modules/mob/screen.dm @@ -606,6 +606,33 @@ var/mob/living/L = usr + //Resisting control by an alien mind. + 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 + + H << "\red You begin doggedly resisting the parasite's control (this will take approximately sixty seconds)." + B.host << "\red You feel the captive mind of [src] begin to resist your control." + + spawn(rand(350,450)+B.host.brainloss) + + if(!B || !B.controlling) + return + + B.host.adjustBrainLoss(rand(5,10)) + H << "\red With an immense exertion of will, you regain control of your body!" + B.host << "\red You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you." + B.controlling = 0 + + B.ckey = B.host.ckey + B.host.ckey = H.ckey + + H.ckey = null + H.name = "host brain" + H.real_name = "host brain" + + return + //resisting grabs (as if it helps anyone...) if ((!( L.stat ) && L.canmove && !( L.restrained() ))) var/resisting = 0 diff --git a/code/modules/surgery/implant.dm b/code/modules/surgery/implant.dm index 0d67881f242..7e240f737e3 100644 --- a/code/modules/surgery/implant.dm +++ b/code/modules/surgery/implant.dm @@ -166,7 +166,7 @@ if (affected.implants.len) - var/obj/item/weapon/obj = affected.implants[1] + var/obj/item/obj = affected.implants[1] if(istype(obj,/obj/item/weapon/implant)) var/obj/item/weapon/implant/imp = obj @@ -181,6 +181,11 @@ user.visible_message("\blue [user] takes something out of incision on [target]'s [affected.display_name] with \the [tool].", \ "\blue You take [obj] out of incision on [target]'s [affected.display_name]s with \the [tool]." ) affected.implants -= obj + + //Handle possessive brain borers. + if(istype(obj,/mob/living/simple_animal/borer)) + target.release_control() + obj.loc = get_turf(target) if(istype(obj,/obj/item/weapon/implant)) var/obj/item/weapon/implant/imp = obj diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi index 88fb1e19fcf..e269d169aa7 100644 Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ
Ninjas