Ported component AI to the new component system (#30751)

* wip new component ai

* wip 2

* he lives

* fixes

* comment
This commit is contained in:
DamianX
2021-09-22 21:30:47 +02:00
committed by GitHub
parent 6553dfaccd
commit 4a5f2e40a8
45 changed files with 433 additions and 683 deletions

View File

@@ -2,7 +2,7 @@
var/list/datum_components
/datum/proc/initialize()
return
return TRUE
//Called when a variable is edited by admin powers
//Return 1 to block the varedit!

View File

@@ -14,7 +14,7 @@ var/global/list/cyborg_list = list() //List of all living cyborgs, includin
var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/new_player
var/list/observers = new/list()
var/global/list/areas = list()
var/global/list/active_component_containers = list() //List of all component containers that have registered for updating
var/global/list/active_components = list() //List of all components that have registered for updating
var/global/list/chemical_reactions_list //list of all /datum/chemical_reaction datums. Used during chemical reactions
var/global/list/chemical_reagents_list //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff

View File

@@ -238,6 +238,28 @@
// mob/source: the mob performing the emote
/event/emote
/event/comp_ai_friend_attacked
/event/comp_ai_cmd_get_best_target
/event/comp_ai_cmd_add_target
/event/comp_ai_cmd_remove_target
/event/comp_ai_cmd_find_targets
/event/comp_ai_cmd_can_attack
/event/comp_ai_cmd_move
/event/comp_ai_cmd_attack
/event/comp_ai_cmd_evaluate_target
/event/comp_ai_cmd_get_damage_type
/event/comp_ai_cmd_set_busy
/event/comp_ai_cmd_get_busy
/event/comp_ai_cmd_set_target
/event/comp_ai_cmd_get_target
/event/comp_ai_cmd_set_state
/event/comp_ai_cmd_get_state
/datum
/// Associative list of type path -> list(),
/// where the type path is a descendant of /event_type.
@@ -262,12 +284,13 @@
if(!length(event_handlers))
// This datum does not have any handler registered for this event_type.
return
. = NONE
for(var/key in event_handlers)
var/list/handler = event_handlers[key]
var/objRef = handler[EVENT_HANDLER_OBJREF_INDEX]
var/procName = handler[EVENT_HANDLER_PROCNAME_INDEX]
. |= call(objRef, procName)(arglist(arguments))
// not |= because `null |= list()` is a runtime error
// but `null = null | list()` is not.
. = . | call(objRef, procName)(arglist(arguments))
/**
* Registers a proc to be called on an object whenever the specified event_type

View File

@@ -32,8 +32,7 @@
I.attack(src, user, def_zone, originator)
else
I.attack(src, user, def_zone)
if(BrainContainer)
BrainContainer.SendSignal(COMSIG_ATTACKEDBY, list("assailant"=user,"damage"=I.force))
invoke_event(/event/attackby, list("attacker" = user, "item" = I))

View File

@@ -15,27 +15,21 @@ var/datum/subsystem/component/SScomp
/datum/subsystem/component/stat_entry()
..("P:[active_component_containers.len]")
..("P:[active_components.len]")
/datum/subsystem/component/fire(resumed = FALSE)
if (!resumed)
currentrun = active_component_containers.Copy()
currentrun = active_components.Copy()
while (currentrun.len)
var/datum/component_container/C = currentrun[currentrun.len]
var/datum/component/C = currentrun[currentrun.len]
currentrun.len--
if(!C || C.gcDestroyed || !C.holder || !C.components.len)
if(!C || C.gcDestroyed)
continue
if(isliving(C.holder))
var/mob/living/M = C.holder
if (!M || M.gcDestroyed || M.timestopped || M.monkeyizing || M.stat == DEAD)
continue
C.SendSignal(COMSIG_LIFE, list())
C.process()
if(MC_TICK_CHECK)
return

View File

@@ -78,7 +78,7 @@ var/global/list/pathmakers = list()
var/list/closed = new() //the closed list
var/list/path = null //the returned path, if any
var/PathNode/cur //current processed turf
var/proc_to_call //how we can tell the owner the finished path
var/callback/callback //how we can tell the owner the finished path
var/adjacent //How we check which turfs that are adjacent to our checked turf are valid
var/dist //How we check the distance between points
@@ -90,16 +90,16 @@ var/global/list/pathmakers = list()
var/debug = FALSE //Whether we paint our turfs as we calculate
var/PM_id //How we will identify PathNodes associated with this, to prevent PathNode conflict
/datum/path_maker/New(var/nowner, var/nproc_to_call, var/turf/nstart, var/turf/nend, var/atom/ntarget, var/nadjacent, var/ndist, var/nmaxnodes, var/nmaxnodedepth, var/nmintargetdist, var/nid=null, var/turf/nexclude, var/ndebug)
/datum/path_maker/New(var/nowner, var/ncallback, var/turf/nstart, var/turf/nend, var/atom/ntarget, var/nadjacent, var/ndist, var/nmaxnodes, var/nmaxnodedepth, var/nmintargetdist, var/nid=null, var/turf/nexclude, var/ndebug)
ASSERT(nowner)
ASSERT(nstart)
ASSERT(nend)
ASSERT(nproc_to_call)
ASSERT(ncallback)
owner = nowner
start = nstart
end = nend
proc_to_call = nproc_to_call
callback = ncallback
target = ntarget
adjacent = nadjacent
dist = ndist
@@ -216,7 +216,7 @@ var/global/list/pathmakers = list()
astar_debug("open:[open.List().len]")
/datum/path_maker/proc/fail()
call(owner, proc_to_call)()
callback.invoke_async()
qdel(src)
/datum/path_maker/proc/finish()
@@ -230,5 +230,5 @@ var/global/list/pathmakers = list()
for(var/i = 1; i <= path.len/2; i++)
path.Swap(i,path.len-i+1)
call(owner, proc_to_call)(path.Copy(), target)
callback.invoke_async(path.Copy(), target)
qdel(src)

View File

@@ -77,3 +77,6 @@
. = get_component(c_type)
if(!.)
return add_component(arglist(args))
/datum/component/proc/process()
set waitfor = FALSE

View File

@@ -170,7 +170,7 @@ length to avoid portals or something i guess?? Not that they're counted right no
/*
* ASTAR
* source: the atom which calls this Astar call.TRUE
* proc_to_call: the proc to call on the source
* callback: the callback to invoke when the path is ready
* start: starting atom
* end: end of targetted path
* Adjacent: the proc which rules what is adjacent for us
@@ -180,7 +180,7 @@ length to avoid portals or something i guess?? Not that they're counted right no
* Creates a pathmaker datum to process the path if we aren't processing the path.
* Returns nothing if this path is already being processed.
*/
/proc/AStar(source, proc_to_call, start,end,adjacent,dist,maxnodes,maxnodedepth = 30,mintargetdist,minnodedist,id=null, var/turf/exclude=null, var/debug = ASTAR_DEBUG)
/proc/AStar(source, callback, start,end,adjacent,dist,maxnodes,maxnodedepth = 30,mintargetdist,minnodedist,id=null, var/turf/exclude=null, var/debug = ASTAR_DEBUG)
ASSERT(!istype(end,/area)) //Because yeah some things might be doing this and we want to know what
if(start:z != end:z) //if you're feeling ambitious and make something that can ASTAR through z levels, feel free to remove this check
return ASTAR_FAIL
@@ -191,8 +191,8 @@ length to avoid portals or something i guess?? Not that they're counted right no
if(!isturf(end))
target = end
astar_debug("ASTAR called [source] [proc_to_call] [start:x][start:y][start:z] [end:x][end:y][end:z] [adjacent] [dist] [maxnodes] [maxnodedepth] [mintargetdist] [minnodedist] [id] [exclude] [debug]")
new /datum/path_maker(source,proc_to_call, get_turf(start), get_turf(end), target, adjacent, dist, maxnodes, maxnodedepth, mintargetdist, id, exclude, debug)
astar_debug("ASTAR called [source] [callback] [start:x],[start:y],[start:z] [end:x],[end:y],[end:z] [adjacent] [dist] [maxnodes] [maxnodedepth] [mintargetdist] [minnodedist] [id] [exclude] [debug]")
new /datum/path_maker(source,callback, get_turf(start), get_turf(end), target, adjacent, dist, maxnodes, maxnodedepth, mintargetdist, id, exclude, debug)
return ASTAR_REGISTERED
// Only use if you just need to check if a path exists, and is a reasonable length
@@ -394,8 +394,8 @@ length to avoid portals or something i guess?? Not that they're counted right no
/////////////////////////////////////////////////////////////////////////
/atom/proc/make_astar_path(var/atom/target, var/receiving_proc = .proc/get_astar_path)
AStar(src, receiving_proc, get_turf(src), target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 30, 30)
/atom/proc/make_astar_path(var/atom/target, var/callback = new /callback(src, .proc/get_astar_path))
AStar(src, callback, get_turf(src), target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 30, 30)
//override when needed to receive your path
/atom/proc/get_astar_path(var/list/L)

View File

@@ -185,7 +185,7 @@
if(target)
if (waiting_for_path)
return 1
calc_path(target, .proc/get_path)
calc_path(target, new /callback(src, .proc/get_path))
if (path && length(path))
process_path()
return 1
@@ -225,7 +225,7 @@
if(frustration > 5)
summoned = FALSE // Let's not try again.
if (target && !target.gcDestroyed)
calc_path(target, .proc/get_path, next)
calc_path(target, new /callback(src, .proc/get_path), next)
else
target = null
path = list()
@@ -306,7 +306,7 @@
if(patrol_target)
waiting_for_patrol = TRUE
calc_patrol_path(patrol_target, .proc/get_patrol_path)
calc_patrol_path(patrol_target, new /callback(.proc/get_patrol_path))
// This proc send out a singal to every beacon listening to the "beacon_freq" variable.
// The signal says, "i'm a bot looking for a beacon to patrol to."
// Every beacon with the flag "patrol" responds by trasmitting its location.
@@ -368,7 +368,7 @@
return TRUE
if(frustration > 5)
if (target && !target.gcDestroyed)
calc_path(target, .proc/get_path, next)
calc_path(target, new /callback(src, .proc/get_path), next)
else
target = null
patrol_path = list()
@@ -484,11 +484,11 @@
// Caluculate a path between the bot and the target.
// Target is the target to go to.
// proc_to_call is the proc which is called by the pathmaker once it's done its work and wishes to return a path.
// callback gets called by the pathmaker once it's done its work and wishes to return a path.
// avoid is a turf the path should NOT go through. (a previous obstacle.) This info is then given to the pathmaker.
// Fast bots use quick_AStar method to direcly calculate a path and move on it.
/obj/machinery/bot/proc/calc_path(var/target, var/proc_to_call, var/turf/avoid = null)
ASSERT(target && proc_to_call)
/obj/machinery/bot/proc/calc_path(var/target, var/callback, var/turf/avoid = null)
ASSERT(target && callback)
var/cardinal_proc = bot_flags & BOT_SPACEWORTHY ? /turf/proc/AdjacentTurfsSpace : /turf/proc/CardinalTurfsWithAccess
if ((get_dist(src, target) < 13) && !(bot_flags & BOT_NOT_CHASING)) // For beepers and ED209
// IMPORTANT: Quick AStar only takes TURFS as arguments.
@@ -497,12 +497,12 @@
log_astar_bot("path is [path.len]")
return TRUE
waiting_for_path = 1
. = AStar(src, proc_to_call, src.loc, target, cardinal_proc, /turf/proc/Distance_cardinal, 0, max(10,get_dist(src,target)*3), id=botcard, exclude=avoid)
. = AStar(src, callback, src.loc, target, cardinal_proc, /turf/proc/Distance_cardinal, 0, max(10,get_dist(src,target)*3), id=botcard, exclude=avoid)
if (!.)
waiting_for_path = 0
/obj/machinery/bot/proc/calc_patrol_path(var/target, var/proc_to_call, var/turf/avoid = null)
ASSERT(target && proc_to_call)
/obj/machinery/bot/proc/calc_patrol_path(var/target, var/callback, var/turf/avoid = null)
ASSERT(target && callback)
log_astar_beacon("[new_destination]")
var/cardinal_proc = bot_flags & BOT_SPACEWORTHY ? /turf/proc/AdjacentTurfsSpace : /turf/proc/CardinalTurfsWithAccess
if ((get_dist(src, target) < 13) && !(bot_flags & BOT_NOT_CHASING)) // For beepers and ED209
@@ -510,7 +510,7 @@
waiting_for_patrol = FALSE // Case we are calculating a quick path for a patrol.
patrol_path = quick_AStar(src.loc, get_turf(target), cardinal_proc, /turf/proc/Distance_cardinal, 0, max(10,get_dist(src,target)*3), id=botcard, exclude=avoid, reference="\ref[src]")
return TRUE
return AStar(src, proc_to_call, src.loc, target, cardinal_proc, /turf/proc/Distance_cardinal, 0, max(10,get_dist(src,target)*3), id=botcard, exclude=avoid)
return AStar(src, callback, src.loc, target, cardinal_proc, /turf/proc/Distance_cardinal, 0, max(10,get_dist(src,target)*3), id=botcard, exclude=avoid)
// This proc is called by the path maker once it has calculated a path.

View File

@@ -1,13 +1 @@
/datum/component/ai
var/datum/component/controller/controller
var/state=0 // AI_STATE_* of the AI.
/datum/component/ai/RecieveSignal(var/message_type, var/list/args)
switch(message_type)
if(COMSIG_STATE) // list("name"="statename")
state = args["name"]
/datum/component/ai/New(var/datum/component_container/CC)
..(CC)
controller=GetComponent(/datum/component/controller)

View File

@@ -20,30 +20,22 @@
var/min_overheat_temp=40
/datum/component/ai/atmos_checker/RecieveSignal(var/message_type, var/list/args)
switch(message_type)
if("life")
OnLife()
else
..(message_type, args)
/datum/component/ai/atmos_checker/proc/OnLife()
if(!isliving(container.holder))
return 1
if(container.holder & INVULNERABLE)
/datum/component/ai/atmos_checker/process()
var/mob/living/dude = parent
if(dude.flags & INVULNERABLE)
return 1
var/atmos_suitable = 1
var/atom/A = container.holder.loc
var/atom/A = dude.loc
if(isturf(A))
var/turf/T = A
var/datum/gas_mixture/Environment = T.return_air()
if(Environment)
if(abs(Environment.temperature - controller.getBodyTemperature()) > min_overheat_temp)
SendSignal(COMSIG_ADJUST_BODYTEMP, list("temp"=((Environment.temperature - controller.getBodyTemperature()) / 5)))
if(abs(Environment.temperature - dude.bodytemperature) > min_overheat_temp)
dude.bodytemperature = (Environment.temperature - dude.bodytemperature) / 5
if(min_oxy)
if(Environment.molar_density(GAS_OXYGEN) < min_oxy)
@@ -84,14 +76,14 @@
atmos_suitable = 0
//Atmos effect
if(controller.getBodyTemperature() < minbodytemp)
if(dude.bodytemperature < minbodytemp)
fire_alert = 2
SendSignal(COMSIG_ADJUST_BRUTE, list("amount"=cold_damage_per_tick))
else if(controller.getBodyTemperature() > maxbodytemp)
dude.adjustBruteLoss(cold_damage_per_tick)
else if(dude.bodytemperature > maxbodytemp)
fire_alert = 1
SendSignal(COMSIG_ADJUST_BRUTE, list("amount"=heat_damage_per_tick))
dude.adjustBruteLoss(heat_damage_per_tick)
else
fire_alert = 0
if(!atmos_suitable)
SendSignal(COMSIG_ADJUST_BRUTE, list("amount"=unsuitable_damage))
dude.adjustBruteLoss(unsuitable_damage)

View File

@@ -1,68 +1,46 @@
/datum/component/controller
var/atom/holder
var/_busy = FALSE
var/atom/_target = null
var/_busy=FALSE
var/atom/_target=null
var/_state = HOSTILE_STANCE_IDLE
var/_state=HOSTILE_STANCE_IDLE
/datum/component/controller/initialize()
parent.register_event(/event/comp_ai_cmd_set_busy, src, .proc/cmd_set_busy)
parent.register_event(/event/comp_ai_cmd_get_busy, src, .proc/cmd_get_busy)
/datum/component/controller/New(var/datum/component_container/container, var/atom/_holder)
..(container)
holder=_holder
parent.register_event(/event/comp_ai_cmd_set_target, src, .proc/cmd_set_target)
parent.register_event(/event/comp_ai_cmd_get_target, src, .proc/cmd_get_target)
parent.register_event(/event/comp_ai_cmd_set_state, src, .proc/cmd_set_state)
parent.register_event(/event/comp_ai_cmd_get_state, src, .proc/cmd_get_state)
return TRUE
/datum/component/controller/Destroy()
holder = null
parent.unregister_event(/event/comp_ai_cmd_set_busy, src, .proc/cmd_set_busy)
parent.unregister_event(/event/comp_ai_cmd_get_busy, src, .proc/cmd_get_busy)
parent.unregister_event(/event/comp_ai_cmd_set_target, src, .proc/cmd_set_target)
parent.unregister_event(/event/comp_ai_cmd_get_target, src, .proc/cmd_get_target)
parent.unregister_event(/event/comp_ai_cmd_set_state, src, .proc/cmd_set_state)
parent.unregister_event(/event/comp_ai_cmd_get_state, src, .proc/cmd_get_state)
return ..()
// Called when we are bumped by another movable atom.
/datum/component/controller/proc/OnBumped(var/atom/A)
if(istype(A, /atom/movable))
var/atom/movable/AM = A
SendSignal(COMSIG_BUMPED, list("movable"=AM))
// Called when we bump another movable atom.
/datum/component/controller/proc/Onto_bump(var/atom/A)
if(istype(A, /atom/movable))
var/atom/movable/AM = A
SendSignal(COMSIG_BUMP, list("movable"=AM))
// Called when we receive the Life() tick from the MC/scheduler/whatever
/datum/component/controller/proc/Life()
SendSignal(COMSIG_LIFE, list())
//* Mob calls these to send signals to components. */
/datum/component/controller/proc/AttackTarget(var/atom/A)
container.SendSignalToFirst(/datum/component/ai, COMSIG_ATTACKING, list("target"=A))
/datum/component/controller/proc/setBusy(var/yes)
/datum/component/controller/proc/cmd_set_busy(yes)
_busy = yes
SendSignal(COMSIG_BUSY, list("state"=_busy))
/datum/component/controller/proc/getBusy()
/datum/component/controller/proc/cmd_get_busy()
return _busy
/datum/component/controller/proc/setTarget(var/atom/A)
_target = A
SendSignal(COMSIG_TARGET, list("target"=_target))
/datum/component/controller/proc/cmd_set_target(atom/target)
_target = target
/datum/component/controller/proc/getTarget()
/datum/component/controller/proc/cmd_get_target()
return _target
/datum/component/controller/proc/setState(var/new_state)
/datum/component/controller/proc/cmd_set_state(new_state)
_state = new_state
SendSignal(COMSIG_STATE, list("state"=_state))
/datum/component/controller/proc/getState()
/datum/component/controller/proc/cmd_get_state()
return _state
/datum/component/controller/proc/setBodyTemperature(var/temp)
SendSignal(COMSIG_SET_BODYTEMP, list("temp"=temp,"from"=src))
/datum/component/controller/proc/getBodyTemperature()
return -1
/datum/component/controller/proc/canAttack(var/atom/A)
if(istype(container.holder, /mob/living/simple_animal))
var/mob/living/simple_animal/SA = container.holder
return SA.CanAttack(A)
return FALSE

View File

@@ -1,26 +0,0 @@
/datum/component/controller/mob
var/walk_delay=4
/datum/component/controller/mob/RecieveSignal(var/message_type, var/list/args)
if(isliving(container.holder))
var/mob/living/M=container.holder
//testing("Got command: \[[message_type]\]: [json_encode(args)]")
switch(message_type)
if(COMSIG_CLICKON)
var/atom/A = args["target"]
var/params
if(args["def_zone"])
var/list/L = list("def_zone" = args["def_zone"])
params = list2params(L)
M.ClickOn(A, params)
if(COMSIG_STEP)
step(M, args["dir"], walk_delay)
if(COMSIG_ADJUST_BODYTEMP) // list("temp"=TEMP_IN_KELVIN)
M.bodytemperature += args["temp"]
if(COMSIG_SET_BODYTEMP) // list("temp"=TEMP_IN_KELVIN)
M.bodytemperature = args["temp"]
if(COMSIG_STATE) // list("state"=HOSTILE_STANCE_ATTACK)
setState(args["state"])

View File

@@ -1,42 +1,61 @@
/datum/component/controller/movement
var/walk_delay = 4
/datum/component/controller/movement/basic/RecieveSignal(var/message_type, var/list/args)
if(isliving(container.holder))
var/mob/living/M=container.holder
switch(message_type)
if(COMSIG_MOVE)
if("loc" in args)
M.start_walk_to(args["loc"], 1, walk_delay)
if("dir" in args)
M.set_glide_size(DELAY2GLIDESIZE(walk_delay))
walk(M, args["dir"], walk_delay)
/datum/component/controller/movement/initialize()
parent.register_event(/event/comp_ai_cmd_move, src, .proc/cmd_move)
return TRUE
/datum/component/controller/movement/Destroy()
parent.unregister_event(/event/comp_ai_cmd_move, src, .proc/cmd_move)
..()
/datum/component/controller/movement/proc/cmd_move(target)
CRASH("not implemented")
/datum/component/controller/movement/basic/cmd_move(target)
var/mob/living/dude = parent
if(isatom(target))
dude.start_walk_to(target, 1, walk_delay)
else if(isnum(target))
dude.set_glide_size(DELAY2GLIDESIZE(walk_delay))
walk(dude, target, walk_delay)
else
CRASH("target [target] is not an atom or a dir")
/datum/component/controller/movement/astar
var/list/movement_nodes = list()
var/target
/datum/component/controller/movement/astar/RecieveSignal(var/message_type, var/list/args)
if(isliving(container.holder))
var/mob/living/M=container.holder
if(message_type == COMSIG_MOVE)
if("loc" in args)
if(args["loc"] == target)
return //We're already on our way there
target = args["loc"]
AStar(src, .proc/receive_path, M, target, /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30, id=M.get_visible_id())
if("dir" in args)
movement_nodes = list()
walk(M, args["dir"], walk_delay)
if(message_type == COMSIG_LIFE)
if(movement_nodes && movement_nodes.len && target && (target != null))
if(movement_nodes.len > 0)
step_to(M, movement_nodes[1])
movement_nodes -= movement_nodes[1]
else if(movement_nodes.len == 1)
step_to(src, target)
movement_nodes.Cut()
return 1
/datum/component/controller/movement/astar/initialize()
active_components += src
return ..()
/datum/component/controller/movement/astar/Destroy()
active_components -= src
..()
/datum/component/controller/movement/astar/cmd_move(target)
var/mob/living/dude = parent
if(isatom(target))
if(src.target == target)
return //We're already on our way there
src.target = target
walk_to(dude, target, 0, walk_delay)
else if(isnum(target))
movement_nodes = list()
dude.set_glide_size(DELAY2GLIDESIZE(walk_delay))
walk(dude, target, walk_delay)
else
CRASH("target [target] is not an atom or a dir")
/datum/component/controller/movement/astar/process()
if(movement_nodes && movement_nodes.len && target)
if(movement_nodes.len > 0)
step_to(parent, movement_nodes[1])
movement_nodes -= movement_nodes[1]
else if(movement_nodes.len == 1)
step_to(parent, target)
movement_nodes.Cut()
return 1
/datum/component/controller/movement/astar/proc/receive_path(var/list/L)
if(islist(L))

View File

@@ -1,8 +1,20 @@
/datum/component/controller/simple_animal
var/disable_automove_on_busy=1
var/disable_automove_on_busy = TRUE
/datum/component/controller/simple_animal/setBusy(var/yes)
..(yes)
/datum/component/controller/simple_animal/initialize()
parent.register_event(/event/comp_ai_cmd_can_attack, src, .proc/cmd_can_attack)
return ..()
/datum/component/controller/simple_animal/Destroy()
parent.unregister_event(/event/comp_ai_cmd_can_attack, src, .proc/cmd_can_attack)
..()
/datum/component/controller/simple_animal/proc/cmd_can_attack(target)
var/mob/living/simple_animal/SA = parent
return SA.CanAttack(target)
/datum/component/controller/simple_animal/cmd_set_busy(yes)
..()
if(disable_automove_on_busy)
var/mob/living/simple_animal/SA = holder
var/mob/living/simple_animal/SA = parent
SA.stop_automated_movement = yes

View File

@@ -1,14 +1,4 @@
/datum/component/ai/targetting_handler/RecieveAndReturnSignal(var/message_type, var/list/args)
if(message_type == COMSIG_GETDEFZONE)
var/mob/living/target = args["target"]
var/damagetype = args["damage_type"]
ASSERT(istype(target))
return EvaluateTarget(target, damagetype)
/datum/component/ai/targetting_handler/proc/EvaluateTarget(var/mob/living/target, var/damagetype) //Center mass.
return LIMB_CHEST
/datum/component/ai/targetting_handler/dumb/EvaluateTarget(var/mob/living/target, var/damagetype) //Random
/datum/component/ai/targetting_handler
var/list/static/potential_targets = list(
LIMB_HEAD,
LIMB_CHEST,
@@ -21,27 +11,31 @@
LIMB_RIGHT_LEG,
LIMB_LEFT_FOOT,
LIMB_RIGHT_FOOT,
TARGET_MOUTH)
TARGET_MOUTH,
)
/datum/component/ai/targetting_handler/initialize()
parent.register_event(/event/comp_ai_cmd_evaluate_target, src, .proc/evaluate_target)
return TRUE
/datum/component/ai/targetting_handler/Destroy()
parent.unregister_event(/event/comp_ai_cmd_evaluate_target, src, .proc/evaluate_target)
..()
//Center mass.
/datum/component/ai/targetting_handler/proc/evaluate_target(mob/living/target, damage_type)
return LIMB_CHEST
//Random
/datum/component/ai/targetting_handler/dumb/evaluate_target(mob/living/target, damage_type)
return pick(potential_targets)
/datum/component/ai/targetting_handler/smart/EvaluateTarget(var/mob/living/target, var/damagetype) //Goes for the part with the least armor
var/list/static/potential_target = list(
LIMB_HEAD,
LIMB_CHEST,
LIMB_GROIN,
LIMB_LEFT_ARM,
LIMB_RIGHT_ARM,
LIMB_LEFT_HAND,
LIMB_RIGHT_HAND,
LIMB_LEFT_LEG,
LIMB_RIGHT_LEG,
LIMB_LEFT_FOOT,
LIMB_RIGHT_FOOT,
TARGET_MOUTH)
//Goes for the part with the least armor
/datum/component/ai/targetting_handler/smart/evaluate_target(mob/living/target, damage_type)
var/weakpoint
var/weakpoint_armor = 100
for(var/i in potential_target)
var/armor = target.getarmor(i, damagetype)
for(var/i in potential_targets)
var/armor = target.getarmor(i, damage_type)
if(!weakpoint || weakpoint_armor > armor)
weakpoint = i

View File

@@ -1,22 +1,23 @@
/datum/component/ai/door_opener
var/pressure_check=TRUE
var/max_pressure_diff=-1
var/pressure_check = TRUE
var/max_pressure_diff = -1
/datum/component/ai/door_opener/RecieveSignal(var/message_type, var/list/args)
switch(message_type)
if(COMSIG_ATTACKING) // list("target"=A)
OnAttackingTarget(args["target"])
else
..(message_type, args)
/datum/component/ai/door_opener/initialize()
parent.register_event(/event/comp_ai_cmd_attack, src, .proc/cmd_attack)
return TRUE
/datum/component/ai/door_opener/proc/OnAttackingTarget(var/atom/target)
/datum/component/ai/door_opener/Destroy()
parent.unregister_event(/event/comp_ai_cmd_attack, src, .proc/cmd_attack)
..()
/datum/component/ai/door_opener/proc/cmd_attack(atom/target)
if(istype(target,/obj/machinery/door))
var/obj/machinery/door/D = target
if(CanOpenDoor(D))
if(get_dist(src, target) > 1)
return // keep movin'.
controller.setBusy(TRUE)
SendSignal(COMSIG_MOVE, list("dir"=0)) // Stop movement?
parent.invoke_event(/event/comp_ai_cmd_set_busy, list("yes" = TRUE))
parent.invoke_event(/event/comp_ai_cmd_move, list("target" = 0))
D.visible_message("<span class='warning'>\The [D]'s motors whine as four arachnid claws begin trying to force it open!</span>")
spawn(50)
if(CanOpenDoor(D) && prob(25))
@@ -29,11 +30,10 @@
FD.open(1)
// Reset targetting
controller.setBusy(FALSE)
controller.setTarget(null)
parent.invoke_event(/event/comp_ai_cmd_set_busy, list("yes" = FALSE))
parent.invoke_event(/event/comp_ai_cmd_set_target, list("target" = null))
return
controller.setBusy(FALSE)
return
parent.invoke_event(/event/comp_ai_cmd_set_busy, list("yes" = FALSE))
/datum/component/ai/door_opener/proc/performPressureCheck(var/turf/loc)
var/turf/simulated/lT=loc

View File

@@ -1,4 +0,0 @@
/datum/component/ai/get_def_zone/RecieveAndReturnSignal(var/message_type, var/list/args)
if(message_type == COMSIG_GETDAMTYPE)
return "melee"
//var/mob/M = args["user"]

View File

@@ -1,30 +1,31 @@
/datum/component/ai/escape_confinement
var/life_tick=0
var/life_tick = 0
/datum/component/ai/escape_confinement/RecieveSignal(var/message_type, var/list/args)
switch(message_type)
if(COMSIG_LIFE)
OnLife()
/datum/component/ai/escape_confinement/initialize()
active_components += src
return TRUE
/datum/component/ai/escape_confinement/proc/OnLife()
/datum/component/ai/escape_confinement/Destroy()
active_components -= src
..()
/datum/component/ai/escape_confinement/process()
life_tick++
var/mob/M = container.holder
if(!controller)
controller = GetComponent(/datum/component/controller)
if(controller.getBusy())
if(parent.invoke_event(/event/comp_ai_cmd_get_busy))
return
switch(controller.getState())
var/atom/parent_atom = parent
switch(parent.invoke_event(/event/comp_ai_cmd_get_state))
if(HOSTILE_STANCE_IDLE)
EscapeConfinement()
if(HOSTILE_STANCE_ATTACK)
if(!(M.flags & INVULNERABLE))
if(!(parent_atom.flags & INVULNERABLE))
DestroySurroundings()
if(HOSTILE_STANCE_ATTACKING)
if(!(M.flags & INVULNERABLE))
if(!(parent_atom.flags & INVULNERABLE))
DestroySurroundings()
/datum/component/ai/escape_confinement/proc/EscapeConfinement()
var/atom/A = container.holder
var/atom/A = parent
if(istype(A, /mob))
var/mob/M = A
if(M.locked_to)
@@ -36,16 +37,24 @@
/datum/component/ai/escape_confinement/proc/DestroySurroundings()
EscapeConfinement()
var/list/smash_dirs = list(0)
var/atom/target = controller.getTarget()
if(!target || !controller.canAttack(target))
var/atom/target = parent.invoke_event(/event/comp_ai_cmd_get_target)
if(!target || !parent.invoke_event(/event/comp_ai_cmd_can_attack, list("target" = target)))
smash_dirs |= alldirs //if no target, attack everywhere
else
var/targdir = get_dir(src, target)
smash_dirs |= widen_dir(targdir) //otherwise smash towards the target
for(var/dir in smash_dirs)
var/turf/T = get_step(src, dir)
if(istype(T, /turf/simulated/wall) && container.holder.Adjacent(T))
var/atom/parent_atom = parent
if(istype(T, /turf/simulated/wall) && parent_atom.Adjacent(T))
T.attack_animal(src)
for(var/atom/A in T)
if((istype(A, /obj/structure/window) || istype(A, /obj/structure/closet) || istype(A, /obj/structure/table) || istype(A, /obj/structure/grille) || istype(A, /obj/structure/rack)) && container.holder.Adjacent(A))
var/static/list/attackable_objs = list(
/obj/structure/window,
/obj/structure/closet,
/obj/structure/table,
/obj/structure/grille,
/obj/structure/rack,
)
if(is_type_in_list(A, attackable_objs) && parent_atom.Adjacent(A))
A.attack_animal(src)

View File

@@ -1,53 +1,40 @@
// Hunting controller from spiders
/datum/component/ai/hunt
var/last_dir=0 // cardinal direction
var/last_was_bumped=0 // Boolean, indicates whether the last movement resulted in a to_bump().
var/life_tick=0
var/last_dir = 0 // cardinal direction
var/last_was_bumped = 0 // Boolean, indicates whether the last movement resulted in a to_bump().
var/life_tick = 0
var/movement_range=20 // Maximum range of points we move to (20 in spiders)
var/movement_range = 20 // Maximum range of points we move to (20 in spiders)
var/targetfind_delay=10
var/datum/component/ai/target_holder/target_holder = null
var/targetfind_delay = 10
/datum/component/ai/hunt/RecieveSignal(var/message_type, var/list/args)
switch(message_type)
if(COMSIG_LIFE) // no arguments
OnLife()
if(COMSIG_BUMPED) // list("movable"=AM)
OnBumped(args["movable"])
/datum/component/ai/hunt/proc/OnLife()
life_tick++
//testing("HUNT LIFE, controller=[!isnull(controller)], busy=[controller && controller.getBusy()], state=[controller && controller.getState()]")
if(!target_holder)
target_holder = GetComponent(/datum/component/ai/target_holder)
if(!controller)
controller = GetComponent(/datum/component/controller)
if(controller.getBusy())
return
switch(controller.getState())
if(HOSTILE_STANCE_IDLE)
var/atom/target = target_holder.GetBestTarget(src, "target_evaluator")
//testing(" IDLE STANCE, target=\ref[target]")
if(!isnull(target))
SendSignal(COMSIG_TARGET, list("target"=target))
SendSignal(COMSIG_STATE, list("state"=HOSTILE_STANCE_ATTACK))
else
SendSignal(COMSIG_MOVE, list("loc" = pick(orange(movement_range, src))))
if(HOSTILE_STANCE_ATTACK)
var/atom/target = target_holder.GetBestTarget(src, "target_evaluator")
//testing(" ATTACK STANCE, target=\ref[target]")
if(!isnull(target))
var/turf/T = get_turf(target)
container.SendSignalToFirst(/datum/component/ai, COMSIG_ATTACKING, list("target"=target)) // We're telling the attack modules that we have attack intention. They then individually decide whether to fire.
if(T)
SendSignal(COMSIG_MOVE, list("loc" = T))
return
SendSignal(COMSIG_STATE, list("state"=HOSTILE_STANCE_IDLE)) // Lost target
/datum/component/ai/hunt/proc/OnBumped(var/atom/movable/AM)
// TODO
/datum/component/ai/hunt/proc/target_evaluator(var/atom/target)
/datum/component/ai/hunt/initialize()
active_components += src
return TRUE
/datum/component/ai/hunt/Destroy()
active_components -= src
..()
/datum/component/ai/hunt/process()
life_tick++
if(parent.invoke_event(/event/comp_ai_cmd_get_busy))
return
switch(parent.invoke_event(/event/comp_ai_cmd_get_state))
if(HOSTILE_STANCE_IDLE)
var/atom/target = parent.invoke_event(/event/comp_ai_cmd_get_best_target)
if(!isnull(target))
parent.invoke_event(/event/comp_ai_cmd_set_target, list("target" = target))
parent.invoke_event(/event/comp_ai_cmd_set_state, list("new_state" = HOSTILE_STANCE_ATTACK))
else
parent.invoke_event(/event/comp_ai_cmd_move, list("target" = pick(orange(movement_range, src))))
if(HOSTILE_STANCE_ATTACK)
var/atom/target = parent.invoke_event(/event/comp_ai_cmd_get_best_target)
if(!isnull(target))
// We're telling the attack modules that we have attack intention. They then individually decide whether to fire.
parent.invoke_event(/event/comp_ai_cmd_attack, list("target" = target))
var/turf/T = get_turf(target)
if(T)
parent.invoke_event(/event/comp_ai_cmd_move, list("target" = T))
return
parent.invoke_event(/event/comp_ai_cmd_set_state, list("new_state" = HOSTILE_STANCE_IDLE))

View File

@@ -1,7 +1,7 @@
// This just calls animal_attack() on stuff.
/datum/component/ai/melee/attack_animal/OnAttackingTarget(var/atom/target)
if(..(target))
/datum/component/ai/melee/attack_animal/cmd_attack(atom/target)
if(can_attack(target))
var/mob/living/L = target
L.attack_animal(container.holder)
L.attack_animal(parent)
return 1 // Accepted
return 0 // Unaccepted

View File

@@ -4,16 +4,16 @@
var/inject_prob = 0 // Chance to inject, -1 = ALWAYS
var/max_poison = 0 // Maximum mols in target's blood. 0 = INF
/datum/component/ai/melee/inject_reagent/OnAttackingTarget(var/atom/target)
if(..(target))
/datum/component/ai/melee/inject_reagent/cmd_attack(atom/target)
if(can_attack(target))
var/mob/living/L = target
if(L.reagents)
if(inject_prob == -1 || prob(inject_prob))
var/curamt = L.reagents.get_reagent_amount(poison_type)
var/newamt = max_poison - curamt
if(newamt >= 1)
// TEXT-FORMATTING FUNCTIONS WHEN BYOND?
container.holder.visible_message("<span class='warning'>\The [src] injects something into \the [target]!</span>")
var/atom/parent_atom = parent
parent_atom.visible_message("<span class='warning'>\The [src] injects something into \the [target]!</span>")
L.reagents.add_reagent(poison_type, poison_per_bite)
return 1 // Accepted signal
return 0 // Did not accept signal

View File

@@ -1,14 +1,15 @@
/datum/component/ai/melee
/datum/component/ai/melee/RecieveSignal(var/message_type, var/list/args)
switch(message_type)
if(COMSIG_ATTACKING) // list("target"=A)
return OnAttackingTarget(args["target"])
else
return ..(message_type, args)
/datum/component/ai/melee/initialize()
parent.register_event(/event/comp_ai_cmd_attack, src, .proc/cmd_attack)
return TRUE
/datum/component/ai/melee/proc/OnAttackingTarget(var/atom/target)
if(!isliving(target))
return 0
var/mob/living/L = target
return L.Adjacent(container.holder)
/datum/component/ai/melee/Destroy()
parent.unregister_event(/event/comp_ai_cmd_attack, src, .proc/cmd_attack)
..()
/datum/component/ai/melee/proc/can_attack(atom/target)
return target.Adjacent(parent)
/datum/component/ai/melee/proc/cmd_attack(atom/target)
return can_attack(target)

View File

@@ -1,6 +1,6 @@
//Basic thought processes
/datum/component/ai/human_brain
var/life_tick=0
var/life_tick = 0
var/wander = TRUE //Whether the mob will walk around searching for goals, or wait for them to become visible
var/lastdir = null
@@ -14,34 +14,32 @@
var/list/friends = list()
var/list/enemies = list()
var/datum/component/ai/target_holder/target_holder = null
var/atom/current_target
/datum/component/ai/human_brain/RecieveSignal(var/message_type, var/list/args)
switch(message_type)
if(COMSIG_LIFE) // no arguments
OnLife()
/datum/component/ai/human_brain/initialize()
active_components += src
return TRUE
/datum/component/ai/human_brain/proc/OnLife()
/datum/component/ai/human_brain/Destroy()
active_components -= src
..()
/datum/component/ai/human_brain/process()
life_tick++
if(!target_holder)
target_holder = GetComponent(/datum/component/ai/target_holder)
if(!controller)
controller = GetComponent(/datum/component/controller)
if(controller.getBusy())
if(parent.invoke_event(/event/comp_ai_cmd_get_busy))
return
if(!ishuman(container.holder))
if(!ishuman(parent))
return
var/mob/living/carbon/human/H = container.holder
var/mob/living/carbon/human/H = parent
if(H.stat != CONSCIOUS || !H.canmove || !isturf(H.loc))
SendSignal(COMSIG_MOVE, list("dir" = 0))
parent.invoke_event(/event/comp_ai_cmd_move, list("target" = 0))
return
current_target = target_holder.GetBestTarget(src, "target_evaluator")
current_target = parent.invoke_event(/event/comp_ai_cmd_get_best_target)
if(!isnull(current_target))
personal_desires.Add(DESIRE_CONFLICT)
SendSignal(COMSIG_TARGET, list("target"=current_target))
parent.invoke_event(/event/comp_ai_cmd_set_target, list("target" = current_target))
if(IsBetterWeapon(H))
personal_desires.Add(DESIRE_HAVE_WEAPON)
if(IsBetterWeapon(H, H.contents))
@@ -53,18 +51,18 @@
if(I)
if(H.Adjacent(I))
AcquireItem(H, I)
SendSignal(COMSIG_MOVE, list("dir" = 0))
parent.invoke_event(/event/comp_ai_cmd_move, list("target" = 0))
else
if(H.stat == CONSCIOUS && H.canmove && isturf(H.loc))
SendSignal(COMSIG_MOVE, list("loc" = get_turf(I)))
parent.invoke_event(/event/comp_ai_cmd_move, list("target" = get_turf(I)))
return
if(!isnull(current_target))
SendSignal(COMSIG_ATTACKING, list("target"=current_target))
parent.invoke_event(/event/comp_ai_cmd_attack, list("target" = current_target))
var/turf/T = get_turf(current_target)
if(T)
if(H.stat == CONSCIOUS && H.canmove && isturf(H.loc))
SendSignal(COMSIG_MOVE, list("loc" = T))
parent.invoke_event(/event/comp_ai_cmd_move, list("target" = T))
return
else
personal_desires.Remove(DESIRE_CONFLICT)
@@ -80,7 +78,7 @@
else
dir = turn(lastdir, 180)
if(H.stat == CONSCIOUS && H.canmove && isturf(H.loc))
SendSignal(COMSIG_STEP, list("dir" = dir))
parent.invoke_event(/event/comp_ai_cmd_move, list("target" = dir))
lastdir = dir
/datum/component/ai/human_brain/proc/AssessNeeds(mob/living/carbon/human/H)
@@ -164,28 +162,29 @@
return goal
/datum/component/ai/human_brain/proc/AcquireItem(mob/living/carbon/human/H, obj/item/I)
SendSignal(COMSIG_ACTVEMPTYHAND, list())
SendSignal(COMSIG_CLICKON, list("target" = I))
H.activate_empty_hand()
H.ClickOn(I)
if(istype(I, /obj/item/weapon/reagent_containers/food/snacks))
ConsumeFood(H, I)
else
SendSignal(COMSIG_EQUIPACTVHAND, list())
var/obj/item/held = H.get_active_hand()
if(held)
H.equip_to_appropriate_slot(held)
/datum/component/ai/human_brain/proc/ConsumeFood(mob/living/carbon/human/H, obj/item/I)
while(H.nutrition < 250 && !I.gcDestroyed)
SendSignal(COMSIG_ITMATKSELF, list())
var/obj/item/held = H.get_active_hand()
if(held)
held.attack_self(H)
sleep(1)
SendSignal(COMSIG_DROP, list())
/datum/component/ai/human_brain/proc/target_evaluator(var/atom/target)
return TRUE
H.drop_item()
/datum/component/ai/human_brain/proc/WieldBestWeapon(mob/living/carbon/human/H, var/list/excluded)
if(H.isStunned()) //We're on the floor, nothing we can do
return 0
SendSignal(COMSIG_ACTVEMPTYHAND, list())
H.activate_empty_hand()
if(H.get_active_hand())
SendSignal(COMSIG_DROP, list())
H.drop_item()
if(H.get_active_hand())
return 1
if(!excluded)
@@ -199,7 +198,7 @@
if(I.force > current_candidate.force || (I.force == current_candidate.force && I.sharpness > current_candidate.sharpness))
current_candidate = I
if(current_candidate)
SendSignal(COMSIG_CLICKON, list("target" = current_candidate))
H.ClickOn(current_candidate)
if(current_candidate != H.get_active_hand())
excluded.Add(current_candidate)
.(H, excluded)
@@ -218,4 +217,4 @@
return 0
for(var/obj/item/I in search_location)
if((!O && I.force > 2) || (O && (I.force > O.force || (I.force == O.force && I.sharpness > O.sharpness))))
return 1
return 1

View File

@@ -1,32 +0,0 @@
/datum/component/ai/hand_control/RecieveSignal(var/message_type, var/list/args)
if(iscarbon(container.holder))
var/mob/living/carbon/M = container.holder
//testing("Got command: \[[message_type]\]: [json_encode(args)]")
switch(message_type)
if(COMSIG_DROP) // list("pickup" = item)
if(M.get_active_hand())
M.drop_item()
if(COMSIG_ACTVHANDBYITEM) // list("target" = item)
var/obj/item/I = args["target"]
for(var/j = 1 to M.held_items.len)
if(M.held_items[j] == I)
M.active_hand = j
break
if(COMSIG_ACTVEMPTYHAND)
for(var/j = 1 to M.held_items.len)
if(M.held_items[j] == null)
M.active_hand = j
break
if(COMSIG_THROWAT) // list("target" = atom)
var/atom/A = args["target"]
M.throw_mode_on()
M.ClickOn(A)
M.throw_mode_off()
if(COMSIG_ITMATKSELF)
var/obj/item/I = M.get_active_hand()
if(I)
I.attack_self(M)
if(COMSIG_EQUIPACTVHAND)
var/obj/item/I = M.get_active_hand()
if(I)
M.equip_to_appropriate_slot(I)

View File

@@ -1,12 +1,12 @@
/datum/component/ai/melee/attack_human
// var/datum/component/ai/human_brain/B
/datum/component/ai/melee/attack_human/OnAttackingTarget(var/atom/target)
if(..(target))
var/mob/living/carbon/human/H = container.holder
H.a_intent = I_HURT
var/damage_type = container.ReturnFromSignalFirst(COMSIG_GETDAMTYPE, list("user" = H))
var/def_zone = container.ReturnFromSignalFirst(COMSIG_GETDEFZONE, list("target" = target, "damage_type" = damage_type))
SendSignal(COMSIG_CLICKON, list("target" = target, "def_zone" = def_zone))
return 1 // Accepted
return 0 // Unaccepted
/datum/component/ai/melee/attack_human/cmd_attack(atom/target)
if(!can_attack(target))
return FALSE
var/mob/living/carbon/human/H = parent
H.a_intent = I_HURT
var/damage_type = H.invoke_event(/event/comp_ai_cmd_get_damage_type)
// TODO make it target the correct def_zone
H.invoke_event(/event/comp_ai_cmd_evaluate_target, list("target" = target, "damage_type" = damage_type))
H.ClickOn(target)
return TRUE

View File

@@ -1,31 +1,36 @@
/datum/component/ai/target_finder/human
range = 7
var/datum/component/ai/human_brain/B
/datum/component/ai/target_finder/human/GetTargets()
ASSERT(container.holder!=null)
if(!B)
B = GetComponent(/datum/component/ai/human_brain)
/datum/component/ai/target_finder/human/initialize()
parent.register_event(/event/attackby, src, .proc/on_attackby)
parent.register_event(/event/comp_ai_cmd_find_targets, src, .proc/cmd_find_targets)
return TRUE
/datum/component/ai/target_finder/human/Destroy()
parent.unregister_event(/event/attackby, src, .proc/on_attackby)
parent.unregister_event(/event/comp_ai_cmd_find_targets, src, .proc/cmd_find_targets)
..()
/datum/component/ai/target_finder/human/cmd_find_targets()
var/datum/component/ai/human_brain/brain = parent.get_component(/datum/component/ai/human_brain)
var/list/o = list()
for(var/mob/M in view(range, container.holder))
for(var/mob/M in view(range, parent))
if(is_type_in_list(M, exclude_types))
continue
if(M.isUnconscious())
continue
if((M in B.enemies) || (M.faction && (M.faction in B.enemy_factions)) || (M.type in B.enemy_types))
if((M in brain.enemies) || (M.faction && (M.faction in brain.enemy_factions)) || (M.type in brain.enemy_types))
o += M
else if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.species && (H.species.name in B.enemy_species))
if(H.species && (H.species.name in brain.enemy_species))
o += M
return o
/datum/component/ai/target_finder/human/RecieveSignal(var/message_type, var/list/args)
..()
if(message_type == COMSIG_ATTACKEDBY) //YOU HAVE JUST MADE AN ENEMY FOR LIFE
var/assailant = args["assailant"]
var/damage_done = args["damage"]
if(damage_done > 15) //Intent to kill!
B.friends.Remove(assailant)
if(damage_done > 2)
B.enemies |= assailant
// YOU HAVE JUST MADE AN ENEMY FOR LIFE
/datum/component/ai/target_finder/human/proc/on_attackby(mob/attacker, obj/item/item)
var/datum/component/ai/human_brain/brain = parent.get_component(/datum/component/ai/human_brain)
if(item.force > 15) //Intent to kill!
brain.friends.Remove(attacker)
if(item.force > 2)
brain.enemies |= attacker

View File

@@ -1,13 +1,18 @@
/datum/component/ai/crowd_attack
var/datum/component/ai/human_brain/B
/datum/component/ai/crowd_attack/RecieveSignal(var/message_type, var/list/args)
if(!B)
B = GetComponent(/datum/component/ai/human_brain)
if(B && message_type == COMSIG_ATTACKEDBY)
var/assailant = args["assailant"]
var/damage_done = args["damage"]
for(var/mob/living/M in oview(7, container.holder))
if(!M.isUnconscious() || !M.BrainContainer || !(M in B.friends)) //THEY'RE ATTACKING OUR BOY, GET HIM!
continue
M.BrainContainer.SendSignal(COMSIG_ATTACKEDBY, list("assailant"=assailant,"damage"=damage_done))
/datum/component/ai/crowd_attack/initialize()
parent.register_event(/event/attackby, src, .proc/on_attackby)
return TRUE
/datum/component/ai/crowd_attack/Destroy()
parent.unregister_event(/event/attackby, src, .proc/on_attackby)
..()
/datum/component/ai/crowd_attack/proc/on_attackby(mob/attacker, obj/item/item)
var/datum/component/ai/human_brain/brain = parent.get_component(/datum/component/ai/human_brain)
if(!brain)
return
for(var/mob/living/M in oview(7, parent))
if(!(M in brain.friends)) //THEY'RE ATTACKING OUR BOY, GET HIM!
continue
M.invoke_event(/event/comp_ai_friend_attacked, list("attacker"=attacker))

View File

@@ -1,12 +1,14 @@
/datum/component/ai/melee/throw_attack
/datum/component/ai/melee/throw_attack/OnAttackingTarget(var/atom/target)
/datum/component/ai/melee/throw_attack/cmd_attack(atom/target)
if(!isliving(target))
return 0
var/mob/M = container.holder
var/mob/living/M = parent
if(!istype(M))
return 0
var/obj/item/I = M.get_active_hand()
if(I && I.throwforce > I.force && get_dist(M,target) > 2) //Better to throw it at the fucker
SendSignal(COMSIG_THROWAT, list("target" = target))
return 1
M.throw_mode_on()
M.ClickOn(target)
M.throw_mode_off()
return 1

View File

@@ -4,13 +4,3 @@
* See /datum/component and /datum/component_container.
*/
/mob/living/component
/mob/living/component/New()
..()
BrainContainer = new (src)
InitializeComponents()
BrainContainer.register_for_updates()
/mob/living/component/proc/InitializeComponents()
// Set up components here
//var/datum/component/.../ref = container.AddComponent(/datum/component/...)

View File

@@ -1,22 +1,22 @@
/mob/living/component/giant_spider
name="component giant spider"
name = "component giant spider"
icon_state = "guard"
icon = 'icons/mob/animal.dmi'
/mob/living/component/giant_spider/InitializeComponents()
BrainContainer.AddComponent(/datum/component/controller/mob)
BrainContainer.AddComponent(/datum/component/ai/escape_confinement)
BrainContainer.AddComponent(/datum/component/ai/hunt)
BrainContainer.AddComponent(/datum/component/ai/melee/attack_animal)
var/datum/component/ai/melee/inject_reagent/injector = BrainContainer.AddComponent(/datum/component/ai/melee/inject_reagent)
/mob/living/component/giant_spider/New()
..()
add_component(/datum/component/controller)
add_component(/datum/component/ai/escape_confinement)
add_component(/datum/component/ai/hunt)
add_component(/datum/component/ai/melee/attack_animal)
var/datum/component/ai/melee/inject_reagent/injector = add_component(/datum/component/ai/melee/inject_reagent)
injector.poison_type = STOXIN
injector.poison_per_bite = 5
var/datum/component/ai/target_finder/simple_view/sv = BrainContainer.AddComponent(/datum/component/ai/target_finder/simple_view)
var/datum/component/ai/target_finder/simple_view/sv = add_component(/datum/component/ai/target_finder/simple_view)
sv.range = 5
// These two should probably be done on New() based on container.holder.
sv.exclude_types += src.type
sv.exclude_types += /mob/living/silicon/robot/mommi // Because we wuv dem
var/datum/component/ai/target_holder/prioritizing/th = BrainContainer.AddComponent(/datum/component/ai/target_holder/prioritizing)
var/datum/component/ai/target_holder/prioritizing/th = add_component(/datum/component/ai/target_holder/prioritizing)
th.type_priorities[src.type]=0
BrainContainer.AddComponent(/datum/component/ai/door_opener)
add_component(/datum/component/ai/door_opener)

View File

@@ -1,7 +1,6 @@
/datum/component/ai/target_finder/simple_view/GetTargets()
ASSERT(container.holder!=null)
/datum/component/ai/target_finder/simple_view/cmd_find_targets()
var/list/o = list()
for(var/atom/A in view(range, container.holder))
for(var/atom/A in view(range, parent))
if(is_type_in_list(A, exclude_types))
continue
o += A

View File

@@ -1,14 +1,18 @@
/*
var/datum/controller/target_finder/TF = GetComponent(/datum/controller/target_finder)
var/list/target = TF.GetTargets()
*/
/datum/component/ai/target_finder
var/range=0
var/list/exclude_types=list(
/obj/effect,
/atom/movable/lighting_overlay,
/turf
var/range = 0
var/list/exclude_types = list(
/obj/effect,
/atom/movable/lighting_overlay,
/turf
)
/datum/component/ai/target_finder/proc/GetTargets()
/datum/component/ai/target_finder/initialize()
parent.register_event(/event/comp_ai_cmd_find_targets, src, .proc/cmd_find_targets)
return TRUE
/datum/component/ai/target_finder/Destroy()
parent.unregister_event(/event/comp_ai_cmd_find_targets, src, .proc/cmd_find_targets)
..()
/datum/component/ai/target_finder/proc/cmd_find_targets()
return list()

View File

@@ -1,22 +1,18 @@
/datum/component/ai/target_holder/prioritizing
var/list/targets= list()
var/list/type_priorities = list(
/mob/living = 1,
// /mob/living/simple_animal/hostile/giant_spider=0,
/obj/machinery/door = 2,
/obj/machinery/light = 3
/mob/living = 1,
// /mob/living/simple_animal/hostile/giant_spider=0,
/obj/machinery/door = 2,
/obj/machinery/light = 3
)
var/default_priority=2
var/default_priority = 2
var/max_priority=3
var/max_priority = 3
var/datum/component/ai/target_finder/finder = null
/datum/component/ai/target_holder/prioritizing/proc/attach()
if(!finder)
finder = GetComponent(/datum/component/ai/target_finder)
/datum/component/ai/target_holder/prioritizing/AddTarget(var/atom/A)
/datum/component/ai/target_holder/prioritizing/cmd_add_target(atom/A)
var/priority=-1
for(var/priority_type in type_priorities)
if(istype(A, priority_type))
@@ -31,25 +27,19 @@
if(!(A in targets["[priority]"]))
targets["[priority]"] += A
/datum/component/ai/target_holder/prioritizing/RemoveTarget(var/atom/A)
/datum/component/ai/target_holder/prioritizing/cmd_remove_target(atom/A)
for(var/priority in targets)
if(A in targets[priority])
targets[priority] -= A
/datum/component/ai/target_holder/prioritizing/GetBestTarget(var/objRef, var/procName, var/from_finder=1)
if(from_finder)
attach()
targets.Cut() // Clear first
//var/n=0
for(var/atom/target in finder.GetTargets())
AddTarget(target)
//n++
//testing(" TH: Got [n] targets from TF")
for(var/priority=1;priority<=max_priority;priority++)
/datum/component/ai/target_holder/prioritizing/cmd_get_best_target()
targets.Cut() // Clear first
for(var/atom/target in parent.invoke_event(/event/comp_ai_cmd_find_targets))
parent.invoke_event(/event/comp_ai_cmd_add_target, list("target" = target))
for(var/priority in 1 to max_priority)
var/list/priority_targets = targets["[priority]"]
if(priority_targets == null)
continue
for(var/atom/target in sortMerge(priority_targets, cmp=/proc/cmp_dist_asc, associative=0, fromIndex=1, toIndex=priority_targets.len))
if(call(objRef, procName)(target))
return target
return target
return null

View File

@@ -1,19 +1,22 @@
/datum/component/ai/target_holder
/datum/component/ai/target_holder/proc/AddTarget(var/atom/A)
/datum/component/ai/target_holder/initialize()
parent.register_event(/event/comp_ai_cmd_add_target, src, .proc/cmd_add_target)
parent.register_event(/event/comp_ai_cmd_remove_target, src, .proc/cmd_remove_target)
parent.register_event(/event/comp_ai_cmd_get_best_target, src, .proc/cmd_get_best_target)
return TRUE
/datum/component/ai/target_holder/proc/cmd_add_target(var/atom/A)
return
/datum/component/ai/target_holder/proc/RemoveTarget(var/atom/A)
/datum/component/ai/target_holder/proc/cmd_remove_target(var/atom/A)
return
/**
* Get the best target
*
* @param objRef Direct reference to the holding object containing the target validation callback
* @param procName Name of the callback proc
* @param from_finder Use /datum/component/ai/target_finder.GetTargets()
* @return null if not target found, /atom if a target is found.
*/
/datum/component/ai/target_holder/proc/GetBestTarget(var/objRef, var/procName, var/from_finder=1)
/datum/component/ai/target_holder/proc/cmd_get_best_target()
return

View File

@@ -1,29 +0,0 @@
/datum/component
var/datum/component_container/container
// Enables or disables the components
var/enabled=1
/datum/component/New(var/datum/component_container/CC)
..()
container=CC
// Override to receive signals.
/datum/component/proc/RecieveSignal(var/sigtype, var/list/args)
return
// Send a signal to all other components in the container.
/datum/component/proc/SendSignal(var/sigtype, var/list/args)
container.SendSignal(sigtype, args)
// Return first /datum/component that is subtype of c_type.
/datum/component/proc/GetComponent(var/c_type)
return container.GetComponent(c_type)
// Returns ALL /datum/components in parent container that are subtypes of c_type.
/datum/component/proc/GetComponents(var/c_type)
return container.GetComponents(c_type)
// Returns a value depending on what the signal and args were.
/datum/component/proc/RecieveAndReturnSignal(var/sigtype, var/list/args)
return 0

View File

@@ -1,127 +0,0 @@
// Basic multipurpose component container.
/datum/component_container
// Where the components themselves are stored after initialization.
var/list/components=list()
// The types to initialize the holder with. Later, holds unique list of all types in container.
var/list/types=list()
// The entity that holds this datum.
var/atom/holder
// Used for dependency management.
/datum/component_container/New(var/atom/holder_atom)
holder=holder_atom
/datum/component_container/Destroy()
for(var/i in components)
qdel(i)
components.Cut()
holder = null
unregister_for_updates()
..()
/datum/component_container/proc/AddComponentsByType(var/list/new_types)
var/list/newtypes=list()
for(var/new_type in new_types)
AddComponent(new_type,TRUE)
if(!(new_type in newtypes))
newtypes.Add(new_type)
types=newtypes
/**
* Add component to the container.
*
* @param new_type The type we wish to instantiate in the component_container.
* @param initializing Do not use, only used in container New() for internal purposes.
*/
/datum/component_container/proc/AddComponent(var/new_type, var/initializing=FALSE)
if(!initializing && !(new_type in types))
types.Add(new_type)
var/datum/component/C = new new_type(src)
components.Add(C)
SendSignal(COMSIG_COMPONENT_ADDED,list("component"=C))
return C
/**
* Removes a component from the container.
*
* @param C The component to remove
*/
/datum/component_container/proc/RemoveComponent(var/datum/component/C)
SendSignal(COMSIG_COMPONENT_REMOVING,list("component"=C))
components.Remove(C)
types.Cut()
for(var/datum/component/CC in components)
if(!(CC.type in types))
types.Add(CC.type)
//qdel(C) Will most likely get GC'd anyway.
/**
* Send a signal to all components in the container.
*
* @param message_type Name of the signal.
* @param args List of arguments to send with the signal.
*/
/datum/component_container/proc/SendSignal(var/message_type, var/list/args)
for(var/datum/component/C in components)
if(C.enabled)
C.RecieveSignal(message_type, args)
/**
* Send a signal to the first component of type accepting a signal.
*
* @param component_type
* @param message_type Name of the signal.
* @param args List of arguments to send with the signal.
*/
/datum/component_container/proc/SendSignalToFirst(var/desired_type, var/message_type, var/list/args, var/shuffle=TRUE)
var/list/shuffled=list(components) // Copy list so we don't disorder the container.
if(shuffle)
shuffled=shuffle(shuffled)
for(var/datum/component/C in components)
if(C.enabled && istype(C, desired_type))
if(C.RecieveSignal(message_type, args)) // return 1 to accept signal.
return
/**
* Send a signal, and see if anyone replies with information.
*
* @param message_type: String(DEFINE): The message to send
* @param args: List(ref): The arguments associated with this message
*/
/datum/component_container/proc/ReturnFromSignalFirst(var/message_type, var/list/args)
for(var/datum/component/C in components)
if(C.enabled)
var/received_information = C.RecieveAndReturnSignal(message_type, args)
if(received_information)
return received_information
/**
* Get the first component matching the specified type.
*/
/datum/component_container/proc/GetComponent(var/desired_type)
for(var/datum/component/C in components)
if(istype(C, desired_type))
return C
return null
/**
* Get the all components matching the specified type.
*/
/datum/component_container/proc/GetComponents(var/desired_type)
. = list()
for(var/datum/component/C in components)
if(istype(C, desired_type))
. += C
return .
/datum/component_container/proc/register_for_updates()
active_component_containers.Add(src)
/datum/component_container/proc/unregister_for_updates()
active_component_containers.Remove(src)

View File

@@ -1,15 +0,0 @@
/datum/component/debugger //Exists to shout 'HEY, WE'RE USING THIS SIGNAL'
var/spam = 1
/datum/component/debugger/RecieveSignal(var/message_type, var/list/args)
if(spam)
to_chat(world, "===================================")
to_chat(world, "[container.holder] received [message_type], args are")
for(var/i in args)
to_chat(world, "[i]: [args[i]]")
to_chat(world, "<a HREF='?src=\ref[src];pause=1'>\[Press here to stop and start the spam\]</a>")
/datum/component/debugger/Topic(href, href_list)
.=..()
if(href_list["pause"])
spam = !spam

View File

@@ -60,6 +60,11 @@
return 0
/mob/proc/activate_empty_hand()
var/empty_hand = find_empty_hand_index()
if(empty_hand)
activate_hand(empty_hand)
/mob/proc/empty_hand_indexes_amount()
. = 0

View File

@@ -8,8 +8,8 @@
/mob/living/clickbot/ClickOn(var/atom/A, var/params)
make_astar_path(A)
/mob/living/clickbot/make_astar_path(var/atom/target, var/receiving_proc = .get_astar_path)
AStar(src, receiving_proc, get_turf(src), target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 30, 30, debug = TRUE)
/mob/living/clickbot/make_astar_path(var/atom/target, var/callback = new /callback(src, .get_astar_path))
AStar(src, callback, get_turf(src), target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 30, 30, debug = TRUE)
/mob/living/clickbot/get_astar_path(var/list/L)

View File

@@ -1880,21 +1880,17 @@ mob/living/carbon/human/isincrit()
else return image(icon = 'icons/mob/attackanims.dmi', icon_state = "default")
/mob/living/carbon/human/proc/initialize_barebones_NPC_components() //doesn't actually do anything, but contains tools needed for other types to do things
BrainContainer = new (src)
BrainContainer.AddComponent(/datum/component/controller/mob)
BrainContainer.AddComponent(/datum/component/ai/hand_control)
BrainContainer.AddComponent(/datum/component/controller/movement/astar)
BrainContainer.register_for_updates()
add_component(/datum/component/controller/movement/astar)
/mob/living/carbon/human/proc/initialize_basic_NPC_components() //will wander around
initialize_barebones_NPC_components()
BrainContainer.AddComponent(/datum/component/ai/human_brain)
BrainContainer.AddComponent(/datum/component/ai/target_finder/human)
BrainContainer.AddComponent(/datum/component/ai/target_holder/prioritizing)
BrainContainer.AddComponent(/datum/component/ai/melee/attack_human)
BrainContainer.AddComponent(/datum/component/ai/melee/throw_attack)
BrainContainer.AddComponent(/datum/component/ai/crowd_attack)
BrainContainer.AddComponent(pick(typesof(/datum/component/ai/targetting_handler)))
add_component(/datum/component/ai/human_brain)
add_component(/datum/component/ai/target_finder/human)
add_component(/datum/component/ai/target_holder/prioritizing)
add_component(/datum/component/ai/melee/attack_human)
add_component(/datum/component/ai/melee/throw_attack)
add_component(/datum/component/ai/crowd_attack)
add_component(pick(typesof(/datum/component/ai/targetting_handler)))
/mob/living/carbon/human/can_show_flavor_text()
// Wearing a mask...

View File

@@ -125,8 +125,6 @@
damage += sharpness
damage_done = target.apply_damage(damage, damage_type, affecting, armor_block)
if(target.BrainContainer)
target.BrainContainer.SendSignal(COMSIG_ATTACKEDBY, list("assailant"=src,"damage"=damage_done))
target.unarmed_attacked(src, damage, damage_type, zone)
after_unarmed_attack(target, damage, damage_type, affecting, armor_block)

View File

@@ -34,10 +34,6 @@
qdel(B)
B = null
if(BrainContainer)
qdel(BrainContainer)
BrainContainer = null
if(immune_system)
qdel(immune_system)
immune_system = null

View File

@@ -77,8 +77,6 @@
var/calorie_burning_heat_multiplier = 1 //The heat generated from burning calories is multiplied by this value.
var/thermal_loss_multiplier = 1 //The heat the mob loses to the environment is multiplied by this value.
var/datum/component_container/BrainContainer
var/list/datum/disease2/disease/virus2 = list()
var/image/pathogen
var/datum/immune_system/immune_system

View File

@@ -1502,16 +1502,11 @@
#include "code\modules\clothing\under\jobs\medsci.dm"
#include "code\modules\clothing\under\jobs\security.dm"
#include "code\modules\cmc\crew.dm"
#include "code\modules\components\component.dm"
#include "code\modules\components\component_container.dm"
#include "code\modules\components\debugger.dm"
#include "code\modules\components\ai\ai_component.dm"
#include "code\modules\components\ai\atmos.dm"
#include "code\modules\components\ai\controller.dm"
#include "code\modules\components\ai\def_zone_handler.dm"
#include "code\modules\components\ai\door_opener.dm"
#include "code\modules\components\ai\get_damtype.dm"
#include "code\modules\components\ai\controllers\mob_controller.dm"
#include "code\modules\components\ai\controllers\movement.dm"
#include "code\modules\components\ai\controllers\simple_animal.dm"
#include "code\modules\components\ai\hostile\escape_confinement.dm"
@@ -1520,7 +1515,6 @@
#include "code\modules\components\ai\hostile\melee\inject_reagent.dm"
#include "code\modules\components\ai\hostile\melee\melee.dm"
#include "code\modules\components\ai\human\brain.dm"
#include "code\modules\components\ai\human\hand_usage.dm"
#include "code\modules\components\ai\human\human_attack.dm"
#include "code\modules\components\ai\human\human_target_finder.dm"
#include "code\modules\components\ai\human\retaliate.dm"