diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 1372e99a8e..1b239f5fc6 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -76,6 +76,8 @@ #define isrevenant(A) (istype(A, /mob/living/simple_animal/revenant)) +#define isborer(A) (istype(A, /mob/living/simple_animal/borer)) + #define isbot(A) (istype(A, /mob/living/simple_animal/bot)) #define iscrab(A) (istype(A, /mob/living/simple_animal/crab)) diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 86fa509e30..cb4d552e5d 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -23,6 +23,7 @@ #define ROLE_REVENANT "revenant" #define ROLE_DEVIL "devil" #define ROLE_SERVANT_OF_RATVAR "servant of Ratvar" +#define ROLE_BORER "borer" //Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR. //The gamemode specific ones are just so the gamemodes can query whether a player is old enough @@ -44,6 +45,7 @@ GLOBAL_LIST_INIT(special_roles, list( ROLE_REVENANT, ROLE_ABDUCTOR = /datum/game_mode/abduction, ROLE_DEVIL = /datum/game_mode/devil, + ROLE_BORER, ROLE_SERVANT_OF_RATVAR = /datum/game_mode/clockwork_cult )) diff --git a/code/datums/mutations.dm b/code/datums/mutations.dm index 5b44fd966f..df124260fe 100644 --- a/code/datums/mutations.dm +++ b/code/datums/mutations.dm @@ -377,6 +377,9 @@ GLOBAL_LIST_EMPTY(mutations_list) time_coeff = 2 /datum/mutation/human/race/on_acquiring(mob/living/carbon/human/owner) + if(owner.has_brain_worms()) + to_chat(owner, "You feel something strongly clinging to your humanity!") + return if(..()) return . = owner.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE) diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 2821356db5..2e5854509a 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -162,11 +162,14 @@ var/image/holder = hud_list[STATUS_HUD] var/icon/I = icon(icon, icon_state, dir) var/virus_state = check_virus() + var/mob/living/simple_animal/borer/B = has_brain_worms() holder.pixel_y = I.Height() - world.icon_size if(status_flags & XENO_HOST) holder.icon_state = "hudxeno" else if(stat == DEAD || (status_flags & FAKEDEATH)) holder.icon_state = "huddead" + else if(has_brain_worms() && B != null && B.controlling) + holder.icon_state = "hudbrainworm" else if(virus_state == 2) holder.icon_state = "hudill" else if(virus_state == 1) diff --git a/code/game/gamemodes/antag_spawner_cit.dm b/code/game/gamemodes/antag_spawner_cit.dm index 1009d9706f..175ad0e8a4 100644 --- a/code/game/gamemodes/antag_spawner_cit.dm +++ b/code/game/gamemodes/antag_spawner_cit.dm @@ -4,6 +4,7 @@ obj/item/weapon/antag_spawner/syndi_borer desc = "Releases a modified cortical borer to assist the user." icon = 'icons/obj/device.dmi' //Temporary? Doesn't really look like a container for xenofauna... but IDK what else could work. icon_state = "locator" + var/polling = FALSE obj/item/weapon/antag_spawner/syndi_borer/spawn_antag(client/C, turf/T, mob/owner) var/mob/living/simple_animal/borer/syndi_borer/B = new /mob/living/simple_animal/borer/syndi_borer(T) @@ -31,14 +32,18 @@ obj/item/weapon/antag_spawner/syndi_borer/spawn_antag(client/C, turf/T, mob/owne if(used) to_chat(user, "[src] appears to be empty!") return 0 + if(polling == TRUE) + to_chat(user, "[src] is busy activating!") + return 0 return 1 /obj/item/weapon/antag_spawner/syndi_borer/attack_self(mob/user) if(!(check_usability(user))) return - + polling = TRUE var/list/borer_candidates = pollCandidatesForMob("Do you want to play as a syndicate cortical borer?", ROLE_BORER, null, ROLE_BORER, 150, src) if(borer_candidates.len) + polling = FALSE if(!(check_usability(user))) return used = 1 @@ -49,4 +54,5 @@ obj/item/weapon/antag_spawner/syndi_borer/spawn_antag(client/C, turf/T, mob/owne S.start() qdel(src) else + polling = FALSE to_chat(user, "Unable to connect to release specimen. Please wait and try again later or use the container on your uplink to get your points refunded.") \ No newline at end of file diff --git a/code/game/gamemodes/changeling/powers/panacea.dm b/code/game/gamemodes/changeling/powers/panacea.dm index a0c9562aa4..40ce63a60e 100644 --- a/code/game/gamemodes/changeling/powers/panacea.dm +++ b/code/game/gamemodes/changeling/powers/panacea.dm @@ -10,6 +10,17 @@ /obj/effect/proc_holder/changeling/panacea/sting_action(mob/user) to_chat(user, "We cleanse impurities from our form.") + + var/mob/living/simple_animal/borer/B = user.has_brain_worms() + if(B) + if(B.controlling) + B.detatch() + B.leave_victim() + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.vomit(0, toxic = TRUE) + to_chat(user, "A parasite exits our form.") + var/list/bad_organs = list( user.getorgan(/obj/item/organ/body_egg), user.getorgan(/obj/item/organ/zombie_infection)) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index b906386d65..349656d48b 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -269,6 +269,42 @@ if(cult.len && !istype(SSticker.mode,/datum/game_mode/cult)) datum_cult_completion() + + if(GLOB.borers.len) + var/borerwin = FALSE + var/borertext = "
The borers were:" + for(var/mob/living/simple_animal/borer/B in GLOB.borers) + if((B.key || B.controlling) && B.stat != DEAD) + borertext += "
[B.controlling ? B.victim.key : B.key] was [B.truename] (" + var/turf/location = get_turf(B) + if(location.z == ZLEVEL_CENTCOM && B.victim) + borertext += "escaped with host" + else + borertext += "failed" + borertext += ")" + to_chat(world, borertext) + + var/total_borers = 0 + for(var/mob/living/simple_animal/borer/B in GLOB.borers) + if((B.key || B.victim) && B.stat != DEAD) + total_borers++ + if(total_borers) + var/total_borer_hosts = 0 + for(var/mob/living/carbon/C in GLOB.mob_list) + var/mob/living/simple_animal/borer/D = C.has_brain_worms() + var/turf/location = get_turf(C) + if(location.z == ZLEVEL_CENTCOM && D && D.stat != DEAD) + total_borer_hosts++ + if(GLOB.total_borer_hosts_needed <= total_borer_hosts) + borerwin = TRUE + to_chat(world, "There were [total_borers] borers alive at round end!") + to_chat(world, "A total of [total_borer_hosts] borers with hosts escaped on the shuttle alive. The borers needed [GLOB.total_borer_hosts_needed] hosts to escape.") + if(borerwin) + to_chat(world, "The borers were successful!") + else + to_chat(world, "The borers have failed!") + + CHECK_TICK return 0 diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm index 92cff03ec9..7a88a3306c 100644 --- a/code/game/gamemodes/miniantags/borer/borer.dm +++ b/code/game/gamemodes/miniantags/borer/borer.dm @@ -59,6 +59,7 @@ GLOBAL_VAR_INIT(total_borer_hosts_needed, 10) name = "cortical borer" real_name = "cortical borer" desc = "A small, quivering, slug-like creature." + icon = 'icons/mob/borer.dmi' icon_state = "brainslug" icon_living = "brainslug" icon_dead = "brainslug_dead" @@ -512,7 +513,7 @@ GLOBAL_VAR_INIT(total_borer_hosts_needed, 10) to_chat(src, "You focus your psychic lance on [M] and freeze their limbs with a wave of terrible dread.") to_chat(M, "You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing.") - M.Stun(3) + M.Stun(60) used_dominate = world.time @@ -613,9 +614,9 @@ GLOBAL_VAR_INIT(total_borer_hosts_needed, 10) victim.setToxLoss(0) victim.setOxyLoss(0) victim.setCloneLoss(0) - victim.SetParalysis(0) - victim.SetStunned(0) - victim.SetWeakened(0) + victim.SetUnconscious(0) + victim.SetStun(0) + victim.SetKnockdown(0) victim.radiation = 0 victim.heal_overall_damage(victim.getBruteLoss(), victim.getFireLoss()) victim.reagents.clear_reagents() @@ -768,11 +769,11 @@ GLOBAL_VAR_INIT(total_borer_hosts_needed, 10) switch(punishment) //Hardcoding this stuff. if("Blindness") - victim.blind_eyes(2) + victim.blind_eyes(4) if("Deafness") - victim.minimumDeafTicks(20) + victim.minimumDeafTicks(40) if("Stun") - victim.Weaken(10) + victim.Knockdown(100) log_game("[src]/([src.ckey]) punished [victim]/([victim.ckey] with [punishment]") diff --git a/code/game/gamemodes/miniantags/readme.txt b/code/game/gamemodes/miniantags/readme.txt index 8ade34bf2e..dc7bd5a66f 100644 --- a/code/game/gamemodes/miniantags/readme.txt +++ b/code/game/gamemodes/miniantags/readme.txt @@ -1,6 +1,7 @@ This folder contains all "mini-antagonists" - antagonists that can still spice up the round but aren't enough to be a roundtype in their own right. Currently, that list consists of: -Abductors + -Borers -Swarmers -Prophets of sin -The Jungle Fever virus (infected monkey bites human, human becomes another infected monkey) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 5b3dfbb994..70b6d54f73 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -120,6 +120,11 @@ MASS SPECTROMETER if(H.undergoing_cardiac_arrest() && H.stat != DEAD) to_chat(user, "Subject suffering from heart attack: Apply defibrillator immediately!") + if(iscarbon(M)) + var/mob/living/carbon/C = M + if(C.has_brain_worms()) + to_chat(user, "Foreign organism detected in subject's cranium. Recommended treatment: Dosage of sucrose solution and removal of object via surgery.") + to_chat(user, "Analyzing results for [M]:\n\tOverall status: [mob_status]") // Damage descriptions diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 66b17af462..ae37d0f9a1 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -835,6 +835,12 @@ else dat += "Abductor" + //Borer + if(jobban_isbanned(M, "borer") || isbanned_dept) + dat += "Borer" + else + dat += "Borer" + //Alien if(jobban_isbanned(M, "alien candidate") || isbanned_dept) dat += "Alien" diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm index ce8a6784dd..9a3b225726 100644 --- a/code/modules/atmospherics/machinery/atmosmachinery.dm +++ b/code/modules/atmospherics/machinery/atmosmachinery.dm @@ -280,7 +280,10 @@ Pipelines + Other Objects -> Pipe network return list() /obj/machinery/atmospherics/update_remote_sight(mob/user) - user.sight |= (SEE_TURFS|BLIND) + if(isborer(user)) + user.sight |= (SEE_PIXELS) + else + user.sight |= (SEE_TURFS|BLIND) //Used for certain children of obj/machinery/atmospherics to not show pipe vision when mob is inside it. /obj/machinery/atmospherics/proc/can_see_pipes() diff --git a/code/modules/client/verbs/suicide.dm b/code/modules/client/verbs/suicide.dm index 548fcf636d..cf0f9fe023 100644 --- a/code/modules/client/verbs/suicide.dm +++ b/code/modules/client/verbs/suicide.dm @@ -173,4 +173,7 @@ if(!canmove || restrained()) //just while I finish up the new 'fun' suiciding verb. This is to prevent metagaming via suicide to_chat(src, "You can't commit suicide whilst restrained! ((You can type Ghost instead however.))") return + if(has_brain_worms()) + to_chat(src, "You can't bring yourself to commit suicide!") + return return TRUE diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 30e7c843b0..6c03200869 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -27,7 +27,7 @@ Gunshots/explosions/opening doors/less rare audio (done) //Something's wrong here var/list/medium = list("fake_alert"=15,"items"=10,"items_other"=10,"dangerflash"=10,"bolts"=5,"flood"=5,"husks"=10,"battle"=15,"self_delusion"=10) //AAAAH - var/list/major = list("fake"=20,"death"=10,"xeno"=10,"singulo"=10,"delusion"=20,"koolaid"=10) + var/list/major = list("fake"=20,"death"=10,"xeno"=10,"singulo"=10,"borer"=10,"delusion"=20,"koolaid"=10) handling_hal = 1 while(hallucination > 20) @@ -217,6 +217,39 @@ Gunshots/explosions/opening doors/less rare audio (done) /obj/effect/hallucination/simple/clown/scary image_state = "scary_clown" +/obj/effect/hallucination/simple/borer + image_icon = 'icons/mob/borer.dmi' + image_state = "brainslug" + +/obj/effect/hallucination/borer + //A borer unconsciouss you and crawls in your ear + var/obj/machinery/atmospherics/components/unary/vent_pump/pump = null + var/obj/effect/hallucination/simple/borer/borer = null + +/obj/effect/hallucination/borer/Initialize(mapload, var/mob/living/carbon/T) + ..() + target = T + for(var/obj/machinery/atmospherics/components/unary/vent_pump/U in orange(7,target)) + if(!U.welded) + pump = U + break + if(pump) + borer = new(pump.loc,target) + for(var/i=0, i<11, i++) + walk_to(borer, get_step(borer, get_cardinal_dir(borer, T))) + if(borer.Adjacent(T)) + to_chat(T, "You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing.") + T.Stun(80) + sleep(50) + qdel(borer) + sleep(rand(60, 90)) + to_chat(T, "Primary [rand(1000,9999)] states: [pick("Hello","Hi","You're my slave now!","Don't try to get rid of me...")]") + break + sleep(4) + if(!QDELETED(borer)) + qdel(borer) + qdel(src) + /obj/effect/hallucination/simple/bubblegum name = "Bubblegum" image_icon = 'icons/mob/lavaland/96x96megafauna.dmi' @@ -726,6 +759,8 @@ Gunshots/explosions/opening doors/less rare audio (done) switch(hal_type) if("xeno") new /obj/effect/hallucination/xeno_attack(src.loc,src) + if("borer") + new /obj/effect/hallucination/borer(src.loc,src) if("singulo") new /obj/effect/hallucination/singularity_scare(src.loc,src) if("koolaid") diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css index ed77ea54a7..d554008c1f 100644 --- a/code/modules/goonchat/browserassets/css/browserOutput.css +++ b/code/modules/goonchat/browserassets/css/browserOutput.css @@ -339,6 +339,7 @@ h1.alert, h2.alert {color: #000000;} .alien {color: #543354;} .noticealien {color: #00c000;} .alertalien {color: #00c000; font-weight: bold;} +.borer {color: #543354; font-style: italic;} .changeling {color: #800080; font-style: italic;} .interface {color: #330033;} diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index 4b2fd44a49..2ee3007bb1 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -46,6 +46,10 @@ /obj/item/organ/brain/Remove(mob/living/carbon/C, special = 0) ..() + if(!special) + if(C.has_brain_worms()) + var/mob/living/simple_animal/borer/B = C.has_brain_worms() + B.leave_victim() //Should remove borer if the brain is removed - RR if(!gc_destroyed || (owner && !owner.gc_destroyed)) transfer_identity(C) C.update_hair() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 09486cce18..5ecc6260a5 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -84,6 +84,10 @@ stat("Tank Pressure", internal.air_contents.return_pressure()) stat("Distribution Pressure", internal.distribute_pressure) + var/mob/living/simple_animal/borer/B = has_brain_worms() + if(B && B.controlling) + stat("Chemicals", B.chemicals) + if(mind) if(mind.changeling) stat("Chemical Storage", "[mind.changeling.chem_charges]/[mind.changeling.chem_storage]") diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm index d014674144..9e79555383 100644 --- a/code/modules/surgery/organ_manipulation.dm +++ b/code/modules/surgery/organ_manipulation.dm @@ -77,6 +77,13 @@ else if(implement_type in implements_extract) current_type = "extract" var/list/organs = target.getorganszone(target_zone) + + var/mob/living/simple_animal/borer/B = target.has_brain_worms() + if(target.has_brain_worms()) + user.visible_message("[user] begins to extract [B] from [target]'s [parse_zone(target_zone)].", + "You begin to extract [B] from [target]'s [parse_zone(target_zone)]...") + return TRUE + if(!organs.len) to_chat(user, "There are no removeable organs in [target]'s [parse_zone(target_zone)]!") return -1 @@ -126,6 +133,13 @@ "You insert [tool] into [target]'s [parse_zone(target_zone)].") else if(current_type == "extract") + var/mob/living/simple_animal/borer/B = target.has_brain_worms() + if(B && B.victim == target) + user.visible_message("[user] successfully extracts [B] from [target]'s [parse_zone(target_zone)]!", + "You successfully extract [B] from [target]'s [parse_zone(target_zone)].") + add_logs(user, target, "surgically removed [B] from", addition="INTENT: [uppertext(user.a_intent)]") + B.leave_victim() + return FALSE if(I && I.owner == target) user.visible_message("[user] successfully extracts [I] from [target]'s [parse_zone(target_zone)]!", "You successfully extract [I] from [target]'s [parse_zone(target_zone)].") diff --git a/code/modules/uplink/uplink_item_cit.dm b/code/modules/uplink/uplink_item_cit.dm index 1ae0f1aef0..fbbab69031 100644 --- a/code/modules/uplink/uplink_item_cit.dm +++ b/code/modules/uplink/uplink_item_cit.dm @@ -1,4 +1,4 @@ -/*/datum/uplink_item/stealthy_tools/syndi_borer +/datum/uplink_item/stealthy_tools/syndi_borer name = "Syndicate Brain Slug" desc = "A small cortical borer, modified to be completely loyal to the owner. \ Genetically infertile, these brain slugs can assist medically in a support role, or take direct action \ @@ -7,7 +7,7 @@ refundable = TRUE cost = 10 surplus = 20 //Let's not have this be too common - exclude_modes = list(/datum/game_mode/nuclear) */ + exclude_modes = list(/datum/game_mode/nuclear) /datum/uplink_item/stealthy_tools/holoparasite name="Holoparasite Injector" diff --git a/icons/mob/borer.dmi b/icons/mob/borer.dmi new file mode 100644 index 0000000000..c0f047b222 Binary files /dev/null and b/icons/mob/borer.dmi differ diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm index 10dd0dd74d..6f38560b92 100644 --- a/interface/stylesheet.dm +++ b/interface/stylesheet.dm @@ -133,6 +133,7 @@ h1.alert, h2.alert {color: #000000;} .alien {color: #543354;} .noticealien {color: #00c000;} .alertalien {color: #00c000; font-weight: bold;} +.borer {color: #543354; font-style: italic;} .changeling {color: #800080; font-style: italic;} .interface {color: #330033;} diff --git a/tgstation.dme b/tgstation.dme index 1daecfda5c..791f198f67 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -384,6 +384,7 @@ #include "code\game\area\areas\space_content.dm" #include "code\game\gamemodes\antag_hud.dm" #include "code\game\gamemodes\antag_spawner.dm" +#include "code\game\gamemodes\antag_spawner_cit.dm" #include "code\game\gamemodes\events.dm" #include "code\game\gamemodes\factions.dm" #include "code\game\gamemodes\game_mode.dm" @@ -514,6 +515,12 @@ #include "code\game\gamemodes\miniantags\abduction\machinery\dispenser.dm" #include "code\game\gamemodes\miniantags\abduction\machinery\experiment.dm" #include "code\game\gamemodes\miniantags\abduction\machinery\pad.dm" +#include "code\game\gamemodes\miniantags\borer\borer.dm" +#include "code\game\gamemodes\miniantags\borer\borer_chemicals.dm" +#include "code\game\gamemodes\miniantags\borer\borer_event.dm" +#include "code\game\gamemodes\miniantags\borer\borer_html.dm" +#include "code\game\gamemodes\miniantags\borer\borer_topic.dm" +#include "code\game\gamemodes\miniantags\borer\syndi_borer.dm" #include "code\game\gamemodes\miniantags\bot_swarm\swarmer.dm" #include "code\game\gamemodes\miniantags\bot_swarm\swarmer_event.dm" #include "code\game\gamemodes\miniantags\monkey\monkey.dm"