mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-12 06:41:24 +01:00
0f70e6810b
- Added 4 new HUD styles - Humans recreate their hud if they change their UI pref mid-game - Refactored how objects are added to the client screen - HUD's are now handled by subtype and mob/proc/create_mob_hud() - HUD visibility is no longer chained directly to the F12 button, it's a proc on HUD datums now - Mobs only create/destroy their HUD when necessary, not every Login() - Destroyed aim-mode, it didn't work and I couldn't make it work. - Renamed all of the screen1_x.dmi files to screen_x.dmi - Removed screen1.dmi, screen_gen.dmi now handles generic icons.
954 lines
28 KiB
Plaintext
954 lines
28 KiB
Plaintext
/*
|
|
NPC VAR EXPLANATIONS (for modules and other things)
|
|
|
|
doing = their current action, SNPC_INTERACTING, SNPC_TRAVEL or SNPC_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
|
|
*/
|
|
/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/atom/TARGET = null
|
|
var/atom/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/default_job = /datum/job/civilian // the type for the default job
|
|
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 = SNPC_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 = 0
|
|
var/forceProcess = 0
|
|
var/processTime = 10
|
|
var/speak_file = "npc_chatter.json"
|
|
|
|
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)
|
|
change_eye_color(255, 0, 0)
|
|
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)
|
|
change_gender(pick("male", "female"))
|
|
rename_character(real_name, species.get_random_name(gender))
|
|
//job handling
|
|
myjob = new default_job()
|
|
job = myjob.title
|
|
mind.assigned_role = job
|
|
myjob.equip(src)
|
|
myjob.apply_fingerprints(T)
|
|
|
|
/mob/living/carbon/human/interactive/proc/reset()
|
|
walk(src, 0)
|
|
timeout = 100
|
|
retal = 0
|
|
doing = 0
|
|
inactivity_period = 0
|
|
|
|
/client/proc/resetSNPC(mob/living/carbon/human/interactive/T in npc_master.botPool_l)
|
|
set name = "Reset SNPC"
|
|
set desc = "Reset the SNPC"
|
|
set category = "Debug"
|
|
|
|
if(!holder)
|
|
return
|
|
|
|
if(istype(T))
|
|
T.reset()
|
|
|
|
/client/proc/toggleSNPC(mob/living/carbon/human/interactive/T in npc_master.botPool_l)
|
|
set name = "Toggle SNPC Proccessing Mode"
|
|
set desc = "Toggle SNPC Proccessing Mode"
|
|
set category = "Debug"
|
|
|
|
if(!holder)
|
|
return
|
|
|
|
if(istype(T))
|
|
T.alternateProcessing = !T.alternateProcessing
|
|
T.forceProcess = 1
|
|
to_chat(usr, "[T]'s processing has been switched to [T.alternateProcessing ? "High Profile" : "Low Profile"]")
|
|
|
|
/client/proc/customiseSNPC(mob/living/carbon/human/interactive/T in npc_master.botPool_l)
|
|
set name = "Customize SNPC"
|
|
set desc = "Customise the SNPC"
|
|
set category = "Debug"
|
|
|
|
if(!holder)
|
|
return
|
|
|
|
if(!istype(T))
|
|
return
|
|
|
|
var/list/jobs[0]
|
|
for(var/datum/job/j in job_master.occupations)
|
|
if(j.title != "AI" && j.title != "Cyborg")
|
|
jobs[j.title] = j
|
|
jobs = sortAssoc(jobs)
|
|
var/job_name = input("Choose Job") as null|anything in jobs
|
|
|
|
if(job_name)
|
|
var/datum/job/cjob = jobs[job_name]
|
|
var/alt_title = input("Choose Alt Title") in (list(job_name) + cjob.alt_titles)
|
|
|
|
T.myjob = cjob
|
|
T.job = cjob.title
|
|
T.mind.assigned_role = cjob.title
|
|
for(var/obj/item/W in T)
|
|
qdel(W)
|
|
T.myjob.equip(T)
|
|
T.myjob.apply_fingerprints(T)
|
|
T.doSetup(alt_title)
|
|
|
|
var/shouldDoppel = input("Do you want the SNPC to disguise themself as a crewmember?") as anything in list("Yes", "No")
|
|
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
|
|
T.set_species(chosen.species.name)
|
|
T.body_accessory = chosen.body_accessory
|
|
T.dna = toDoppel.Clone()
|
|
T.UpdateAppearance()
|
|
domutcheck(T)
|
|
|
|
var/doTrait = input("Do you want the SNPC to be a traitor?") as anything in list("Yes", "No")
|
|
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 anything in list("Yes", "No")
|
|
if(doTele == "Yes")
|
|
T.loc = pick(get_area_turfs(T.job2area(T.myjob)))
|
|
|
|
T.revive()
|
|
|
|
/mob/living/carbon/human/interactive/proc/doSetup(alt_title = null)
|
|
Path_ID = new /obj/item/weapon/card/id(src)
|
|
|
|
var/datum/job/captain/C = new/datum/job/captain
|
|
Path_ID.access = C.get_access()
|
|
|
|
if(!alt_title)
|
|
alt_title = job
|
|
|
|
MYID = new(src)
|
|
MYID.name = "[real_name]'s ID Card ([alt_title])"
|
|
MYID.assignment = "[alt_title]"
|
|
MYID.rank = job
|
|
MYID.sex = capitalize(gender)
|
|
MYID.age = age
|
|
MYID.registered_name = real_name
|
|
MYID.photo = get_id_photo(src)
|
|
MYID.access = Path_ID.access // Automatons have strange powers... strange indeed
|
|
|
|
RPID = new(src)
|
|
RPID.name = "[real_name]'s ID Card ([alt_title])"
|
|
RPID.assignment = "[alt_title]"
|
|
RPID.rank = job
|
|
RPID.sex = capitalize(gender)
|
|
RPID.age = age
|
|
RPID.registered_name = real_name
|
|
RPID.photo = get_id_photo(src)
|
|
RPID.access = myjob.get_access()
|
|
|
|
equip_to_slot_or_del(MYID, slot_wear_id)
|
|
MYPDA = new(src)
|
|
MYPDA.owner = real_name
|
|
MYPDA.ownjob = alt_title
|
|
MYPDA.ownrank = job
|
|
MYPDA.name = "PDA-[real_name] ([alt_title])"
|
|
equip_to_slot_or_del(MYPDA, slot_belt)
|
|
zone_sel.selecting = "chest"
|
|
//arms
|
|
if(prob((SNPC_FUZZY_CHANCE_LOW+SNPC_FUZZY_CHANCE_HIGH)/4))
|
|
var/obj/item/organ/external/R = organs_by_name["r_arm"]
|
|
if(R)
|
|
R.robotize()
|
|
else
|
|
var/obj/item/organ/external/L = organs_by_name["l_arm"]
|
|
if(L)
|
|
L.robotize()
|
|
//legs
|
|
if(prob((SNPC_FUZZY_CHANCE_LOW+SNPC_FUZZY_CHANCE_HIGH)/4))
|
|
var/obj/item/organ/external/R = organs_by_name["r_leg"]
|
|
if(R)
|
|
R.robotize()
|
|
else
|
|
var/obj/item/organ/external/L = organs_by_name["l_leg"]
|
|
if(L)
|
|
L.robotize()
|
|
UpdateDamageIcon()
|
|
regenerate_icons()
|
|
|
|
hand = 0
|
|
functions = list("nearbyscan", "combat", "shitcurity", "chatter") // stop customize adding multiple copies of a function
|
|
setup_job(job)
|
|
|
|
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/setup_job(thejob)
|
|
switch(thejob)
|
|
if("Civilian")
|
|
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("Nanotrasen Representative")
|
|
favoured_types = list(/obj/item/clothing, /obj/item/weapon/stamp/centcom, /obj/item/weapon/paper, /obj/item/weapon/melee/baton/loaded/ntcane)
|
|
functions += "paperwork"
|
|
if("Magistrate", "Internal Affairs Agent")
|
|
favoured_types = list(/obj/item/clothing, /obj/item/weapon/stamp/law, /obj/item/weapon/paper)
|
|
functions += "paperwork"
|
|
if("Quartermaster", "Cargo Technician")
|
|
favoured_types = list(/obj/item/clothing, /obj/item/weapon/stamp/granted, /obj/item/weapon/stamp/denied, /obj/item/weapon/paper, /obj/item/weapon/clipboard)
|
|
functions += "stamping"
|
|
if("Chef")
|
|
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", "Life Support Specialist", "Mechanic")
|
|
favoured_types = list(/obj/item/stack, /obj/item/weapon, /obj/item/clothing)
|
|
if("Chief Medical Officer", "Medical Doctor", "Chemist", "Virologist", "Geneticist", "Psychiatrist", "Paramedic", "Brig Physician")
|
|
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", "Security Pod Pilot", "Blueshield")
|
|
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/bananapeel)
|
|
functions += "clowning"
|
|
if("Botanist")
|
|
favoured_types = list(/obj/machinery/portable_atmospherics/hydroponics, /obj/item/weapon/reagent_containers, /obj/item/weapon)
|
|
functions += "botany"
|
|
restrictedJob = 1
|
|
else
|
|
favoured_types = list(/obj/item/clothing)
|
|
|
|
/mob/living/carbon/human/interactive/proc/makeTraitor(var/inPers)
|
|
isTraitor = 1
|
|
traitorScale = (slyness + smartness) + rand(-10,10)
|
|
traitorType = inPers
|
|
|
|
ticker.mode.traitors += mind
|
|
mind.special_role = "traitor"
|
|
var/datum/mindslaves/slaved = new()
|
|
slaved.masters += mind
|
|
mind.som = slaved
|
|
ticker.mode.update_traitor_icons_added(mind)
|
|
|
|
switch(traitorType)
|
|
if(SNPC_BRUTE) // SMASH KILL RAAARGH
|
|
var/datum/objective/assassinate/A = new
|
|
A.owner = mind
|
|
A.find_target()
|
|
mind.objectives += A
|
|
traitorTarget = A.target.current
|
|
if(SNPC_STEALTH) // Shhh we is sneekies
|
|
var/datum/objective/steal/S = new
|
|
S.owner = mind
|
|
S.find_target()
|
|
mind.objectives += S
|
|
traitorTarget = locate(S.steal_target.typepath) in world
|
|
if(SNPC_MARTYR) // MY LIFE FOR SPESZUL
|
|
var/targetType = pick(/obj/structure/particle_accelerator, /obj/machinery/gravity_generator/main, /obj/machinery/power/smes)
|
|
traitorTarget = locate(targetType) in world
|
|
var/datum/objective/O = new("Destroy \the [traitorTarget].")
|
|
O.owner = mind
|
|
mind.objectives += O
|
|
if(SNPC_PSYCHO) // YOU'RE LIKE A FLESH BICYLE AND I WANT TO DISMANTLE YOU
|
|
var/datum/objective/hijack/H = new
|
|
H.owner = mind
|
|
mind.objectives += H
|
|
traitorTarget = null
|
|
|
|
functions += "traitor"
|
|
faction -= "neutral"
|
|
faction += "hostile"
|
|
|
|
/mob/living/carbon/human/interactive/create_mob_hud()
|
|
if(!hud_used)
|
|
hud_used = new /datum/hud/human(src)
|
|
|
|
/mob/living/carbon/human/interactive/New()
|
|
..()
|
|
snpc_list += src
|
|
|
|
create_mob_hud()
|
|
|
|
sync_mind()
|
|
random()
|
|
doSetup()
|
|
npc_master.insertBot(src)
|
|
loadVoice()
|
|
hear_radio_list += src
|
|
|
|
// 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/Destroy()
|
|
hear_radio_list -= src
|
|
snpc_list -= src
|
|
return ..()
|
|
|
|
/mob/living/carbon/human/interactive/proc/retalTarget(mob/living/target)
|
|
var/mob/living/M = target
|
|
if(istype(M))
|
|
if(health > 0)
|
|
if(M.a_intent == "help" && !incapacitated())
|
|
chatter()
|
|
if(istype(target, /mob/living/carbon) && !retal && prob(SNPC_FUZZY_CHANCE_LOW))
|
|
var/mob/living/carbon/C = target
|
|
if(!Adjacent(target))
|
|
tryWalk(target)
|
|
else
|
|
C.help_shake_act(src)
|
|
if(M.a_intent == "harm")
|
|
retal = 1
|
|
retal_target = target
|
|
|
|
//Retaliation clauses
|
|
|
|
/mob/living/carbon/human/interactive/attacked_by(obj/item/I, mob/living/user, def_zone)
|
|
..()
|
|
retalTarget(user)
|
|
|
|
/mob/living/carbon/human/interactive/hitby(atom/movable/AM)
|
|
..()
|
|
var/mob/living/carbon/C = locate(/mob/living/carbon) in view(SNPC_MIN_RANGE_FIND, src)
|
|
if(C)
|
|
retalTarget(C)
|
|
|
|
/mob/living/carbon/human/interactive/bullet_act(obj/item/projectile/P)
|
|
..()
|
|
retalTarget(P.firer)
|
|
|
|
/mob/living/carbon/human/interactive/attack_hand(mob/living/carbon/human/M)
|
|
..()
|
|
retalTarget(M)
|
|
|
|
/mob/living/carbon/human/interactive/show_inv(mob/user)
|
|
..()
|
|
retalTarget(user)
|
|
|
|
/mob/living/carbon/human/interactive/can_inject(mob/user, error_msg, target_zone, var/penetrate_thick = 0)
|
|
..()
|
|
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 & SNPC_INTERACTING)
|
|
toReturn += "interacting with something, "
|
|
if(doin & SNPC_FIGHTING)
|
|
toReturn += "engaging in combat, "
|
|
if(doin & SNPC_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
|
|
if(hand)
|
|
if(!l_hand)
|
|
main_hand = null
|
|
if(r_hand)
|
|
swap_hands()
|
|
else
|
|
if(!r_hand)
|
|
main_hand = null
|
|
if(l_hand)
|
|
swap_hands()
|
|
|
|
/mob/living/carbon/human/interactive/proc/swap_hands()
|
|
hand = !hand
|
|
var/obj/item/T = other_hand
|
|
main_hand = other_hand
|
|
other_hand = T
|
|
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_l_hand, "right hand" = slot_r_hand)
|
|
if(hands)
|
|
slots = list("left hand" = slot_l_hand, "right hand" = slot_r_hand)
|
|
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(slot_l_store, slot_r_store, slot_l_hand, slot_r_hand)
|
|
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))
|
|
BP.handle_item_insertion(I)
|
|
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/death()
|
|
saveVoice()
|
|
..()
|
|
|
|
/mob/living/carbon/human/interactive/hear_say(message, verb = "says", datum/language/language = null, alt_name = "", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol)
|
|
if(!istype(speaker, /mob/living/carbon/human/interactive))
|
|
knownStrings |= html_decode(message)
|
|
..()
|
|
|
|
/mob/living/carbon/human/interactive/hear_radio(message, verb = "says", datum/language/language=null, part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target)
|
|
if(!istype(speaker, /mob/living/carbon/human/interactive))
|
|
knownStrings |= html_decode(message)
|
|
..()
|
|
|
|
/mob/living/carbon/human/interactive/proc/doProcess()
|
|
forceProcess = 0
|
|
|
|
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())
|
|
reset()
|
|
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
|
|
var/mistake_chance = (100 - smartness) / 2
|
|
spawn(0)
|
|
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)
|
|
spawn(0)
|
|
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.p_open = 1
|
|
AL.update_icon()
|
|
AL.shock(src, mistake_chance)
|
|
sleep(5)
|
|
AL.unlock()
|
|
if(prob(mistake_chance))
|
|
if(!AL.wires.IsIndexCut(AIRLOCK_WIRE_DOOR_BOLTS))
|
|
AL.wires.CutWireIndex(AIRLOCK_WIRE_DOOR_BOLTS)
|
|
else
|
|
if(AL.wires.IsIndexCut(AIRLOCK_WIRE_DOOR_BOLTS))
|
|
AL.wires.CutWireIndex(AIRLOCK_WIRE_DOOR_BOLTS, 1)
|
|
if(AL.locked)
|
|
AL.wires.UpdatePulsed(AIRLOCK_WIRE_DOOR_BOLTS)
|
|
if(!AL.wires.IsIndexCut(AIRLOCK_WIRE_MAIN_POWER1))
|
|
AL.wires.CutWireIndex(AIRLOCK_WIRE_MAIN_POWER1)
|
|
if(!AL.wires.IsIndexCut(AIRLOCK_WIRE_MAIN_POWER2))
|
|
AL.wires.CutWireIndex(AIRLOCK_WIRE_MAIN_POWER2)
|
|
if(prob(mistake_chance) && !AL.wires.IsIndexCut(AIRLOCK_WIRE_SAFETY))
|
|
AL.wires.CutWireIndex(AIRLOCK_WIRE_SAFETY)
|
|
if(prob(mistake_chance) && !AL.wires.IsIndexCut(AIRLOCK_WIRE_ELECTRIFY))
|
|
AL.wires.CutWireIndex(AIRLOCK_WIRE_ELECTRIFY)
|
|
sleep(5)
|
|
AL.p_open = 0
|
|
AL.update_icon()
|
|
D.open()
|
|
|
|
if(update_hands)
|
|
if(l_hand || r_hand)
|
|
if(l_hand)
|
|
hand = 1
|
|
main_hand = l_hand
|
|
if(r_hand)
|
|
other_hand = r_hand
|
|
else if(r_hand)
|
|
hand = 0
|
|
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(grabbed_by.len > 0)
|
|
for(var/obj/item/weapon/grab/G in grabbed_by)
|
|
if(Adjacent(G))
|
|
a_intent = "disarm"
|
|
G.assailant.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 && (TARGET in blacklistItems)) // don't use blacklisted items
|
|
TARGET = null
|
|
|
|
if(TARGET && Adjacent(TARGET))
|
|
doing |= SNPC_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
|
|
insert_into_backpack() // dump random item into backpack to make space
|
|
//---------ITEMS
|
|
if(istype(TARGET, /obj/item))
|
|
var/obj/item/I = TARGET
|
|
if(I.anchored)
|
|
TARGET = null
|
|
else if(istype(TARGET, /obj/item/weapon))
|
|
var/obj/item/weapon/W = TARGET
|
|
if(W.force >= best_force || prob((SNPC_FUZZY_CHANCE_LOW + SNPC_FUZZY_CHANCE_HIGH) / 2) || favouredObjIn(list(W)))
|
|
if(!l_hand || !r_hand)
|
|
put_in_hands(W)
|
|
else
|
|
insert_into_backpack()
|
|
else
|
|
if(!l_hand || !r_hand)
|
|
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 loc)
|
|
equip_to_appropriate_slot(MYPDA)
|
|
if(MYID in 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 & ~SNPC_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 |= SNPC_TRAVEL
|
|
if(!isTraitor || !traitorTarget || get_dist(src, traitorTarget) >= SNPC_MAX_RANGE_FIND || get_dist(src, traitorTarget) <= 1)
|
|
var/choice = rand(1,50)
|
|
switch(choice)
|
|
if(1 to 30)
|
|
//chance to chase an item
|
|
TARGET = locate(/obj/item) in favouredObjIn(oview(SNPC_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(SNPC_MAX_RANGE_FIND, src, 1))))
|
|
if(46 to 50)
|
|
TARGET = pick(target_filter(oview(SNPC_MIN_RANGE_FIND, src)))
|
|
else
|
|
TARGET = traitorTarget
|
|
tryWalk(TARGET)
|
|
LAST_TARGET = TARGET
|
|
if(alternateProcessing)
|
|
spawn(processTime)
|
|
doProcess()
|
|
|
|
/mob/living/carbon/human/interactive/proc/favouredObjIn(list/inList)
|
|
var/list/outList = list()
|
|
for(var/i in inList)
|
|
for(var/path in favoured_types)
|
|
if(istype(i, path))
|
|
outList += i
|
|
if(!outList.len)
|
|
outList = inList
|
|
return outList
|
|
|
|
/mob/living/carbon/human/interactive/proc/callfunction(Proc)
|
|
spawn(0)
|
|
call(src, Proc)(src)
|
|
|
|
/mob/living/carbon/human/interactive/proc/tryWalk(turf/inTarget)
|
|
if(restrictedJob) // 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, 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(myPath.len <= 0)
|
|
myPath = get_path_to(src, get_turf(target), /turf/proc/Distance, SNPC_MAX_RANGE_FIND + 1, 250,1, id=Path_ID)
|
|
|
|
if(myPath)
|
|
if(myPath.len > 0)
|
|
doing = doing & ~SNPC_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
|
|
switch(T.title)
|
|
if("Civilian", "Paramedic")
|
|
return /area/hallway/primary
|
|
if("Captain", "Head of Personnel", "Blueshield")
|
|
return /area/bridge
|
|
if("Bartender")
|
|
return /area/crew_quarters/bar
|
|
if("Chef")
|
|
return /area/crew_quarters/kitchen
|
|
if("Station Engineer", "Chief Engineer", "Mechanic")
|
|
return /area/engine
|
|
if("Life Support Specialist")
|
|
return /area/atmos
|
|
if("Chief Medical Officer", "Medical Doctor", "Chemist", "Virologist", "Psychiatrist")
|
|
return /area/medical
|
|
if("Geneticist")
|
|
return /area/medical/genetics
|
|
if("Research Director", "Scientist")
|
|
return /area/toxins
|
|
if("Roboticist")
|
|
return /area/assembly/robotics
|
|
if("Head of Security", "Warden", "Security Officer", "Detective", "Security Pod Pilot", "Brig Physician", "Magistrate", "Internal Affairs Agent")
|
|
return /area/security
|
|
if("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/lighting_overlay, /obj/structure/cable, /obj/machinery/atmospherics, /obj/item/device/radio/intercom)
|
|
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 & (SNPC_FIGHTING | SNPC_SPECIAL))
|
|
return 1
|
|
if(retal)
|
|
return 1
|
|
return 0
|
|
|
|
/mob/living/carbon/human/interactive/proc/getAllContents()
|
|
var/list/allContents = list()
|
|
for(var/atom/A in contents)
|
|
allContents += A
|
|
if(A.contents.len)
|
|
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(obj/item/A, blacklist = 0)
|
|
if(blacklist)
|
|
blacklistItems += A
|
|
unEquip(A)
|
|
enforce_hands()
|
|
update_icons()
|
|
|
|
/mob/living/carbon/human/interactive/proc/compareFaction(list/targetFactions)
|
|
var/hasSame = 0
|
|
|
|
for(var/A in targetFactions)
|
|
if(A in faction)
|
|
hasSame = 1
|
|
|
|
return hasSame
|
|
|
|
/mob/living/carbon/human/interactive/proc/nearbyscan(obj)
|
|
nearby = list()
|
|
for(var/mob/living/M in view(4,src))
|
|
if(M != src)
|
|
nearby += M
|
|
|
|
/mob/living/carbon/human/interactive/rename_character(oldname, newname)
|
|
if(!..())
|
|
return 0
|
|
|
|
if(oldname)
|
|
if(MYID)
|
|
MYID.registered_name = newname
|
|
MYID.name = "[newname]'s ID Card ([MYID.assignment])"
|
|
if(RPID)
|
|
RPID.registered_name = MYID.registered_name
|
|
RPID.name = MYID.name
|
|
return 1 |