Files
vgstation13/code/modules/mob/living/simple_animal/borer.dm
Anthony "Shifty Rail 189f77cbb7 Refactors player preferences for modularity + SQLite Unit Test (#37615)
* Pref code refactor

* Empty database reference

* Unit testing SQLite

* Everything else

* Disable unit testing.

* Equivalent

* more robust unit tests
2025-06-07 09:54:09 -04:00

1249 lines
43 KiB
Plaintext

#define BORER_MODE_DETACHED 0
#define BORER_MODE_SEVERED 1
#define BORER_MODE_ATTACHED_HEAD 2
#define BORER_MODE_ATTACHED_CHEST 3
#define BORER_MODE_ATTACHED_ARM 4
#define BORER_MODE_ATTACHED_LEG 5
#define BORER_CAN_ASSUME_CONTROL TRUE
var/global/borer_chem_types_head = typesof(/datum/borer_chem/head) - /datum/borer_chem - /datum/borer_chem/head
var/global/borer_chem_types_chest = typesof(/datum/borer_chem/chest) - /datum/borer_chem - /datum/borer_chem/chest
var/global/borer_chem_types_arm = typesof(/datum/borer_chem/arm) - /datum/borer_chem - /datum/borer_chem/arm
var/global/borer_chem_types_leg = typesof(/datum/borer_chem/leg) - /datum/borer_chem - /datum/borer_chem/leg
var/global/borer_unlock_types_head = typesof(/datum/unlockable/borer/head) - /datum/unlockable/borer - /datum/unlockable/borer/head - /datum/unlockable/borer/head/chem_unlock - /datum/unlockable/borer/head/verb_unlock
var/global/borer_unlock_types_chest = typesof(/datum/unlockable/borer/chest) - /datum/unlockable/borer - /datum/unlockable/borer/chest - /datum/unlockable/borer/chest/chem_unlock - /datum/unlockable/borer/chest/verb_unlock
var/global/borer_unlock_types_arm = typesof(/datum/unlockable/borer/arm) - /datum/unlockable/borer - /datum/unlockable/borer/arm - /datum/unlockable/borer/arm/chem_unlock - /datum/unlockable/borer/arm/verb_unlock
var/global/borer_unlock_types_leg = typesof(/datum/unlockable/borer/leg) - /datum/unlockable/borer - /datum/unlockable/borer/leg - /datum/unlockable/borer/leg/chem_unlock - /datum/unlockable/borer/leg/verb_unlock
/mob/living/simple_animal/borer
name = "borer"
real_name = "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 = 6
size = W_CLASS_TINY
min_tox = 0
max_tox = 0
density = 0
a_intent = I_HURT
stop_automated_movement = 1
status_flags = CANPUSH
attacktext = "nips"
friendly = "prods"
wander = 0
pass_flags = PASSTABLE
universal_understand=1
heat_damage_per_tick = 1
cold_damage_per_tick = 1
holder_type = /obj/item/weapon/holder/animal/borer
var/busy = 0 // So we aren't trying to lay many eggs at once.
var/chemicals = 10 // Chemicals used for reproduction and spitting neurotoxin.
var/mob/living/carbon/human/host // Human host for the brain worm.
var/hostlimb = null // Which limb of the host is inhabited by the borer.
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/host_name // Stores the old name of the host to revert to after namepick
var/controlling // Used in human death check.
var/list/avail_chems=list()
var/list/unlocked_chems_head=list()
var/list/unlocked_chems_chest=list()
var/list/unlocked_chems_arm=list()
var/list/unlocked_chems_leg=list()
var/list/avail_abilities=list() // Unlocked powers.
var/list/attached_verbs_head=list(/obj/item/verbs/borer/attached_head)
var/list/attached_verbs_chest=list(/obj/item/verbs/borer/attached_chest)
var/list/attached_verbs_arm=list(/obj/item/verbs/borer/attached_arm)
var/list/attached_verbs_leg=list(/obj/item/verbs/borer/attached_leg)
var/list/severed_verbs=list(/obj/item/verbs/borer/severed)
var/list/detached_verbs=list(/obj/item/verbs/borer/detached)
var/numChildren=0
var/datum/research_tree/borer/research
var/list/verb_holders = list()
var/list/borer_avail_unlocks_head = list()
var/list/borer_avail_unlocks_chest = list()
var/list/borer_avail_unlocks_arm = list()
var/list/borer_avail_unlocks_leg = list()
var/channeling = 0 //For abilities that require constant expenditure of chemicals.
var/channeling_brute_resist = 0
var/channeling_burn_resist = 0
var/channeling_speed_increase = 0
var/channeling_bone_talons = 0
var/channeling_bone_sword = 0
var/channeling_bone_shield = 0
var/channeling_bone_hammer = 0
var/channeling_bone_cocoon = 0
var/channeling_night_vision = 0
var/obj/item/weapon/gun/hookshot/flesh/extend_o_arm = null
var/extend_o_arm_unlocked = 0
// Event handles
var/eh_emote
var/static/list/name_prefixes = list("Primary","Secondary","Tertiary","Quaternary","Quinary","Senary","Septenary","Octonary","Nonary","Denary")
var/name_prefix_index = 1
held_items = list()
/mob/living/simple_animal/borer/check_environment_susceptibility()
return !host
/mob/living/simple_animal/borer/whisper()
return FALSE
/mob/living/simple_animal/borer/canEnterVentWith()
var/static/list/allowed_items = list(
/mob/living/captive_brain,
/obj/item/verbs/borer,
)
return allowed_items
/mob/living/simple_animal/borer/defected_borer
name = "special borer"
real_name = "special borer"
desc = "A slightly defected, yet incredibly happy little brainslug."
speak_emote = list("borks")
emote_hear = list("barks")
attacktext = "barks at"
friendly = "barks at"
icon_state = "bestborer"
icon_living = "bestborer"
icon_dead = "bestborer"
/mob/living/simple_animal/borer/defected_borer/singularity_act(current_size, obj/machinery/singularity/S)
if(S.modifier != "special_")
to_chat(world, "<span class='numb'><font size='15'>You feel Very Nice.</font></span>")
S.modifier = "special_"
S.name = "specialarity"
world << sound('sound/effects/explosionfar.ogg')
..()
/mob/living/simple_animal/borer/New(var/loc, var/egg_prefix_index = 1)
..(loc)
name_prefix_index = min(egg_prefix_index, 10)
truename = "[name_prefixes[name_prefix_index]] [capitalize(pick(borer_names))]"
host_brain = new/mob/living/captive_brain(src)
if(name == initial(name)) // Easier reporting of griff.
name = "[name] ([rand(1, 1000)])"
real_name = name
update_verbs(BORER_MODE_DETACHED)
research = new (src)
for(var/ultype in borer_unlock_types_head)
var/datum/unlockable/borer/head/U = new ultype()
if(U.id!="")
borer_avail_unlocks_head.Add(U)
for(var/ultype in borer_unlock_types_chest)
var/datum/unlockable/borer/chest/U = new ultype()
if(U.id!="")
borer_avail_unlocks_chest.Add(U)
for(var/ultype in borer_unlock_types_arm)
var/datum/unlockable/borer/arm/U = new ultype()
if(U.id!="")
borer_avail_unlocks_arm.Add(U)
for(var/ultype in borer_unlock_types_leg)
var/datum/unlockable/borer/leg/U = new ultype()
if(U.id!="")
borer_avail_unlocks_leg.Add(U)
extend_o_arm = new /obj/item/weapon/gun/hookshot/flesh(src, src)
/*
/mob/living/simple_animal/borer/Login()
..()
if(mind)
RemoveAllFactionIcons(mind)*/
/mob/living/simple_animal/borer/Life()
if(timestopped)
return 0 //under effects of time magick
..()
if(host)
if(!stat && !host.stat)
if(health < 20)
health += 0.5
if(chemicals < 250 && !channeling)
chemicals++
if(client)
regular_hud_updates()
standard_damage_overlay_updates()
/mob/living/simple_animal/borer/regular_hud_updates()
if(fire_alert)
throw_alert(SCREEN_ALARM_FIRE, fire_alert == 1 ? /obj/abstract/screen/alert/carbon/burn/ice : /obj/abstract/screen/alert/carbon/burn/fire, fire_alert) //fire_alert is either 0 if no alert, 1 for cold and 2 for heat.
else
clear_alert(SCREEN_ALARM_FIRE)
var/healthpercent = (health/maxHealth)*100
switch(healthpercent)
if(100 to INFINITY)
healths.icon_state = "borer_health0"
if(75 to 100)
healths.icon_state = "borer_health1"
if(50 to 75)
healths.icon_state = "borer_health2"
if(25 to 50)
healths.icon_state = "borer_health3"
if(1 to 25)
healths.icon_state = "borer_health4"
else
healths.icon_state = "borer_health5"
/mob/living/simple_animal/borer/proc/update_verbs(var/mode)
if(verb_holders.len>0)
for(var/VH in verb_holders)
qdel(VH)
verb_holders=list()
var/list/verbtypes = list()
avail_chems.len = 0
switch(mode)
if(BORER_MODE_DETACHED) // 0
verbtypes=detached_verbs
if(BORER_MODE_SEVERED) // 1
verbtypes=severed_verbs
if(BORER_MODE_ATTACHED_HEAD) // 2
verbtypes=attached_verbs_head
for(var/chemtype in borer_chem_types_head)
var/datum/borer_chem/C = new chemtype()
if(!C.unlockable)
avail_chems[C.name]=C
avail_chems += unlocked_chems_head
if(BORER_MODE_ATTACHED_CHEST) // 3
verbtypes=attached_verbs_chest
for(var/chemtype in borer_chem_types_chest)
var/datum/borer_chem/C = new chemtype()
if(!C.unlockable)
avail_chems[C.name]=C
avail_chems += unlocked_chems_chest
if(BORER_MODE_ATTACHED_ARM) // 4
verbtypes=attached_verbs_arm
for(var/chemtype in borer_chem_types_arm)
var/datum/borer_chem/C = new chemtype()
if(!C.unlockable)
avail_chems[C.name]=C
avail_chems += unlocked_chems_arm
if(BORER_MODE_ATTACHED_LEG) // 5
verbtypes=attached_verbs_leg
for(var/chemtype in borer_chem_types_leg)
var/datum/borer_chem/C = new chemtype()
if(!C.unlockable)
avail_chems[C.name]=C
avail_chems += unlocked_chems_leg
if(host && ismonkey(host) && BORER_CAN_ASSUME_CONTROL) //allow borers to control monkeys
to_chat(src, "<span class='danger'>This host appears sufficiently simple for you to assume control.</span>")
verb_holders+=new /obj/item/verbs/borer/special(src)
for(var/verbtype in verbtypes)
verb_holders+=new verbtype(src)
/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(user.check_rights(R_ADMIN))
html += "<li><a href=\"?src=\ref[src]&act=add_chem\">Give Chem</a></li>" // PARTY SLUG
html += "<li><a href=\"?src=\ref[src]&act=detach\">Detach</a></li>"
html += "<li><a href=\"?src=\ref[src]&act=verbs\">Resend Verbs</a></li>"
if(host)
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(!usr.check_rights(R_ADMIN))
to_chat(usr, "<span class='danger'>Hell no.</span>")
return
switch(href_list["act"])
if("detach")
to_chat(src, "<span class='danger'>You feel dazed, and then appear outside of your host!</span>")
if(host)
to_chat(host, "<span class='info'>You no longer feel the presence in your mind!</span>")
detach()
if("release")
if(host)
host.do_release_control()
if("verbs")
update_verbs(!isnull(host))
if("add_chem")
var/chemID = copytext(sanitize(input("Chem name (ex: creatine):","Chemicals") as text|null),1,MAX_NAME_LEN)
if(isnull(chemID))
return
var/datum/borer_chem/C = new /datum/borer_chem()
C.id=chemID
var/datum/reagent/chem = chemical_reagents_list[C.id]
C.name = chem.name
C.cost=0
avail_chems[C.name]=C
to_chat(usr, "ADDED!")
to_chat(src, "<span class='info'>You learned how to secrete [C.name]!</span>")
/mob/living/simple_animal/borer/handle_inherent_channels(var/datum/speech/speech, var/message_mode)
. = ..()
if(.)
return .
var/mob/living/simple_animal/borer/B = src
var/mob/living/carbon/human/host = B.host
if(host)
speech.message = trim(speech.message)
if (!speech.message)
return
var/encoded_message = html_encode(speech.message)
to_chat(src, "You drop words into [host]'s body: <span class='borer'>\"[encoded_message]\"</span>")
if(host.transmogged_to)
to_chat(host.transmogged_to, "<b>Something speaks within you:</b> <span class='borer'>\"[encoded_message]\"</span>")
else if(hostlimb == LIMB_HEAD)
to_chat(host, "<b>Your mind speaks to you:</b> <span class='borer'>\"[encoded_message]\"</span>")
else
to_chat(host, "<b>Your [limb_to_name(hostlimb)] speaks to you:</b> <span class='borer'>\"[encoded_message]\"</span>")
var/list/borers_in_host = host.get_brain_worms()
borers_in_host.Remove(src)
if(borers_in_host.len)
for(var/I in borers_in_host)
to_chat(I, "<b>[truename]</b> speaks from your host's [limb_to_name(hostlimb)]: <span class='borer'>\"[encoded_message]\"</span>")
var/turf/T = get_turf(src)
log_say("[truename] [key_name(src)] (@[T.x],[T.y],[T.z]) -> [host]([key_name(host)]) Borer->Host Speech: [encoded_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.get_pref(/datum/preference_setting/binary_flag/toggles) & CHAT_GHOSTEARS || (get_turf(src) in view(M))))
var/controls = formatFollow(src,"Follow")
if(M.client.holder)
controls+= " | <A HREF='?_src_=holder;adminmoreinfo=\ref[src]'>?</A>"
var/rendered="<span class='borer'>Thought-speech, <b>[truename]</b> ([controls]) in <b>[host]</b>'s [limb_to_name(hostlimb)]: [encoded_message]</span>"
M.show_message(rendered, 2) //Takes into account blindness and such.
return 1
else
to_chat(src, "You have no host to speak to.")
return 1 //this ensures we don't end up speaking out loud
/mob/living/simple_animal/borer/Stat()
..()
if(statpanel("Status"))
if(emergency_shuttle)
if(emergency_shuttle.online && emergency_shuttle.location < 2)
var/timeleft = emergency_shuttle.timeleft()
if (timeleft)
var/acronym = emergency_shuttle.location == 1 ? "ETD" : "ETA"
stat(null, "[acronym]-[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]")
stat("Health", health)
stat("Chemicals", chemicals)
/mob/living/simple_animal/borer/earprot()
if(host)
return host.earprot()
else
return ..()
/mob/living/simple_animal/borer/eyecheck()
if(host)
return host.eyecheck()
else
return ..()
// VERBS!
/obj/item/verbs/borer/special/verb/bond_brain()
set category = "Alien"
set name = "Assume Control"
set desc = "Fully connect to the brain of your host."
var/mob/living/simple_animal/borer/B=loc
if(!istype(B))
return
B.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."
if(!check_can_do())
return
if(hostlimb != LIMB_HEAD)
to_chat(src, "You are not attached to your host's brain.")
return
if(host.ckey || !istype(host, /mob/living/carbon/monkey))
to_chat(src, "<span class='danger'>The host consciousness resists your attempts to overwhelm it!</span>")
return
to_chat(src, "You begin delicately adjusting your connection to the host brain...")
var/mod = max(300 - host.brainloss, 0) //braindamaged hosts are overwhelmed faster
spawn(mod)
if(!host || !src || controlling)
return
else
do_bonding(rptext=1)
/mob/living/simple_animal/borer/proc/do_bonding(var/rptext=0)
if(!host || host.stat==DEAD || !src || research.unlocking)
return
if(host.ckey || !istype(host, /mob/living/carbon/monkey)) //check again just to be sure
to_chat(src, "<span class='danger'>You attempt to interface with the host's nervous system, but their consciousness resists!</span>")
return
if (rptext)
to_chat(src, "<span class='danger'>You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system.</span>")
to_chat(host, "<span class='danger'>You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours.</span>")
if(!controlling)
host_brain.ckey = host.ckey
host_brain.name = host.real_name
host.ckey = src.ckey
controlling = 1
var/newname
for(var/i = 1 to 3)
newname = reject_bad_name(stripped_input(host,"You may assume a new identity for the host you've infested. Enter a name, or cancel to keep your host's original name.", "Name change [4-i] [0-i != 1 ? "tries":"try"] left",""),1,MAX_NAME_LEN)
if(!newname || newname == "")
if(alert(host,"Are you sure you want to keep your host's original name?",,"Yes","No") == "Yes")
break
else
if(alert(host,"Do you really want the name:\n[newname]?",,"Yes","No") == "Yes")
break
if(newname)
host_name = host.real_name //store the old host name in the borer
host.fully_replace_character_name(null, newname)
host.verbs += /mob/living/proc/release_control
/* Broken
host.verbs += /mob/living/carbon/proc/punish_host
host.verbs += /mob/living/carbon/proc/spawn_larvae
*/
/**
* Kill switch for shit hosts.
*/
/mob/living/simple_animal/borer/proc/kill_host()
set category = "Alien"
set name = "Kill Host"
set desc = "Give the host massive brain damage, killing them nearly instantly."
if(!check_can_do())
return
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
to_chat(src, "<span class='danger'>You thrash your probosci around the host's brain, triggering massive brain damage and stopping your host's heart.</span>")
to_chat(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 = "Retard Host"
set desc = "Give the host a bit of brain damage. Can be healed with alkysine."
if(!check_can_do())
return
to_chat(src, "<span class='danger'>You twitch your probosci.</span>")
to_chat(host, "<span class='sinister'>You feel something twitch, and get a headache.</span>")
host.adjustBrainLoss(15)
/mob/living/simple_animal/borer/proc/evolve()
set category = "Alien"
set name = "Evolve"
set desc = "Upgrade yourself or your host."
if(!check_can_do())
return
research.display(src)
/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."
if(!check_can_do())
return
var/chem_name = input("Select a chemical to secrete.", "Chemicals") as null|anything in avail_chems
if(!chem_name)
return
var/datum/borer_chem/chem = avail_chems[chem_name]
var/max_amount = 50
if(chem.cost>0)
max_amount = round(chemicals / chem.cost)
if(max_amount==0)
to_chat(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)
to_chat(src, "<span class='warning'>You cannot synthesize this little.</span>")
return
if(chemicals < chem.cost*units)
to_chat(src, "<span class='warning'>You don't have enough energy to synthesize this much!</span>")
return
var/datum/reagent/C = chemical_reagents_list[chem.id] //we need to get the datum for this reagent to read the overdose threshold
if(units >= C.overdose_am - host.reagents.get_reagent_amount(chem.id) && C.overdose_am > 0)
if(alert("Secreting that much [chem.name] would cause an overdose in your host. Are you sure?", "Secrete Chemicals", "Yes", "No") != "Yes")
return
add_gamelogs(src, "intentionally overdosed \the [host] with '[chem.id]' ([chem.name])", admin = TRUE, tp_link = TRUE, span_class = "danger")
if(!host || controlling || !src || stat) //Sanity check.
return
if(chem.id == BLOOD)
if(istype(host, /mob/living/carbon/human) && !(host.species.anatomy_flags & NO_BLOOD))
host.vessel.add_reagent(chem.id, units)
else
to_chat(src, "<span class='notice'>Your host seems to be a species that doesn't use blood.<span>")
return
else
host.reagents.add_reagent(chem.id, units)
to_chat(src, "<span class='info'>You squirt a measure of [chem.name] from your reservoirs into [host]'s bloodstream.</span>")
add_gamelogs(src, "secreted [units]U of '[chem.id]' ([chem.name]) into \the [host]", admin = TRUE, tp_link = TRUE, span_class = "message")
chemicals -= chem.cost*units
// We've been moved to someone's head.
/mob/living/simple_animal/borer/proc/infest_limb(var/obj/item/organ/external/limb)
detach()
limb.borer=src
forceMove(limb)
update_verbs(BORER_MODE_SEVERED)
/mob/living/simple_animal/borer/proc/abandon_host()
set category = "Alien"
set name = "Abandon Host"
set desc = "Slither out of your host."
var/severed = istype(loc, /obj/item/organ/external)
if(!host && !severed)
to_chat(src, "<span class='warning'>You are not inside a host body.</span>")
return
if(stat == UNCONSCIOUS)
to_chat(src, "<span class='warning'>You cannot leave your host while unconscious.</span>")
return
if(channeling)
to_chat(src, "<span class='warning'>You cannot do this while your focus is directed elsewhere.</span>")
return
if(stat)
to_chat(src, "<span class='warning'>You cannot leave your host in your current state.</span>")
return
if(research.unlocking && !severed)
to_chat(src, "<span class='warning'>You are busy evolving.</span>")
return
var/response = alert(src, "Are you -sure- you want to abandon your current host?\n(This will take a few seconds and cannot be halted!)","Are you sure you want to abandon host?","Yes","No")
if(response != "Yes")
return
if(!src)
return
if(severed)
if(istype(loc, /obj/item/organ/external/head))
to_chat(src, "<span class='info'>You begin disconnecting from \the [loc]'s synapses and prodding at its internal ear canal.</span>")
else
to_chat(src, "<span class='info'>You begin disconnecting from \the [loc]'s nerve endings and prodding at the surface of its skin.</span>")
else
if(hostlimb == LIMB_HEAD)
to_chat(src, "<span class='info'>You begin disconnecting from \the [host]'s synapses and prodding at their internal ear canal.</span>")
else
to_chat(src, "<span class='info'>You begin disconnecting from \the [host]'s nerve endings and prodding at the surface of their skin.</span>")
var/leave_time = 5 SECONDS
if(severed)
leave_time = 2 SECONDS
spawn(leave_time)
if((!host && !severed) || !src)
return
if(src.stat)
to_chat(src, "<span class='warning'>You cannot abandon [host ? host : "\the [loc]"] in your current state.</span>")
return
if(channeling)
to_chat(src, "<span class='warning'>You cannot abandon [host ? host : "\the [loc]"] while your focus is directed elsewhere.</span>")
return
if(controlling)
to_chat(src, "<span class='warning'>You're too busy controlling your host.</span>")
return
if(research.unlocking)
to_chat(src, "<span class='warning'>You are busy evolving.</span>")
return
if(severed)
if(istype(loc, /obj/item/organ/external/head))
to_chat(src, "<span class='info'>You wiggle out of the ear of \the [loc] and plop to the ground.</span>")
else
to_chat(src, "<span class='info'>You wiggle out of \the [loc] and plop to the ground.</span>")
else
if(hostlimb == LIMB_HEAD)
to_chat(src, "<span class='info'>You wiggle out of \the [host]'s ear and plop to the ground.</span>")
else
to_chat(src, "<span class='info'>You wiggle out of \the [host]'s [limb_to_name(hostlimb)] 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)
if(istype(host,/mob/living/carbon/human))
var/mob/living/carbon/human/H = host
var/datum/organ/external/implanted = H.get_organ(hostlimb)
implanted.implants -= src
src.forceMove(get_turf(src))
controlling = 0
reset_view(null)
machine = null
if(host)
host.reset_view(null)
host.machine = null
host.verbs -= /mob/living/proc/release_control
host.verbs -= /mob/living/proc/punish_host
// Remove any unlocks that affect the host.
for(var/uid in research.unlocked.Copy())
var/datum/unlockable/borer/U = research.get(uid)
if(U)
if(U.remove_on_detach)
U.relock()
U.on_detached()
if(host_brain && 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
hostlimb = null
channeling = 0
channeling_brute_resist = 0
channeling_burn_resist = 0
channeling_speed_increase = 0
channeling_bone_talons = 0
channeling_bone_sword = 0
channeling_bone_shield = 0
channeling_bone_hammer = 0
channeling_bone_cocoon = 0
update_verbs(BORER_MODE_DETACHED)
extend_o_arm.forceMove(src)
/client/proc/borer_infest()
set category = "Alien"
set name = "Infest"
set desc = "Infest a suitable humanoid host."
var/mob/living/simple_animal/borer/B=mob
if(!istype(B))
return
B.infest()
/mob/living/simple_animal/borer/proc/limb_to_name(var/limb = null)
if(!limb)
return
var/limbname = ""
switch(limb)
if(LIMB_HEAD)
limbname = LIMB_HEAD
if(LIMB_CHEST)
limbname = LIMB_CHEST
if(LIMB_RIGHT_ARM)
limbname = "right arm"
if(LIMB_LEFT_ARM)
limbname = "left arm"
if(LIMB_RIGHT_LEG)
limbname = "right leg"
if(LIMB_LEFT_LEG)
limbname = "left leg"
return limbname
/mob/living/simple_animal/borer/proc/limb_to_mode(var/limb = null)
if(!limb)
return
var/mode = 0
switch(limb)
if(LIMB_HEAD)
mode = BORER_MODE_ATTACHED_HEAD
if(LIMB_CHEST)
mode = BORER_MODE_ATTACHED_CHEST
if(LIMB_RIGHT_ARM)
mode = BORER_MODE_ATTACHED_ARM
if(LIMB_LEFT_ARM)
mode = BORER_MODE_ATTACHED_ARM
if(LIMB_RIGHT_LEG)
mode = BORER_MODE_ATTACHED_LEG
if(LIMB_LEFT_LEG)
mode = BORER_MODE_ATTACHED_LEG
return mode
/mob/living/simple_animal/borer/proc/limb_covered(var/mob/living/carbon/C = null, var/limb = null)
if(!limb || !C)
return
if(!istype(C,/mob/living/carbon/human))
return 0
var/mob/living/carbon/human/H = C
switch(limb)
if(LIMB_HEAD)
if(H.check_body_part_coverage(EARS))
return 1
if(LIMB_CHEST)
if(H.check_body_part_coverage(UPPER_TORSO) && limb_covered(C, LIMB_RIGHT_ARM) && limb_covered(C, LIMB_LEFT_ARM) && limb_covered(C, LIMB_RIGHT_LEG) && limb_covered(C, LIMB_LEFT_LEG)) //any gap in protection will allow a borer to squeeze underneath chest protection
return 1
if(LIMB_RIGHT_ARM)
if(H.check_body_part_coverage(ARM_RIGHT) && H.check_body_part_coverage(HAND_RIGHT))
return 1
if(LIMB_LEFT_ARM)
if(H.check_body_part_coverage(ARM_LEFT) && H.check_body_part_coverage(HAND_LEFT))
return 1
if(LIMB_RIGHT_LEG)
if(H.check_body_part_coverage(LEG_RIGHT) && H.check_body_part_coverage(FOOT_RIGHT))
return 1
if(LIMB_LEFT_LEG)
if(H.check_body_part_coverage(LEG_LEFT) && H.check_body_part_coverage(FOOT_LEFT))
return 1
return 0
/mob/living/simple_animal/borer/proc/infest()
set category = "Alien"
set name = "Infest"
set desc = "Infest a suitable humanoid host."
if(host)
to_chat(src, "You are already within a host.")
return
if(stat == UNCONSCIOUS)
to_chat(src, "<span class='warning'>You cannot infest a target while unconscious.</span>")
return
if(channeling)
to_chat(src, "<span class='warning'>You cannot do this while your focus is directed elsewhere.</span>")
if(stat)
to_chat(src, "You cannot infest a target in your current state.")
return
if(research.unlocking)
to_chat(src, "<span class='warning'>You are busy evolving.</span>")
return
var/list/choices = list()
for(var/mob/living/carbon/C in view(1,src))
if(C.stat != 2 && src.Adjacent(C))
choices += C
var/mob/living/carbon/M = input(src,"Who do you wish to infest?") in null|choices
if(!M || !src)
return
if(!(src.Adjacent(M)))
return
var/area = src.zone_sel.selecting
var/region = LIMB_HEAD
if(istype(M, /mob/living/carbon/human))
switch(area)
if(LIMB_HEAD)
region = LIMB_HEAD
if("mouth")
region = LIMB_HEAD
if("eyes")
region = LIMB_HEAD
if(LIMB_CHEST)
region = LIMB_CHEST
if(LIMB_GROIN)
region = LIMB_CHEST
if(LIMB_RIGHT_ARM)
region = LIMB_RIGHT_ARM
if(LIMB_RIGHT_HAND)
region = LIMB_RIGHT_ARM
if(LIMB_LEFT_ARM)
region = LIMB_LEFT_ARM
if(LIMB_LEFT_HAND)
region = LIMB_LEFT_ARM
if(LIMB_RIGHT_LEG)
region = LIMB_RIGHT_LEG
if(LIMB_RIGHT_FOOT)
region = LIMB_RIGHT_LEG
if(LIMB_LEFT_LEG)
region = LIMB_LEFT_LEG
if(LIMB_LEFT_FOOT)
region = LIMB_LEFT_LEG
var/mob/living/carbon/human/H = M
var/datum/organ/external/O = H.get_organ(region)
if(!O.is_organic())
to_chat(src, "You cannot infest this host's inorganic [limb_to_name(region)]!")
return
if(!O.is_existing())
to_chat(src, "This host does not have a [limb_to_name(region)]!")
return
if(M.has_brain_worms(region))
to_chat(src, "This host's [limb_to_name(region)] is already infested!")
return
switch(region)
if(LIMB_HEAD)
to_chat(src, "You slither up [M] and begin probing at their ear canal...")
to_chat(M, "<span class='sinister'>You feel something slithering up your leg and probing at your ear canal...</span>")
if(LIMB_CHEST)
to_chat(src, "You slither up [M] and begin probing just below their sternum...")
to_chat(M, "<span class='sinister'>You feel something slithering up your leg and probing just below your sternum...</span>")
if(LIMB_RIGHT_ARM)
to_chat(src, "You slither up [M] and begin probing at their right arm...")
to_chat(M, "<span class='sinister'>You feel something slithering up your leg and probing at your right arm...</span>")
if(LIMB_LEFT_ARM)
to_chat(src, "You slither up [M] and begin probing at their left arm...")
to_chat(M, "<span class='sinister'>You feel something slithering up your leg and probing at your left arm...</span>")
if(LIMB_RIGHT_LEG)
to_chat(src, "You slither up [M]'s right leg and begin probing at the back of their knee...")
to_chat(M, "<span class='sinister'>You feel something slithering up your right leg and probing just behind your knee...</span>")
if(LIMB_LEFT_LEG)
to_chat(src, "You slither up [M]'s left leg and begin probing at the back of their knee...")
to_chat(M, "<span class='sinister'>You feel something slithering up your left leg and probing just behind your knee...</span>")
if(!do_after(src,M,50))
to_chat(src, "As [M] moves away, you are dislodged and fall to the ground.")
return
if(!M || !src)
return
if(src.stat)
to_chat(src, "You cannot infest a target in your current state.")
return
if(M.isDead())
to_chat(src, "That is not an appropriate target.")
return
if(M.has_brain_worms(region))
to_chat(src, "This host's [limb_to_name(region)] is already infested!")
return
if(M in view(1, src))
to_chat(src, "[region == LIMB_HEAD ? "You wiggle into [M]'s ear." : "You burrow under [M]'s skin."]")
src.perform_infestation(M, region)
return
else
to_chat(src, "They are no longer in range!")
return
/mob/living/simple_animal/borer/proc/perform_infestation(var/mob/living/carbon/M, var/body_region = LIMB_HEAD)
if(!M || !istype(M))
error("[src]: Unable to perform_infestation on [M]!")
return 0
hostlimb = body_region
src.host = M
update_verbs(limb_to_mode(hostlimb)) // Must be called before being removed from turf. (BYOND verb transfer bug)
src.forceMove(M)
if(istype(M,/mob/living/carbon/human))
var/mob/living/carbon/human/H = M
var/datum/organ/external/implanted = H.get_organ(body_region)
implanted.implants += src
host_brain.name = M.name
host_brain.real_name = M.real_name
// Tell our upgrades that we've attached.
for(var/uid in research.unlocked.Copy())
var/datum/unlockable/borer/U = research.get(uid)
if(U)
U.on_attached()
// /vg/ - Our users are shit, so we start with control over host.
if(config.borer_takeover_immediately)
do_bonding(rptext=1)
extend_o_arm.forceMove(host)
// So we can hear our host doing things.
// NOTE: We handle both visible and audible emotes because we're a brainslug that can see the impulses and shit.
/mob/living/simple_animal/borer/proc/host_emote(var/list/args)
src.show_message(args["message"], args["m_type"])
host_brain.show_message(args["message"], args["m_type"])
/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"
if(stat == UNCONSCIOUS)
to_chat(src, "<span class='warning'>You cannot ventcrawl while unconscious.</span>")
return
if(stat)
to_chat(src, "You cannot ventcrawl in your current state.")
return
if(research.unlocking)
to_chat(src, "<span class='warning'>You are busy evolving.</span>")
return
var/pipe = start_ventcrawl()
if(pipe)
handle_ventcrawl(pipe)
//copy paste from alien/larva, if that func is updated please update this one alsoghost
/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"
if(isUnconscious())
return
if (plane != HIDING_MOB_PLANE)
plane = HIDING_MOB_PLANE
to_chat(src, text("<span class='notice'>You are now hiding.</span>"))
else
plane = MOB_PLANE
to_chat(src, text("<span class='notice'>You have stopped hiding.</span>"))
/mob/living/simple_animal/borer/proc/reproduce()
set name = "Reproduce"
set desc = "Spawn offspring in the form of an egg."
set category = "Alien"
if(stat == UNCONSCIOUS)
to_chat(src, "<span class='warning'>You cannot reproduce while unconscious.</span>")
return
if(stat)
to_chat(src, "You cannot reproduce in your current state.")
return
if(research.unlocking)
to_chat(src, "<span class='warning'>You are busy evolving.</span>")
return
if(busy)
to_chat(src, "<span class='warning'>You are already doing something.</span>")
return
if(chemicals >= 100)
busy=1
to_chat(src, "<span class='warning'>You strain, trying to push out your young...</span>")
visible_message("<span class='warning'>\The [src] begins to struggle and strain!</span>", \
drugged_message = "<span class='notice'>\The [src] starts dancing.</span>")
var/turf/T = get_turf(src)
if(do_after(src, T, 5 SECONDS))
to_chat(src, "<span class='danger'>You twitch and quiver as you rapidly excrete an egg from your sluglike body.</span>")
visible_message("<span class='danger'>\The [src] heaves violently, expelling a small, gelatinous egg!</span>", \
drugged_message = "<span class='notice'>\The [src] starts farting a rainbow! Suddenly, a pot of gold appears.</span>")
chemicals -= 100
numChildren++
playsound(T, 'sound/effects/splat.ogg', 50, 1)
if(istype(T, /turf/simulated))
T.add_vomit_floor(null, 1)
var/obj/item/weapon/reagent_containers/food/snacks/borer_egg/E = new (T)
E.child_prefix_index = (name_prefix_index + 1)
busy=0
else
to_chat(src, "You do not have enough chemicals stored to reproduce.")
return
//Procs for grabbing players.
/mob/living/simple_animal/borer/proc/request_player()
var/list/candidates=list()
//testing("Polling for borers.")
for(var/mob/dead/observer/G in get_active_candidates(ROLE_BORER, poll="HEY KID, YOU WANNA BE A BORER?"))
if(!G.client)
//testing("Client of [G] inexistent")
continue
//#warn Uncomment me.
/*if(G.client.holder)
//testing("Client of [G] is admin.")
continue*/
if(isantagbanned(G))
//testing("[G] is jobbanned.")
continue
candidates += G
if(!candidates.len)
//message_admins("Unable to find a mind for [src.name]")
return 0
shuffle(candidates)
for(var/mob/i in candidates)
if(!i || !i.client)
continue //Dont bother removing them from the list since we only grab one wizard
return i
return 0
/mob/living/simple_animal/borer/proc/transfer_personality(var/client/candidate)
if(!candidate)
return
src.ckey = candidate.ckey
if(src.mind)
src.mind.assigned_role = "Borer"
// tl;dr
to_chat(src, "<span class='danger'>You are a Borer!</span>")
to_chat(src, "<span class='info'>You are a small slug-like symbiote that attaches to your host's body. Your only goals are to survive and procreate. However, there are those who would like to destroy you, and hosts don't take kindly to jerks. Being as helpful to your host as possible is the best option for survival.</span>")
to_chat(src, "<span class='info'>Borers can speak with other borers over the Cortical Link by prefixing their messages with :&. To communicate with your host only, speak normally.</span>")
to_chat(src, "<span class='info'><b>New:</b> To get new abilities for you and your host, use <em>Evolve</em> to unlock things. Borers are now symbiotic biological pAIs.</span>")
if(config.borer_takeover_immediately)
to_chat(src, "<span class='info'><b>Important:</b> While you receive full control at the start, <em>it is asked that you release control at some point so your host has a chance to play.</em> If they misbehave, you are permitted to kill them.</span>")
/mob/living/simple_animal/borer/proc/analyze_host()
set name = "Analyze Health"
set desc = "Check the health of your host."
set category = "Alien"
to_chat(src, "<span class='info'>You listen to the song of your host's nervous system, hunting for discordant notes...</span>")
spawn(5 SECONDS)
healthanalyze(host, src, mode=1, silent=1, skip_checks=1) // I am not rewriting this shit with more immersive strings. Deal with it. - N3X
/mob/living/simple_animal/borer/proc/taste_blood()
set name = "Taste Blood"
set desc = "See if there's anything within the blood of your host."
set category = "Alien"
if(!check_can_do())
return
to_chat(src, "<span class='info'>You taste the blood of your host, and process it for abnormalities.</span>")
if(!isnull(host.reagents))
var/dat = ""
if(host.reagents.reagent_list.len > 0)
for (var/datum/reagent/R in host.reagents.reagent_list)
if(R.id == BLOOD)
continue // Like we need to know that blood contains blood.
dat += "\n \t <span class='notice'>[R] ([R.volume] units)</span>"
if(dat)
to_chat(src, "<span class='notice'>Chemicals found: [dat]</span>")
else
to_chat(src, "<span class='notice'>No active chemical agents found in [host]'s blood.</span>")
else
to_chat(src, "<span class='notice'>No significant chemical agents found in [host]'s blood.</span>")
/mob/living/simple_animal/borer/attack_ghost(var/mob/dead/observer/O)
if(!(src.key))
if(O.can_reenter_corpse)
var/response = alert(O,"Do you want to take it over?","This borer has no soul","Yes","No")
if(response == "Yes")
if(!(src.key))
src.transfer_personality(O.client)
else if(src.key)
to_chat(src, "<span class='notice'>Somebody jumped your claim on this borer and is already controlling it. Try another </span>")
else if(!(O.can_reenter_corpse))
to_chat(O,"<span class='notice'>While the borer may be mindless, you have recently ghosted and thus are not allowed to take over for now.</span>")
/mob/living/simple_animal/borer/proc/passout(var/wait_time = 0, var/show_message = 0)
if((status_flags & BUDDHAMODE) || !wait_time)
return
if(show_message)
to_chat(src, "<span class='warning'>You lose consciousness due to overexertion.</span>")
wait_time = min(wait_time, 60)
stat = UNCONSCIOUS
spawn()
sleep(wait_time*10)
stat = CONSCIOUS
to_chat(src, "<span class='notice'>You have regained consciousness.</span>")
/mob/living/simple_animal/borer/proc/check_can_do(var/check_channeling = 1)
if(!host)
to_chat(src, "<span class='warning'>You are not inside a host body.</span>")
return 0
if(stat == UNCONSCIOUS)
to_chat(src, "<span class='warning'>You cannot do this while unconscious.</span>")
return 0
if(stat)
to_chat(src, "<span class='warning'>You cannot do this in your current state.</span>")
return 0
if(controlling)
to_chat(src, "<span class='warning'>You're too busy controlling your host.</span>")
return 0
if(host.stat==DEAD)
to_chat(src, "<span class='warning'>You cannot do that in your host's current state.</span>")
return 0
if(research.unlocking)
to_chat(src, "<span class='warning'>You are busy evolving.</span>")
return 0
if(check_channeling)
if(channeling)
to_chat(src, "<span class='warning'>You can't do this while your focus is directed elsewhere.</span>")
return 0
return 1
/mob/living/simple_animal/borer/ClickOn( var/atom/A, var/params )
..()
if(params2list(params)["shift"])
return
if(host)
if(extend_o_arm_unlocked)
if(hostlimb == LIMB_RIGHT_ARM || hostlimb == LIMB_LEFT_ARM)
if(!extend_o_arm)
extend_o_arm = new /obj/item/weapon/gun/hookshot/flesh(src, src)
extend_o_arm.forceMove(host)
if(!check_can_do())
return
if(istype(host.get_held_item_by_index(GRASP_RIGHT_HAND), /obj/item/offhand) || istype(host.get_held_item_by_index(GRASP_LEFT_HAND), /obj/item/offhand)) //If the host is two-handing something.
to_chat(src, "<span class='warning'>You cannot swing this item while your host holds it with both hands!</span>")
return
if(host.stunned)
to_chat(src, "<span class='warning'>Your host's muscles are tightened. You can't extend your arm!</span>")
return
var/datum/reagents/R = host.reagents
if(R)
if(R.has_reagent(SILICATE))
to_chat(src, "<span class='warning'>Something in your host's bloodstream is tightening their muscles. You can't extend your arm!</span>")
return
if(host.Adjacent(A))
if(hostlimb == LIMB_RIGHT_ARM)
if(host.get_held_item_by_index(GRASP_RIGHT_HAND))
if(check_attack_cooldown())
return
else
A.attackby(host.get_held_item_by_index(GRASP_RIGHT_HAND), host, 1, src)
set_attack_cooldown()
return
else if(istype(A, /obj/item))
var/obj/item/I = A
if(!I.anchored)
host.put_in_r_hand(A)
return
else
if(host.get_held_item_by_index(GRASP_LEFT_HAND))
if(check_attack_cooldown())
return
else
A.attackby(host.get_held_item_by_index(GRASP_LEFT_HAND), host, 1, src)
set_attack_cooldown()
return
else if(istype(A, /obj/item))
var/obj/item/I = A
if(!I.anchored)
host.put_in_l_hand(A)
return
if(get_turf(A) == get_turf(host) && !istype(A, /obj/item))
return
if(hostlimb == LIMB_RIGHT_ARM)
if(host.get_held_item_by_index(GRASP_RIGHT_HAND))
if(istype(host.get_held_item_by_index(GRASP_RIGHT_HAND), /obj/item/weapon/gun/hookshot)) //I don't want to deal with the fleshshot interacting with hookshots
return
if(chemicals < 10)
to_chat(src, "<span class='warning'>You don't have enough chemicals stored to swing an item with this arm!</span>")
return
else
if(!(extend_o_arm.hook || extend_o_arm.chain_datum || extend_o_arm.rewinding)) //If the arm is not currently extended.
chemicals -= 10 //It costs 10 chems to fire the fleshshot while holding an item.
else if(hostlimb == LIMB_LEFT_ARM)
if(host.get_held_item_by_index(GRASP_LEFT_HAND))
if(istype(host.get_held_item_by_index(GRASP_LEFT_HAND), /obj/item/weapon/gun/hookshot))
return
if(chemicals < 10)
to_chat(src, "<span class='warning'>You don't have enough chemicals stored to swing an item with this arm!</span>")
return
else
if(!(extend_o_arm.hook || extend_o_arm.chain_datum || extend_o_arm.rewinding))
chemicals -= 10
extend_o_arm.afterattack(A, host)
/mob/living/simple_animal/borer/proc/check_attack_cooldown()
var/datum/delay_controller/host_attack_delayer = host.attack_delayer
return host_attack_delayer.blocked()
/mob/living/simple_animal/borer/proc/set_attack_cooldown()
host.delayNextAttack(10)
#undef BORER_CAN_ASSUME_CONTROL