diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 60a0f565f24..61aa9962d6b 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -134,3 +134,26 @@
#define PEN_FONT "Verdana"
#define CRAYON_FONT "Comic Sans MS"
#define SIGNFONT "Times New Roman"
+
+
+//NPC DEFINES
+#define INTERACTING 2
+#define TRAVEL 4
+#define FIGHTING 8
+
+//TRAITS
+
+#define TRAIT_ROBUST 2
+#define TRAIT_UNROBUST 4
+#define TRAIT_SMART 8
+#define TRAIT_DUMB 16
+#define TRAIT_MEAN 32
+#define TRAIT_FRIENDLY 64
+#define TRAIT_THIEVING 128
+
+//defines
+#define MAX_RANGE_FIND 32
+#define MIN_RANGE_FIND 16
+#define FUZZY_CHANCE_HIGH 85
+#define FUZZY_CHANCE_LOW 50
+#define CHANCE_TALK 15
diff --git a/code/controllers/subsystem/npcpool.dm b/code/controllers/subsystem/npcpool.dm
new file mode 100644
index 00000000000..16326980e2f
--- /dev/null
+++ b/code/controllers/subsystem/npcpool.dm
@@ -0,0 +1,114 @@
+var/datum/subsystem/npcpool/SSbp
+
+/datum/subsystem/npcpool
+ name = "NPCPool"
+ priority = 27
+
+ var/list/canBeUsed = list()
+ var/list/canBeUsed_non = list()
+ var/list/needsDelegate = list()
+ var/list/needsAssistant = list()
+ var/list/needsHelp_non = list()
+ var/list/botPool_l = list() //list of all npcs using the pool
+ var/list/botPool_l_non = list() //list of all non SNPC mobs using the pool
+
+/datum/subsystem/npcpool/proc/insertBot(var/toInsert)
+ if(istype(toInsert,/mob/living/carbon/human/interactive))
+ botPool_l |= toInsert
+ else if(istype(toInsert,/obj/machinery/bot))
+ botPool_l_non |= toInsert
+
+/datum/subsystem/npcpool/New()
+ NEW_SS_GLOBAL(SSbp)
+
+
+/datum/subsystem/npcpool/stat_entry()
+ stat(name, "[round(cost,0.001)]ds (CPU:[round(cpu,1)]%) (T:[botPool_l.len + botPool_l_non] | D: [needsDelegate.len] | A: [needsAssistant.len + needsHelp_non.len] | U: [canBeUsed.len + canBeUsed_non.len])")
+
+
+/datum/subsystem/npcpool/fire()
+ //bot delegation and coordination systems
+ //General checklist/Tasks for delegating a task or coordinating it (for SNPCs)
+ // 1. Bot proximity to task target: if too far, delegate, if close, coordinate
+ // 2. Bot Health/status: check health with bots in local area, if their health is higher, delegate task to them, else coordinate
+ // 3. Process delegation: if a bot (or bots) has been delegated, assign them to the task.
+ // 4. Process coordination: if a bot(or bots) has been asked to coordinate, assign them to help.
+ // 5. Do all assignments: goes through the delegated/coordianted bots and assigns the right variables/tasks to them.
+ var/npcCount = 1
+
+ //bot handling
+ for(var/obj/machinery/bot/check in botPool_l_non)
+ if(!check)
+ botPool_l_non.Cut(npcCount,npcCount+1)
+
+ if(check.hacked)
+ needsHelp_non |= check
+ else if(check.frustration > 5) //average for most bots
+ needsHelp_non |= check
+ else if(check.mode == 0)
+ canBeUsed_non |= check
+ npcCount++
+
+ npcCount = 1 //reset the count
+
+ //SNPC handling
+ for(var/mob/living/carbon/human/interactive/check in botPool_l)
+ var/checkInRange = view(MAX_RANGE_FIND,check)
+ if(!check)
+ botPool_l.Cut(npcCount,npcCount+1)
+ if(!(locate(check.TARGET) in checkInRange))
+ needsDelegate |= check
+
+ else if(check.isnotfunc(FALSE))
+ needsDelegate |= check
+
+ else if(check.doing & FIGHTING)
+ needsAssistant |= check
+
+ else
+ canBeUsed |= check
+ npcCount++
+
+ if(needsDelegate.len)
+ for(var/mob/living/carbon/human/interactive/check in needsDelegate)
+ if(canBeUsed.len)
+ var/mob/living/carbon/human/interactive/candidate = pick(canBeUsed)
+ var/facCount = 0
+ var/helpProb = 0
+ for(var/C in check.faction)
+ if(candidate.faction[facCount] == C)
+ helpProb = min(100,helpProb + 25)
+ facCount++
+ if(facCount == 1 && helpProb > 0)
+ helpProb = 100
+ if(prob(helpProb))
+ if(candidate.takeDelegate(check))
+ needsDelegate -= check
+ canBeUsed -= candidate
+ candidate.eye_color = "red"
+
+ if(needsAssistant.len)
+ for(var/mob/living/carbon/human/interactive/check in needsAssistant)
+ if(canBeUsed.len)
+ var/mob/living/carbon/human/interactive/candidate = pick(canBeUsed)
+ var/facCount = 0
+ var/helpProb = 0
+ for(var/C in check.faction)
+ if(candidate.faction[facCount] == C)
+ helpProb = min(100,helpProb + 10)
+ facCount++
+ if(facCount == 1 && helpProb > 0)
+ helpProb = 100
+ if(prob(helpProb))
+ if(candidate.takeDelegate(check,FALSE))
+ needsAssistant -= check
+ canBeUsed -= candidate
+ candidate.eye_color = "yellow"
+
+ if(needsHelp_non.len)
+ for(var/obj/machinery/bot/B in needsHelp_non)
+ if(canBeUsed_non.len)
+ var/obj/machinery/bot/candidate = pick(canBeUsed_non.len)
+ candidate.call_bot(B,get_turf(B),FALSE)
+ canBeUsed_non -= B
+ needsHelp_non -= candidate
\ No newline at end of file
diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm
index f2bb4d9c38e..19883679181 100644
--- a/code/game/machinery/bots/bots.dm
+++ b/code/game/machinery/bots/bots.dm
@@ -98,6 +98,7 @@
/obj/machinery/bot/New()
..()
SSbot.processing += src //Global bot list
+ SSbp.insertBot(src)
botcard = new /obj/item/weapon/card/id(src)
set_custom_texts()
Radio = new /obj/item/device/radio(src)
@@ -439,7 +440,7 @@ obj/machinery/bot/proc/bot_step(var/dest)
if(mode != BOT_SUMMON && mode != BOT_RESPONDING)
botcard.access = prev_access
-/obj/machinery/bot/proc/call_bot(var/caller, var/turf/waypoint)
+/obj/machinery/bot/proc/call_bot(var/caller, var/turf/waypoint, var/message=TRUE)
bot_reset() //Reset a bot before setting it to call mode.
var/area/end_area = get_area(waypoint)
@@ -456,12 +457,14 @@ obj/machinery/bot/proc/bot_step(var/dest)
if(!on)
turn_on() //Saves the AI the hassle of having to activate a bot manually.
botcard = all_access //Give the bot all-access while under the AI's command.
- calling_ai << "\icon[src] [name] called to [end_area.name]. [path.len-1] meters to destination."
+ if(message)
+ calling_ai << "\icon[src] [name] called to [end_area.name]. [path.len-1] meters to destination."
pathset = 1
mode = BOT_RESPONDING
tries = 0
else
- calling_ai << "Failed to calculate a valid route. Ensure destination is clear of obstructions and within range."
+ if(message)
+ calling_ai << "Failed to calculate a valid route. Ensure destination is clear of obstructions and within range."
calling_ai = null
path = list()
diff --git a/code/modules/mob/interactive.dm b/code/modules/mob/interactive.dm
index c7d0cd73725..cd7dd65d019 100644
--- a/code/modules/mob/interactive.dm
+++ b/code/modules/mob/interactive.dm
@@ -1,24 +1,3 @@
-#define INTERACTING 2
-#define TRAVEL 4
-#define FIGHTING 8
-
-//TRAITS
-
-#define TRAIT_ROBUST 2
-#define TRAIT_UNROBUST 4
-#define TRAIT_SMART 8
-#define TRAIT_DUMB 16
-#define TRAIT_MEAN 32
-#define TRAIT_FRIENDLY 64
-#define TRAIT_THIEVING 128
-
-//defines
-#define MAX_RANGE_FIND 32
-#define MIN_RANGE_FIND 16
-#define FUZZY_CHANCE_HIGH 85
-#define FUZZY_CHANCE_LOW 50
-#define CHANCE_TALK 15
-
/*
NPC VAR EXPLANATIONS (for modules and other things)
@@ -69,6 +48,7 @@
var/obj/item/other_hand
var/TRAITS = 0
var/datum/job/myjob
+ faction = list("station")
//trait vars
var/robustness = 50
var/smartness = 50
@@ -79,6 +59,31 @@
//modules
var/list/functions = list("nearbyscan","combat","doorscan","shitcurity","chatter")
+//botPool funcs
+/mob/living/carbon/human/interactive/proc/takeDelegate(var/mob/living/carbon/human/interactive/from,var/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 = 100
+ //
+ 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.
underwear = random_underwear(gender)
@@ -87,7 +92,7 @@
facial_hair_style = random_facial_hair_style(gender)
hair_color = random_short_color()
facial_hair_color = hair_color
- eye_color = random_eye_color()
+ eye_color = "blue"
age = rand(AGE_MIN,AGE_MAX)
ready_dna(src,random_blood_type())
//job handling
@@ -106,6 +111,12 @@
retal = 1
retal_target = user
+/mob/living/carbon/human/interactive/bullet_act(var/obj/item/projectile/P)
+ var/potentialAssault = locate(/mob/living) in view(2,P.starting)
+ if(potentialAssault)
+ attacked_by(P,potentialAssault)
+ ..()
+
/mob/living/carbon/human/interactive/New()
..()
gender = pick(MALE,FEMALE)
@@ -133,29 +144,29 @@
//arms
if(prob((FUZZY_CHANCE_LOW+FUZZY_CHANCE_HIGH)/2))
var/obj/item/organ/limb/r_arm/R = locate(/obj/item/organ/limb/r_arm) in organs
- del(R)
+ qdel(R)
organs += new /obj/item/organ/limb/robot/r_arm
else
var/obj/item/organ/limb/l_arm/L = locate(/obj/item/organ/limb/l_arm) in organs
- del(L)
+ qdel(L)
organs += new /obj/item/organ/limb/robot/l_arm
//legs
if(prob((FUZZY_CHANCE_LOW+FUZZY_CHANCE_HIGH)/2))
var/obj/item/organ/limb/r_leg/R = locate(/obj/item/organ/limb/r_leg) in organs
- del(R)
+ qdel(R)
organs += new /obj/item/organ/limb/robot/r_leg
else
var/obj/item/organ/limb/l_leg/L = locate(/obj/item/organ/limb/l_leg) in organs
- del(L)
+ qdel(L)
organs += new /obj/item/organ/limb/robot/l_leg
//chest and head
if(prob((FUZZY_CHANCE_LOW+FUZZY_CHANCE_HIGH)/2))
var/obj/item/organ/limb/chest/R = locate(/obj/item/organ/limb/chest) in organs
- del(R)
+ qdel(R)
organs += new /obj/item/organ/limb/robot/chest
else
var/obj/item/organ/limb/head/L = locate(/obj/item/organ/limb/head) in organs
- del(L)
+ qdel(L)
organs += new /obj/item/organ/limb/robot/head
for(var/obj/item/organ/limb/LIMB in organs)
LIMB.owner = src
@@ -220,10 +231,10 @@
toReturn = "Excited"
return toReturn
//END DEBUG
-/mob/living/carbon/human/interactive/proc/isnotfunc()
+/mob/living/carbon/human/interactive/proc/isnotfunc(var/checkDead = TRUE)
if(!canmove)
return 1
- if(health <= 0)
+ if(health <= 0 && checkDead)
return 1
if(restrained())
return 1
@@ -634,12 +645,14 @@
New()
TRAITS |= TRAIT_ROBUST
TRAITS |= TRAIT_MEAN
+ faction = list("bot_angry")
..()
/mob/living/carbon/human/interactive/friendly
New()
TRAITS |= TRAIT_FRIENDLY
TRAITS |= TRAIT_UNROBUST
+ faction = list("bot_friendly")
..()
/mob/living/carbon/human/interactive/greytide
@@ -648,16 +661,6 @@
TRAITS |= TRAIT_MEAN
TRAITS |= TRAIT_THIEVING
TRAITS |= TRAIT_DUMB
+ faction = list("bot_grey")
graytide = 1
- ..()
-
-#undef INTERACTING
-#undef TRAVEL
-#undef FIGHTING
-#undef TRAIT_ROBUST
-#undef TRAIT_UNROBUST
-#undef TRAIT_SMART
-#undef TRAIT_DUMB
-#undef TRAIT_MEAN
-#undef TRAIT_FRIENDLY
-#undef TRAIT_THIEVING
\ No newline at end of file
+ ..()
\ No newline at end of file
diff --git a/tgstation.dme b/tgstation.dme
index 1c8c71dedb4..ada2436efc7 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -134,6 +134,7 @@
#include "code\controllers\subsystem\machines.dm"
#include "code\controllers\subsystem\mobs.dm"
#include "code\controllers\subsystem\nanoUI.dm"
+#include "code\controllers\subsystem\npcpool.dm"
#include "code\controllers\subsystem\objects.dm"
#include "code\controllers\subsystem\pai.dm"
#include "code\controllers\subsystem\pipenets.dm"