mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-27 01:51:50 +00:00
* Make energy guns able to use burst fire. * Changed obj/item/weapon/gun/projectile to /gun/ballistic and the name of the folder from "projectile" to "ballistic" to avoid confusion between actually projectiles and guns. Syringe gun, energy guns and magic guns can now use burst fire. * fixing merge conflict shit * fixing map conflicts * more map conflict fix * two tiny fixes. * tiny tweak * fixing merge conflicts. Moving the practice mini egun to the gun module. Renamed nuclear.dm to energy_gun.dm * map conflict fixes
1643 lines
51 KiB
Plaintext
1643 lines
51 KiB
Plaintext
/*
|
|
NPC VAR EXPLANATIONS (for modules and other things)
|
|
|
|
doing = their current action, INTERACTING, TRAVEL or FIGHTING
|
|
interest = how interested the NPC is in the situation, if they are idle, this drops
|
|
timeout = this is internal
|
|
TARGET = their current target
|
|
LAST_TARGET = their last target
|
|
nearby = a list of nearby mobs
|
|
best_force = the highest force object, used for checking when to swap items
|
|
retal = this is internal
|
|
retal_target = this is internal
|
|
update_hands = this is a bool (1/0) to determine if the NPC should update what is in his hands
|
|
|
|
MYID = their ID card
|
|
MYPDA = their PDA
|
|
main_hand = what is in their "main" hand (chosen from left > right)
|
|
TRAITS = the traits assigned to this npc
|
|
mymjob = the job assigned to the npc
|
|
|
|
robustness = the chance for the npc to hit something
|
|
smartness = the inverse chance for an npc to do stupid things
|
|
attitude = the chance for an npc to do rude or mean things
|
|
slyness = the chance for an npc to do naughty things ie thieving
|
|
|
|
functions = the list of procs that the npc will use for modules
|
|
|
|
graytide = shitmin var to make them go psycho
|
|
*/
|
|
#define NPC_SPEAK_FILE "npc_chatter.json"
|
|
/mob/living/carbon/human/interactive
|
|
name = "interactive station member"
|
|
var/doing = 0
|
|
var/interest = 10
|
|
var/maxInterest = 10
|
|
var/timeout = 0
|
|
var/inactivity_period = 0
|
|
var/TARGET = null
|
|
var/LAST_TARGET = null
|
|
var/list/nearby = list()
|
|
var/best_force = 0
|
|
var/retal = 0
|
|
var/mob/retal_target = null
|
|
var/update_hands = 0
|
|
var/list/blacklistItems = list() // items we should be ignoring
|
|
var/maxStepsTick = 6 // step as many times as we can per frame
|
|
//Job and mind data
|
|
var/obj/item/weapon/card/id/MYID
|
|
var/obj/item/weapon/card/id/RPID // the "real" idea they use
|
|
var/obj/item/device/pda/MYPDA
|
|
var/obj/item/main_hand
|
|
var/obj/item/other_hand
|
|
var/TRAITS = 0
|
|
var/obj/item/weapon/card/id/Path_ID
|
|
var/datum/job/myjob
|
|
var/list/myPath = list()
|
|
faction = list("synth")
|
|
//trait vars
|
|
var/robustness = 50
|
|
var/smartness = 50
|
|
var/attitude = 50
|
|
var/slyness = 50
|
|
var/graytide = 0
|
|
var/list/favoured_types = list() // allow a mob to favour a type, and hold onto them
|
|
var/chattyness = CHANCE_TALK
|
|
var/targetInterestShift = 5 // how much a good action should "reward" the npc
|
|
//modules
|
|
var/list/functions = list("nearbyscan","combat","shitcurity","chatter")
|
|
var/restrictedJob = 0
|
|
var/shouldUseDynamicProc = 0 // switch to make the AI control it's own proccessing
|
|
var/alternateProcessing = 1
|
|
var/forceProcess = 0
|
|
var/processTime = 8
|
|
var/lastProc = 0
|
|
var/walkdebug = 0 //causes sparks in our path target. used for debugging
|
|
var/debugexamine = 0 //If we show debug info in our examine
|
|
var/showexaminetext = 1 //If we show our telltale examine text
|
|
|
|
var/list/knownStrings = list()
|
|
|
|
//snpc traitor variables
|
|
|
|
var/isTraitor = 0
|
|
var/traitorTarget
|
|
var/traitorScale = 0 // our ability as a traitor
|
|
var/traitorType = 0
|
|
|
|
|
|
/// SNPC voice handling
|
|
|
|
/mob/living/carbon/human/interactive/proc/loadVoice()
|
|
var/savefile/S = new /savefile("data/npc_saves/snpc.sav")
|
|
S["knownStrings"] >> knownStrings
|
|
|
|
if(isnull(knownStrings))
|
|
knownStrings = list()
|
|
|
|
/mob/living/carbon/human/interactive/proc/saveVoice()
|
|
var/savefile/S = new /savefile("data/npc_saves/snpc.sav")
|
|
S["knownStrings"] << knownStrings
|
|
|
|
//botPool funcs
|
|
/mob/living/carbon/human/interactive/proc/takeDelegate(mob/living/carbon/human/interactive/from,doReset=TRUE)
|
|
eye_color = "red"
|
|
if(from == src)
|
|
return FALSE
|
|
TARGET = from.TARGET
|
|
LAST_TARGET = from.LAST_TARGET
|
|
retal = from.retal
|
|
retal_target = from.retal_target
|
|
doing = from.doing
|
|
//
|
|
timeout = 0
|
|
inactivity_period = 0
|
|
interest = maxInterest
|
|
//
|
|
update_icons()
|
|
if(doReset)
|
|
from.TARGET = null
|
|
from.LAST_TARGET = null
|
|
from.retal = 0
|
|
from.retal_target = null
|
|
from.doing = 0
|
|
return TRUE
|
|
|
|
//end pool funcs
|
|
|
|
/mob/living/carbon/human/interactive/proc/random()
|
|
//this is here because this has no client/prefs/brain whatever.
|
|
age = rand(AGE_MIN,AGE_MAX)
|
|
//job handling
|
|
myjob = new/datum/job/assistant()
|
|
job = myjob.title
|
|
myjob.equip(src)
|
|
myjob.apply_fingerprints(src)
|
|
|
|
/mob/living/carbon/human/interactive/attacked_by(obj/item/I, mob/living/user, def_zone)
|
|
. = ..()
|
|
retal = 1
|
|
retal_target = user
|
|
|
|
/mob/living/carbon/human/interactive/bullet_act(obj/item/projectile/P, def_zone)
|
|
var/potentialAssault = locate(/mob/living) in view(2,P.starting)
|
|
if(potentialAssault)
|
|
retal = 1
|
|
retal_target = potentialAssault
|
|
..()
|
|
|
|
|
|
/client/proc/resetSNPC(var/mob/A in SSnpc.botPool_l)
|
|
set name = "Reset SNPC"
|
|
set desc = "Reset the SNPC"
|
|
set category = "Debug"
|
|
|
|
if(!holder)
|
|
return
|
|
|
|
if(A)
|
|
if(!istype(A,/mob/living/carbon/human/interactive))
|
|
return
|
|
var/mob/living/carbon/human/interactive/T = A
|
|
if(T)
|
|
T.timeout = 100
|
|
T.retal = 0
|
|
T.doing = 0
|
|
|
|
/client/proc/toggleSNPC(var/mob/A in SSnpc.botPool_l)
|
|
set name = "Toggle SNPC Proccessing Mode"
|
|
set desc = "Toggle SNPC Proccessing Mode"
|
|
set category = "Debug"
|
|
|
|
if(!holder)
|
|
return
|
|
|
|
if(A)
|
|
if(!istype(A,/mob/living/carbon/human/interactive))
|
|
return
|
|
var/mob/living/carbon/human/interactive/T = A
|
|
if(T)
|
|
T.alternateProcessing = !T.alternateProcessing
|
|
T.forceProcess = 1
|
|
usr << "[T]'s processing has been switched to [T.alternateProcessing ? "High Profile" : "Low Profile"]"
|
|
|
|
/client/proc/customiseSNPC(var/mob/A in SSnpc.botPool_l)
|
|
set name = "Customize SNPC"
|
|
set desc = "Customise the SNPC"
|
|
set category = "Debug"
|
|
|
|
if(!holder)
|
|
return
|
|
|
|
if(A)
|
|
if(!istype(A,/mob/living/carbon/human/interactive))
|
|
return
|
|
var/mob/living/carbon/human/interactive/T = A
|
|
|
|
var/choice = input("Customization Choices") as null|anything in list("Service NPC","Security NPC","Random","Custom")
|
|
if(choice)
|
|
if(choice == "Service NPC" || choice == "Security NPC")
|
|
var/job = choice == "Service NPC" ? pick("Bartender","Cook","Botanist","Janitor") : pick("Warden","Detective","Security Officer")
|
|
for(var/j in SSjob.occupations)
|
|
var/datum/job/J = j
|
|
if(J.title == job)
|
|
T.myjob = J
|
|
T.job = T.myjob.title
|
|
for(var/obj/item/W in T)
|
|
qdel(W)
|
|
T.myjob.equip(T)
|
|
T.myjob.apply_fingerprints(T)
|
|
T.doSetup()
|
|
break
|
|
if(choice == "Random")
|
|
T.myjob = pick(SSjob.occupations)
|
|
T.job = T.myjob.title
|
|
for(var/obj/item/W in T)
|
|
qdel(W)
|
|
T.myjob.equip(T)
|
|
T.myjob.apply_fingerprints(T)
|
|
T.doSetup()
|
|
if(prob(25))
|
|
var/list/validchoices = list()
|
|
for(var/mob/living/carbon/human/M in mob_list)
|
|
validchoices += M
|
|
var/mob/living/carbon/human/chosen = pick(validchoices)
|
|
var/datum/dna/toDoppel = chosen.dna
|
|
T.real_name = toDoppel.real_name
|
|
toDoppel.transfer_identity(T, transfer_SE=1)
|
|
T.updateappearance(mutcolor_update=1)
|
|
T.domutcheck()
|
|
if(prob(25))
|
|
var/cType = pick(list(SNPC_BRUTE,SNPC_STEALTH,SNPC_MARTYR,SNPC_PSYCHO))
|
|
T.makeTraitor(cType)
|
|
T.loc = pick(get_area_turfs(T.job2area(T.myjob)))
|
|
if(choice == "Custom")
|
|
var/cjob = input("Choose Job") as null|anything in SSjob.occupations
|
|
if(cjob)
|
|
T.myjob = cjob
|
|
T.job = T.myjob.title
|
|
for(var/obj/item/W in T)
|
|
qdel(W)
|
|
T.myjob.equip(T)
|
|
T.myjob.apply_fingerprints(T)
|
|
T.doSetup()
|
|
var/shouldDoppel = input("Do you want the SNPC to disguise themself as a crewmember?") as null|anything in list("Yes","No")
|
|
if(shouldDoppel)
|
|
if(shouldDoppel == "Yes")
|
|
var/list/validchoices = list()
|
|
for(var/mob/living/carbon/human/M in mob_list)
|
|
validchoices += M
|
|
|
|
var/mob/living/carbon/human/chosen = input("Which crewmember?") as null|anything in validchoices
|
|
|
|
if(chosen)
|
|
var/datum/dna/toDoppel = chosen.dna
|
|
|
|
T.real_name = toDoppel.real_name
|
|
toDoppel.transfer_identity(T, transfer_SE=1)
|
|
T.updateappearance(mutcolor_update=1)
|
|
T.domutcheck()
|
|
var/doTrait = input("Do you want the SNPC to be a traitor?") as null|anything in list("Yes","No")
|
|
if(doTrait)
|
|
if(doTrait == "Yes")
|
|
var/list/tType = list("Brute" = SNPC_BRUTE, "Stealth" = SNPC_STEALTH, "Martyr" = SNPC_MARTYR, "Psycho" = SNPC_PSYCHO)
|
|
var/cType = input("Choose the traitor personality.") as null|anything in tType
|
|
if(cType)
|
|
var/value = tType[cType]
|
|
T.makeTraitor(value)
|
|
var/doTele = input("Place the SNPC in their department?") as null|anything in list("Yes","No")
|
|
if(doTele)
|
|
if(doTele == "Yes")
|
|
T.loc = pick(get_area_turfs(T.job2area(T.myjob)))
|
|
|
|
/mob/living/carbon/human/interactive/proc/doSetup()
|
|
Path_ID = new /obj/item/weapon/card/id(src)
|
|
|
|
var/datum/job/captain/C = new/datum/job/captain
|
|
Path_ID.access = C.get_access()
|
|
|
|
MYID = new(src)
|
|
MYID.name = "[src.real_name]'s ID Card ([myjob.title])"
|
|
MYID.assignment = "[myjob.title]"
|
|
MYID.registered_name = src.real_name
|
|
MYID.access = Path_ID.access // Automatons have strange powers... strange indeed
|
|
|
|
RPID = new(src)
|
|
RPID.name = "[src.real_name]'s ID Card ([myjob.title])"
|
|
RPID.assignment = "[myjob.title]"
|
|
RPID.registered_name = src.real_name
|
|
RPID.access = myjob.get_access()
|
|
|
|
equip_to_slot_or_del(MYID, slot_wear_id)
|
|
MYPDA = new(src)
|
|
MYPDA.owner = real_name
|
|
MYPDA.ownjob = "Crew"
|
|
MYPDA.name = "PDA-[real_name] ([myjob.title])"
|
|
equip_to_slot_or_del(MYPDA, slot_belt)
|
|
zone_selected = "chest"
|
|
//arms
|
|
for(var/X in bodyparts)
|
|
var/obj/item/bodypart/BP = X
|
|
if(prob((FUZZY_CHANCE_LOW+FUZZY_CHANCE_HIGH)/4))
|
|
BP.change_bodypart_status(BODYPART_ROBOTIC)
|
|
update_icons()
|
|
update_damage_overlays()
|
|
functions = list("nearbyscan","combat","shitcurity","chatter") // stop customize adding multiple copies of a function
|
|
//job specific favours
|
|
switch(myjob.title)
|
|
if("Assistant")
|
|
favoured_types = list(/obj/item/clothing, /obj/item/weapon)
|
|
if("Captain","Head of Personnel")
|
|
favoured_types = list(/obj/item/clothing, /obj/item/weapon/stamp/captain,/obj/item/weapon/disk/nuclear)
|
|
if("Cook")
|
|
favoured_types = list(/obj/item/weapon/reagent_containers/food, /obj/item/weapon/kitchen)
|
|
functions += "souschef"
|
|
restrictedJob = 1
|
|
if("Bartender")
|
|
favoured_types = list(/obj/item/weapon/reagent_containers/food, /obj/item/weapon/kitchen)
|
|
functions += "bartend"
|
|
restrictedJob = 1
|
|
if("Station Engineer","Chief Engineer","Atmospheric Technician")
|
|
favoured_types = list(/obj/item/stack, /obj/item/weapon, /obj/item/clothing)
|
|
if("Chief Medical Officer","Medical Doctor","Chemist","Virologist","Geneticist")
|
|
favoured_types = list(/obj/item/weapon/reagent_containers/glass/beaker, /obj/item/weapon/storage/firstaid, /obj/item/stack/medical, /obj/item/weapon/reagent_containers/syringe)
|
|
functions += "healpeople"
|
|
if("Research Director","Scientist","Roboticist")
|
|
favoured_types = list(/obj/item/weapon/reagent_containers/glass/beaker, /obj/item/stack, /obj/item/weapon/reagent_containers)
|
|
if("Head of Security","Warden","Security Officer","Detective")
|
|
favoured_types = list(/obj/item/clothing, /obj/item/weapon, /obj/item/weapon/restraints)
|
|
if("Janitor")
|
|
favoured_types = list(/obj/item/weapon/mop, /obj/item/weapon/reagent_containers/glass/bucket, /obj/item/weapon/reagent_containers/spray/cleaner, /obj/effect/decal/cleanable)
|
|
functions += "dojanitor"
|
|
if("Clown")
|
|
favoured_types = list(/obj/item/weapon/soap, /obj/item/weapon/reagent_containers/food/snacks/grown/banana,/obj/item/weapon/grown/bananapeel)
|
|
functions += "clowning"
|
|
if("Mime")
|
|
functions -= "chatter"
|
|
if("Botanist")
|
|
favoured_types = list(/obj/machinery/hydroponics, /obj/item/weapon/reagent_containers, /obj/item/weapon)
|
|
functions += "botany"
|
|
restrictedJob = 1
|
|
else
|
|
favoured_types = list(/obj/item/clothing)
|
|
|
|
|
|
if(TRAITS & TRAIT_ROBUST)
|
|
robustness = 75
|
|
else if(TRAITS & TRAIT_UNROBUST)
|
|
robustness = 25
|
|
|
|
//modifiers are prob chances, lower = smarter
|
|
if(TRAITS & TRAIT_SMART)
|
|
smartness = 75
|
|
else if(TRAITS & TRAIT_DUMB)
|
|
disabilities |= CLUMSY
|
|
smartness = 25
|
|
|
|
if(TRAITS & TRAIT_MEAN)
|
|
attitude = 75
|
|
else if(TRAITS & TRAIT_FRIENDLY)
|
|
attitude = 1
|
|
|
|
if(TRAITS & TRAIT_THIEVING)
|
|
slyness = 75
|
|
|
|
|
|
/mob/living/carbon/human/interactive/proc/makeTraitor(var/inPers)
|
|
isTraitor = 1
|
|
traitorScale = (slyness + smartness) + rand(-10,10)
|
|
traitorType = inPers
|
|
|
|
switch(traitorType)
|
|
if(SNPC_BRUTE) // SMASH KILL RAAARGH
|
|
traitorTarget = pick(mob_list)
|
|
if(SNPC_STEALTH) // Shhh we is sneekies
|
|
var/A = pick(typesof(/datum/objective_item/steal) - /datum/objective_item/steal)
|
|
var/datum/objective_item/steal/S = new A
|
|
traitorTarget = locate(S.targetitem) in world
|
|
if(SNPC_MARTYR) // MY LIFE FOR SPESZUL
|
|
var/targetType = pick(/obj/machinery/gravity_generator/main/station,/obj/machinery/power/smes/engineering,/obj/machinery/telecomms/hub)
|
|
traitorTarget = locate(targetType) in machines
|
|
if(SNPC_PSYCHO) // YOU'RE LIKE A FLESH BICYLE AND I WANT TO DISMANTLE YOU
|
|
traitorTarget = null
|
|
|
|
functions += "traitor"
|
|
faction -= "neutral"
|
|
faction += "hostile"
|
|
|
|
/mob/living/carbon/human/interactive/New()
|
|
..()
|
|
|
|
set_species(/datum/species/synth)
|
|
|
|
random()
|
|
|
|
doSetup()
|
|
|
|
SSnpc.insertBot(src)
|
|
|
|
loadVoice()
|
|
|
|
// a little bit of variation to make individuals more unique
|
|
robustness += rand(-10,10)
|
|
smartness += rand(-10,10)
|
|
attitude += rand(-10,10)
|
|
slyness += rand(-10,10)
|
|
|
|
doProcess()
|
|
|
|
|
|
/mob/living/carbon/human/interactive/proc/retalTarget(var/target)
|
|
var/mob/living/carbon/human/M = target
|
|
if(target)
|
|
if(health > 0)
|
|
if(M.a_intent == "help")
|
|
chatter()
|
|
if(M.a_intent == "harm")
|
|
retal = 1
|
|
retal_target = target
|
|
|
|
//Retaliation clauses
|
|
|
|
/mob/living/carbon/human/interactive/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
|
|
..(AM,skipcatch,hitpush,blocked)
|
|
var/mob/living/carbon/C = locate(/mob/living/carbon) in view(MIN_RANGE_FIND,src)
|
|
if(C)
|
|
retalTarget(C)
|
|
|
|
/mob/living/carbon/human/interactive/attack_hand(mob/living/carbon/human/M)
|
|
..(M)
|
|
retalTarget(M)
|
|
|
|
/mob/living/carbon/human/interactive/show_inv(mob/user)
|
|
..(user)
|
|
retalTarget(user)
|
|
|
|
/mob/living/carbon/human/interactive/can_inject(mob/user, error_msg, target_zone, var/penetrate_thick = 0)
|
|
..(user,error_msg,target_zone,penetrate_thick)
|
|
retalTarget(user)
|
|
|
|
|
|
//THESE EXIST FOR DEBUGGING OF THE DOING/INTEREST SYSTEM EASILY
|
|
/mob/living/carbon/human/interactive/proc/doing2string(doin)
|
|
var/toReturn = ""
|
|
if(!doin)
|
|
toReturn = "not doing anything"
|
|
if(doin & INTERACTING)
|
|
toReturn += "interacting with something, "
|
|
if(doin & FIGHTING)
|
|
toReturn += "engaging in combat, "
|
|
if(doin & TRAVEL)
|
|
toReturn += "and going somewhere"
|
|
return toReturn
|
|
|
|
/mob/living/carbon/human/interactive/proc/interest2string(inter)
|
|
var/toReturn = "Flatlined"
|
|
if(inter >= 0 && inter <= 25)
|
|
toReturn = "Very Bored"
|
|
if(inter >= 26 && inter <= 50)
|
|
toReturn = "Bored"
|
|
if(inter >= 51 && inter <= 75)
|
|
toReturn = "Content"
|
|
if(inter >= 76)
|
|
toReturn = "Excited"
|
|
return toReturn
|
|
//END DEBUG
|
|
|
|
/mob/living/carbon/human/interactive/proc/IsDeadOrIncap(checkDead = TRUE)
|
|
if(!canmove)
|
|
return 1
|
|
if(health <= 0 && checkDead)
|
|
return 1
|
|
if(restrained())
|
|
return 1
|
|
if(paralysis)
|
|
return 1
|
|
if(stunned)
|
|
return 1
|
|
if(stat)
|
|
return 1
|
|
if(inactivity_period > 0)
|
|
return 1
|
|
return 0
|
|
|
|
/mob/living/carbon/human/interactive/proc/enforce_hands()
|
|
if(main_hand)
|
|
if(main_hand.loc != src)
|
|
main_hand = null
|
|
if(other_hand)
|
|
if(other_hand.loc != src)
|
|
other_hand = null
|
|
|
|
var/obj/item/L = get_item_for_held_index(1) //just going to hardcode SNPCs to 2 hands, for now.
|
|
var/obj/item/R = get_item_for_held_index(2) //they're just VERY assume-y about 2 hands.
|
|
if(active_hand_index == 1)
|
|
if(!L)
|
|
main_hand = null
|
|
if(R)
|
|
swap_hands()
|
|
else
|
|
if(!R)
|
|
main_hand = null
|
|
if(L)
|
|
swap_hands()
|
|
|
|
|
|
/mob/living/carbon/human/interactive/proc/swap_hands()
|
|
var/oindex = active_hand_index
|
|
if(active_hand_index == 1)
|
|
active_hand_index = 2
|
|
else
|
|
active_hand_index = 1
|
|
main_hand = get_active_held_item()
|
|
other_hand = get_item_for_held_index(oindex)
|
|
update_hands = 1
|
|
|
|
/mob/living/carbon/human/interactive/proc/take_to_slot(obj/item/G, var/hands=0)
|
|
var/list/slots = list ("left pocket" = slot_l_store,"right pocket" = slot_r_store,"left hand" = slot_hands,"right hand" = slot_hands)
|
|
if(hands)
|
|
slots = list ("left hand" = slot_hands,"right hand" = slot_hands)
|
|
G.loc = src
|
|
if(G.force && G.force > best_force)
|
|
best_force = G.force
|
|
equip_in_one_of_slots(G, slots)
|
|
update_hands = 1
|
|
|
|
/mob/living/carbon/human/interactive/proc/insert_into_backpack()
|
|
var/list/slots = list ("left pocket" = slot_l_store,"right pocket" = slot_r_store,"left hand" = slot_hands,"right hand" = slot_hands)
|
|
var/obj/item/I = get_item_by_slot(pick(slots))
|
|
var/obj/item/weapon/storage/BP = get_item_by_slot(slot_back)
|
|
if(back && BP && I)
|
|
if(BP.can_be_inserted(I,0))
|
|
BP.handle_item_insertion(I,0)
|
|
else
|
|
unEquip(I,TRUE)
|
|
update_hands = 1
|
|
|
|
/mob/living/carbon/human/interactive/proc/targetRange(towhere)
|
|
return get_dist(get_turf(towhere), get_turf(src))
|
|
|
|
/mob/living/carbon/human/interactive/Life()
|
|
..()
|
|
if(ticker.current_state == GAME_STATE_FINISHED)
|
|
saveVoice()
|
|
if(!alternateProcessing || forceProcess || world.time > lastProc + processTime)
|
|
doProcess()
|
|
|
|
/mob/living/carbon/human/interactive/death()
|
|
saveVoice()
|
|
..()
|
|
|
|
/mob/living/carbon/human/interactive/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans)
|
|
if(speaker != src)
|
|
knownStrings |= html_decode(raw_message)
|
|
..()
|
|
|
|
/mob/living/carbon/human/interactive/proc/doProcess()
|
|
set waitfor = 0
|
|
forceProcess = 0
|
|
lastProc = world.time
|
|
|
|
if(shouldUseDynamicProc)
|
|
var/isSeen = 0
|
|
for(var/mob/living/carbon/human/A in orange(12,src))
|
|
if(A.client)
|
|
isSeen = 1
|
|
alternateProcessing = isSeen
|
|
if(alternateProcessing)
|
|
forceProcess = 1
|
|
|
|
if(IsDeadOrIncap())
|
|
walk(src,0)
|
|
return
|
|
//---------------------------
|
|
//---- interest flow control
|
|
if(interest < 0 || inactivity_period < 0)
|
|
if(interest < 0)
|
|
interest = 0
|
|
if(inactivity_period < 0)
|
|
inactivity_period = 0
|
|
if(interest > maxInterest)
|
|
interest = maxInterest
|
|
//---------------------------
|
|
//VIEW FUNCTIONS
|
|
|
|
//doorscan is now integrated into life and runs before all other procs
|
|
for(var/dir in alldirs)
|
|
var/turf/T = get_step(src,dir)
|
|
if(T)
|
|
for(var/obj/machinery/door/D in T.contents)
|
|
if(!istype(D,/obj/machinery/door/poddoor) && D.density)
|
|
if(istype(D,/obj/machinery/door/airlock))
|
|
var/obj/machinery/door/airlock/AL = D
|
|
if(!AL.CanAStarPass(RPID)) // only crack open doors we can't get through
|
|
AL.panel_open = 1
|
|
AL.update_icon()
|
|
AL.shock(src,(100 - smartness)/2)
|
|
sleep(5)
|
|
AL.unbolt()
|
|
if(!AL.wires.is_cut(WIRE_BOLTS))
|
|
AL.wires.cut(WIRE_BOLTS)
|
|
if(!AL.wires.is_cut(WIRE_POWER1))
|
|
AL.wires.cut(WIRE_POWER1)
|
|
if(!AL.wires.is_cut(WIRE_POWER2))
|
|
AL.wires.cut(WIRE_POWER2)
|
|
sleep(5)
|
|
AL.panel_open = 0
|
|
AL.update_icon()
|
|
D.open()
|
|
|
|
if(update_hands)
|
|
var/obj/item/l_hand = get_item_for_held_index(1)
|
|
var/obj/item/r_hand = get_item_for_held_index(2)
|
|
if(l_hand || r_hand)
|
|
if(l_hand)
|
|
active_hand_index = 1
|
|
main_hand = l_hand
|
|
if(r_hand)
|
|
other_hand = r_hand
|
|
else if(r_hand)
|
|
active_hand_index = 2
|
|
main_hand = r_hand
|
|
if(l_hand) //this technically shouldnt occur, but its a redundancy
|
|
other_hand = l_hand
|
|
update_icons()
|
|
update_hands = 0
|
|
|
|
if(pulledby)
|
|
if(Adjacent(pulledby))
|
|
a_intent = "disarm"
|
|
pulledby.attack_hand(src)
|
|
inactivity_period = 10
|
|
|
|
if(buckled)
|
|
resist()
|
|
inactivity_period = 10
|
|
|
|
//proc functions
|
|
for(var/Proc in functions)
|
|
if(!IsDeadOrIncap())
|
|
callfunction(Proc)
|
|
|
|
|
|
//target interaction stays hardcoded
|
|
|
|
if(TARGET) // don't use blacklisted items
|
|
if(TARGET in blacklistItems)
|
|
TARGET = null
|
|
|
|
|
|
if((TARGET && Adjacent(TARGET)))
|
|
doing |= INTERACTING
|
|
//--------DOORS
|
|
if(istype(TARGET, /obj/machinery/door))
|
|
var/obj/machinery/door/D = TARGET
|
|
if(D.check_access(MYID) && !istype(D,/obj/machinery/door/poddoor))
|
|
D.open()
|
|
//sleep(15)
|
|
var/turf/T = get_step(get_step(D.loc,dir),dir) //recursion yo
|
|
tryWalk(T)
|
|
//THIEVING SKILLS
|
|
if(!TARGET in blacklistItems)
|
|
insert_into_backpack() // dump random item into backpack to make space
|
|
//---------ITEMS
|
|
if(istype(TARGET, /obj/item))
|
|
if(istype(TARGET, /obj/item/weapon))
|
|
var/obj/item/weapon/W = TARGET
|
|
if(W.force >= best_force || prob((FUZZY_CHANCE_LOW+FUZZY_CHANCE_HIGH)/2))
|
|
if(!get_item_for_held_index(1) || !get_item_for_held_index(2))
|
|
put_in_hands(W)
|
|
else
|
|
insert_into_backpack()
|
|
else
|
|
if(!get_item_for_held_index(1) || !get_item_for_held_index(2))
|
|
put_in_hands(TARGET)
|
|
else
|
|
insert_into_backpack()
|
|
//---------FASHION
|
|
if(istype(TARGET,/obj/item/clothing))
|
|
var/obj/item/clothing/C = TARGET
|
|
drop_item()
|
|
spawn(5)
|
|
take_to_slot(C,1)
|
|
if(!equip_to_appropriate_slot(C))
|
|
var/obj/item/I = get_item_by_slot(C)
|
|
unEquip(I)
|
|
spawn(5)
|
|
equip_to_appropriate_slot(C)
|
|
update_hands = 1
|
|
if(MYPDA in src.loc || MYID in src.loc)
|
|
if(MYPDA in src.loc)
|
|
equip_to_appropriate_slot(MYPDA)
|
|
if(MYID in src.loc)
|
|
equip_to_appropriate_slot(MYID)
|
|
//THIEVING SKILLS END
|
|
//-------------TOUCH ME
|
|
if(istype(TARGET,/obj/structure))
|
|
var/obj/structure/STR = TARGET
|
|
if(main_hand)
|
|
var/obj/item/weapon/W = main_hand
|
|
STR.attackby(W, src)
|
|
else
|
|
STR.attack_hand(src)
|
|
interest += targetInterestShift
|
|
doing = doing & ~INTERACTING
|
|
timeout = 0
|
|
TARGET = null
|
|
else
|
|
if(TARGET)
|
|
tryWalk(TARGET)
|
|
timeout++
|
|
|
|
if(doing == 0)
|
|
interest--
|
|
else
|
|
interest++
|
|
|
|
if(inactivity_period > 0)
|
|
inactivity_period--
|
|
|
|
if(interest <= 0 || timeout >= 10) // facilitate boredom functions
|
|
TARGET = null
|
|
doing = 0
|
|
timeout = 0
|
|
myPath = list()
|
|
|
|
//this is boring, lets move
|
|
if(!doing && !IsDeadOrIncap() && !TARGET)
|
|
doing |= TRAVEL
|
|
if(!isTraitor || !traitorTarget)
|
|
var/choice = rand(1,50)
|
|
switch(choice)
|
|
if(1 to 30)
|
|
//chance to chase an item
|
|
TARGET = locate(/obj/item) in favouredObjIn(oview(MIN_RANGE_FIND,src))
|
|
if(31 to 40)
|
|
TARGET = safepick(get_area_turfs(job2area(myjob)))
|
|
if(41 to 45)
|
|
TARGET = pick(target_filter(favouredObjIn(urange(MAX_RANGE_FIND,src,1))))
|
|
if(46 to 50)
|
|
TARGET = pick(target_filter(oview(MIN_RANGE_FIND,src)))
|
|
else if(isTraitor && traitorTarget)
|
|
TARGET = traitorTarget
|
|
tryWalk(TARGET)
|
|
LAST_TARGET = TARGET
|
|
if(alternateProcessing)
|
|
addtimer(src, "doProcess", processTime)
|
|
|
|
/mob/living/carbon/human/interactive/proc/favouredObjIn(var/list/inList)
|
|
var/list/outList = list()
|
|
for(var/i in inList)
|
|
for(var/path in favoured_types)
|
|
if(ispath(i,path))
|
|
outList += i
|
|
if(outList.len <= 0)
|
|
outList = inList
|
|
return outList
|
|
|
|
/mob/living/carbon/human/interactive/proc/callfunction(Proc)
|
|
set waitfor = 0
|
|
spawn(0)
|
|
call(src,Proc)(src)
|
|
|
|
/mob/living/carbon/human/interactive/proc/tryWalk(turf/inTarget, override = 0)
|
|
if(restrictedJob && !override) // we're a job that has to stay in our home
|
|
if(!(get_turf(inTarget) in get_area_turfs(job2area(myjob))))
|
|
TARGET = null
|
|
return
|
|
|
|
if(!IsDeadOrIncap())
|
|
if(!walk2derpless(inTarget))
|
|
timeout++
|
|
else
|
|
timeout++
|
|
|
|
/mob/living/carbon/human/interactive/proc/getGoodPath(target,var/maxtries=512)
|
|
set background = 1
|
|
var/turf/end = get_turf(target)
|
|
|
|
var/turf/current = get_turf(src)
|
|
|
|
var/list/path = list()
|
|
var/tries = 0
|
|
while(current != end && tries < maxtries)
|
|
var/turf/shortest = current
|
|
for(var/turf/T in view(current,1))
|
|
var/foundDense = 0
|
|
for(var/atom/A in T)
|
|
if(A.density)
|
|
foundDense = 1
|
|
if(T.density == 0 && !foundDense)
|
|
if(get_dist(T, target) < get_dist(shortest,target))
|
|
shortest = T
|
|
else
|
|
tries++
|
|
else
|
|
tries++
|
|
current = shortest
|
|
path += shortest
|
|
return path
|
|
|
|
/mob/living/carbon/human/interactive/proc/walk2derpless(target)
|
|
set background = 1
|
|
if(!target)
|
|
return 0
|
|
|
|
if(walkdebug)
|
|
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread
|
|
spark_system.set_up(5, 0, target)
|
|
spark_system.attach(target)
|
|
spark_system.start()
|
|
|
|
|
|
if(myPath.len <= 0)
|
|
myPath = get_path_to(src, get_turf(target), /turf/proc/Distance, MAX_RANGE_FIND + 1, 250,1, id=Path_ID)
|
|
|
|
if(myPath)
|
|
if(myPath.len > 0)
|
|
doing = doing & ~TRAVEL
|
|
for(var/i = 0; i < maxStepsTick; ++i)
|
|
if(!IsDeadOrIncap())
|
|
if(myPath.len >= 1)
|
|
walk_to(src,myPath[1],0,5)
|
|
myPath -= myPath[1]
|
|
return 1
|
|
return 0
|
|
|
|
/mob/living/carbon/human/interactive/proc/job2area(target)
|
|
var/datum/job/T = target
|
|
if(T.title == "Assistant")
|
|
return /area/hallway/primary
|
|
if(T.title == "Captain" || T.title == "Head of Personnel")
|
|
return /area/bridge
|
|
if(T.title == "Bartender")
|
|
return /area/crew_quarters/bar
|
|
if(T.title == "Cook")
|
|
return /area/crew_quarters/kitchen
|
|
if(T.title == "Station Engineer" || T.title == "Chief Engineer" || T.title == "Atmospheric Technician")
|
|
return /area/engine
|
|
if(T.title == "Chief Medical Officer" || T.title == "Medical Doctor" || T.title == "Chemist" || T.title == "Virologist" || T.title == "Geneticist")
|
|
return /area/medical
|
|
if(T.title == "Research Director" || T.title == "Scientist" || T.title == "Roboticist")
|
|
return /area/toxins
|
|
if(T.title == "Head of Security" || T.title == "Warden" || T.title == "Security Officer" || T.title == "Detective")
|
|
return /area/security
|
|
if(T.title == "Botanist")
|
|
return /area/hydroponics
|
|
else
|
|
return pick(/area/hallway,/area/crew_quarters)
|
|
|
|
/mob/living/carbon/human/interactive/proc/target_filter(target)
|
|
var/list/filtered_targets = list(/area, /turf, /obj/machinery/door, /atom/movable/light, /obj/structure/cable, /obj/machinery/atmospherics)
|
|
var/list/L = target
|
|
for(var/atom/A in target) // added a bunch of "junk" that clogs up the general find procs
|
|
if(is_type_in_list(A,filtered_targets))
|
|
L -= A
|
|
return L
|
|
|
|
/mob/living/carbon/human/interactive/proc/shouldModulePass() // returns 1 if the npc is in anything "primary"
|
|
if(doing & FIGHTING)
|
|
return 1
|
|
if(retal)
|
|
return 1
|
|
return 0
|
|
|
|
///BUILT IN MODULES
|
|
/mob/living/carbon/human/interactive/proc/chatter(obj)
|
|
var/verbs_use = pick_list(NPC_SPEAK_FILE,"verbs_use")
|
|
var/verbs_touch = pick_list(NPC_SPEAK_FILE,"verbs_touch")
|
|
var/verbs_move = pick_list(NPC_SPEAK_FILE,"verbs_move")
|
|
var/nouns_insult = pick_list(NPC_SPEAK_FILE,"nouns_insult")
|
|
var/nouns_generic = pick_list(NPC_SPEAK_FILE,"nouns_generic")
|
|
var/nouns_objects = pick_list(NPC_SPEAK_FILE,"nouns_objects")
|
|
var/nouns_body = pick_list(NPC_SPEAK_FILE,"nouns_body")
|
|
var/adjective_insult = pick_list(NPC_SPEAK_FILE,"adjective_insult")
|
|
var/adjective_objects = pick_list(NPC_SPEAK_FILE,"adjective_objects")
|
|
var/adjective_generic = pick_list(NPC_SPEAK_FILE,"adjective_generic")
|
|
var/curse_words = pick_list(NPC_SPEAK_FILE,"curse_words")
|
|
|
|
var/chatmsg = ""
|
|
|
|
if(prob(10)) // 10% chance to broadcast it over the radio
|
|
chatmsg = ";"
|
|
|
|
if(prob(chattyness) || knownStrings.len < 10) // say a generic phrase, otherwise draw from our strings.
|
|
if(doing & INTERACTING)
|
|
if(prob(chattyness))
|
|
chatmsg += pick("This [nouns_objects] is a little [adjective_objects].",
|
|
"Well [verbs_use] my [nouns_body], this [nouns_insult] is pretty [adjective_insult].",
|
|
"[capitalize(curse_words)], what am I meant to do with this [adjective_insult] [nouns_objects].")
|
|
else if(doing & TRAVEL)
|
|
if(prob(chattyness))
|
|
chatmsg += pick("Oh [curse_words], [verbs_move]!",
|
|
"Time to get my [adjective_generic] [adjective_insult] [nouns_body] elsewhere.",
|
|
"I wonder if there is anything to [verbs_use] and [verbs_touch] somewhere else..")
|
|
else if(doing & FIGHTING)
|
|
if(prob(chattyness))
|
|
chatmsg += pick("I'm going to [verbs_use] you, you [adjective_insult] [nouns_insult]!",
|
|
"Rend and [verbs_touch], rend and [verbs_use]!",
|
|
"You [nouns_insult], I'm going to [verbs_use] you right in the [nouns_body]. JUST YOU WAIT!")
|
|
if(prob(chattyness/2))
|
|
chatmsg = ";"
|
|
var/what = pick(1,2,3,4,5)
|
|
switch(what)
|
|
if(1)
|
|
chatmsg += "Well [curse_words], this is a [adjective_generic] situation."
|
|
if(2)
|
|
chatmsg += "Oh [curse_words], that [nouns_insult] was one hell of an [adjective_insult] [nouns_body]."
|
|
if(3)
|
|
chatmsg += "I want to [verbs_use] that [nouns_insult] when I find them."
|
|
if(4)
|
|
chatmsg += "[pick("Innocent","Guilty","Traitorous","Honk")] until proven [adjective_generic]!"
|
|
if(5)
|
|
var/toSay = ""
|
|
for(var/i = 0; i < 5; i++)
|
|
curse_words = pick_list(NPC_SPEAK_FILE,"curse_words")
|
|
toSay += "[curse_words] "
|
|
chatmsg += "Hey [nouns_generic], why dont you go [toSay], you [nouns_insult]!"
|
|
else if(prob(chattyness))
|
|
chatmsg += pick(knownStrings)
|
|
if(prob(25)) // cut out some phrases now and then to make sure we're fresh and new
|
|
knownStrings -= pick(chatmsg)
|
|
|
|
if(chatmsg != ";" && chatmsg != "")
|
|
src.say(chatmsg)
|
|
|
|
|
|
/mob/living/carbon/human/interactive/proc/getAllContents()
|
|
var/list/allContents = list()
|
|
for(var/atom/A in contents)
|
|
allContents += A
|
|
if(A.contents.len > 0)
|
|
for(var/atom/B in A)
|
|
allContents += B
|
|
return allContents
|
|
|
|
/mob/living/carbon/human/interactive/proc/enforceHome()
|
|
var/list/validHome = get_area_turfs(job2area(myjob))
|
|
|
|
if(TARGET)
|
|
var/atom/tcheck = TARGET
|
|
if(tcheck)
|
|
if(!(get_turf(tcheck) in validHome))
|
|
TARGET = null
|
|
return 1
|
|
|
|
if(!(get_turf(src) in validHome))
|
|
tryWalk(pick(get_area_turfs(job2area(myjob))))
|
|
return 1
|
|
return 0
|
|
|
|
/mob/living/carbon/human/interactive/proc/npcDrop(var/obj/item/A,var/blacklist = 0)
|
|
if(blacklist)
|
|
blacklistItems += A
|
|
A.loc = get_turf(src) // drop item works inconsistently
|
|
enforce_hands()
|
|
update_icons()
|
|
|
|
/mob/living/carbon/human/interactive/proc/traitor(obj)
|
|
|
|
if(traitorType == SNPC_PSYCHO)
|
|
traitorTarget = pick(nearby)
|
|
|
|
if(prob(traitorScale))
|
|
if(!Adjacent(traitorTarget) && !(traitorType == SNPC_PSYCHO))
|
|
tryWalk(get_turf(traitorTarget))
|
|
else
|
|
switch(traitorType)
|
|
if(SNPC_BRUTE)
|
|
retal = 1
|
|
retal_target = traitorTarget
|
|
if(SNPC_STEALTH)
|
|
if(istype(traitorTarget,/mob)) // it's inside something, lets kick their shit in
|
|
var/mob/M = traitorTarget
|
|
if(!M.stat)
|
|
retal = 1
|
|
retal_target = traitorTarget
|
|
else
|
|
var/obj/item/I = traitorTarget
|
|
I.loc = get_turf(traitorTarget) // pull it outta them
|
|
else
|
|
take_to_slot(traitorTarget)
|
|
if(SNPC_MARTYR)
|
|
customEmote("[src]'s chest opens up, revealing a large mass of explosives and tangled wires!")
|
|
if(inactivity_period <= 0)
|
|
inactivity_period = 9999 // technically infinite
|
|
if(do_after(src,60,target=traitorTarget))
|
|
customEmote("A fire bursts from [src]'s eyes, igniting white hot and consuming their body in a flaming explosion!")
|
|
explosion(src, 6, 6, 6)
|
|
else
|
|
inactivity_period = 0
|
|
customEmote("[src]'s chest closes, hiding their insides.")
|
|
if(SNPC_PSYCHO)
|
|
var/choice = pick(typesof(/obj/item/weapon/grenade/chem_grenade) - /obj/item/weapon/grenade/chem_grenade)
|
|
|
|
new choice(src)
|
|
|
|
retal = 1
|
|
retal_target = traitorTarget
|
|
|
|
/mob/living/carbon/human/interactive/proc/botany(obj)
|
|
if(shouldModulePass())
|
|
return
|
|
|
|
if(enforceHome())
|
|
return
|
|
|
|
var/list/allContents = getAllContents()
|
|
|
|
var/obj/item/weapon/reagent_containers/glass/beaker/bluespace/internalBeaker = locate(/obj/item/weapon/reagent_containers/glass/beaker/bluespace) in allContents
|
|
var/obj/item/weapon/storage/bag/plants/internalBag = locate(/obj/item/weapon/storage/bag/plants) in allContents
|
|
|
|
if(!internalBag)
|
|
internalBag = new/obj/item/weapon/storage/bag/plants(src)
|
|
if(!internalBeaker)
|
|
internalBeaker = new/obj/item/weapon/reagent_containers/glass/beaker/bluespace(src)
|
|
internalBeaker.name = "Grow-U-All Super Spray"
|
|
|
|
if(internalBeaker && internalBag)
|
|
var/obj/machinery/hydroponics/HP
|
|
|
|
//consider the appropriate target
|
|
var/list/considered = list()
|
|
|
|
for(var/obj/machinery/hydroponics/tester in view(12,src))
|
|
considered[tester] = 1
|
|
|
|
if(!tester.myseed)
|
|
considered[tester] += 50
|
|
if(tester.weedlevel > 0)
|
|
considered[tester] += 5
|
|
if(tester.pestlevel > 0)
|
|
considered[tester] += 5
|
|
if(tester.nutrilevel < tester.maxnutri)
|
|
considered[tester] += 15
|
|
if(tester.waterlevel < tester.maxwater)
|
|
considered[tester] += 15
|
|
if(tester.harvest || tester.dead)
|
|
considered[tester] += 100
|
|
considered[tester] = max(1,considered[tester] - get_dist(src,tester))
|
|
|
|
var/index = 0
|
|
for(var/A in considered)
|
|
++index
|
|
if(considered[A] > considered[HP] || !HP)
|
|
HP = considered[index]
|
|
|
|
if(HP)
|
|
TARGET = HP
|
|
if(!Adjacent(HP))
|
|
tryWalk(get_turf(HP))
|
|
else
|
|
if(HP.harvest || HP.dead)
|
|
HP.attack_hand(src)
|
|
else if(!HP.myseed)
|
|
var/seedType = pick(typesof(/obj/item/seeds) - /obj/item/seeds)
|
|
var/obj/item/seeds/SEED = new seedType(src)
|
|
customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")] towards [TARGET], producing a [SEED]!")
|
|
HP.attackby(SEED,src)
|
|
else
|
|
var/change = 0
|
|
if(HP.weedlevel > 0)
|
|
change = 1
|
|
if(!internalBeaker.reagents.has_reagent("weedkiller", 10))
|
|
internalBeaker.reagents.add_reagent("weedkiller",10)
|
|
if(HP.pestlevel > 0)
|
|
change = 1
|
|
if(!internalBeaker.reagents.has_reagent("pestkiller", 10))
|
|
internalBeaker.reagents.add_reagent("pestkiller",10)
|
|
if(HP.nutrilevel < HP.maxnutri)
|
|
change = 1
|
|
if(!internalBeaker.reagents.has_reagent("eznutriment", 15))
|
|
internalBeaker.reagents.add_reagent("eznutriment",15)
|
|
if(!internalBeaker.reagents.has_reagent("diethylamine", 15))
|
|
internalBeaker.reagents.add_reagent("diethylamine",15)
|
|
if(HP.waterlevel < HP.maxwater)
|
|
change = 1
|
|
if(!internalBeaker.reagents.has_reagent("holywater", 50))
|
|
internalBeaker.reagents.add_reagent("holywater",50)
|
|
if(change)
|
|
HP.attackby(internalBeaker,src)
|
|
|
|
var/obj/item/weapon/reagent_containers/food/snacks/grown/GF = locate(/obj/item/weapon/reagent_containers/food/snacks/grown) in view(12,src)
|
|
if(GF)
|
|
if(!Adjacent(GF))
|
|
tryWalk(get_turf(GF))
|
|
else
|
|
GF.attackby(internalBag,src)
|
|
|
|
if(internalBag.contents.len > 0)
|
|
var/obj/machinery/smartfridge/SF = locate(/obj/machinery/smartfridge) in range(12,src)
|
|
if(!Adjacent(SF))
|
|
tryWalk(get_turf(SF), 1)
|
|
else
|
|
customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")], upending the [internalBag]'s contents all over the [SF]!")
|
|
//smartfridges call updateUsrDialog when you call attackby, so we're going to have to cheese-magic-space this
|
|
for(var/obj/toLoad in internalBag.contents)
|
|
if(contents.len >= SF.max_n_of_items)
|
|
break
|
|
if(SF.accept_check(toLoad))
|
|
SF.load(toLoad)
|
|
else
|
|
qdel(toLoad) // destroy everything we dont need
|
|
|
|
/mob/living/carbon/human/interactive/proc/bartend(obj)
|
|
if(shouldModulePass())
|
|
return
|
|
|
|
if(enforceHome())
|
|
return
|
|
|
|
var/list/rangeCheck = oview(6,src)
|
|
var/obj/structure/table/RT
|
|
|
|
var/mob/living/carbon/human/serveTarget
|
|
|
|
for(var/mob/living/carbon/human/H in rangeCheck)
|
|
if(!locate(/obj/item/weapon/reagent_containers/food/drinks) in orange(1,H))
|
|
serveTarget = H
|
|
|
|
|
|
if(serveTarget)
|
|
RT = locate(/obj/structure/table) in orange(1,serveTarget)
|
|
|
|
if(RT && serveTarget)
|
|
if(!Adjacent(RT))
|
|
tryWalk(get_turf(RT))
|
|
else
|
|
var/drinkChoice = pick(typesof(/obj/item/weapon/reagent_containers/food/drinks) - /obj/item/weapon/reagent_containers/food/drinks)
|
|
if(drinkChoice)
|
|
var/obj/item/weapon/reagent_containers/food/drinks/D = new drinkChoice(get_turf(src))
|
|
RT.attackby(D,src)
|
|
src.say("[pick("Something to wet your whistle!","Down the hatch, a tasty beverage!","One drink, coming right up!","Tasty liquid for your oral intake!","Enjoy!")]")
|
|
customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")], serving up a [D]!")
|
|
|
|
/mob/living/carbon/human/interactive/proc/shitcurity(obj)
|
|
var/list/allContents = getAllContents()
|
|
|
|
for(var/mob/living/carbon/human/C in nearby)
|
|
var/perpname = C.get_face_name(C.get_id_name())
|
|
var/datum/data/record/R = find_record("name", perpname, data_core.security)
|
|
if(R && R.fields["criminal"])
|
|
switch(R.fields["criminal"])
|
|
if("*Arrest*")
|
|
retalTarget(C)
|
|
|
|
if(retal && TARGET)
|
|
for(var/obj/item/I in allContents)
|
|
if(istype(I,/obj/item/weapon/restraints))
|
|
I.attack(TARGET,src) // go go bluespace restraint launcher!
|
|
sleep(25)
|
|
|
|
/mob/living/carbon/human/interactive/proc/clowning(obj)
|
|
if(shouldModulePass())
|
|
return
|
|
var/list/allContents = getAllContents()
|
|
var/list/rangeCheck = orange(12,src)
|
|
|
|
var/mob/living/carbon/human/clownTarget
|
|
var/list/clownPriority = list()
|
|
|
|
var/obj/item/weapon/reagent_containers/spray/S = locate(/obj/item/weapon/reagent_containers/spray) in allContents
|
|
|
|
if(!S)
|
|
S = new/obj/item/weapon/reagent_containers/spray(src)
|
|
S.amount_per_transfer_from_this = 10
|
|
|
|
for(var/mob/living/carbon/human/C in rangeCheck)
|
|
var/pranksNearby = 100
|
|
for(var/turf/open/T in orange(1,C))
|
|
for(var/obj/item/A in T)
|
|
if(istype(A,/obj/item/weapon/soap) || istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana) || istype(A,/obj/item/weapon/grown/bananapeel))
|
|
pranksNearby--
|
|
if(T.wet)
|
|
pranksNearby -= 10
|
|
clownPriority[C] = pranksNearby
|
|
|
|
for(var/A in clownPriority)
|
|
if(!clownTarget)
|
|
clownTarget = A
|
|
else
|
|
if(clownPriority[A] > clownPriority[clownTarget])
|
|
clownTarget = clownPriority[A]
|
|
|
|
if(clownTarget)
|
|
if(!Adjacent(clownTarget))
|
|
tryWalk(clownTarget)
|
|
else
|
|
var/hasPranked = 0
|
|
for(var/A in allContents)
|
|
if(prob(smartness/2) && !hasPranked)
|
|
if(istype(A,/obj/item/weapon/soap))
|
|
npcDrop(A)
|
|
hasPranked = 1
|
|
if(istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana))
|
|
var/obj/item/weapon/reagent_containers/food/snacks/B = A
|
|
B.attack(src, src)
|
|
if(istype(A,/obj/item/weapon/grown/bananapeel))
|
|
npcDrop(A)
|
|
hasPranked = 1
|
|
if(!hasPranked)
|
|
if(S.reagents.total_volume <= 5)
|
|
S.reagents.add_reagent("water", 25)
|
|
S.afterattack(get_turf(pick(orange(1,clownTarget))),src)
|
|
|
|
|
|
/mob/living/carbon/human/interactive/proc/healpeople(obj)
|
|
var/shouldTryHeal = 0
|
|
var/obj/item/stack/medical/M
|
|
|
|
var/list/allContents = getAllContents()
|
|
|
|
for(var/A in allContents)
|
|
if(istype(A,/obj/item/stack/medical))
|
|
shouldTryHeal = 1
|
|
M = A
|
|
|
|
var/obj/item/weapon/reagent_containers/hypospray/HPS
|
|
|
|
if(!locate(/obj/item/weapon/reagent_containers/hypospray) in allContents)
|
|
new/obj/item/weapon/reagent_containers/hypospray(src)
|
|
else
|
|
HPS = locate(/obj/item/weapon/reagent_containers/hypospray) in allContents
|
|
if(!shouldTryHeal)
|
|
shouldTryHeal = 2 // we have no stackables to use, lets use our internal hypospray instead
|
|
|
|
if(shouldTryHeal == 1)
|
|
for(var/mob/living/carbon/human/C in nearby)
|
|
if(C.health <= 75)
|
|
if(get_dist(src,C) <= 2)
|
|
src.say("Wait, [C], let me heal you!")
|
|
M.attack(C,src)
|
|
sleep(25)
|
|
else
|
|
tryWalk(get_turf(C))
|
|
else if(shouldTryHeal == 2)
|
|
if(HPS)
|
|
if(HPS.reagents.total_volume <= 0)
|
|
HPS.reagents.add_reagent("tricordrazine",30)
|
|
for(var/mob/living/carbon/human/C in nearby)
|
|
if(C.health <= 75 && C.reagents.get_reagent_amount("tricordrazine") <= 0) // make sure they wont be overdosing
|
|
if(get_dist(src,C) <= 2)
|
|
src.say("Wait, [C], let me heal you!")
|
|
HPS.attack(C,src)
|
|
sleep(25)
|
|
else
|
|
tryWalk(get_turf(C))
|
|
|
|
|
|
/mob/living/carbon/human/interactive/proc/dojanitor(obj)
|
|
if(shouldModulePass())
|
|
return
|
|
var/list/allContents = getAllContents()
|
|
//now with bluespace magic!
|
|
var/obj/item/weapon/reagent_containers/spray/S
|
|
if(!locate(/obj/item/weapon/reagent_containers/spray) in allContents)
|
|
new/obj/item/weapon/reagent_containers/spray(src)
|
|
else
|
|
S = locate(/obj/item/weapon/reagent_containers/spray) in allContents
|
|
|
|
if(S)
|
|
if(S.reagents.total_volume <= 5)
|
|
S.reagents.add_reagent("cleaner", 25) // bluespess water delivery for AI
|
|
|
|
var/obj/effect/decal/cleanable/TC
|
|
TC = locate(/obj/effect/decal/cleanable) in range(MAX_RANGE_FIND,src)
|
|
|
|
if(TC)
|
|
if(!Adjacent(TC))
|
|
tryWalk(TC)
|
|
else
|
|
S.afterattack(TC,src)
|
|
sleep(25)
|
|
|
|
/mob/living/carbon/human/interactive/proc/customEmote(var/text)
|
|
visible_message("<span class='notice'>[text]</span>")
|
|
|
|
// START COOKING MODULE
|
|
/mob/living/carbon/human/interactive/proc/cookingwithmagic(var/obj/item/weapon/reagent_containers/food/snacks/target)
|
|
if(Adjacent(target))
|
|
customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")] towards [target], and with a bang, it's instantly cooked!")
|
|
var/obj/item/weapon/reagent_containers/food/snacks/S = new target.cooked_type (get_turf(src))
|
|
target.initialize_cooked_food(S, 100)
|
|
if(target) // cleaning up old food seems inconsistent, so this will clean up stragglers
|
|
qdel(target)
|
|
else
|
|
tryWalk(target)
|
|
|
|
/mob/living/carbon/human/interactive/proc/souschef(obj)
|
|
if(shouldModulePass())
|
|
return
|
|
|
|
if(enforceHome())
|
|
return
|
|
|
|
var/list/allContents = getAllContents()
|
|
|
|
//Bluespace in some inbuilt tools
|
|
var/obj/item/weapon/kitchen/rollingpin/RP = locate(/obj/item/weapon/kitchen/rollingpin) in allContents
|
|
if(!RP)
|
|
new/obj/item/weapon/kitchen/rollingpin(src)
|
|
|
|
var/obj/item/weapon/kitchen/knife/KK = locate(/obj/item/weapon/kitchen/knife) in allContents
|
|
if(!KK)
|
|
new/obj/item/weapon/kitchen/knife(src)
|
|
|
|
var/foundCookable = 0
|
|
|
|
if(RP && KK) // Ready to cook!
|
|
|
|
var/list/rangeCheck = view(6,src)
|
|
|
|
//Make some basic custom food
|
|
var/list/customableTypes = list(/obj/item/weapon/reagent_containers/food/snacks/customizable,/obj/item/weapon/reagent_containers/food/snacks/store/bread/plain,/obj/item/weapon/reagent_containers/food/snacks/pizzabread,/obj/item/weapon/reagent_containers/food/snacks/bun,/obj/item/weapon/reagent_containers/food/snacks/store/cake/plain,/obj/item/weapon/reagent_containers/food/snacks/pie/plain,/obj/item/weapon/reagent_containers/food/snacks/pastrybase)
|
|
|
|
var/foundCustom
|
|
|
|
for(var/obj/item/weapon/reagent_containers/food/snacks/donkpocket/DP in rangeCheck) // donkpockets are hitler to chef SNPCs
|
|
if(prob(50))
|
|
customEmote("[src] points at the [DP], emitting a loud [pick("bellow","screech","yell","scream")], and it bursts into flame.")
|
|
qdel(DP)
|
|
|
|
for(var/customType in customableTypes)
|
|
var/A = locate(customType) in rangeCheck
|
|
if(A)
|
|
foundCustom = A // this will eventually wittle down to 0
|
|
|
|
|
|
var/obj/machinery/smartfridge/SF = locate(/obj/machinery/smartfridge) in rangeCheck
|
|
if(SF)
|
|
if(SF.contents.len > 0)
|
|
if(!Adjacent(SF))
|
|
tryWalk(get_turf(SF),1)
|
|
else
|
|
customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")], grabbing various foodstuffs from [SF] and sticking them in it's mouth!")
|
|
for(var/obj/item/A in SF.contents)
|
|
if(prob(smartness/2))
|
|
A.loc = src
|
|
|
|
|
|
if(foundCustom)
|
|
var/obj/item/weapon/reagent_containers/food/snacks/FC = foundCustom
|
|
for(var/obj/item/weapon/reagent_containers/food/snacks/toMake in allContents)
|
|
if(prob(smartness))
|
|
if(FC.reagents)
|
|
FC.attackby(toMake,src)
|
|
else
|
|
qdel(FC) // this food is usless, toss it
|
|
|
|
|
|
//Process dough into various states
|
|
var/obj/item/weapon/reagent_containers/food/snacks/dough/D = locate(/obj/item/weapon/reagent_containers/food/snacks/dough) in rangeCheck
|
|
var/obj/item/weapon/reagent_containers/food/snacks/flatdough/FD = locate(/obj/item/weapon/reagent_containers/food/snacks/flatdough) in rangeCheck
|
|
var/obj/item/weapon/reagent_containers/food/snacks/cakebatter/CB = locate(/obj/item/weapon/reagent_containers/food/snacks/cakebatter) in rangeCheck
|
|
var/obj/item/weapon/reagent_containers/food/snacks/piedough/PD = locate(/obj/item/weapon/reagent_containers/food/snacks/piedough) in rangeCheck
|
|
|
|
if(D)
|
|
TARGET = D
|
|
var/choice = pick(1,2)
|
|
if(choice == 1)
|
|
tryWalk(get_turf(D))
|
|
sleep(get_dist(src,D))
|
|
D.attackby(RP,src)
|
|
else
|
|
cookingwithmagic(D)
|
|
foundCookable = 1
|
|
else if(FD)
|
|
TARGET = FD
|
|
var/choice = pick(1,2)
|
|
if(choice == 1)
|
|
tryWalk(get_turf(D))
|
|
sleep(get_dist(src,D))
|
|
FD.attackby(KK,src)
|
|
else
|
|
cookingwithmagic(FD)
|
|
foundCookable = 1
|
|
else if(CB)
|
|
TARGET = CB
|
|
var/choice = pick(1,2)
|
|
if(choice == 1)
|
|
tryWalk(get_turf(D))
|
|
sleep(get_dist(src,D))
|
|
CB.attackby(RP,src)
|
|
else
|
|
cookingwithmagic(CB)
|
|
foundCookable = 1
|
|
else if(PD)
|
|
TARGET = PD
|
|
var/choice = pick(1,2)
|
|
if(choice == 1)
|
|
tryWalk(get_turf(D))
|
|
sleep(get_dist(src,D))
|
|
PD.attackby(KK,src)
|
|
else
|
|
cookingwithmagic(PD)
|
|
foundCookable = 1
|
|
|
|
|
|
//Cook various regular foods into processed versions
|
|
var/obj/item/weapon/reagent_containers/food/snacks/toCook = locate(/obj/item/weapon/reagent_containers/food/snacks) in rangeCheck
|
|
if(toCook)
|
|
if(toCook.cooked_type)
|
|
TARGET = toCook
|
|
foundCookable = 1
|
|
if(Adjacent(toCook))
|
|
cookingwithmagic(toCook)
|
|
else
|
|
tryWalk(get_turf(toCook))
|
|
|
|
var/list/finishedList = list()
|
|
for(var/obj/item/weapon/reagent_containers/food/snacks/toDisplay in allContents)
|
|
if(!toDisplay.cooked_type && !istype(toDisplay,/obj/item/weapon/reagent_containers/food/snacks/grown)) // dont display our ingredients
|
|
finishedList += toDisplay
|
|
|
|
for(var/obj/item/weapon/reagent_containers/food/snacks/toGrab in rangeCheck)
|
|
if(!(locate(/obj/structure/table/reinforced) in get_turf(toGrab))) //it's not being displayed
|
|
foundCookable = 1
|
|
if(!Adjacent(toGrab))
|
|
tryWalk(toGrab)
|
|
else
|
|
toGrab.loc = src
|
|
|
|
if(finishedList.len > 0)
|
|
var/obj/structure/table/reinforced/RT
|
|
|
|
for(var/obj/structure/table/reinforced/toCheck in rangeCheck)
|
|
var/counted = 0
|
|
for(var/obj/item/weapon/reagent_containers/food/snacks/S in get_turf(toCheck))
|
|
++counted
|
|
if(counted < 12) // make sure theres not too much food here
|
|
RT = toCheck
|
|
break
|
|
|
|
if(RT)
|
|
foundCookable = 1
|
|
if(!Adjacent(RT))
|
|
tryWalk(get_turf(RT))
|
|
else
|
|
for(var/obj/item/weapon/reagent_containers/food/snacks/toPlop in allContents)
|
|
RT.attackby(toPlop,src)
|
|
|
|
if(!foundCookable)
|
|
var/list/allTypes = list(/obj/item/weapon/reagent_containers/food/snacks/piedough,/obj/item/weapon/reagent_containers/food/snacks/cakebatter,/obj/item/weapon/reagent_containers/food/snacks/dough,/obj/item/weapon/reagent_containers/food/snacks/flatdough)
|
|
|
|
for(var/A in typesof(/obj/item/weapon/reagent_containers/food/snacks))
|
|
var/obj/item/weapon/reagent_containers/food/snacks/O = A
|
|
if(initial(O.cooked_type))
|
|
allTypes += A
|
|
|
|
var/chosenType = pick(allTypes)
|
|
|
|
var/obj/item/weapon/reagent_containers/food/snacks/newSnack = new chosenType(get_turf(src))
|
|
TARGET = newSnack
|
|
newSnack.reagents.remove_any((newSnack.reagents.total_volume/2)-1)
|
|
newSnack.name = "Synthetic [newSnack.name]"
|
|
customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")] as they vomit [newSnack] from their mouth!")
|
|
// END COOKING MODULE
|
|
|
|
/mob/living/carbon/human/interactive/proc/compareFaction(var/list/targetFactions)
|
|
var/hasSame = 0
|
|
|
|
for(var/A in targetFactions)
|
|
if(A in faction)
|
|
hasSame = 1
|
|
|
|
return hasSame
|
|
|
|
/mob/living/carbon/human/interactive/proc/combat(obj)
|
|
enforce_hands()
|
|
if(canmove)
|
|
if((graytide || (TRAITS & TRAIT_MEAN)) || retal)
|
|
interest += targetInterestShift
|
|
a_intent = "harm"
|
|
zone_selected = pick("chest","r_leg","l_leg","r_arm","l_arm","head")
|
|
doing |= FIGHTING
|
|
if(retal)
|
|
TARGET = retal_target
|
|
else
|
|
var/mob/living/M = locate(/mob/living) in oview(7,src)
|
|
if(M != src && !compareFaction(M.faction))
|
|
TARGET = M
|
|
if(!M)
|
|
doing = doing & ~FIGHTING
|
|
|
|
//no infighting
|
|
if(retal)
|
|
if(retal_target)
|
|
if(compareFaction(retal_target.faction))
|
|
retal = 0
|
|
retal_target = null
|
|
TARGET = null
|
|
doing = 0
|
|
|
|
//ensure we're using the best object possible
|
|
|
|
var/obj/item/weapon/best
|
|
var/foundFav = 0
|
|
var/list/allContents = getAllContents()
|
|
for(var/test in allContents)
|
|
for(var/a in favoured_types)
|
|
if(ispath(test,a) && !(doing & FIGHTING)) // if we're not in combat and we find our favourite things, use them (for people like janitor and doctors)
|
|
best = test
|
|
foundFav = 1
|
|
return
|
|
if(!foundFav)
|
|
if(istype(test,/obj/item/weapon))
|
|
var/obj/item/weapon/R = test
|
|
if(R.force > 2) // make sure we don't equip any non-weaponlike items, ie bags and stuff
|
|
if(!best)
|
|
best = R
|
|
else
|
|
if(best.force < R.force)
|
|
best = R
|
|
if(istype(R,/obj/item/weapon/gun))
|
|
var/obj/item/weapon/gun/G = R
|
|
if(G.can_shoot())
|
|
best = R
|
|
break // gun with ammo? screw the rest
|
|
if(best)
|
|
take_to_slot(best,1)
|
|
|
|
if((TARGET && (doing & FIGHTING))) // this is a redundancy check
|
|
var/mob/living/M = TARGET
|
|
if(isliving(M))
|
|
if(M.health > 1)
|
|
//THROWING OBJECTS
|
|
for(var/A in allContents)
|
|
if(istype(A,/obj/item/weapon/gun)) // guns are for shooting, not throwing.
|
|
continue
|
|
if(prob(robustness))
|
|
if(istype(A,/obj/item/weapon))
|
|
var/obj/item/weapon/W = A
|
|
if(W.throwforce > 19) // Only throw worthwile stuff, no more lobbing wrenches at wenches
|
|
npcDrop(W,1)
|
|
throw_item(TARGET)
|
|
if(istype(A,/obj/item/weapon/grenade)) // Allahu ackbar! ALLAHU ACKBARR!!
|
|
var/obj/item/weapon/grenade/G = A
|
|
G.attack_self(src)
|
|
if(prob(smartness))
|
|
npcDrop(G,1)
|
|
sleep(15)
|
|
throw_item(TARGET)
|
|
|
|
if(!main_hand && other_hand)
|
|
swap_hands()
|
|
if(main_hand)
|
|
if(main_hand.force != 0)
|
|
if(istype(main_hand,/obj/item/weapon/gun))
|
|
var/obj/item/weapon/gun/G = main_hand
|
|
if(G.can_trigger_gun(src))
|
|
if(istype(main_hand,/obj/item/weapon/gun/ballistic))
|
|
var/obj/item/weapon/gun/ballistic/P = main_hand
|
|
if(!P.chambered)
|
|
P.chamber_round()
|
|
P.update_icon()
|
|
else if(P.get_ammo(1) == 0)
|
|
P.update_icon()
|
|
npcDrop(P,1)
|
|
else
|
|
P.afterattack(TARGET, src)
|
|
else if(istype(main_hand,/obj/item/weapon/gun/energy))
|
|
var/obj/item/weapon/gun/energy/P = main_hand
|
|
var/stunning = 0
|
|
for(var/A in P.ammo_type)
|
|
if(ispath(A,/obj/item/ammo_casing/energy/electrode))
|
|
stunning = 1
|
|
var/shouldFire = 1
|
|
var/mob/stunCheck = TARGET
|
|
if(stunning && stunCheck.stunned)
|
|
shouldFire = 0
|
|
if(shouldFire)
|
|
if(P.power_supply.charge <= 10) // can shoot seems to bug out for tasers, using this hacky method instead
|
|
P.update_icon()
|
|
npcDrop(P,1)
|
|
else
|
|
P.afterattack(TARGET, src)
|
|
else
|
|
if(get_dist(src,TARGET) > 6)
|
|
if(!walk2derpless(TARGET))
|
|
timeout++
|
|
else
|
|
var/obj/item/weapon/W = main_hand
|
|
W.attack(TARGET,src)
|
|
else
|
|
G.loc = get_turf(src) // drop item works inconsistently
|
|
enforce_hands()
|
|
update_icons()
|
|
else
|
|
if(targetRange(TARGET) > 2)
|
|
tryWalk(TARGET)
|
|
else
|
|
if(Adjacent(TARGET))
|
|
a_intent = pick("disarm","harm")
|
|
M.attack_hand(src)
|
|
timeout++
|
|
else if(timeout >= 10 || !(targetRange(M) > 14))
|
|
doing = doing & ~FIGHTING
|
|
timeout = 0
|
|
TARGET = null
|
|
retal = 0
|
|
retal_target = null
|
|
else
|
|
timeout++
|
|
|
|
|
|
/mob/living/carbon/human/interactive/proc/nearbyscan(obj)
|
|
nearby = list()
|
|
for(var/mob/living/M in view(4,src))
|
|
if(M != src)
|
|
nearby += M
|
|
|
|
//END OF MODULES
|
|
/mob/living/carbon/human/interactive/angry/New()
|
|
TRAITS |= TRAIT_ROBUST
|
|
TRAITS |= TRAIT_MEAN
|
|
faction += "bot_angry"
|
|
..()
|
|
|
|
/mob/living/carbon/human/interactive/friendly/New()
|
|
TRAITS |= TRAIT_FRIENDLY
|
|
TRAITS |= TRAIT_UNROBUST
|
|
faction += "bot_friendly"
|
|
faction += "neutral"
|
|
functions -= "combat"
|
|
..()
|
|
|
|
/mob/living/carbon/human/interactive/greytide/New()
|
|
TRAITS |= TRAIT_ROBUST
|
|
TRAITS |= TRAIT_MEAN
|
|
TRAITS |= TRAIT_THIEVING
|
|
TRAITS |= TRAIT_DUMB
|
|
maxInterest = 5 // really short attention span
|
|
targetInterestShift = 2 // likewise
|
|
faction += "bot_grey"
|
|
graytide = 1
|
|
..()
|
|
|
|
//Walk softly and carry a big stick
|
|
/mob/living/carbon/human/interactive/robust/New()
|
|
TRAITS |= TRAIT_FRIENDLY
|
|
TRAITS |= TRAIT_ROBUST
|
|
TRAITS |= TRAIT_SMART
|
|
faction += "bot_power"
|
|
..() |