WHOOOAAAAA BORER REWORK

```yaml
changes:
- rscadd: "Borers start with full control after infesting their host."
- rscadd: "'Kill Host' verb added to borers.  A reason is required to kill the host, and is logged and sent to the host."
- rscadd: "'Punish Host' verb added to borers: adds brain damage."
- rscadd: "Ghosts can see who a borer's host is."
- rscadd: "Borer chat and thoughtspeak is now logged and presented to admins."
- rscadd: "Borers are easier to manage with admin tools (Player Panel)."
- rscadd: "Borers can be safely detached from a host by admins."
- rscadd: "Borers have more control over what dosage of chems they use."
- bugfix: "More reliable detaching, with several failover checks in case of problems."
- bugfix: "Only verbs you can use are presented when attached or detached"
- tweak: "'Release Host' renamed to 'Abandon Host' for clarity."
- tweak: "Borers are no longer completely ejected after failing to maintain control of their hosts."
- tweak: "Borers: Alkysine is now free to use."
```
This commit is contained in:
Rob Nelson
2014-11-10 17:23:39 -08:00
parent fd55d36e46
commit 9bffdfd6f4
8 changed files with 325 additions and 84 deletions

View File

@@ -95,6 +95,9 @@ var/global/floorIsLava = 0
<A href='?src=\ref[src];subtlemessage=\ref[M]'>Subtle message</A>
"}
// Mob-specific controls.
body += M.player_panel_controls(usr)
if (M.client)
if(!istype(M, /mob/new_player))
@@ -1026,6 +1029,10 @@ var/global/floorIsLava = 0
if (ticker.mode.config_tag == "changeling")
return 2
return 1
if(M.mind in ticker.mode.borers)
if (ticker.mode.config_tag == "borer")
return 2
return 1
for(var/datum/disease/D in M.viruses)
if(istype(D, /datum/disease/jungle_fever))

View File

@@ -256,6 +256,8 @@
else if(isanimal(M)) //simple animals
if(iscorgi(M))
M_job = "Corgi"
else if(isborer(M))
M_job = "Borer"
else
M_job = "Animal"
@@ -354,6 +356,8 @@
dat += "<td>Monkey</td>"
else if(isalien(M))
dat += "<td>Alien</td>"
else if(isborer(M))
dat += "<td>Borer</td>"
else
dat += "<td>Unknown</td>"

View File

@@ -583,12 +583,16 @@
set name = "Release Control"
set desc = "Release control of your host's body."
do_release_control(0)
/mob/living/carbon/proc/do_release_control(var/rptext=1)
var/mob/living/simple_animal/borer/B = has_brain_worms()
if(!B)
return
if(B.controlling)
if(rptext)
src << "\red <B>You withdraw your probosci, releasing control of [B.host_brain]</B>"
B.host_brain << "\red <B>Your vision swims as the alien parasite releases control of your body.</B>"
B.ckey = ckey

View File

@@ -572,7 +572,8 @@
H << "\red <B>With an immense exertion of will, you regain control of your body!</B>"
B.host << "\red <B>You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you.</b>"
B.detach()
var/mob/living/carbon/C=L
C.do_release_control(0) // Was detach().
verbs -= /mob/living/carbon/proc/release_control
verbs -= /mob/living/carbon/proc/punish_host

View File

@@ -16,13 +16,55 @@
src << "You whisper silently, \"[message]\""
B.host << "The captive mind of [src] whispers, \"[message]\""
for(var/mob/M in mob_list)
if(M.mind && (istype(M, /mob/dead/observer)))
M << "<i>Thought-speech, <b>[src]</b> -> <b>[B.truename]:</b> [message]</i>"
log_say("THOUGHTSPEECH: [key_name(src)] -> [key_name(B)]: [message]")
for(var/mob/M in player_list)
if(istype(M, /mob/new_player))
continue
if(istype(M,/mob/dead/observer) && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS))
var/controls = "<a href='byond://?src=\ref[M];follow2=\ref[M];follow=\ref[src]'>Follow</a>"
if(M.client.holder)
controls+= " | <A HREF='?_src_=holder;adminmoreinfo=\ref[src]'>?</A>"
var/rendered="<span class='thoughtspeech'>Thought-speech, <b>[src.name]</b> ([controls]) -> <b>[B.truename]:</b> [copytext(message, 2)]</span>"
M.show_message(rendered, 2) //Takes into account blindness and such.
/mob/living/captive_brain/emote(var/message)
return
var/global/list/borer_attached_verbs = list(
/mob/living/simple_animal/borer/proc/bond_brain,
/mob/living/simple_animal/borer/proc/borer_speak,
/mob/living/simple_animal/borer/proc/kill_host,
/mob/living/simple_animal/borer/proc/damage_brain,
/mob/living/simple_animal/borer/proc/secrete_chemicals,
/mob/living/simple_animal/borer/proc/abandon_host,
)
var/global/list/borer_detached_verbs = list(
/mob/living/simple_animal/borer/proc/infest,
/mob/living/simple_animal/borer/proc/ventcrawl,
/mob/living/simple_animal/borer/proc/hide,
)
/datum/borer_chem
var/name = ""
var/cost = 1 // Per unit delivered.
var/dose_size = 15
/datum/borer_chem/bicaridine
name = "bicaridine"
/datum/borer_chem/tramadol
name = "tramadol"
/datum/borer_chem/alkysine
name = "alkysine"
cost = 0
/datum/borer_chem/hyperzine
name = "hyperzine"
var/global/borer_chem_types = typesof(/datum/borer_chem) - /datum/borer_chem
/mob/living/simple_animal/borer
name = "cortical borer"
real_name = "cortical borer"
@@ -51,22 +93,7 @@
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.
/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)
var/list/avail_chems=list()
/mob/living/simple_animal/borer/New(var/loc,var/by_gamemode=0)
..(loc)
@@ -84,6 +111,73 @@
message_admins("[src.name] self-deleting due to lack of appropriate ghosts.")
del(src)
transfer_personality(O.client)
update_verbs(0)
for(var/datum/borer_chem/chemtype in borer_chem_types)
var/datum/borer_chem/C = new chemtype()
avail_chems[C.name]=C
/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"))]")
/mob/living/simple_animal/borer/proc/update_verbs(var/attached)
if(attached)
client.verbs += borer_attached_verbs
client.verbs -= borer_detached_verbs
else
client.verbs -= borer_attached_verbs
client.verbs += borer_detached_verbs
/mob/living/simple_animal/borer/player_panel_controls(var/mob/user)
var/html="<h2>[src] Controls</h2>"
if(host)
html +="<b>Host:</b> [host] (<A HREF='?_src_=holder;adminmoreinfo=\ref[host]'>?</A> | <a href='?_src_=vars;mob_player_panel=\ref[host]'>PP</a>)"
else
html += "<em>No host</em>"
html += "<ul>"
if(check_rights(R_FUN))
if(host)
html += "<li><a href=\"?src=\ref[src]&act=add_chem\"Give Chem</a></li>" // PARTY SLUG
if(check_rights(R_ADMIN|R_MOD))
if(host)
html += "<li><a href=\"?src=\ref[src]&act=detach\">Detach</a></li>"
if(controlling)
html += "<li><a href=\"?src=\ref[src]&act=release\">Release Control</a></li>"
return html + "</ul>"
/mob/living/simple_animal/borer/Topic(href, href_list)
if(!check_rights(R_ADMIN|R_MOD))
usr << "<span class='danger'>Hell no.</span>"
return
switch(href["act"])
if("detach")
src << "<span class='danger'>You feel dazed, and then appear outside of your host!</span>"
if(host)
host << "<span class='info'>You no longer feel the presence in your mind!</span>"
detach()
if("release")
if(host && controlling)
host.release_control()
if("add_chem")
var/chemID = input("Chem name (ex: creatine):","Chemicals") as text|null
if(isnull(chemID))
return
var/datum/borer_chem/C = new
C.name=chemID
C.cost=1
avail_chems[C.name]=C
usr << "ADDED!"
/mob/living/simple_animal/borer/say(var/message)
@@ -120,9 +214,23 @@
src << "You drop words into [host]'s mind: \"[message]\""
host << "Your own thoughts speak: \"[message]\""
log_say("THOUGHTSPEECH: [truename] ([key_name(src)]) -> [host] ([key_name(host)]): [message]")
for(var/mob/M in player_list)
if(istype(M, /mob/new_player))
continue
if(istype(M,/mob/dead/observer) && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS))
var/controls = "<a href='byond://?src=\ref[M];follow2=\ref[M];follow=\ref[src]'>Follow</a>"
if(M.client.holder)
controls+= " | <A HREF='?_src_=holder;adminmoreinfo=\ref[src]'>?</A>"
var/rendered="<span class='thoughtspeech'>Thought-speech, <b>[truename]</b> ([controls]) -> <b>[host]:</b> [copytext(message, 2)]</span>"
M.show_message(rendered, 2) //Takes into account blindness and such.
/*
for(var/mob/M in mob_list)
if(M.mind && (istype(M, /mob/dead/observer)))
M << "<i>Thought-speech, <b>[truename]</b> -> <b>[host]:</b> [copytext(message, 2)]</i>"
*/
/mob/living/simple_animal/borer/Stat()
..()
@@ -143,11 +251,19 @@
if(!message)
return
log_say("CORTICAL: [key_name(src)]: [message]")
for(var/mob/M in mob_list)
if(M.mind && (istype(M, /mob/living/simple_animal/borer) || istype(M, /mob/dead/observer)))
M << "<i>Cortical link, <b>[truename]:</b> [copytext(message, 2)]</i>"
var/controls = ""
if(isobserver(M))
controls = " (<a href='byond://?src=\ref[M];follow2=\ref[M];follow=\ref[src]'>Follow</a>"
if(M.client.holder)
controls+= " | <A HREF='?_src_=holder;adminmoreinfo=\ref[src]'>?</A>"
controls += ") in [host]"
M << "<i>Cortical link, <b>[truename]</b>[controls]: [copytext(message, 2)]</i>"
/mob/living/simple_animal/borer/verb/bond_brain()
/mob/living/simple_animal/borer/proc/bond_brain()
set category = "Alien"
set name = "Assume Control"
set desc = "Fully connect to the brain of your host."
@@ -164,9 +280,15 @@
spawn(300+(host.brainloss*5))
if(!host || !src || controlling) return
if(!host || !src || controlling)
return
else
do_bonding(rptext=1)
/mob/living/simple_animal/borer/proc/do_bonding(var/rptext=0)
if(!host || !src || controlling)
return
src << "\red <B>You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system.</B>"
host << "\red <B>You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours.</B>"
@@ -178,10 +300,13 @@
host.verbs += /mob/living/carbon/proc/punish_host
host.verbs += /mob/living/carbon/proc/spawn_larvae
/mob/living/simple_animal/borer/verb/secrete_chemicals()
/**
* Kill switch for shit hosts.
*/
/mob/living/simple_animal/borer/proc/kill_host()
set category = "Alien"
set name = "Secrete Chemicals"
set desc = "Push some chemicals into your host's bloodstream."
set name = "Kill Host"
set desc = "Give the host massive brain damage, killing them nearly instantly."
if(!host)
src << "You are not inside a host body."
@@ -189,63 +314,139 @@
if(stat)
src << "You cannot secrete chemicals in your current state."
if(chemicals < 50)
src << "You don't have enough chemicals!"
var/chem = input("Select a chemical to secrete.", "Chemicals") in list("bicaridine","tramadol","hyperzine","alkysine")
if(chemicals < 50 || !host || controlling || !src || stat) //Sanity check.
return
src << "\red <B>You squirt a measure of [chem] from your reservoirs into [host]'s bloodstream.</B>"
host.reagents.add_reagent(chem, 15)
chemicals -= 50
var/reason = sanitize(input(usr,"Please enter a brief reason for killing the host, or press cancel.\n\nThis will be logged, and presented to the host.","Oh snap") as null|text, MAX_MESSAGE_LEN)
if(isnull(reason) || reason=="")
return
/mob/living/simple_animal/borer/verb/release_host()
src << "<span class='danger'>You thrash your probosci around the host's brain, triggering massive brain damage and stopping your host's heart.</span>"
host << "<span class='sinister'>You get a splitting headache, and then, as blackness descends upon you, you hear: [reason]</span>"
spawn(10)
if(!host || !src || stat)
return
host.adjustBrainLoss(100)
if(host.stat != DEAD)
host.death(0)
host.attack_log += "\[[time_stamp()]\]<font color='red'>Killed by an unhappy borer: [key_name(src)] Reason: [reason]</font>"
message_admins("Borer [key_name_admin(src)] killed [key_name_admin(host)] for reason: [reason]")
detach()
/mob/living/simple_animal/borer/proc/damage_brain()
set category = "Alien"
set name = "Release Host"
set desc = "Slither out of your host."
set name = "Retard Host"
set desc = "Give the host a bit of brain damage. Can be healed with alkysine."
if(!host)
src << "You are not inside a host body."
return
if(stat)
src << "You cannot leave your host in your current state."
src << "You cannot secrete chemicals in your current state."
return
src << "<span class='danger'>You twitch your probosci.</span>"
host << "<span class='sinister'>You feel something twitch, and get a headache.</span>"
host.adjustBrainLoss(15)
if(!host || !src) return
/mob/living/simple_animal/borer/proc/secrete_chemicals()
set category = "Alien"
set name = "Secrete Chemicals"
set desc = "Push some chemicals into your host's bloodstream."
src << "You begin disconnecting from [host]'s synapses and prodding at their internal ear canal."
if(!host)
src << "<span class='warning'>You are not inside a host body.</span>"
return
if(stat)
src << "<span class='warning'>You cannot secrete chemicals in your current state.</span>"
return
if(controlling)
src << "<span class='warning'>You're too busy controlling your host.</span>"
return
var/chemID = input("Select a chemical to secrete.", "Chemicals") in list("bicaridine","tramadol","hyperzine","alkysine")
var/datum/borer_chem/chem = avail_chems[chemID]
var/max_amount = 50
if(chem.cost>0)
max_amount = round(chemicals / chem.cost)
if(max_amount==0)
src << "<span class='warning'>You don't have enough energy to even synthesize one unit!</span>"
return
var/units = input("Enter dosage in units.\n\nMax: [max_amount]\nCost: [chem.cost]/unit","Chemicals") as num
units = round(units)
if(units < 1)
src << "<span class='warning'>You cannot synthesize this little.</span>"
return
if(chemicals < chem.cost*units)
src << "<span class='warning'>You don't have enough energy to synthesize this much!</span>"
return
if(!host || controlling || !src || stat) //Sanity check.
return
src << "<span class='info'>You squirt a measure of [chem.name] from your reservoirs into [host]'s bloodstream.</span>"
host.reagents.add_reagent(chem.name, units)
chemicals -= chem.cost*units
/mob/living/simple_animal/borer/proc/abandon_host()
set category = "Alien"
set name = "Abandon Host"
set desc = "Slither out of your host."
if(!host)
src << "<span class='warning'>You are not inside a host body.</span>"
return
if(stat)
src << "<span class='warning'>You cannot leave your host in your current state.</span>"
return
if(!src)
return
src << "<span class='info'>You begin disconnecting from [host]'s synapses and prodding at their internal ear canal.</span>"
spawn(200)
if(!host || !src) return
if(src.stat)
src << "You cannot infest a target in your current state."
src << "<span class='warning'>You cannot abandon [host] in your current state.</span>"
return
src << "You wiggle out of [host]'s ear and plop to the ground."
src << "<span class='info'>You wiggle out of [host]'s ear and plop to the ground.</span>"
detach()
// Try to reset everything, also while handling invalid host/host_brain states.
mob/living/simple_animal/borer/proc/detach()
if(!host) return
if(host)
if(istype(host,/mob/living/carbon/human))
var/mob/living/carbon/human/H = host
var/datum/organ/external/head = H.get_organ("head")
head.implants -= src
src.loc = get_turf(host)
src.loc = get_turf(src)
controlling = 0
reset_view(null)
machine = null
if(host)
host.reset_view(null)
host.machine = null
@@ -253,7 +454,7 @@ mob/living/simple_animal/borer/proc/detach()
host.verbs -= /mob/living/carbon/proc/punish_host
host.verbs -= /mob/living/carbon/proc/spawn_larvae
if(host_brain.ckey)
if(host_brain && host_brain.ckey)
src.ckey = host.ckey
host.ckey = host_brain.ckey
host_brain.ckey = null
@@ -261,8 +462,9 @@ mob/living/simple_animal/borer/proc/detach()
host_brain.real_name = "host brain"
host = null
update_verbs(0)
/mob/living/simple_animal/borer/verb/infest()
/mob/living/simple_animal/borer/proc/infest()
set category = "Alien"
set name = "Infest"
set desc = "Infest a suitable humanoid host."
@@ -336,7 +538,13 @@ mob/living/simple_animal/borer/proc/detach()
host_brain.name = M.name
host_brain.real_name = M.real_name
/mob/living/simple_animal/borer/verb/ventcrawl()
// /vg/ - Our users are shit, so we start with control over host.
// TODO: Config value.
do_bonding(rptext=1)
update_verbs(1)
/mob/living/simple_animal/borer/proc/ventcrawl()
set name = "Crawl through Vent"
set desc = "Enter an air vent and crawl through the pipe system."
set category = "Alien"
@@ -382,7 +590,7 @@ mob/living/simple_animal/borer/proc/detach()
return
//copy paste from alien/larva, if that func is updated please update this one alsoghost
/mob/living/simple_animal/borer/verb/hide()
/mob/living/simple_animal/borer/proc/hide()
set name = "Hide"
set desc = "Allows to hide beneath tables or certain items. Toggled on or off."
set category = "Alien"
@@ -403,9 +611,10 @@ mob/living/simple_animal/borer/proc/request_player()
//testing("Client of [G] inexistent")
continue
if(G.client.holder)
#warning Uncomment me.
//if(G.client.holder)
//testing("Client of [G] is admin.")
continue
//continue
if(jobban_isbanned(G, "Syndicate"))
//testing("[G] is jobbanned.")
@@ -414,7 +623,7 @@ mob/living/simple_animal/borer/proc/request_player()
candidates += G
if(!candidates.len)
message_admins("Unable to find a mind for [src.name]")
//message_admins("Unable to find a mind for [src.name]")
return 0
shuffle(candidates)
@@ -424,15 +633,6 @@ mob/living/simple_animal/borer/proc/request_player()
return 0
mob/living/simple_animal/borer/proc/question(var/client/C)
spawn(0)
if(!C) return
var/response = alert(C, "A cortical borer needs a player. Are you interested?", "Cortical borer request", "Yes", "No", "Never for this round")
if(!C || ckey)
return
if(response == "Yes")
transfer_personality(C)
mob/living/simple_animal/borer/proc/transfer_personality(var/client/candidate)
if(!candidate)

View File

@@ -24,6 +24,12 @@
/mob/proc/generate_name()
return name
/**
* Player panel controls for this mob.
*/
/mob/proc/player_panel_controls(var/mob/user)
return ""
/mob/proc/Cell()
set category = "Admin"
set hidden = 1

View File

@@ -115,6 +115,9 @@
return 1
return 0
/proc/isborer(A)
return istype(A, /mob/living/simple_animal/borer)
/proc/isshade(A)
if(istype(A, /mob/living/simple_animal/shade))
return 1

View File

@@ -0,0 +1,16 @@
author: N3X15
delete-after: true
changes:
- rscadd: "Borers start with full control after infesting their host."
- rscadd: "'Kill Host' verb added to borers. A reason is required to kill the host, and is logged and sent to the host."
- rscadd: "'Punish Host' verb added to borers: adds brain damage."
- rscadd: "Ghosts can see who a borer's host is."
- rscadd: "Borer chat and thoughtspeak is now logged and presented to admins."
- rscadd: "Borers are easier to manage with admin tools (Player Panel)."
- rscadd: "Borers can be safely detached from a host by admins."
- rscadd: "Borers have more control over what dosage of chems they use."
- bugfix: "More reliable detaching, with several failover checks in case of problems."
- bugfix: "Only verbs you can use are presented when attached or detached"
- tweak: "'Release Host' renamed to 'Abandon Host' for clarity."
- tweak: "Borers are no longer completely ejected after failing to maintain control of their hosts."
- tweak: "Borers: Alkysine is now free to use."