Experiment with replacing weakrefs in AI blackboard with deleting signals, ideally making it easier to work with and harder to cause hard deletes (#74791)

## About The Pull Request

Replaces weakref usage in AI blackboards with deleting signals

All blackboard var setting must go through setters rather than directly

## Why It's Good For The Game

This both makes it a ton easier to develop AI for, and also makes it
harder for hard deletes to sneak in, as has been seen with recent 515
prs showing hard deletes in AI blackboards

(To quantify "making it easier to develop AI", I found multiple bugs in
existing AI code due to the usage of weakrefs.)

I'm looking for `@Jacquerel` `@tralezab` 's opinions on the matter, also
maybe `@LemonInTheDark` if they're interested

## Changelog

🆑 Melbert
refactor: Mob ai refactored once again
/🆑
This commit is contained in:
MrMelbert
2023-04-23 18:07:17 -05:00
committed by GitHub
parent 23b09f8a14
commit ed2f04f486
92 changed files with 688 additions and 592 deletions
+255 -2
View File
@@ -7,6 +7,16 @@ multiple modular subtrees with behaviors
/datum/ai_controller
///The atom this controller is controlling
var/atom/pawn
/**
* This is a list of variables the AI uses and can be mutated by actions.
*
* When an action is performed you pass this list and any relevant keys for the variables it can mutate.
*
* DO NOT set values in the blackboard directly, and especially not if you're adding a datum reference to this!
* Use the setters, this is important for reference handing.
*/
var/list/blackboard = list()
///Bitfield of traits for this AI to handle extra behavior
var/ai_traits = NONE
///Current actions planned to be performed by the AI in the upcoming plan
@@ -21,8 +31,6 @@ multiple modular subtrees with behaviors
var/atom/current_movement_target
///Identifier for what last touched our movement target, so it can be cleared conditionally
var/movement_target_source
///This is a list of variables the AI uses and can be mutated by actions. When an action is performed you pass this list and any relevant keys for the variables it can mutate.
var/list/blackboard = list()
///Stored arguments for behaviors given during their initial creation
var/list/behavior_args = list()
///Tracks recent pathing attempts, if we fail too many in a row we fail our current plans.
@@ -336,3 +344,248 @@ multiple modular subtrees with behaviors
if(iter_behavior.required_distance < minimum_distance)
minimum_distance = iter_behavior.required_distance
return minimum_distance
/**
* Used to manage references to datum by AI controllers
*
* * tracked_datum - something being added to an ai blackboard
* * key - the associated key
*/
#define TRACK_AI_DATUM_TARGET(tracked_datum, key) do { \
if(isweakref(tracked_datum)) { \
var/datum/weakref/_bad_weakref = tracked_datum; \
stack_trace("Weakref (Actual datum: [_bad_weakref.resolve()]) found in ai datum blackboard! \
This is an outdated method of ai reference handling, please remove it."); \
}; \
else if(isdatum(tracked_datum)) { \
var/datum/_tracked_datum = tracked_datum; \
if(!HAS_TRAIT_FROM(_tracked_datum, TRAIT_AI_TRACKING, "[REF(src)]_[key]")) { \
RegisterSignal(_tracked_datum, COMSIG_PARENT_QDELETING, PROC_REF(sig_remove_from_blackboard), override = TRUE); \
ADD_TRAIT(_tracked_datum, TRAIT_AI_TRACKING, "[REF(src)]_[key]"); \
}; \
}; \
} while(FALSE)
/**
* Used to clear previously set reference handing by AI controllers
*
* * tracked_datum - something being removed from an ai blackboard
* * key - the associated key
*/
#define CLEAR_AI_DATUM_TARGET(tracked_datum, key) do { \
if(isdatum(tracked_datum)) { \
var/datum/_tracked_datum = tracked_datum; \
REMOVE_TRAIT(_tracked_datum, TRAIT_AI_TRACKING, "[REF(src)]_[key]"); \
if(!HAS_TRAIT(_tracked_datum, TRAIT_AI_TRACKING)) { \
UnregisterSignal(_tracked_datum, COMSIG_PARENT_QDELETING); \
}; \
}; \
} while(FALSE)
/// Used for above to track all the keys that have registered a signal
#define TRAIT_AI_TRACKING "tracked_by_ai"
/**
* Sets the key to the passed "thing".
*
* * key - A blackboard key
* * thing - a value to set the blackboard key to.
*/
/datum/ai_controller/proc/set_blackboard_key(key, thing)
// Assume it is an error when trying to set a value overtop a list
if(islist(blackboard[key]))
CRASH("set_blackboard_key attempting to set a blackboard value to key [key] when it's a list!")
// Clear existing values
if(!isnull(blackboard[key]))
clear_blackboard_key(key)
TRACK_AI_DATUM_TARGET(thing, key)
blackboard[key] = thing
/**
* Sets the key at index thing to the passed value
*
* Assumes the key value is already a list, if not throws an error.
*
* * key - A blackboard key, with its value set to a list
* * thing - a value which becomes the inner list value's key
* * value - what to set the inner list's value to
*/
/datum/ai_controller/proc/set_blackboard_key_assoc(key, thing, value)
if(!islist(blackboard[key]))
CRASH("set_blackboard_key_assoc called on non-list key [key]!")
TRACK_AI_DATUM_TARGET(thing, key)
TRACK_AI_DATUM_TARGET(value, key)
blackboard[key][thing] = value
/**
* Similar to [proc/set_blackboard_key_assoc] but operates under the assumption the key is a lazylist (so it will create a list)
* More dangerous / easier to override values, only use when you want to use a lazylist
*
* * key - A blackboard key, with its value set to a list
* * thing - a value which becomes the inner list value's key
* * value - what to set the inner list's value to
*/
/datum/ai_controller/proc/set_blackboard_key_assoc_lazylist(key, thing, value)
LAZYINITLIST(blackboard[key])
TRACK_AI_DATUM_TARGET(thing, key)
TRACK_AI_DATUM_TARGET(value, key)
blackboard[key][thing] = value
/**
* Adds the passed "thing" to the associated key
*
* Works with lists or numbers, but not lazylists.
*
* * key - A blackboard key
* * thing - a value to set the blackboard key to.
*/
/datum/ai_controller/proc/add_blackboard_key(key, thing)
TRACK_AI_DATUM_TARGET(thing, key)
blackboard[key] += thing
/**
* Similar to [proc/add_blackboard_key], but performs an insertion rather than an add
* Throws an error if the key is not a list already, intended only for use with lists
*
* * key - A blackboard key, with its value set to a list
* * thing - a value to set the blackboard key to.
*/
/datum/ai_controller/proc/insert_blackboard_key(key, thing)
if(!islist(blackboard[key]))
CRASH("insert_blackboard_key called on non-list key [key]!")
TRACK_AI_DATUM_TARGET(thing, key)
blackboard[key] |= thing
/**
* Adds the passed "thing" to the associated key, assuming key is intended to be a lazylist (so it will create a list)
* More dangerous / easier to override values, only use when you want to use a lazylist
*
* * key - A blackboard key
* * thing - a value to set the blackboard key to.
*/
/datum/ai_controller/proc/add_blackboard_key_lazylist(key, thing)
LAZYINITLIST(blackboard[key])
TRACK_AI_DATUM_TARGET(thing, key)
blackboard[key] += thing
/**
* Similar to [proc/insert_blackboard_key_lazylist], but performs an insertion / or rather than an add
*
* * key - A blackboard key
* * thing - a value to set the blackboard key to.
*/
/datum/ai_controller/proc/insert_blackboard_key_lazylist(key, thing)
LAZYINITLIST(blackboard[key])
TRACK_AI_DATUM_TARGET(thing, key)
blackboard[key] |= thing
/**
* Adds the value to the inner list at key with the inner key set to "thing"
* Throws an error if the key is not a list already, intended only for use with lists
*
* * key - A blackboard key, with its value set to a list
* * thing - a value which becomes the inner list value's key
* * value - what to set the inner list's value to
*/
/datum/ai_controller/proc/add_blackboard_key_assoc(key, thing, value)
if(!islist(blackboard[key]))
CRASH("add_blackboard_key_assoc called on non-list key [key]!")
TRACK_AI_DATUM_TARGET(thing, key)
TRACK_AI_DATUM_TARGET(value, key)
blackboard[key][thing] += value
/**
* Similar to [proc/add_blackboard_key_assoc], assuming key is intended to be a lazylist (so it will create a list)
* More dangerous / easier to override values, only use when you want to use a lazylist
*
* * key - A blackboard key, with its value set to a list
* * thing - a value which becomes the inner list value's key
* * value - what to set the inner list's value to
*/
/datum/ai_controller/proc/add_blackboard_key_assoc_lazylist(key, thing, value)
LAZYINITLIST(blackboard[key])
TRACK_AI_DATUM_TARGET(thing, key)
TRACK_AI_DATUM_TARGET(value, key)
blackboard[key][thing] += value
/**
* Clears the passed key, resetting it to null
*
* Not intended for use with list keys - use [proc/remove_thing_from_blackboard_key] if you are removing a value from a list at a key
*
* * key - A blackboard key
*/
/datum/ai_controller/proc/clear_blackboard_key(key)
CLEAR_AI_DATUM_TARGET(blackboard[key], key)
blackboard[key] = null
/**
* Remove the passed thing from the associated blackboard key
*
* Intended for use with lists, if you're just clearing a reference from a key use [proc/clear_blackboard_key]
*
* * key - A blackboard key
* * thing - a value to set the blackboard key to.
*/
/datum/ai_controller/proc/remove_thing_from_blackboard_key(key, thing)
var/associated_value = blackboard[key]
if(thing == associated_value)
stack_trace("remove_thing_from_blackboard_key was called un-necessarily in a situation where clear_blackboard_key would suffice. ")
clear_blackboard_key(key)
return
if(!islist(associated_value))
CRASH("remove_thing_from_blackboard_key called with an invalid \"thing\" argument ([thing]). \
(The associated value of the passed key is not a list and is also not the passed thing, meaning it is clearing an unintended value.)")
for(var/inner_key in associated_value)
if(inner_key == thing)
// flat list
CLEAR_AI_DATUM_TARGET(thing, key)
associated_value -= thing
return
else if(associated_value[inner_key] == thing)
// assoc list
CLEAR_AI_DATUM_TARGET(thing, key)
associated_value -= inner_key
return
CRASH("remove_thing_from_blackboard_key called with an invalid \"thing\" argument ([thing]). \
(The passed value is not tracked in the passed list.)")
/// Signal proc to go through every key and remove the datum from all keys it finds
/datum/ai_controller/proc/sig_remove_from_blackboard(datum/source)
SIGNAL_HANDLER
var/list/list/remove_queue = list(blackboard)
var/index = 1
while(index <= length(remove_queue))
var/list/next_to_clear = remove_queue[index]
for(var/inner_value in next_to_clear)
var/associated_value = next_to_clear[inner_value]
// We are a lists of lists, add the next value to the queue so we can handle references in there
// (But we only need to bother checking the list if it's not empty.)
if(islist(inner_value) && length(inner_value))
UNTYPED_LIST_ADD(remove_queue, inner_value)
// We found the value that's been deleted. Clear it out from this list
else if(inner_value == source)
next_to_clear -= inner_value
// We are an assoc lists of lists, the list at the next value so we can handle references in there
// (But again, we only need to bother checking the list if it's not empty.)
if(islist(associated_value) && length(associated_value))
UNTYPED_LIST_ADD(remove_queue, associated_value)
// We found the value that's been deleted, it was an assoc value. Clear it out entirely
else if(associated_value == source)
next_to_clear -= inner_value
index += 1
#undef TRACK_AI_DATUM_TARGET
#undef CLEAR_AI_DATUM_TARGET
#undef TRAIT_AI_TRACKING
+6 -9
View File
@@ -24,8 +24,7 @@
/datum/ai_behavior/item_move_close_and_attack/setup(datum/ai_controller/controller, target_key, throw_count_key)
. = ..()
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/atom/target = target_ref?.resolve()
var/atom/target = controller.blackboard[target_key]
if (isnull(target))
return FALSE
set_movement_target(controller, target)
@@ -33,13 +32,12 @@
/datum/ai_behavior/item_move_close_and_attack/perform(seconds_per_tick, datum/ai_controller/controller, target_key, throw_count_key)
. = ..()
var/obj/item/item_pawn = controller.pawn
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/atom/throw_target = target_ref?.resolve()
var/atom/throw_target = controller.blackboard[target_key]
item_pawn.visible_message(span_warning("[item_pawn] hurls towards [throw_target]!"))
item_pawn.throw_at(throw_target, rand(4,5), 9)
playsound(item_pawn.loc, attack_sound, 100, TRUE)
controller.blackboard[throw_count_key]++
controller.add_blackboard_key(throw_count_key, 1)
if(controller.blackboard[throw_count_key] >= max_attempts)
finish_action(controller, TRUE, target_key, throw_count_key)
@@ -48,8 +46,8 @@
reset_blackboard(controller, succeeded, target_key, throw_count_key)
/datum/ai_behavior/item_move_close_and_attack/proc/reset_blackboard(datum/ai_controller/controller, succeeded, target_key, throw_count_key)
controller.blackboard -= target_key
controller.blackboard[throw_count_key] = 0
controller.clear_blackboard_key(target_key)
controller.set_blackboard_key(throw_count_key, 0)
/datum/ai_behavior/item_move_close_and_attack/ghostly
attack_sound = 'sound/items/haunted/ghostitemattack.ogg'
@@ -58,6 +56,5 @@
/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted
/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted/finish_action(datum/ai_controller/controller, succeeded, target_key, throw_count_key)
var/datum/weakref/target_ref = controller.blackboard[target_key]
controller.blackboard[BB_TO_HAUNT_LIST][target_ref]--
controller.add_blackboard_key_assoc(BB_TO_HAUNT_LIST, controller.blackboard[target_key], -1)
return ..()
+4 -6
View File
@@ -37,7 +37,7 @@
return
if(partner && children < max_children)
controller.blackboard[target_key] = WEAKREF(partner)
controller.set_blackboard_key(target_key, partner)
finish_action(controller, TRUE)
@@ -49,8 +49,7 @@
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
/datum/ai_behavior/make_babies/setup(datum/ai_controller/controller, target_key, child_types_key)
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[target_key]
if(!target)
return FALSE
set_movement_target(controller, target)
@@ -58,8 +57,7 @@
/datum/ai_behavior/make_babies/perform(seconds_per_tick, datum/ai_controller/controller, target_key, child_types_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/mob/target = weak_target?.resolve()
var/mob/target = controller.blackboard[target_key]
if(!target || target.stat != CONSCIOUS)
finish_action(controller, FALSE, target_key)
return
@@ -74,4 +72,4 @@
/datum/ai_behavior/make_babies/finish_action(datum/ai_controller/controller, succeeded, target_key)
. = ..()
controller.blackboard -= target_key
controller.clear_blackboard_key(target_key)
+4 -6
View File
@@ -20,15 +20,13 @@
if(is_type_in_list(controller.pawn, baby_types))
return
var/datum/weakref/weak_target = controller.blackboard[BB_BABIES_TARGET]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[BB_BABIES_TARGET]
// Find target
if(!target || QDELETED(target))
if(QDELETED(target))
controller.queue_behavior(/datum/ai_behavior/find_partner, BB_BABIES_TARGET, BB_BABIES_PARTNER_TYPES, BB_BABIES_CHILD_TYPES)
return
// Do target
if(target)
controller.queue_behavior(/datum/ai_behavior/make_babies, BB_BABIES_TARGET, BB_BABIES_CHILD_TYPES)
return SUBTREE_RETURN_FINISH_PLANNING
controller.queue_behavior(/datum/ai_behavior/make_babies, BB_BABIES_TARGET, BB_BABIES_CHILD_TYPES)
return SUBTREE_RETURN_FINISH_PLANNING
+6 -5
View File
@@ -1,6 +1,7 @@
/datum/ai_behavior/break_spine/bane/finish_action(datum/ai_controller/controller, succeeded)
var/list/bane_quotes = strings("bane.json", "bane")
var/mob/living/bane = controller.pawn
bane.say(pick(bane_quotes))
. = ..()
/datum/ai_behavior/break_spine/bane/finish_action(datum/ai_controller/controller, succeeded, target_key)
if(succeeded)
var/list/bane_quotes = strings("bane.json", "bane")
var/mob/living/bane = controller.pawn
bane.say(pick(bane_quotes))
return ..()
+8 -5
View File
@@ -1,13 +1,16 @@
///The bat is broken!
/datum/ai_planning_subtree/bane_hunting/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/mob/living/batman = controller.blackboard[BB_BANE_BATMAN]
if(!batman)
if(QDELETED(batman))
for(var/mob/living/possibly_the_dark_knight in oview(7, controller.pawn))
if(IS_DEAD_OR_INCAP(possibly_the_dark_knight)) //I HAVE BROKEN THE BAT
continue
controller.blackboard[BB_BANE_BATMAN] = possibly_the_dark_knight
controller.set_blackboard_key(BB_BANE_BATMAN, possibly_the_dark_knight)
batman = possibly_the_dark_knight
break
if(batman)
controller.queue_behavior(/datum/ai_behavior/break_spine/bane, BB_BANE_BATMAN)
return SUBTREE_RETURN_FINISH_PLANNING
if(isnull(batman))
return
controller.queue_behavior(/datum/ai_behavior/break_spine/bane, BB_BANE_BATMAN)
return SUBTREE_RETURN_FINISH_PLANNING
@@ -4,22 +4,22 @@
/datum/ai_behavior/basic_melee_attack/setup(datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key)
. = ..()
//Hiding location is priority
var/datum/weakref/weak_target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
if(!target)
return FALSE
var/datum/targetting_datum/targetting_datum = controller.blackboard[targetting_datum_key]
if (!targetting_datum)
return
if(isnull(targetting_datum))
CRASH("No target datum was supplied in the blackboard for [controller.pawn]")
//Hiding location is priority
var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key]
if(QDELETED(target))
return FALSE
set_movement_target(controller, target)
/datum/ai_behavior/basic_melee_attack/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key)
. = ..()
var/mob/living/basic/basic_mob = controller.pawn
//targetting datum will kill the action if not real anymore
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[target_key]
var/datum/targetting_datum/targetting_datum = controller.blackboard[targetting_datum_key]
if(!targetting_datum.can_attack(basic_mob, target))
@@ -28,7 +28,7 @@
var/hiding_target = targetting_datum.find_hidden_mobs(basic_mob, target) //If this is valid, theyre hidden in something!
controller.blackboard[hiding_location_key] = hiding_target
controller.set_blackboard_key(hiding_location_key, hiding_target)
if(hiding_target) //Slap it!
basic_mob.melee_attack(hiding_target)
@@ -39,7 +39,7 @@
/datum/ai_behavior/basic_melee_attack/finish_action(datum/ai_controller/controller, succeeded, target_key, targetting_datum_key, hiding_location_key)
. = ..()
if(!succeeded)
controller.blackboard -= target_key
controller.clear_blackboard_key(target_key)
/datum/ai_behavior/basic_melee_attack/average_speed
action_cooldown = 1 SECONDS
@@ -55,9 +55,8 @@
/datum/ai_behavior/basic_ranged_attack/setup(datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
if(!target)
var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key]
if(QDELETED(target))
return FALSE
set_movement_target(controller, target)
@@ -65,8 +64,7 @@
. = ..()
var/mob/living/basic/basic_mob = controller.pawn
//targetting datum will kill the action if not real anymore
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[target_key]
var/datum/targetting_datum/targetting_datum = controller.blackboard[targetting_datum_key]
if(!targetting_datum.can_attack(basic_mob, target))
@@ -80,7 +78,7 @@
finish_action(controller, FALSE, target_key)
return
controller.blackboard[hiding_location_key] = WEAKREF(hiding_target)
controller.set_blackboard_key(hiding_location_key, hiding_target)
if(shots>1)
var/atom/burst_target = final_target
@@ -94,4 +92,4 @@
/datum/ai_behavior/basic_ranged_attack/finish_action(datum/ai_controller/controller, succeeded, target_key, targetting_datum_key, hiding_location_key)
. = ..()
if(!succeeded)
controller.blackboard -= target_key
controller.clear_blackboard_key(target_key)
@@ -7,18 +7,16 @@
/datum/ai_behavior/pick_up_item/setup(datum/ai_controller/controller, target_key, storage_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/obj/item/target = weak_target?.resolve()
var/obj/item/target = controller.blackboard[target_key]
return isitem(target) && isturf(target.loc) && !target.anchored
/datum/ai_behavior/pick_up_item/perform(seconds_per_tick, datum/ai_controller/controller, target_key, storage_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/obj/item/target = weak_target?.resolve()
if(!isturf(target?.loc)) // Someone picked it up or it got deleted
var/obj/item/target = controller.blackboard[target_key]
if(QDELETED(target) || !isturf(target.loc)) // Someone picked it up or it got deleted
finish_action(controller, FALSE, target_key)
return
if(!in_range(controller.pawn, target)) // It teleported
if(!controller.pawn.Adjacent(target)) // It teleported
finish_action(controller, FALSE, target_key)
return
pickup_item(controller, target, storage_key)
@@ -26,22 +24,21 @@
/datum/ai_behavior/pick_up_item/finish_action(datum/ai_controller/controller, success, target_key, storage_key)
. = ..()
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
/datum/ai_behavior/pick_up_item/proc/pickup_item(datum/ai_controller/controller, obj/item/target, storage_key)
var/atom/pawn = controller.pawn
drop_existing_item(controller, storage_key)
pawn.visible_message(span_notice("[pawn] picks up [target]."))
target.forceMove(pawn)
controller.blackboard[storage_key] = WEAKREF(target)
controller.set_blackboard_key(storage_key, target)
return TRUE
/datum/ai_behavior/pick_up_item/proc/drop_existing_item(datum/ai_controller/controller, storage_key)
var/datum/weakref/carried_ref = controller.blackboard[storage_key]
var/obj/item/carried_item = carried_ref?.resolve()
var/obj/item/carried_item = controller.blackboard[storage_key]
if(!carried_item)
return
controller.blackboard[storage_key] = null
controller.clear_blackboard_key(storage_key)
var/atom/pawn = controller.pawn
if(carried_item.loc != pawn)
return
@@ -7,9 +7,8 @@
var/run_distance = 9
/datum/ai_behavior/run_away_from_target/setup(datum/ai_controller/controller, target_key, hiding_location_key)
var/datum/weakref/weak_target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
if(!target)
var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key]
if(QDELETED(target))
return FALSE
if(!plot_path_away_from(controller, target))
return FALSE
@@ -17,9 +16,8 @@
/datum/ai_behavior/run_away_from_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hiding_location_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
var/escaped = !target || !can_see(controller.pawn, target, run_distance) // If we can't see it we got away
var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key]
var/escaped = QDELETED(target) || !can_see(controller.pawn, target, run_distance) // If we can't see it we got away
if (escaped)
finish_action(controller, succeeded = TRUE, target_key = target_key, hiding_location_key = hiding_location_key)
return
@@ -43,4 +41,4 @@
/datum/ai_behavior/run_away_from_target/finish_action(datum/ai_controller/controller, succeeded, target_key, hiding_location_key)
. = ..()
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
@@ -11,13 +11,12 @@
var/step_distance = 3
/datum/ai_behavior/step_towards_turf/setup(datum/ai_controller/controller, turf_key)
var/datum/weakref/weak_turf = controller.blackboard[turf_key]
var/turf/target_turf = weak_turf?.resolve()
if (!target_turf || target_turf.is_blocked_turf(exclude_mobs = TRUE))
var/turf/target_turf = controller.blackboard[turf_key]
if (QDELETED(target_turf) || target_turf.is_blocked_turf(exclude_mobs = TRUE))
target_turf = find_destination_turf(args)
if (!target_turf)
return FALSE
controller.blackboard[turf_key] = WEAKREF(target_turf)
controller.set_blackboard_key(turf_key, target_turf)
if (target_turf.z != controller.pawn.z)
return FALSE
@@ -4,16 +4,14 @@
/datum/ai_behavior/stop_and_stare/setup(datum/ai_controller/controller, target_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/movable/target = weak_target?.resolve()
var/atom/movable/target = controller.blackboard[target_key]
return ismovable(target) && isturf(target.loc) && ismob(controller.pawn)
/datum/ai_behavior/stop_and_stare/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
// i don't really like doing this but we wanna make sure that the cooldown is pertinent to what we need for this specific controller before we invoke parent
action_cooldown = controller.blackboard[BB_STATIONARY_COOLDOWN]
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/movable/target = weak_target?.resolve()
var/atom/movable/target = controller.blackboard[target_key]
if(!ismovable(target) || !isturf(target.loc)) // just to make sure that nothing funky happened between setup and perform
return
@@ -5,12 +5,9 @@
/datum/ai_behavior/targeted_mob_ability
/datum/ai_behavior/targeted_mob_ability/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key)
var/datum/weakref/weak_ability = controller.blackboard[ability_key]
var/datum/action/cooldown/ability = weak_ability?.resolve()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/mob/living/target = weak_target?.resolve()
if(!ability || QDELETED(target))
var/datum/action/cooldown/ability = controller.blackboard[ability_key]
var/mob/living/target = controller.blackboard[target_key]
if(QDELETED(ability) || QDELETED(target))
finish_action(controller, FALSE, ability_key, target_key)
return
var/mob/pawn = controller.pawn
@@ -19,16 +16,15 @@
/datum/ai_behavior/targeted_mob_ability/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[target_key]
if (QDELETED(target))
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
return
if (!isliving(target))
return
var/mob/living/living_target = target
if(living_target.stat >= UNCONSCIOUS)
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
/**
* # Try Mob Ability and plan execute
@@ -38,7 +34,7 @@
/datum/ai_behavior/targeted_mob_ability/and_plan_execute
/datum/ai_behavior/targeted_mob_ability/and_plan_execute/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key)
controller.blackboard[BB_BASIC_MOB_EXECUTION_TARGET] = controller.blackboard[target_key]
controller.set_blackboard_key(BB_BASIC_MOB_EXECUTION_TARGET, controller.blackboard[target_key])
return ..()
/**
@@ -49,4 +45,4 @@
/datum/ai_behavior/targeted_mob_ability/and_clear_target/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key)
. = ..()
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
@@ -13,13 +13,12 @@
if(!targetting_datum)
CRASH("No target datum was supplied in the blackboard for [controller.pawn]")
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/current_target = weak_target?.resolve()
var/atom/current_target = controller.blackboard[target_key]
if (targetting_datum.can_attack(living_mob, current_target))
finish_action(controller, succeeded = FALSE)
return
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
var/list/potential_targets = hearers(vision_range, controller.pawn) - living_mob //Remove self, so we don't suicide
for(var/HM in typecache_filter_list(range(vision_range, living_mob), hostile_machines)) //Can we see any hostile machines?
@@ -42,12 +41,12 @@
return
var/atom/target = pick_final_target(controller, filtered_targets)
controller.blackboard[target_key] = WEAKREF(target)
controller.set_blackboard_key(target_key, target)
var/atom/potential_hiding_location = targetting_datum.find_hidden_mobs(living_mob, target)
if(potential_hiding_location) //If they're hiding inside of something, we need to know so we can go for that instead initially.
controller.blackboard[hiding_location_key] = WEAKREF(potential_hiding_location)
controller.set_blackboard_key(hiding_location_key, potential_hiding_location)
finish_action(controller, succeeded = TRUE)
@@ -33,5 +33,5 @@
/datum/ai_behavior/tipped_reaction/finish_action(datum/ai_controller/controller, succeeded, tipper_key, reacting_key)
. = ..()
//I'VE SAID MY PEACE...
controller.blackboard[reacting_key] = FALSE
controller.blackboard[tipper_key] = null
controller.set_blackboard_key(reacting_key, FALSE)
controller.clear_blackboard_key(tipper_key)
@@ -9,9 +9,8 @@
/datum/ai_behavior/travel_towards/setup(datum/ai_controller/controller, target_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
if(isnull(target))
var/atom/target = controller.blackboard[target_key]
if(QDELETED(target))
return FALSE
set_movement_target(controller, target)
@@ -7,10 +7,8 @@
/datum/ai_planning_subtree/attack_obstacle_in_path/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
if(isnull(target))
var/atom/target = controller.blackboard[target_key]
if(QDELETED(target))
return
var/turf/next_step = get_step_towards(controller.pawn, target)
@@ -31,10 +29,9 @@
/datum/ai_behavior/attack_obstructions/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
. = ..()
var/mob/living/basic/basic_mob = controller.pawn
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[target_key]
if (!target)
if (QDELETED(target))
finish_action(controller, succeeded = FALSE)
return
@@ -1,10 +1,11 @@
/// similar to finding a target but looks for food types in the
/// similar to finding a target but looks for food types in the // the what?
/datum/ai_planning_subtree/find_food
/datum/ai_planning_subtree/find_food/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
. = ..()
var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
if(target && !QDELETED(target))
if(!QDELETED(target))
// Busy with something
return
var/list/wanted = controller.blackboard[BB_BASIC_FOODS]
controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list, BB_BASIC_MOB_CURRENT_TARGET, wanted)
controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list, BB_BASIC_MOB_CURRENT_TARGET, controller.blackboard[BB_BASIC_FOODS])
@@ -11,9 +11,8 @@
. = ..()
if (!controller.blackboard[BB_BASIC_MOB_FLEEING])
return
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
if(!target || QDELETED(target))
var/atom/target = controller.blackboard[target_key]
if(QDELETED(target))
return
controller.queue_behavior(flee_behaviour, target_key, hiding_place_key)
return SUBTREE_RETURN_FINISH_PLANNING //we gotta get out of here.
@@ -3,10 +3,8 @@
/datum/ai_planning_subtree/basic_melee_attack_subtree/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
var/atom/target = weak_target?.resolve()
if(!target || QDELETED(target))
var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
if(QDELETED(target))
return
controller.queue_behavior(melee_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETTING_DATUM, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
return SUBTREE_RETURN_FINISH_PLANNING //we are going into battle...no distractions.
@@ -20,9 +18,8 @@
/datum/ai_planning_subtree/basic_ranged_attack_subtree/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
var/atom/target = weak_target?.resolve()
if(!target || QDELETED(target))
var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
if(QDELETED(target))
return
controller.queue_behavior(ranged_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETTING_DATUM, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
return SUBTREE_RETURN_FINISH_PLANNING //we are going into battle...no distractions.
@@ -15,16 +15,15 @@
var/time_to_wait = 10
/datum/ai_behavior/sleep_after_targetless_time/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[target_key]
finish_action(controller, succeeded = !target, seconds_per_tick = seconds_per_tick)
/datum/ai_behavior/sleep_after_targetless_time/finish_action(datum/ai_controller/controller, succeeded, seconds_per_tick)
. = ..()
if (!succeeded)
controller.blackboard[BB_TARGETLESS_TIME] = 0
controller.set_blackboard_key(BB_TARGETLESS_TIME, 0)
return
controller.blackboard[BB_TARGETLESS_TIME] += seconds_per_tick
controller.add_blackboard_key(BB_TARGETLESS_TIME, seconds_per_tick)
if (controller.blackboard[BB_TARGETLESS_TIME] > time_to_wait)
enter_sleep(controller)
@@ -2,8 +2,7 @@
/datum/ai_planning_subtree/stare_at_thing
/datum/ai_planning_subtree/stare_at_thing/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/datum/weakref/weak_target = controller.blackboard[BB_STATIONARY_CAUSE]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[BB_STATIONARY_CAUSE]
if(isnull(target)) // No target? Time to locate one using the list we set in this mob's blackboard.
var/list/potential_scares = controller.blackboard[BB_STATIONARY_TARGETS]
@@ -11,4 +10,3 @@
return
controller.queue_behavior(/datum/ai_behavior/stop_and_stare, BB_STATIONARY_CAUSE)
@@ -35,36 +35,22 @@
if(!targetting_datum)
CRASH("No target datum was supplied in the blackboard for [controller.pawn]")
var/list/enemy_refs = controller.blackboard[shitlist_key]
if (!length(enemy_refs))
finish_action(controller, succeeded = FALSE)
return
var/list/enemies_list = list()
for (var/datum/weakref/enemy_ref as anything in enemy_refs)
var/atom/enemy = enemy_ref.resolve()
if (!can_attack_target(living_mob, enemy, targetting_datum))
controller.blackboard[shitlist_key] -= enemy_ref
continue
enemies_list += enemy
var/list/enemies_list = controller.blackboard[shitlist_key]
if (!length(enemies_list))
finish_action(controller, succeeded = FALSE)
return
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
if (target && (locate(target) in enemies_list)) // Don't bother changing
if (controller.blackboard[target_key] in enemies_list) // Don't bother changing
finish_action(controller, succeeded = FALSE)
return
var/atom/new_target = pick_final_target(controller, enemies_list)
controller.blackboard[target_key] = WEAKREF(new_target)
controller.set_blackboard_key(target_key, new_target)
var/atom/potential_hiding_location = targetting_datum.find_hidden_mobs(living_mob, new_target)
if(potential_hiding_location) //If they're hiding inside of something, we need to know so we can go for that instead initially.
controller.blackboard[hiding_location_key] = WEAKREF(potential_hiding_location)
controller.set_blackboard_key(hiding_location_key, potential_hiding_location)
finish_action(controller, succeeded = TRUE)
@@ -13,14 +13,9 @@
if (!ability_key)
CRASH("You forgot to tell this mob where to find its ability")
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/mob/living/target = weak_target?.resolve()
if (QDELETED(target))
return
var/datum/weakref/weak_ability = controller.blackboard[ability_key]
var/datum/action/cooldown/using_action = weak_ability?.resolve()
if (!using_action || !using_action.IsAvailable())
var/mob/living/target = controller.blackboard[target_key]
var/datum/action/cooldown/using_action = controller.blackboard[ability_key]
if (QDELETED(target) || QDELETED(using_action) || !using_action.IsAvailable())
return
controller.queue_behavior(use_ability_behaviour, ability_key, target_key)
@@ -14,9 +14,8 @@
if (!ability_key)
CRASH("You forgot to tell this mob where to find its ability")
var/datum/weakref/weak_ability = controller.blackboard[ability_key]
var/datum/action/cooldown/using_action = weak_ability?.resolve()
if (!using_action || !using_action.IsAvailable())
var/datum/action/cooldown/using_action = controller.blackboard[ability_key]
if (QDELETED(using_action) || !using_action.IsAvailable())
return
controller.queue_behavior(use_ability_behaviour, ability_key)
@@ -26,9 +25,8 @@
/datum/ai_behavior/use_mob_ability
/datum/ai_behavior/use_mob_ability/perform(seconds_per_tick, datum/ai_controller/controller, ability_key)
var/datum/weakref/weak_ability = controller.blackboard[ability_key]
var/datum/action/cooldown/using_action = weak_ability?.resolve()
if (!using_action)
var/datum/action/cooldown/using_action = controller.blackboard[ability_key]
if (QDELETED(using_action))
finish_action(controller, FALSE, ability_key)
return
var/result = using_action.Trigger()
+24 -35
View File
@@ -7,21 +7,18 @@
/datum/ai_behavior/fetch_seek/setup(datum/ai_controller/controller, target_key, delivery_key)
. = ..()
var/datum/weakref/thing_ref = controller.blackboard[target_key]
var/obj/item/fetch_thing = thing_ref?.resolve()
var/obj/item/fetch_thing = controller.blackboard[target_key]
// It stopped existing
if (!fetch_thing)
if (QDELETED(fetch_thing))
return FALSE
set_movement_target(controller, fetch_thing)
/datum/ai_behavior/fetch_seek/perform(seconds_per_tick, datum/ai_controller/controller, target_key, delivery_key)
. = ..()
var/datum/weakref/thing_ref = controller.blackboard[target_key]
var/obj/item/fetch_thing = thing_ref?.resolve()
var/obj/item/fetch_thing = controller.blackboard[target_key]
// It stopped existing
if (!fetch_thing)
if (QDELETED(fetch_thing))
finish_action(controller, FALSE, target_key, delivery_key)
return
var/mob/living/living_pawn = controller.pawn
@@ -37,12 +34,11 @@
if (success)
return
// Blacklist item if we failed
var/datum/weakref/thing_ref = controller.blackboard[target_key]
var/obj/item/target = thing_ref?.resolve()
var/obj/item/target = controller.blackboard[target_key]
if (target)
controller.blackboard[BB_FETCH_IGNORE_LIST][thing_ref] = TRUE
controller.blackboard[target_key] = null
controller.blackboard[delivery_key] = null
controller.set_blackboard_key_assoc_lazylist(BB_FETCH_IGNORE_LIST, target, TRUE)
controller.clear_blackboard_key(target_key)
controller.clear_blackboard_key(delivery_key)
/**
* The second half of fetching, deliver the item to a target.
@@ -52,17 +48,15 @@
/datum/ai_behavior/deliver_fetched_item/setup(datum/ai_controller/controller, delivery_key, storage_key)
. = ..()
var/datum/weakref/return_ref = controller.blackboard[delivery_key]
var/mob/living/return_target = return_ref?.resolve()
if(!return_target) // Guess it's mine now
var/mob/living/return_target = controller.blackboard[delivery_key]
if(QDELETED(return_target)) // Guess it's mine now
return FALSE
set_movement_target(controller, return_target)
/datum/ai_behavior/deliver_fetched_item/perform(seconds_per_tick, datum/ai_controller/controller, delivery_key, storage_key)
. = ..()
var/datum/weakref/return_ref = controller.blackboard[delivery_key]
var/mob/living/return_target = return_ref?.resolve()
if(!return_target)
var/mob/living/return_target = controller.blackboard[delivery_key]
if(QDELETED(return_target))
finish_action(controller, FALSE, delivery_key)
return
@@ -71,22 +65,21 @@
/datum/ai_behavior/deliver_fetched_item/finish_action(datum/ai_controller/controller, success, delivery_key)
. = ..()
controller.blackboard[delivery_key] = null
controller.blackboard[BB_ACTIVE_PET_COMMAND] = null
controller.clear_blackboard_key(delivery_key)
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
/// Actually deliver the fetched item to the target, if we still have it
/datum/ai_behavior/deliver_fetched_item/proc/deliver_item(datum/ai_controller/controller, return_target, storage_key)
var/mob/pawn = controller.pawn
var/datum/weakref/carried_ref = controller.blackboard[storage_key]
var/obj/item/carried_item = carried_ref?.resolve()
if(!carried_item || carried_item.loc != pawn)
var/obj/item/carried_item = controller.blackboard[storage_key]
if(QDELETED(carried_item) || carried_item.loc != pawn)
pawn.visible_message(span_notice("[pawn] looks around as if [pawn.p_they()] [pawn.p_have()] lost something."))
finish_action(controller, FALSE)
return
pawn.visible_message(span_notice("[pawn] delivers [carried_item] to [return_target]."))
carried_item.forceMove(get_turf(return_target))
controller.blackboard[storage_key] = null
controller.clear_blackboard_key(storage_key)
return TRUE
/**
@@ -101,19 +94,15 @@
/datum/ai_behavior/eat_fetched_snack/setup(datum/ai_controller/controller, target_key, delivery_key)
. = ..()
var/datum/weakref/thing_ref = controller.blackboard[target_key]
var/obj/item/snack = thing_ref?.resolve()
var/obj/item/snack = controller.blackboard[target_key]
if(!istype(snack) || !IS_EDIBLE(snack) || !(isturf(snack.loc) || ishuman(snack.loc)))
return FALSE // This isn't food at all!
set_movement_target(controller, snack)
/datum/ai_behavior/eat_fetched_snack/perform(seconds_per_tick, datum/ai_controller/controller, target_key, delivery_key)
. = ..()
var/datum/weakref/thing_ref = controller.blackboard[target_key]
var/obj/item/snack = thing_ref?.resolve()
if(!(isturf(snack.loc) || ishuman(snack.loc)))
var/obj/item/snack = controller.blackboard[target_key]
if(QDELETED(snack) || !isturf(snack.loc) || ishuman(snack.loc))
finish_action(controller, FALSE) // Where did it go?
var/mob/living/basic/basic_pawn = controller.pawn
@@ -130,9 +119,9 @@
/datum/ai_behavior/eat_fetched_snack/finish_action(datum/ai_controller/controller, succeeded, target_key, delivery_key)
. = ..()
controller.blackboard[target_key] = null
controller.blackboard[delivery_key] = null
controller.blackboard[BB_ACTIVE_PET_COMMAND] = null
controller.clear_blackboard_key(target_key)
controller.clear_blackboard_key(delivery_key)
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
/**
* Clear our failed fetch list every so often
@@ -153,5 +142,5 @@
/datum/ai_behavior/forget_failed_fetches/perform(seconds_per_tick, datum/ai_controller/controller)
. = ..()
COOLDOWN_START(src, reset_ignore_cooldown, cooldown_duration)
controller.blackboard[BB_FETCH_IGNORE_LIST] = list()
controller.clear_blackboard_key(BB_FETCH_IGNORE_LIST)
finish_action(controller, TRUE)
@@ -8,8 +8,7 @@
/datum/ai_planning_subtree/pet_planning
/datum/ai_planning_subtree/pet_planning/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/datum/weakref/weak_command = controller.blackboard[BB_ACTIVE_PET_COMMAND]
var/datum/pet_command/command = weak_command?.resolve()
var/datum/pet_command/command = controller.blackboard[BB_ACTIVE_PET_COMMAND]
if (!command)
return // Do something else
return command.execute_action(controller)
@@ -4,16 +4,14 @@
/datum/ai_behavior/pet_follow_friend/setup(datum/ai_controller/controller, target_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
if (!target)
var/atom/target = controller.blackboard[target_key]
if (QDELETED(target))
return FALSE
set_movement_target(controller, target)
/datum/ai_behavior/pet_follow_friend/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
if (!target)
var/atom/target = controller.blackboard[target_key]
if (QDELETED(target))
finish_action(controller, FALSE, target_key)
return
@@ -4,17 +4,15 @@
/datum/ai_behavior/pet_use_ability/setup(datum/ai_controller/controller, ability_key, target_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/mob/living/target = weak_target?.resolve()
var/mob/living/target = controller.blackboard[target_key]
if (QDELETED(target))
return FALSE
set_movement_target(controller, target)
/datum/ai_behavior/pet_use_ability/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key)
var/datum/action/cooldown/mob_cooldown/ability = controller.blackboard[ability_key]
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/mob/living/target = weak_target?.resolve()
if (!ability || QDELETED(target))
var/mob/living/target = controller.blackboard[target_key]
if (QDELETED(ability) || QDELETED(target))
finish_action(controller, FALSE, ability_key, target_key)
return
var/mob/pawn = controller.pawn
@@ -23,4 +21,4 @@
/datum/ai_behavior/pet_use_ability/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key)
. = ..()
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
@@ -21,4 +21,4 @@
return
basic_pawn.visible_message(span_notice("[basic_pawn] miraculously springs back to life!"))
basic_pawn.look_alive()
controller.blackboard[BB_ACTIVE_PET_COMMAND] = null
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
@@ -30,11 +30,10 @@
if (!living_mob.ai_controller) // How did you get here?
return FALSE
var/list/friends_list = living_mob.ai_controller.blackboard[BB_FRIENDS_LIST]
if (!friends_list)
return TRUE // We don't have any friends, anything's fair game
if (!friends_list[WEAKREF(target)])
return TRUE // This is not our friend, fire at will
if (!(target in living_mob.ai_controller.blackboard[BB_FRIENDS_LIST]))
// We don't have any friends, anything's fair game
// OR This is not our friend, fire at will
return TRUE
return FALSE
+2 -3
View File
@@ -1,8 +1,7 @@
/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed
/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed/reset_blackboard(datum/ai_controller/controller, succeeded, target_key, throw_count_key)
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/atom/throw_target = target_ref?.resolve()
var/atom/throw_target = controller.blackboard[target_key]
//dropping our target from the blackboard if they are no longer a valid target after the attack behavior
if(get_dist(throw_target, controller.pawn) > CURSED_VIEW_RANGE)
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
+2 -3
View File
@@ -2,13 +2,12 @@
var/obj/item/item_pawn = controller.pawn
//make sure we have a target
var/datum/weakref/target_ref = controller.blackboard[BB_CURSE_TARGET]
var/mob/living/carbon/curse_target = target_ref?.resolve()
var/mob/living/carbon/curse_target = controller.blackboard[BB_CURSE_TARGET]
if(!curse_target)
controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_CURSE_TARGET, /mob/living/carbon, CURSED_VIEW_RANGE)
return
//make sure attack is valid
if(get_dist(curse_target, item_pawn) > CURSED_VIEW_RANGE)
controller.blackboard[BB_CURSE_TARGET] = null
controller.clear_blackboard_key(BB_CURSE_TARGET)
return
controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed, BB_CURSE_TARGET)
+1 -2
View File
@@ -16,8 +16,7 @@
return
// Unfortunately going to repeat this check in parent call but what can you do
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[target_key]
var/datum/targetting_datum/targetting_datum = controller.blackboard[targetting_datum_key]
if (!targetting_datum.can_attack(living_pawn, target))
finish_action(controller, FALSE, target_key, targetting_datum_key, hiding_location_key)
+4 -5
View File
@@ -5,8 +5,7 @@
if(!SPT_PROB(10, seconds_per_tick))
return
controller.queue_behavior(/datum/ai_behavior/find_hated_dog_target, BB_DOG_HARASS_TARGET, BB_PET_TARGETTING_DATUM)
var/datum/weakref/weak_target = controller.blackboard[BB_DOG_HARASS_TARGET]
var/atom/harass_target = weak_target?.resolve()
var/atom/harass_target = controller.blackboard[BB_DOG_HARASS_TARGET]
if (isnull(harass_target))
return
@@ -29,11 +28,11 @@
continue
dog.audible_message(span_warning("[dog] growls at [iter_living], seemingly annoyed by [iter_living.p_their()] presence."), hearing_distance = COMBAT_MESSAGE_RANGE)
controller.blackboard[target_key] = WEAKREF(iter_living)
controller.blackboard[BB_DOG_HARASS_HARM] = FALSE
controller.set_blackboard_key(target_key, iter_living)
controller.set_blackboard_key(BB_DOG_HARASS_HARM, FALSE)
return TRUE
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
/datum/ai_behavior/find_hated_dog_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
. = ..()
+1 -1
View File
@@ -10,7 +10,7 @@
. = ..()
var/find_this_thing = search_tactic(controller, locate_path, search_range)
if(find_this_thing)
controller.blackboard[set_key] = WEAKREF(find_this_thing)
controller.set_blackboard_key(set_key, find_this_thing)
finish_action(controller, TRUE)
else
finish_action(controller, FALSE)
+33 -35
View File
@@ -31,20 +31,25 @@
/datum/ai_behavior/break_spine/setup(datum/ai_controller/controller, target_key)
. = ..()
set_movement_target(controller, controller.blackboard[target_key])
var/atom/target = controller.blackboard[target_key]
if(QDELETED(target))
return FALSE
set_movement_target(controller, target)
/datum/ai_behavior/break_spine/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
var/mob/living/batman = controller.blackboard[target_key]
var/mob/living/big_guy = controller.pawn //he was molded by the darkness
if(batman.stat)
finish_action(controller, TRUE, target_key)
if(get_dist(batman, big_guy) >= give_up_distance)
if(QDELETED(batman) || get_dist(batman, big_guy) >= give_up_distance)
finish_action(controller, FALSE, target_key)
return
if(batman.stat != CONSCIOUS)
finish_action(controller, TRUE, target_key)
return
big_guy.start_pulling(batman)
big_guy.setDir(get_dir(big_guy, batman))
big_guy.face_atom(batman)
batman.visible_message(span_warning("[batman] gets a slightly too tight hug from [big_guy]!"), span_userdanger("You feel your body break as [big_guy] embraces you!"))
@@ -61,7 +66,9 @@
/datum/ai_behavior/break_spine/finish_action(datum/ai_controller/controller, succeeded, target_key)
if(succeeded)
controller.blackboard -= target_key
var/mob/living/bane = controller.pawn
bane.stop_pulling()
controller.clear_blackboard_key(target_key)
return ..()
/// Use in hand the currently held item
@@ -86,9 +93,8 @@
/datum/ai_behavior/use_on_object/setup(datum/ai_controller/controller, target_key)
. = ..()
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/target = target_ref?.resolve()
if(!target)
var/atom/target = controller.blackboard[target_key]
if(QDELETED(target))
return FALSE
set_movement_target(controller, target)
@@ -96,10 +102,8 @@
. = ..()
var/mob/living/pawn = controller.pawn
var/obj/item/held_item = pawn.get_item_by_slot(pawn.get_active_hand())
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/atom/target = target_ref?.resolve()
if(!target || !pawn.CanReach(target))
var/atom/target = controller.blackboard[target_key]
if(QDELETED(target) || !pawn.CanReach(target))
finish_action(controller, FALSE)
return
@@ -118,15 +122,13 @@
/datum/ai_behavior/give/setup(datum/ai_controller/controller, target_key)
. = ..()
var/datum/weakref/target_ref = controller.blackboard[target_key]
set_movement_target(controller, target_ref?.resolve())
set_movement_target(controller, controller.blackboard[target_key])
/datum/ai_behavior/give/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
. = ..()
var/mob/living/pawn = controller.pawn
var/obj/item/held_item = pawn.get_active_held_item()
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/atom/target = target_ref?.resolve()
var/atom/target = controller.blackboard[target_key]
if(!held_item) //if held_item is null, we pretend that action was succesful
finish_action(controller, TRUE)
@@ -185,14 +187,14 @@
/datum/ai_behavior/consume/setup(datum/ai_controller/controller, target_key)
. = ..()
var/datum/weakref/target_ref = controller.blackboard[target_key]
set_movement_target(controller, target_ref?.resolve())
set_movement_target(controller, controller.blackboard[target_key])
/datum/ai_behavior/consume/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hunger_timer_key)
. = ..()
var/mob/living/living_pawn = controller.pawn
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/obj/item/target = target_ref.resolve()
var/obj/item/target = controller.blackboard[target_key]
if(QDELETED(target))
return
if(!(target in living_pawn.held_items))
if(!living_pawn.get_empty_held_indexes() || !living_pawn.put_in_hands(target))
@@ -207,7 +209,7 @@
/datum/ai_behavior/consume/finish_action(datum/ai_controller/controller, succeeded, target_key, hunger_timer_key)
. = ..()
if(succeeded)
controller.blackboard[hunger_timer_key] = world.time + rand(12 SECONDS, 60 SECONDS)
controller.set_blackboard_key(hunger_timer_key, world.time + rand(12 SECONDS, 60 SECONDS))
/**
* Drops items in hands, very important for future behaviors that require the pawn to grab stuff
@@ -234,9 +236,8 @@
if(!istype(living_pawn) || !isturf(living_pawn.loc))
return
var/datum/weakref/attack_ref = controller.blackboard[BB_ATTACK_TARGET]
var/atom/movable/attack_target = attack_ref?.resolve()
if(!attack_target || !can_see(living_pawn, attack_target, length=controller.blackboard[BB_VISION_RANGE]))
var/atom/movable/attack_target = controller.blackboard[BB_ATTACK_TARGET]
if(!attack_target || !can_see(living_pawn, attack_target, length = controller.blackboard[BB_VISION_RANGE]))
finish_action(controller, FALSE)
return
@@ -250,7 +251,7 @@
/datum/ai_behavior/attack/finish_action(datum/ai_controller/controller, succeeded)
. = ..()
controller.blackboard[BB_ATTACK_TARGET] = null
controller.clear_blackboard_key(BB_ATTACK_TARGET)
/// A proc representing when the mob is pushed to actually attack the target. Again, subtypes can be used to represent different attacks from different animals, or it can be some other generic behavior
/datum/ai_behavior/attack/proc/attack(datum/ai_controller/controller, mob/living/living_target)
@@ -270,8 +271,7 @@
if(!istype(living_pawn) || !isturf(living_pawn.loc))
return
var/datum/weakref/follow_ref = controller.blackboard[BB_FOLLOW_TARGET]
var/atom/movable/follow_target = follow_ref?.resolve()
var/atom/movable/follow_target = controller.blackboard[BB_FOLLOW_TARGET]
if(!follow_target || get_dist(living_pawn, follow_target) > controller.blackboard[BB_VISION_RANGE])
finish_action(controller, FALSE)
return
@@ -285,7 +285,7 @@
/datum/ai_behavior/follow/finish_action(datum/ai_controller/controller, succeeded)
. = ..()
controller.blackboard[BB_FOLLOW_TARGET] = null
controller.clear_blackboard_key(BB_FOLLOW_TARGET)
@@ -316,8 +316,7 @@
/datum/ai_behavior/setup_instrument/perform(seconds_per_tick, datum/ai_controller/controller, song_instrument_key, song_lines_key)
. = ..()
var/datum/weakref/instrument_ref = controller.blackboard[song_instrument_key]
var/obj/item/instrument/song_instrument = instrument_ref.resolve()
var/obj/item/instrument/song_instrument = controller.blackboard[song_instrument_key]
var/datum/song/song = song_instrument.song
var/song_lines = controller.blackboard[song_lines_key]
@@ -333,8 +332,7 @@
/datum/ai_behavior/play_instrument/perform(seconds_per_tick, datum/ai_controller/controller, song_instrument_key)
. = ..()
var/datum/weakref/instrument_ref = controller.blackboard[song_instrument_key]
var/obj/item/instrument/song_instrument = instrument_ref.resolve()
var/obj/item/instrument/song_instrument = controller.blackboard[song_instrument_key]
var/datum/song/song = song_instrument.song
song.start_playing(controller.pawn)
@@ -354,5 +352,5 @@
possible_targets += thing
if(!possible_targets.len)
finish_action(controller, FALSE)
controller.blackboard[target_key] = WEAKREF(pick(possible_targets))
controller.set_blackboard_key(target_key, pick(possible_targets))
finish_action(controller, TRUE)
+8 -8
View File
@@ -8,8 +8,7 @@
* * BB_SONG_LINES - not set by this subtree, is the song loaded into the song datum.
*/
/datum/ai_planning_subtree/generic_play_instrument/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/datum/weakref/player_ref = controller.blackboard[BB_SONG_INSTRUMENT]
var/obj/item/instrument/song_player = player_ref?.resolve()
var/obj/item/instrument/song_player = controller.blackboard[BB_SONG_INSTRUMENT]
if(!song_player)
controller.queue_behavior(/datum/ai_behavior/find_and_set/in_hands, BB_SONG_INSTRUMENT, /obj/item/instrument)
@@ -47,15 +46,16 @@
* * BB_NEXT_HUNGRY - set by this subtree, is when the controller is next hungry
*/
/datum/ai_planning_subtree/generic_hunger/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
//inits the blackboard timer
if(!controller.blackboard[BB_NEXT_HUNGRY])
controller.blackboard[BB_NEXT_HUNGRY] = world.time + rand(0, 30 SECONDS)
var/next_eat = controller.blackboard[BB_NEXT_HUNGRY]
if(!next_eat)
//inits the blackboard timer
next_eat = world.time + rand(0, 30 SECONDS)
controller.set_blackboard_key(BB_NEXT_HUNGRY, next_eat)
if(world.time < controller.blackboard[BB_NEXT_HUNGRY])
if(world.time < next_eat)
return
var/datum/weakref/food_ref = controller.blackboard[BB_FOOD_TARGET]
if(!food_ref?.resolve())
if(!controller.blackboard[BB_FOOD_TARGET])
controller.queue_behavior(/datum/ai_behavior/find_and_set/edible, BB_FOOD_TARGET, /obj/item, 2)
return
@@ -32,9 +32,9 @@
haunt_equipper = FALSE
if(haunt_equipper)
//You have now become one of the victims of the HAAAAUNTTIIIINNGGG OOOOOO~~~
blackboard[BB_TO_HAUNT_LIST][WEAKREF(equipper)] += HAUNTED_ITEM_AGGRO_ADDITION
set_blackboard_key_assoc(BB_TO_HAUNT_LIST, equipper, HAUNTED_ITEM_AGGRO_ADDITION)
else
blackboard[BB_LIKES_EQUIPPER] = TRUE
set_blackboard_key(BB_LIKES_EQUIPPER, TRUE)
RegisterSignal(pawn, COMSIG_ITEM_DROPPED, PROC_REF(on_dropped))
@@ -42,6 +42,6 @@
/datum/ai_controller/haunted/proc/on_dropped(datum/source, mob/user)
SIGNAL_HANDLER
set_blackboard_key(BB_LIKES_EQUIPPER, FALSE)
RegisterSignal(pawn, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equip))
blackboard[BB_LIKES_EQUIPPER] = FALSE
UnregisterSignal(pawn, COMSIG_ITEM_DROPPED)
+5 -10
View File
@@ -13,17 +13,12 @@
var/list/to_haunt_list = controller.blackboard[BB_TO_HAUNT_LIST]
for(var/datum/weakref/target_ref as anything in to_haunt_list)
if(to_haunt_list[target_ref] <= 0)
to_haunt_list -= target_ref
for(var/mob/living/haunt_target as anything in to_haunt_list)
if(to_haunt_list[haunt_target] <= 0)
controller.remove_thing_from_blackboard_key(BB_TO_HAUNT_LIST, haunt_target)
continue
var/mob/living/real_target = target_ref.resolve()
if(QDELETED(real_target))
to_haunt_list -= target_ref
continue
if(get_dist(real_target, item_pawn) <= 7)
controller.blackboard[BB_HAUNT_TARGET] = target_ref
if(get_dist(haunt_target, item_pawn) <= 7)
controller.set_blackboard_key(BB_HAUNT_TARGET, haunt_target)
controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted, BB_HAUNT_TARGET, BB_HAUNTED_THROW_ATTEMPT_COUNT)
return SUBTREE_RETURN_FINISH_PLANNING
@@ -31,8 +31,7 @@
if(HAS_TRAIT(controller.pawn, TRAIT_HANDS_BLOCKED) || living_pawn.stat != CONSCIOUS)
return
var/datum/weakref/hunting_weakref = controller.blackboard[target_key]
var/atom/hunted = hunting_weakref?.resolve()
var/atom/hunted = controller.blackboard[target_key]
// We're not hunting anything, look around for something
if(isnull(hunted))
controller.queue_behavior(finding_behavior, target_key, hunt_targets, hunt_range)
@@ -56,7 +55,7 @@
for(var/atom/possible_dinner as anything in typecache_filter_list(range(hunt_range, living_mob), types_to_hunt))
if(!valid_dinner(living_mob, possible_dinner, hunt_range))
continue
controller.blackboard[hunting_target_key] = WEAKREF(possible_dinner)
controller.set_blackboard_key(hunting_target_key, possible_dinner)
finish_action(controller, TRUE)
return
@@ -80,8 +79,7 @@
/datum/ai_behavior/hunt_target/setup(datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key)
. = ..()
var/datum/weakref/hunting_weakref = controller.blackboard[hunting_target_key]
var/atom/hunt_target = hunting_weakref?.resolve()
var/atom/hunt_target = controller.blackboard[hunting_target_key]
if (isnull(hunt_target))
return FALSE
set_movement_target(controller, hunt_target)
@@ -89,8 +87,7 @@
/datum/ai_behavior/hunt_target/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key)
. = ..()
var/mob/living/hunter = controller.pawn
var/datum/weakref/hunting_weakref = controller.blackboard[hunting_target_key]
var/atom/hunted = hunting_weakref?.resolve()
var/atom/hunted = controller.blackboard[hunting_target_key]
if(isnull(hunted))
//Target is gone for some reason. forget about this task!
@@ -117,11 +114,11 @@
/datum/ai_behavior/hunt_target/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key)
. = ..()
if(succeeded)
controller.blackboard[hunting_cooldown_key] = world.time + hunt_cooldown
controller.set_blackboard_key(hunting_cooldown_key, world.time + hunt_cooldown)
else if(hunting_target_key)
controller.blackboard[hunting_target_key] = null
controller.clear_blackboard_key(hunting_target_key)
if(always_reset_target && hunting_target_key)
controller.blackboard[hunting_target_key] = null
controller.clear_blackboard_key(hunting_target_key)
/datum/ai_behavior/hunt_target/unarmed_attack_target
+1 -2
View File
@@ -4,8 +4,7 @@
if(!isturf(living_pawn.loc) || living_pawn.pulledby)
return
var/datum/weakref/weak_item = controller.blackboard[BB_SIMPLE_CARRY_ITEM]
var/obj/item/carry_item = weak_item?.resolve()
var/obj/item/carry_item = controller.blackboard[BB_SIMPLE_CARRY_ITEM]
// if we're just ditzing around carrying something, occasionally print a message so people know we have something
if(carry_item && SPT_PROB(5, seconds_per_tick))
living_pawn.visible_message(span_notice("[living_pawn] gently teethes on \the [carry_item] in [living_pawn.p_their()] mouth."), vision_distance=COMBAT_MESSAGE_RANGE)
+49 -57
View File
@@ -8,14 +8,9 @@
. = ..()
if(!success) //Don't try again on this item if we failed
var/list/item_blacklist = controller.blackboard[BB_MONKEY_BLACKLISTITEMS]
var/obj/item/target = controller.blackboard[BB_MONKEY_PICKUPTARGET]
controller.set_blackboard_key_assoc(BB_MONKEY_BLACKLISTITEMS, controller.blackboard[BB_MONKEY_PICKUPTARGET], TRUE)
item_blacklist[target] = TRUE
if(istype(controller, /datum/ai_controller/monkey)) //What the fuck
controller.RegisterSignal(target, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/datum/ai_controller/monkey,target_del))
controller.blackboard[BB_MONKEY_PICKUPTARGET] = null
controller.clear_blackboard_key(BB_MONKEY_PICKUPTARGET)
/datum/ai_behavior/monkey_equip/proc/equip_item(datum/ai_controller/controller)
var/mob/living/living_pawn = controller.pawn
@@ -39,7 +34,7 @@
else if(target.force > best_force)
living_pawn.drop_all_held_items()
living_pawn.put_in_hands(target)
controller.blackboard[BB_MONKEY_BEST_FORCE_FOUND] = target.force
controller.set_blackboard_key(BB_MONKEY_BEST_FORCE_FOUND, target.force)
finish_action(controller, TRUE)
return
@@ -77,7 +72,6 @@
/datum/ai_behavior/monkey_equip/pickpocket/proc/attempt_pickpocket(datum/ai_controller/controller)
var/obj/item/target = controller.blackboard[BB_MONKEY_PICKUPTARGET]
var/mob/living/victim = target.loc
var/mob/living/living_pawn = controller.pawn
@@ -89,7 +83,7 @@
victim.visible_message(span_warning("[living_pawn] starts trying to take [target] from [victim]!"), span_danger("[living_pawn] tries to take [target]!"))
controller.blackboard[BB_MONKEY_PICKPOCKETING] = TRUE
controller.set_blackboard_key(BB_MONKEY_PICKPOCKETING, TRUE)
var/success = FALSE
@@ -110,8 +104,8 @@
/datum/ai_behavior/monkey_equip/pickpocket/finish_action(datum/ai_controller/controller, success)
. = ..()
controller.blackboard[BB_MONKEY_PICKPOCKETING] = FALSE
controller.blackboard[BB_MONKEY_PICKUPTARGET] = null
controller.set_blackboard_key(BB_MONKEY_PICKPOCKETING, FALSE)
controller.clear_blackboard_key(BB_MONKEY_PICKUPTARGET)
/datum/ai_behavior/monkey_flee
@@ -128,7 +122,7 @@
// flee from anyone who attacked us and we didn't beat down
for(var/mob/living/L in view(living_pawn, MONKEY_FLEE_VISION))
if(controller.blackboard[BB_MONKEY_ENEMIES][WEAKREF(L)] && L.stat == CONSCIOUS)
if(controller.blackboard[BB_MONKEY_ENEMIES][L] && L.stat == CONSCIOUS)
target = L
break
@@ -142,14 +136,12 @@
/datum/ai_behavior/monkey_attack_mob/setup(datum/ai_controller/controller, target_key)
. = ..()
var/datum/weakref/target_ref = controller.blackboard[target_key]
set_movement_target(controller, target_ref?.resolve())
set_movement_target(controller, controller.blackboard[target_key])
/datum/ai_behavior/monkey_attack_mob/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
. = ..()
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/mob/living/target = target_ref?.resolve()
var/mob/living/target = controller.blackboard[target_key]
var/mob/living/living_pawn = controller.pawn
if(!target || target.stat != CONSCIOUS)
@@ -175,7 +167,7 @@
. = ..()
var/mob/living/living_pawn = controller.pawn
SSmove_manager.stop_looping(living_pawn)
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
/// attack using a held weapon otherwise bite the enemy, then if we are angry there is a chance we might calm down a little
/datum/ai_behavior/monkey_attack_mob/proc/monkey_attack(datum/ai_controller/controller, mob/living/target, seconds_per_tick, disarm)
@@ -193,7 +185,7 @@
living_pawn.set_combat_mode(TRUE)
if(isnull(controller.blackboard[BB_MONKEY_GUN_WORKED]))
controller.blackboard[BB_MONKEY_GUN_WORKED] = TRUE
controller.set_blackboard_key(BB_MONKEY_GUN_WORKED, TRUE)
// attack with weapon if we have one
if(living_pawn.CanReach(target, weapon))
@@ -201,7 +193,7 @@
weapon.melee_attack_chain(living_pawn, target)
else
living_pawn.UnarmedAttack(target, null, disarm ? list("right" = TRUE) : null) //Fake a right click if we're disarmin
controller.blackboard[BB_MONKEY_GUN_WORKED] = TRUE // We reset their memory of the gun being 'broken' if they accomplish some other attack
controller.set_blackboard_key(BB_MONKEY_GUN_WORKED, TRUE) // We reset their memory of the gun being 'broken' if they accomplish some other attack
else if(weapon)
var/atom/real_target = target
if(prob(10)) // Artificial miss
@@ -212,27 +204,32 @@
if(gun && controller.blackboard[BB_MONKEY_GUN_WORKED] && prob(95))
// We attempt to attack even if we can't shoot so we get the effects of pulling the trigger
gun.afterattack(real_target, living_pawn, FALSE)
controller.blackboard[BB_MONKEY_GUN_WORKED] = can_shoot ? TRUE : prob(80) // Only 20% likely to notice it didn't work
controller.set_blackboard_key(BB_MONKEY_GUN_WORKED, can_shoot ? TRUE : prob(80)) // Only 20% likely to notice it didn't work
if(can_shoot)
controller.blackboard[BB_MONKEY_GUN_NEURONS_ACTIVATED] = TRUE
controller.set_blackboard_key(BB_MONKEY_GUN_NEURONS_ACTIVATED, TRUE)
else
living_pawn.throw_item(real_target)
controller.blackboard[BB_MONKEY_GUN_WORKED] = TRUE // 'worked'
controller.set_blackboard_key(BB_MONKEY_GUN_WORKED, TRUE) // 'worked'
// no de-aggro
if(controller.blackboard[BB_MONKEY_AGGRESSIVE])
return
// we've queued up a monkey attack on a mob which isn't already an enemy, so give them 1 threat to start
// note they might immediately reduce threat and drop from the list.
// this is fine, we're just giving them a love tap then leaving them alone.
// unless they fight back, then we retaliate
if(isnull(controller.blackboard[BB_MONKEY_ENEMIES][target]))
controller.set_blackboard_key_assoc(BB_MONKEY_ENEMIES, target, 1)
/// mob refs are uids, so this is safe
var/datum/weakref/target_ref = WEAKREF(target)
if(SPT_PROB(MONKEY_HATRED_REDUCTION_PROB, seconds_per_tick))
controller.blackboard[BB_MONKEY_ENEMIES][target_ref]--
controller.add_blackboard_key_assoc(BB_MONKEY_ENEMIES, target, -1)
// if we are not angry at our target, go back to idle
if(controller.blackboard[BB_MONKEY_ENEMIES][target_ref] <= 0)
var/list/enemies = controller.blackboard[BB_MONKEY_ENEMIES]
enemies.Remove(target_ref)
if(controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET] == WEAKREF(target))
if(controller.blackboard[BB_MONKEY_ENEMIES][target] <= 0)
controller.remove_thing_from_blackboard_key(BB_MONKEY_ENEMIES, target)
if(controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET] == target)
finish_action(controller, TRUE)
/datum/ai_behavior/disposal_mob
@@ -240,14 +237,13 @@
/datum/ai_behavior/disposal_mob/setup(datum/ai_controller/controller, attack_target_key, disposal_target_key)
. = ..()
var/datum/weakref/target_ref = controller.blackboard[attack_target_key]
set_movement_target(controller, target_ref?.resolve())
set_movement_target(controller, controller.blackboard[attack_target_key])
/datum/ai_behavior/disposal_mob/finish_action(datum/ai_controller/controller, succeeded, attack_target_key, disposal_target_key)
. = ..()
controller.blackboard[attack_target_key] = null //Reset attack target
controller.blackboard[BB_MONKEY_DISPOSING] = FALSE //No longer disposing
controller.blackboard[disposal_target_key] = null //No target disposal
controller.clear_blackboard_key(attack_target_key) //Reset attack target
controller.set_blackboard_key(BB_MONKEY_DISPOSING, FALSE) //No longer disposing
controller.clear_blackboard_key(disposal_target_key) //No target disposal
/datum/ai_behavior/disposal_mob/perform(seconds_per_tick, datum/ai_controller/controller, attack_target_key, disposal_target_key)
. = ..()
@@ -255,8 +251,7 @@
if(controller.blackboard[BB_MONKEY_DISPOSING]) //We are disposing, don't do ANYTHING!!!!
return
var/datum/weakref/target_ref = controller.blackboard[attack_target_key]
var/mob/living/target = target_ref?.resolve()
var/mob/living/target = controller.blackboard[attack_target_key]
var/mob/living/living_pawn = controller.pawn
set_movement_target(controller, target)
@@ -270,8 +265,7 @@
target.grabbedby(living_pawn)
return //Do the rest next turn
var/datum/weakref/disposal_ref = controller.blackboard[disposal_target_key]
var/obj/machinery/disposal/disposal = disposal_ref.resolve()
var/obj/machinery/disposal/disposal = controller.blackboard[disposal_target_key]
set_movement_target(controller, disposal)
if(!disposal)
@@ -285,12 +279,10 @@
/datum/ai_behavior/disposal_mob/proc/try_disposal_mob(datum/ai_controller/controller, attack_target_key, disposal_target_key)
var/mob/living/living_pawn = controller.pawn
var/datum/weakref/target_ref = controller.blackboard[attack_target_key]
var/mob/living/target = target_ref?.resolve()
var/datum/weakref/disposal_ref = controller.blackboard[disposal_target_key]
var/obj/machinery/disposal/disposal = disposal_ref?.resolve()
var/mob/living/target = controller.blackboard[attack_target_key]
var/obj/machinery/disposal/disposal = controller.blackboard[disposal_target_key]
controller.blackboard[BB_MONKEY_DISPOSING] = TRUE
controller.set_blackboard_key(BB_MONKEY_DISPOSING, TRUE)
if(target && disposal?.stuff_mob_in(target, living_pawn))
disposal.flush()
@@ -300,20 +292,19 @@
/datum/ai_behavior/recruit_monkeys/perform(seconds_per_tick, datum/ai_controller/controller)
. = ..()
controller.blackboard[BB_MONKEY_RECRUIT_COOLDOWN] = world.time + MONKEY_RECRUIT_COOLDOWN
controller.set_blackboard_key(BB_MONKEY_RECRUIT_COOLDOWN, world.time + MONKEY_RECRUIT_COOLDOWN)
var/mob/living/living_pawn = controller.pawn
for(var/mob/living/L in view(living_pawn, MONKEY_ENEMY_VISION))
if(!HAS_AI_CONTROLLER_TYPE(L, /datum/ai_controller/monkey))
for(var/mob/living/nearby_monkey in view(living_pawn, MONKEY_ENEMY_VISION))
if(!HAS_AI_CONTROLLER_TYPE(nearby_monkey, /datum/ai_controller/monkey))
continue
if(!SPT_PROB(MONKEY_RECRUIT_PROB, seconds_per_tick))
continue
var/datum/ai_controller/monkey/monkey_ai = L.ai_controller
var/datum/weakref/enemy_ref = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
var/list/enemies = L.ai_controller.blackboard[BB_MONKEY_ENEMIES]
enemies[enemy_ref] = MONKEY_RECRUIT_HATED_AMOUNT
monkey_ai.blackboard[BB_MONKEY_RECRUIT_COOLDOWN] = world.time + MONKEY_RECRUIT_COOLDOWN
// Recruited a monkey to our side
controller.set_blackboard_key(BB_MONKEY_RECRUIT_COOLDOWN, world.time + MONKEY_RECRUIT_COOLDOWN)
// Other monkeys now also hate the guy we're currently targeting
nearby_monkey.ai_controller.add_blackboard_key_assoc(BB_MONKEY_ENEMIES, controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET], MONKEY_RECRUIT_HATED_AMOUNT)
finish_action(controller, TRUE)
/datum/ai_behavior/monkey_set_combat_target/perform(seconds_per_tick, datum/ai_controller/controller, set_key, enemies_key)
@@ -322,16 +313,17 @@
for(var/mob/living/possible_enemy in view(MONKEY_ENEMY_VISION, controller.pawn))
if(possible_enemy == controller.pawn)
continue // don't target ourselves
var/datum/weakref/enemy_ref = WEAKREF(possible_enemy)
if(!enemies[enemy_ref]) //We don't hate this creature! But we might still attack it!
if(!enemies[possible_enemy]) //We don't hate this creature! But we might still attack it!
if(!controller.blackboard[BB_MONKEY_AGGRESSIVE]) //We are not aggressive either, so we won't attack!
continue
if(HAS_AI_CONTROLLER_TYPE(possible_enemy, /datum/ai_controller/monkey) && !controller.blackboard[BB_MONKEY_TARGET_MONKEYS]) //Do not target poor monkes
continue
// Weighted list, so the closer they are the more likely they are to be chosen as the enemy
valids[enemy_ref] = CEILING(100 / (get_dist(controller.pawn, possible_enemy) || 1), 1)
valids[possible_enemy] = CEILING(100 / (get_dist(controller.pawn, possible_enemy) || 1), 1)
if(!valids.len)
if(!length(valids))
finish_action(controller, FALSE)
controller.blackboard[set_key] = pick_weight(valids)
return
controller.set_blackboard_key(set_key, pick_weight(valids))
finish_action(controller, TRUE)
+5 -10
View File
@@ -52,7 +52,7 @@ have ways of interacting with a specific mob and control it.
. = ..()
if(. & AI_CONTROLLER_INCOMPATIBLE)
return
blackboard[BB_MONKEY_AGGRESSIVE] = TRUE //Angry cunt
set_blackboard_key(BB_MONKEY_AGGRESSIVE, TRUE) //Angry cunt
/datum/ai_controller/monkey/TryPossessPawn(atom/new_pawn)
if(!isliving(new_pawn))
@@ -103,8 +103,8 @@ have ways of interacting with a specific mob and control it.
/datum/ai_controller/monkey/proc/TryFindWeapon()
var/mob/living/living_pawn = pawn
if(!locate(/obj/item) in living_pawn.held_items)
blackboard[BB_MONKEY_BEST_FORCE_FOUND] = 0
if(!(locate(/obj/item) in living_pawn.held_items))
set_blackboard_key(BB_MONKEY_BEST_FORCE_FOUND, 0)
if(blackboard[BB_MONKEY_GUN_NEURONS_ACTIVATED] && (locate(/obj/item/gun) in living_pawn.held_items))
// We have a gun, what could we possibly want?
@@ -135,7 +135,7 @@ have ways of interacting with a specific mob and control it.
if(weapon.force < 2) // our bite does 2 damage on avarage, no point in settling for anything less
return FALSE
blackboard[BB_MONKEY_PICKUPTARGET] = weapon
set_blackboard_key(BB_MONKEY_PICKUPTARGET, weapon)
set_movement_target(type, weapon)
if(pickpocket)
queue_behavior(/datum/ai_behavior/monkey_equip/pickpocket)
@@ -145,8 +145,7 @@ have ways of interacting with a specific mob and control it.
///Reactive events to being hit
/datum/ai_controller/monkey/proc/retaliate(mob/living/L)
var/list/enemies = blackboard[BB_MONKEY_ENEMIES]
enemies[WEAKREF(L)] += MONKEY_HATRED_AMOUNT
add_blackboard_key_assoc(BB_MONKEY_ENEMIES, L, MONKEY_HATRED_AMOUNT)
/datum/ai_controller/monkey/proc/on_attacked(datum/source, mob/attacker)
SIGNAL_HANDLER
@@ -175,7 +174,3 @@ have ways of interacting with a specific mob and control it.
/datum/ai_controller/monkey/proc/update_movespeed(mob/living/pawn)
SIGNAL_HANDLER
movement_delay = pawn.cached_multiplicative_slowdown
/datum/ai_controller/monkey/proc/target_del(target)
SIGNAL_HANDLER
blackboard[BB_MONKEY_BLACKLISTITEMS] -= target
+2 -3
View File
@@ -37,10 +37,9 @@
living_pawn.set_combat_mode(FALSE)
return SUBTREE_RETURN_FINISH_PLANNING
var/datum/weakref/target_ref = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
var/mob/living/selected_enemy = target_ref?.resolve()
var/mob/living/selected_enemy = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
if(!selected_enemy)
if(QDELETED(selected_enemy))
living_pawn.set_combat_mode(FALSE)
return
+1 -1
View File
@@ -9,7 +9,7 @@
/datum/ai_movement/proc/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance)
SHOULD_CALL_PARENT(TRUE)
controller.pathing_attempts = 0
controller.blackboard[BB_CURRENT_MIN_MOVE_DISTANCE] = min_distance
controller.set_blackboard_key(BB_CURRENT_MIN_MOVE_DISTANCE, min_distance)
moving_controllers[controller] = current_movement_target
/datum/ai_movement/proc/stop_moving_towards(datum/ai_controller/controller)
+1 -1
View File
@@ -43,6 +43,6 @@
/datum/ai_behavior/mod_attach/finish_action(datum/ai_controller/controller, succeeded)
. = ..()
controller.blackboard[BB_MOD_TARGET] = null
controller.clear_blackboard_key(BB_MOD_TARGET)
var/obj/item/implant/mod/implant = controller.blackboard[BB_MOD_IMPLANT]
implant.end_recall(succeeded)
@@ -9,14 +9,13 @@
. = ..()
set_movement_target(controller, controller.blackboard[target_key])
/datum/ai_behavior/vendor_crush/perform(seconds_per_tick, datum/ai_controller/controller)
. = ..()
if(controller.blackboard[BB_VENDING_BUSY_TILTING])
return
controller.ai_movement.stop_moving_towards(controller)
controller.blackboard[BB_VENDING_BUSY_TILTING] = TRUE
controller.set_blackboard_key(BB_VENDING_BUSY_TILTING, TRUE)
var/turf/target_turf = get_turf(controller.blackboard[BB_VENDING_CURRENT_TARGET])
new /obj/effect/temp_visual/telegraphing/vending_machine_tilt(target_turf)
addtimer(CALLBACK(src, PROC_REF(tiltonmob), controller, target_turf), time_to_tilt)
@@ -25,16 +24,16 @@
var/obj/machinery/vending/vendor_pawn = controller.pawn
if(vendor_pawn.tilt(target_turf)) //We hit something
vendor_pawn.say(pick("Supersize this!", "Eat my shiny metal ass!", "Want to consume some of my products?", "SMASH!", "Don't you love these smashing prices!"))
controller.blackboard[BB_VENDING_LAST_HIT_SUCCESFUL] = TRUE
controller.set_blackboard_key(BB_VENDING_LAST_HIT_SUCCESFUL, TRUE)
else
vendor_pawn.say(pick("Get back here!", "Don't you want my well priced love?"))
controller.blackboard[BB_VENDING_LAST_HIT_SUCCESFUL] = FALSE
controller.set_blackboard_key(BB_VENDING_LAST_HIT_SUCCESFUL, FALSE)
finish_action(controller, TRUE)
/datum/ai_behavior/vendor_crush/finish_action(datum/ai_controller/controller, succeeded)
. = ..()
controller.blackboard[BB_VENDING_BUSY_TILTING] = FALSE
controller.blackboard[BB_VENDING_UNTILT_COOLDOWN] = world.time + untilt_cooldown
controller.set_blackboard_key(BB_VENDING_BUSY_TILTING, FALSE)
controller.set_blackboard_key(BB_VENDING_UNTILT_COOLDOWN, world.time + untilt_cooldown)
/datum/ai_behavior/vendor_rise_up //what a gamer
///Time before machine can tilt again after untilting if last hit was a success
@@ -45,9 +44,6 @@
var/obj/machinery/vending/vendor_pawn = controller.pawn
vendor_pawn.visible_message(span_warning("[vendor_pawn] untilts itself!"))
if(controller.blackboard[BB_VENDING_LAST_HIT_SUCCESFUL])
controller.blackboard[BB_VENDING_TILT_COOLDOWN] = world.time + succes_tilt_cooldown
controller.set_blackboard_key(BB_VENDING_TILT_COOLDOWN, world.time + succes_tilt_cooldown)
vendor_pawn.untilt()
finish_action(controller, TRUE)
@@ -42,7 +42,7 @@
for(var/mob/living/living_target in oview(vision_range, pawn))
if(living_target.stat || living_target.incorporeal_move) //They're already fucked up or incorporeal
continue
blackboard[BB_VENDING_CURRENT_TARGET] = living_target
set_blackboard_key(BB_VENDING_CURRENT_TARGET, living_target)
queue_behavior(/datum/ai_behavior/vendor_crush, BB_VENDING_CURRENT_TARGET)
return
blackboard[BB_VENDING_TILT_COOLDOWN] = world.time + search_for_enemy_cooldown
set_blackboard_key(BB_VENDING_TILT_COOLDOWN, world.time + search_for_enemy_cooldown)
+22 -22
View File
@@ -47,7 +47,7 @@
SIGNAL_HANDLER
if(force || ai_status == AI_STATUS_OFF)
return
if(WEAKREF(buckler) != blackboard[BB_HOSTILE_FRIEND])
if(buckler != blackboard[BB_HOSTILE_FRIEND])
return COMPONENT_BLOCK_BUCKLE
/datum/ai_controller/hostile_friend/able_to_run()
@@ -70,36 +70,33 @@
/// Befriends someone
/datum/ai_controller/hostile_friend/proc/befriend(mob/living/new_friend)
var/datum/weakref/current_ref = blackboard[BB_HOSTILE_FRIEND]
var/datum/weakref/friend_ref = WEAKREF(new_friend)
var/mob/living/old_friend = current_ref?.resolve()
var/mob/living/old_friend = blackboard[BB_HOSTILE_FRIEND]
if(old_friend)
unfriend(old_friend)
else
blackboard[BB_HOSTILE_FRIEND] = null
if(in_range(pawn, new_friend))
new_friend.visible_message("<b>[pawn]</b> looks at [new_friend] in a friendly manner!", span_notice("[pawn] looks at you in a friendly manner!"))
blackboard[BB_HOSTILE_FRIEND] = friend_ref
set_blackboard_key(BB_HOSTILE_FRIEND, new_friend)
RegisterSignal(new_friend, COMSIG_MOB_POINTED, PROC_REF(check_point))
RegisterSignal(new_friend, COMSIG_MOB_SAY, PROC_REF(check_verbal_command))
/// Someone is being mean to us, take them off our friends (add actual enemies behavior later)
/datum/ai_controller/hostile_friend/proc/unfriend()
var/datum/weakref/friend_ref = blackboard[BB_HOSTILE_FRIEND]
var/mob/living/old_friend = friend_ref?.resolve()
var/mob/living/old_friend = blackboard[BB_HOSTILE_FRIEND]
if(old_friend)
UnregisterSignal(old_friend, list(COMSIG_MOB_POINTED, COMSIG_MOB_SAY))
blackboard[BB_HOSTILE_FRIEND] = null
clear_blackboard_key(BB_HOSTILE_FRIEND)
/// Someone is looking at us, if we're currently carrying something then show what it is, and include a message if they're our friend
/datum/ai_controller/hostile_friend/proc/on_examined(datum/source, mob/user, list/examine_text)
SIGNAL_HANDLER
if(blackboard[BB_HOSTILE_FRIEND] == WEAKREF(user))
var/mob/living/living_pawn = pawn
if(!IS_DEAD_OR_INCAP(living_pawn))
examine_text += span_notice("[pawn.p_they(TRUE)] seem[pawn.p_s()] happy to see you!")
if(blackboard[BB_HOSTILE_FRIEND] != user)
return
var/mob/living/living_pawn = pawn
if(!IS_DEAD_OR_INCAP(living_pawn))
examine_text += span_notice("[pawn.p_they(TRUE)] seem[pawn.p_s()] happy to see you!")
// next section is regarding commands
@@ -109,7 +106,7 @@
if(!COOLDOWN_FINISHED(src, command_cooldown))
return
if(!istype(clicker) || blackboard[BB_HOSTILE_FRIEND] == WEAKREF(clicker))
if(!istype(clicker) || blackboard[BB_HOSTILE_FRIEND] != clicker)
return
. = COMPONENT_CANCEL_CLICK_ALT
INVOKE_ASYNC(src, PROC_REF(command_radial), clicker)
@@ -139,7 +136,7 @@
/datum/ai_controller/hostile_friend/proc/check_verbal_command(mob/speaker, speech_args)
SIGNAL_HANDLER
if(blackboard[BB_HOSTILE_FRIEND] != WEAKREF(speaker))
if(blackboard[BB_HOSTILE_FRIEND] != speaker)
return
if(!COOLDOWN_FINISHED(src, command_cooldown))
@@ -172,14 +169,14 @@
// heel: stop what you're doing, relax and try not to do anything for a little bit
if(COMMAND_STOP)
pawn.visible_message(span_notice("[pawn] [blackboard[BB_HOSTILE_ATTACK_WORD]] at [commander]'s command, and [pawn.p_they()] stop[pawn.p_s()] obediently, awaiting further orders."))
blackboard[BB_HOSTILE_ORDER_MODE] = HOSTILE_COMMAND_NONE
set_blackboard_key(BB_HOSTILE_ORDER_MODE, HOSTILE_COMMAND_NONE)
CancelActions()
// follow: whatever the commander points to, try and bring it back
if(COMMAND_FOLLOW)
pawn.visible_message(span_notice("[pawn] [blackboard[BB_HOSTILE_ATTACK_WORD]] at [commander]'s command, and [pawn.p_they()] follow[pawn.p_s()] slightly in anticipation."))
CancelActions()
blackboard[BB_HOSTILE_ORDER_MODE] = HOSTILE_COMMAND_FOLLOW
blackboard[BB_FOLLOW_TARGET] = WEAKREF(commander)
set_blackboard_key(BB_HOSTILE_ORDER_MODE, HOSTILE_COMMAND_FOLLOW)
set_blackboard_key(BB_FOLLOW_TARGET, commander)
set_movement_target(type, commander)
var/mob/living/living_pawn = pawn
if(living_pawn.buckled)
@@ -189,7 +186,7 @@
if(COMMAND_ATTACK)
pawn.visible_message(span_danger("[pawn] [blackboard[BB_HOSTILE_ATTACK_WORD]] at [commander]'s command, and [pawn.p_they()] growl[pawn.p_s()] intensely.")) // imagine getting intimidated by a corgi
CancelActions()
blackboard[BB_HOSTILE_ORDER_MODE] = HOSTILE_COMMAND_ATTACK
set_blackboard_key(BB_HOSTILE_ORDER_MODE, HOSTILE_COMMAND_ATTACK)
/// Someone we like is pointing at something, see if it's something we might want to interact with (like if they might want us to fetch something for them)
/datum/ai_controller/hostile_friend/proc/check_point(mob/pointing_friend, atom/movable/pointed_movable)
@@ -201,7 +198,10 @@
if(!COOLDOWN_FINISHED(src, command_cooldown))
return
if(blackboard[BB_HOSTILE_FRIEND] == WEAKREF(pointed_movable) || pointed_movable == pawn || !istype(pointed_movable) || blackboard[BB_HOSTILE_ORDER_MODE] == HOSTILE_COMMAND_NONE) // busy or no command
if(blackboard[BB_HOSTILE_FRIEND] == pointed_movable \
|| pointed_movable == pawn \
|| !istype(pointed_movable) \
|| blackboard[BB_HOSTILE_ORDER_MODE] == HOSTILE_COMMAND_NONE) // busy or no command
return
if(!can_see(pawn, pointing_friend, length=blackboard[BB_VISION_RANGE]) || !can_see(pawn, pointed_movable, length=blackboard[BB_VISION_RANGE]))
return
@@ -212,7 +212,7 @@
if(blackboard[BB_HOSTILE_ORDER_MODE] == HOSTILE_COMMAND_ATTACK)
pawn.visible_message(span_notice("[pawn] follows [pointing_friend]'s gesture towards [pointed_movable] and [blackboard[BB_HOSTILE_ATTACK_WORD]] intensely!"))
set_movement_target(type, pointed_movable)
blackboard[BB_ATTACK_TARGET] = WEAKREF(pointed_movable)
set_blackboard_key(BB_ATTACK_TARGET, pointed_movable)
if(living_pawn.buckled)
queue_behavior(/datum/ai_behavior/resist)//in case they are in bed or something
queue_behavior(/datum/ai_behavior/attack)
@@ -26,7 +26,7 @@
if(found_seat)
customer_pawn.say(pick(customer_data.found_seat_lines))
controller.blackboard[BB_CUSTOMER_MY_SEAT] = WEAKREF(found_seat)
controller.set_blackboard_key(BB_CUSTOMER_MY_SEAT, found_seat)
attending_venue.linked_seats[found_seat] = customer_pawn
finish_action(controller, TRUE)
return
@@ -34,7 +34,7 @@
// SPT_PROB 1.5 is about a 60% chance that the tourist will have vocalised at least once every minute.
if(!controller.blackboard[BB_CUSTOMER_SAID_CANT_FIND_SEAT_LINE] || SPT_PROB(1.5, seconds_per_tick))
customer_pawn.say(pick(customer_data.cant_find_seat_lines))
controller.blackboard[BB_CUSTOMER_SAID_CANT_FIND_SEAT_LINE] = TRUE
controller.set_blackboard_key(BB_CUSTOMER_SAID_CANT_FIND_SEAT_LINE, TRUE)
finish_action(controller, FALSE)
@@ -46,9 +46,7 @@
. = ..()
var/mob/living/simple_animal/robot_customer/customer_pawn = controller.pawn
var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO]
var/datum/weakref/seat_ref = controller.blackboard[BB_CUSTOMER_MY_SEAT]
var/obj/structure/holosign/robot_seat/seat_marker = seat_ref?.resolve()
var/obj/structure/holosign/robot_seat/seat_marker = controller.blackboard[BB_CUSTOMER_MY_SEAT]
if(get_turf(seat_marker) == get_turf(customer_pawn))
var/obj/structure/chair/my_seat = locate(/obj/structure/chair) in get_turf(customer_pawn)
if(my_seat)
@@ -56,7 +54,7 @@
var/datum/venue/attending_venue = controller.blackboard[BB_CUSTOMER_ATTENDING_VENUE]
controller.blackboard[BB_CUSTOMER_CURRENT_ORDER] = attending_venue.order_food(customer_pawn, customer_data)
controller.set_blackboard_key(BB_CUSTOMER_CURRENT_ORDER, attending_venue.order_food(customer_pawn, customer_data))
finish_action(controller, TRUE)
@@ -70,7 +68,7 @@
finish_action(controller, TRUE)
return
controller.blackboard[BB_CUSTOMER_PATIENCE] -= seconds_per_tick * 10 // Convert seconds_per_tick to a SECONDS equivalent.
controller.add_blackboard_key(BB_CUSTOMER_PATIENCE, seconds_per_tick * -1 SECONDS) // Convert seconds_per_tick to a SECONDS equivalent.
if(controller.blackboard[BB_CUSTOMER_PATIENCE] < 0 || controller.blackboard[BB_CUSTOMER_LEAVING]) // Check if we're leaving because sometthing mightve forced us to
finish_action(controller, FALSE)
return
@@ -81,8 +79,7 @@
var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO]
customer_pawn.say(pick(customer_data.wait_for_food_lines))
var/datum/weakref/seat_ref = controller.blackboard[BB_CUSTOMER_MY_SEAT]
var/obj/structure/holosign/robot_seat/seat_marker = seat_ref?.resolve()
var/obj/structure/holosign/robot_seat/seat_marker = controller.blackboard[BB_CUSTOMER_MY_SEAT]
if(get_turf(seat_marker) == get_turf(controller.pawn))
var/obj/structure/chair/my_seat = locate(/obj/structure/chair) in get_turf(controller.pawn)
if(my_seat)
@@ -108,7 +105,7 @@
//or are being qdeleted and don't want runtime errors, so don't switch to leaving
if(greytider || QDELETED(src))
return
controller.blackboard[BB_CUSTOMER_LEAVING] = TRUE
controller.set_blackboard_key(BB_CUSTOMER_LEAVING, TRUE)
customer_pawn.update_icon() //They might have a special leaving accesoiry (french flag)
if(succeeded)
customer_pawn.say(pick(customer_data.leave_happy_lines))
@@ -13,8 +13,8 @@
/datum/ai_controller/robot_customer/Destroy()
// clear possible datum refs
blackboard[BB_CUSTOMER_CURRENT_ORDER] = null
blackboard[BB_CUSTOMER_CUSTOMERINFO] = null
clear_blackboard_key(BB_CUSTOMER_CURRENT_ORDER)
clear_blackboard_key(BB_CUSTOMER_CUSTOMERINFO)
return ..()
/datum/ai_controller/robot_customer/TryPossessPawn(atom/new_pawn)
@@ -51,12 +51,12 @@
if(blackboard[BB_CUSTOMER_EATING])
return
blackboard[BB_CUSTOMER_EATING] = TRUE
set_blackboard_key(BB_CUSTOMER_EATING, TRUE)
attending_venue.on_get_order(pawn, order_item)
var/our_order = blackboard[BB_CUSTOMER_CURRENT_ORDER]
if(isdatum(our_order))
qdel(our_order)
blackboard[BB_CUSTOMER_CURRENT_ORDER] = null
clear_blackboard_key(BB_CUSTOMER_CURRENT_ORDER)
///Called when
@@ -101,7 +101,7 @@
return
if(3)
customer.say(customer_data.self_defense_line)
blackboard[BB_CUSTOMER_CURRENT_TARGET] = greytider
set_blackboard_key(BB_CUSTOMER_CURRENT_TARGET, greytider)
CancelActions()
@@ -8,8 +8,7 @@
controller.queue_behavior(/datum/ai_behavior/break_spine, BB_CUSTOMER_CURRENT_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
var/datum/weakref/seat_ref = controller.blackboard[BB_CUSTOMER_MY_SEAT]
var/obj/structure/holosign/robot_seat/seat_marker = seat_ref?.resolve()
var/obj/structure/holosign/robot_seat/seat_marker = controller.blackboard[BB_CUSTOMER_MY_SEAT]
if(!seat_marker) //We havn't got a seat yet! find one!
controller.queue_behavior(/datum/ai_behavior/find_seat)
@@ -25,9 +25,6 @@
if (!victim.ai_controller)
return
var/list/enemy_refs = victim.ai_controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST]
if (!enemy_refs)
enemy_refs = list()
enemy_refs |= WEAKREF(attacker)
victim.ai_controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST] = enemy_refs
victim.ai_controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker)
post_retaliate_callback?.InvokeAsync(attacker)
+11 -14
View File
@@ -39,7 +39,7 @@
var/list/blackboard = parent.ai_controller.blackboard
if (!trigger_on_throw && !blackboard[BB_ACTIVE_PET_COMMAND])
return // We don't have a command and we only listen to commands
if (blackboard[BB_ACTIVE_PET_COMMAND] && blackboard[BB_ACTIVE_PET_COMMAND] != WEAKREF(src))
if (blackboard[BB_ACTIVE_PET_COMMAND] && blackboard[BB_ACTIVE_PET_COMMAND] != src)
return // We have a command and it's not this one
if (blackboard[BB_CURRENT_PET_TARGET] || blackboard[BB_FETCH_DELIVER_TO])
return // We're already very fetching
@@ -49,7 +49,7 @@
var/obj/item/thrown_thing = thrower.get_active_held_item()
if (!isitem(thrown_thing))
return
if (blackboard[BB_FETCH_IGNORE_LIST] && blackboard[BB_FETCH_IGNORE_LIST][WEAKREF(thrown_thing)])
if (blackboard[BB_FETCH_IGNORE_LIST]?[thrown_thing])
return // We're ignoring it already
RegisterSignal(thrown_thing, COMSIG_MOVABLE_THROW_LANDED, PROC_REF(listen_throw_land))
@@ -69,7 +69,7 @@
try_activate_command(throwing_datum.thrower)
set_command_target(parent, thrown_thing)
parent.ai_controller.blackboard[BB_FETCH_DELIVER_TO] = WEAKREF(throwing_datum.thrower)
parent.ai_controller.set_blackboard_key(BB_FETCH_DELIVER_TO, throwing_datum.thrower)
// Don't try and fetch turfs or anchored objects if someone points at them
/datum/pet_command/point_targetting/fetch/look_for_target(mob/living/pointing_friend, obj/item/pointed_atom)
@@ -82,16 +82,15 @@
return FALSE
var/mob/living/parent = weak_parent.resolve()
parent.ai_controller.blackboard[BB_FETCH_DELIVER_TO] = WEAKREF(pointing_friend)
parent.ai_controller.set_blackboard_key(BB_FETCH_DELIVER_TO, pointing_friend)
// Finally, plan our actions
/datum/pet_command/point_targetting/fetch/execute_action(datum/ai_controller/controller)
controller.queue_behavior(/datum/ai_behavior/forget_failed_fetches)
var/datum/weakref/weak_target = controller.blackboard[BB_CURRENT_PET_TARGET]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[BB_CURRENT_PET_TARGET]
// We got something to fetch so go fetch it
if (target)
if (!QDELETED(target))
if (get_dist(controller.pawn, target) > 1) // We're not there yet
controller.queue_behavior(/datum/ai_behavior/fetch_seek, BB_CURRENT_PET_TARGET, BB_FETCH_DELIVER_TO)
return SUBTREE_RETURN_FINISH_PLANNING
@@ -99,16 +98,14 @@
controller.queue_behavior(/datum/ai_behavior/pick_up_item, BB_CURRENT_PET_TARGET, BB_SIMPLE_CARRY_ITEM)
return SUBTREE_RETURN_FINISH_PLANNING
var/datum/weakref/carried_ref = controller.blackboard[BB_SIMPLE_CARRY_ITEM]
var/obj/item/carried_item = carried_ref?.resolve()
if (!carried_item)
var/obj/item/carried_item = controller.blackboard[BB_SIMPLE_CARRY_ITEM]
if (QDELETED(carried_item))
return
var/datum/weakref/delivery_ref = controller.blackboard[BB_FETCH_DELIVER_TO]
var/atom/delivery_target = delivery_ref?.resolve()
if (!delivery_target || !can_see(controller.pawn, delivery_target, sense_radius))
var/atom/delivery_target = controller.blackboard[BB_FETCH_DELIVER_TO]
if (QDELETED(delivery_target) || !can_see(controller.pawn, delivery_target, sense_radius))
// We don't know where to return this to so we're just going to keep it
controller.blackboard[BB_ACTIVE_PET_COMMAND] = null
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return
// We got something to deliver and someone to deliver it to
@@ -57,10 +57,7 @@
if (IS_DEAD_OR_INCAP(source))
return
if (!source.ai_controller)
return
var/list/friends_list = source.ai_controller.blackboard[BB_FRIENDS_LIST]
if (!friends_list || !friends_list[WEAKREF(user)])
if (!(user in source.ai_controller?.blackboard[BB_FRIENDS_LIST]))
return
examine_list += span_notice("[source.p_they(capitalized = TRUE)] seem[source.p_s()] happy to see you!")
@@ -71,10 +68,7 @@
var/mob/living/living_parent = parent
if (IS_DEAD_OR_INCAP(living_parent))
return
if (!living_parent.ai_controller)
return
var/list/friends_list = living_parent.ai_controller.blackboard[BB_FRIENDS_LIST]
if (!friends_list || !friends_list[WEAKREF(clicker)])
if (!(clicker in living_parent.ai_controller?.blackboard[BB_FRIENDS_LIST]))
return // Not our friend, can't boss us around
INVOKE_ASYNC(src, PROC_REF(display_radial_menu), clicker)
@@ -68,7 +68,7 @@
return
if (IS_DEAD_OR_INCAP(parent)) // Probably can't hear them if we're dead
return
if (parent.ai_controller.blackboard[BB_ACTIVE_PET_COMMAND] == WEAKREF(src)) // We're already doing it
if (parent.ai_controller.blackboard[BB_ACTIVE_PET_COMMAND] == src) // We're already doing it
return
set_command_active(parent, commander)
@@ -77,13 +77,13 @@
set_command_target(parent, null)
parent.ai_controller.CancelActions() // Stop whatever you're doing and do this instead
parent.ai_controller.blackboard[BB_ACTIVE_PET_COMMAND] = WEAKREF(src)
parent.ai_controller.set_blackboard_key(BB_ACTIVE_PET_COMMAND, src)
if (command_feedback)
parent.balloon_alert_to_viewers("[command_feedback]") // If we get a nicer runechat way to do this, refactor this
/// Store the target for the AI blackboard
/datum/pet_command/proc/set_command_target(mob/living/parent, atom/target)
parent.ai_controller.blackboard[BB_CURRENT_PET_TARGET] = WEAKREF(target)
parent.ai_controller.set_blackboard_key(BB_CURRENT_PET_TARGET, target)
/// Provide information about how to display this command in a radial menu
/datum/pet_command/proc/provide_radial_data()
@@ -137,9 +137,9 @@
return FALSE
if (IS_DEAD_OR_INCAP(parent))
return FALSE
if (parent.ai_controller.blackboard[BB_ACTIVE_PET_COMMAND] != WEAKREF(src)) // We're not listening right now
if (parent.ai_controller.blackboard[BB_ACTIVE_PET_COMMAND] != src) // We're not listening right now
return FALSE
if (parent.ai_controller.blackboard[BB_CURRENT_PET_TARGET] == WEAKREF(pointed_atom)) // That's already our target
if (parent.ai_controller.blackboard[BB_CURRENT_PET_TARGET] == pointed_atom) // That's already our target
return FALSE
if (!can_see(parent, pointed_atom, sense_radius))
return FALSE
@@ -28,7 +28,7 @@
command_feedback = "relaxes"
/datum/pet_command/free/execute_action(datum/ai_controller/controller)
controller.blackboard[BB_ACTIVE_PET_COMMAND] = null
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return // Just move on to the next planning subtree.
/**
@@ -87,7 +87,7 @@
// If we get past this point someone has finally added a non-binary dog
/datum/pet_command/good_boy/execute_action(datum/ai_controller/controller)
controller.blackboard[BB_ACTIVE_PET_COMMAND] = null
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
var/mob/living/parent = weak_parent.resolve()
if (!parent)
return SUBTREE_RETURN_FINISH_PLANNING
@@ -36,10 +36,10 @@
if (current_health_percentage < stop_fleeing_at)
return
source.ai_controller.CancelActions() // Stop fleeing go back to whatever you were doing
source.ai_controller.blackboard[BB_BASIC_MOB_FLEEING] = FALSE
source.ai_controller.set_blackboard_key(BB_BASIC_MOB_FLEEING, FALSE)
return
if (current_health_percentage > start_fleeing_below)
return
source.ai_controller.CancelActions()
source.ai_controller.blackboard[BB_BASIC_MOB_FLEEING] = TRUE
source.ai_controller.set_blackboard_key(BB_BASIC_MOB_FLEEING, TRUE)
+5 -7
View File
@@ -20,8 +20,7 @@
/// Returns the item held in a mob's blackboard, if it has one
/datum/element/ai_held_item/proc/get_held_item(mob/living/source)
var/datum/weakref/weak_item = source.ai_controller.blackboard[BB_SIMPLE_CARRY_ITEM]
return weak_item?.resolve()
return source.ai_controller.blackboard[BB_SIMPLE_CARRY_ITEM]
/// Someone's interacting with us by hand, if we have an item and like them we'll hand it over
/datum/element/ai_held_item/proc/on_click(mob/living/source, mob/living/user)
@@ -30,8 +29,7 @@
if (user.combat_mode)
return
var/list/friends = source.ai_controller.blackboard[BB_FRIENDS_LIST]
if (!friends || !friends[WEAKREF(user)])
if (!(user in source.ai_controller.blackboard[BB_FRIENDS_LIST]))
return // We don't care about this bozo
var/obj/item/carried_item = get_held_item(source)
if (!carried_item)
@@ -39,7 +37,7 @@
source.visible_message(span_danger("[source] drops [carried_item] at [user]'s feet!"))
carried_item.forceMove(get_turf(user))
source.ai_controller.blackboard[BB_SIMPLE_CARRY_ITEM] = null
source.ai_controller.clear_blackboard_key(BB_SIMPLE_CARRY_ITEM)
/// If our held item is removed from our atom then take it off the blackboard
/datum/element/ai_held_item/proc/atom_exited(mob/living/source, atom/movable/gone)
@@ -47,7 +45,7 @@
var/obj/item/carried_item = get_held_item(source)
if (carried_item == gone)
source.ai_controller.blackboard[BB_SIMPLE_CARRY_ITEM] = null
source.ai_controller.clear_blackboard_key(BB_SIMPLE_CARRY_ITEM)
/// Report that we're holding an item.
/datum/element/ai_held_item/proc/on_examined(mob/living/source, mob/user, list/examine_text)
@@ -68,4 +66,4 @@
ol_yeller.visible_message(span_danger("[ol_yeller] drops [carried_item] as [ol_yeller.p_they()] die[ol_yeller.p_s()]."))
carried_item.forceMove(ol_yeller.drop_location())
ol_yeller.ai_controller.blackboard[BB_SIMPLE_CARRY_ITEM] = null
ol_yeller.ai_controller.clear_blackboard_key(BB_SIMPLE_CARRY_ITEM)
+1 -7
View File
@@ -20,10 +20,4 @@
/datum/element/ai_retaliate/proc/on_attacked(mob/victim, atom/attacker)
SIGNAL_HANDLER
if (!victim.ai_controller)
return
var/list/enemy_refs = victim.ai_controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST]
if (!enemy_refs)
enemy_refs = list()
enemy_refs |= WEAKREF(attacker)
victim.ai_controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST] = enemy_refs
victim.ai_controller?.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker)
+1 -1
View File
@@ -11,7 +11,7 @@
return COMPONENT_INCOMPATIBLE
var/obj/item/master = target
var/datum/ai_controller/ai = new /datum/ai_controller/cursed(master)
ai.blackboard[BB_TARGET_SLOT] = slot
ai.set_blackboard_key(BB_TARGET_SLOT, slot)
master.ai_controller = ai
master.AddElement(/datum/element/movetype_handler)
ADD_TRAIT(master, TRAIT_MOVE_FLYING, ELEMENT_TRAIT(type))
@@ -526,7 +526,7 @@
playsound(game, 'sound/items/weeoo1.ogg', 100, FALSE)
for(var/i in 1 to 3)
var/mob/living/basic/syndicate/ranged/smg/orion/spaceport_security = new(get_turf(game))
spaceport_security.ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] = REF(usr)
spaceport_security.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, usr)
game.fuel += fuel
game.food += food
@@ -55,7 +55,7 @@
continue
// This gives all mobs in view "5" haunt level
// For reference picking one up gives "2"
haunted_item.ai_controller.blackboard[BB_TO_HAUNT_LIST][WEAKREF(victim)] = 5
haunted_item.ai_controller.add_blackboard_key_assoc(BB_TO_HAUNT_LIST, victim, 5)
if(haunted_item.throwforce < throw_force_max)
pre_haunt_throwforce = haunted_item.throwforce
@@ -81,7 +81,7 @@
if(prob(10))
switch(rand(1,4))
if(1) //blood rage
magnification.ai_controller.blackboard[BB_MONKEY_AGGRESSIVE] = TRUE
magnification.ai_controller.set_blackboard_key(BB_MONKEY_AGGRESSIVE, TRUE)
if(2) //brain death
magnification.apply_damage(500,BRAIN,BODY_ZONE_HEAD,FALSE,FALSE,FALSE)
if(3) //primal gene (gorilla)
@@ -153,7 +153,7 @@
restaurant_portal.update_icon()
STOP_PROCESSING(SSobj, src)
for(var/mob/living/simple_animal/robot_customer as anything in current_visitors)
robot_customer.ai_controller.blackboard[BB_CUSTOMER_LEAVING] = TRUE //LEAVEEEEEE
robot_customer.ai_controller.set_blackboard_key(BB_CUSTOMER_LEAVING, TRUE) //LEAVEEEEEE
/obj/machinery/restaurant_portal
name = "restaurant portal"
@@ -45,7 +45,7 @@
udder_component()
setup_eating()
. = ..()
ai_controller.blackboard[BB_BASIC_FOODS] = food_types
ai_controller.set_blackboard_key(BB_BASIC_FOODS, food_types)
///wrapper for the udder component addition so you can have uniquely uddered cow subtypes
/mob/living/basic/cow/proc/udder_component()
@@ -86,5 +86,5 @@
/mob/living/basic/cow/proc/set_tip_react_blackboard(mob/living/carbon/tipper)
if(!HAS_TRAIT_FROM(src, TRAIT_IMMOBILIZED, TIPPED_OVER) || !ai_controller)
return
ai_controller.blackboard[BB_BASIC_MOB_TIP_REACTING] = TRUE
ai_controller.blackboard[BB_BASIC_MOB_TIPPER] = tipper
ai_controller.set_blackboard_key(BB_BASIC_MOB_TIP_REACTING, TRUE)
ai_controller.set_blackboard_key(BB_BASIC_MOB_TIPPER, tipper)
@@ -28,9 +28,9 @@
. = ..()
AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_SHOE)
var/time_to_freeze_for = (rand(5, 10) SECONDS)
ai_controller.blackboard[BB_STATIONARY_SECONDS] = time_to_freeze_for
ai_controller.blackboard[BB_STATIONARY_COOLDOWN] = (time_to_freeze_for * (rand(3, 5)))
ai_controller.blackboard[BB_STATIONARY_TARGETS] = stationary_scary_things
ai_controller.set_blackboard_key(BB_STATIONARY_SECONDS, time_to_freeze_for)
ai_controller.set_blackboard_key(BB_STATIONARY_COOLDOWN, (time_to_freeze_for * (rand(3, 5))))
ai_controller.set_blackboard_key(BB_STATIONARY_TARGETS, stationary_scary_things)
/datum/ai_controller/basic_controller/deer
blackboard = list(
@@ -45,7 +45,7 @@
AddElement(/datum/element/death_drops, list(/obj/item/stack/rods))
var/datum/action/cooldown/mob_cooldown/charge_apc/charge_ability = new(src)
charge_ability.Grant(src)
ai_controller.blackboard[BB_FESTIVE_APC] = WEAKREF(charge_ability)
ai_controller.set_blackboard_key(BB_FESTIVE_APC, charge_ability)
/datum/ai_controller/basic_controller/festivus_pole
blackboard = list(
@@ -98,8 +98,7 @@
always_reset_target = TRUE
/datum/ai_behavior/hunt_target/apcs/target_caught(mob/living/basic/hunter, obj/machinery/power/apc/hunted)
var/datum/weakref/ability_weakref = hunter.ai_controller.blackboard[BB_FESTIVE_APC]
var/datum/action/cooldown/mob_cooldown/charge_ability = ability_weakref?.resolve()
var/datum/action/cooldown/mob_cooldown/charge_ability = hunter.ai_controller.blackboard[BB_FESTIVE_APC]
if(isnull(charge_ability))
return
charge_ability.Activate(hunted)
@@ -86,8 +86,7 @@
/datum/ai_behavior/basic_melee_attack/star_gazer/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[target_key]
var/mob/living/living_pawn = controller.pawn
if(!isliving(target))
@@ -56,6 +56,6 @@
devour.Grant(src)
var/datum/action/adjust_vision/bileworm/adjust_vision = new(src)
adjust_vision.Grant(src)
ai_controller.blackboard[BB_BILEWORM_SPEW_BILE] = WEAKREF(spew_bile)
ai_controller.blackboard[BB_BILEWORM_RESURFACE] = WEAKREF(resurface)
ai_controller.blackboard[BB_BILEWORM_DEVOUR] = WEAKREF(devour)
ai_controller.set_blackboard_key(BB_BILEWORM_SPEW_BILE, spew_bile)
ai_controller.set_blackboard_key(BB_BILEWORM_RESURFACE, resurface)
ai_controller.set_blackboard_key(BB_BILEWORM_DEVOUR, devour)
@@ -15,7 +15,7 @@
to_chat(burrower, span_warning("Couldn't burrow anywhere near the target!"))
if(burrower.ai_controller?.ai_status == AI_STATUS_ON)
//this is a valid reason to give up on a target
burrower.ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] = null
burrower.ai_controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET)
return
playsound(burrower, 'sound/effects/break_stone.ogg', 50, TRUE)
new /obj/effect/temp_visual/mook_dust(get_turf(burrower))
@@ -13,17 +13,15 @@
/datum/ai_planning_subtree/bileworm_attack/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/datum/weakref/weak_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
var/mob/living/target = weak_target?.resolve()
var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
if(QDELETED(target))
return
var/datum/weakref/weak_action = controller.blackboard[BB_BILEWORM_RESURFACE]
var/datum/action/cooldown/mob_cooldown/resurface = weak_action?.resolve()
var/datum/action/cooldown/mob_cooldown/resurface = controller.blackboard[BB_BILEWORM_RESURFACE]
//because one ability is always INFINITY cooldown, this actually works to check which ability should be used
//sometimes it will try to spew bile on infinity cooldown, but that's okay because as soon as resurface is ready it will attempt that
if(resurface && resurface.next_use_time <= world.time)
if(!QDELETED(resurface) && resurface.next_use_time <= world.time)
controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_plan_execute, BB_BILEWORM_RESURFACE, BB_BASIC_MOB_CURRENT_TARGET)
else
controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_plan_execute, BB_BILEWORM_SPEW_BILE, BB_BASIC_MOB_CURRENT_TARGET)
@@ -33,8 +31,7 @@
/datum/ai_planning_subtree/bileworm_execute/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/datum/weakref/weak_target = controller.blackboard[BB_BASIC_MOB_EXECUTION_TARGET]
var/mob/living/target = weak_target?.resolve()
var/mob/living/target = controller.blackboard[BB_BASIC_MOB_EXECUTION_TARGET]
if(QDELETED(target) || target.stat < UNCONSCIOUS)
return
+3 -3
View File
@@ -14,7 +14,7 @@
/datum/pet_command/point_targetting/attack/dog/set_command_active(mob/living/parent, mob/living/commander)
. = ..()
parent.ai_controller.blackboard[BB_DOG_HARASS_HARM] = TRUE
parent.ai_controller.set_blackboard_key(BB_DOG_HARASS_HARM, TRUE)
/mob/living/basic/pet/dog
mob_biotypes = MOB_ORGANIC|MOB_BEAST
@@ -159,7 +159,7 @@
/mob/living/basic/pet/dog/corgi/proc/stop_deadchat_plays()
var/controller_type = initial(ai_controller)
ai_controller = new controller_type(src)
ai_controller?.blackboard[BB_DOG_IS_SLOW] = is_slow
ai_controller?.set_blackboard_key(BB_DOG_IS_SLOW, is_slow)
/mob/living/basic/pet/dog/corgi/handle_atom_del(atom/A)
if(A == inventory_head)
@@ -556,7 +556,7 @@ GLOBAL_LIST_INIT(strippable_corgi_items, create_strippable_list(list(
held_state = "old_corgi"
icon_dead = "old_corgi_dead"
desc = "At a ripe old age of [record_age], Ian's not as spry as he used to be, but he'll always be the HoP's beloved corgi." //RIP
ai_controller?.blackboard[BB_DOG_IS_SLOW] = TRUE
ai_controller?.set_blackboard_key(BB_DOG_IS_SLOW, TRUE)
is_slow = TRUE
speed = 2
@@ -118,8 +118,9 @@
teleport = new(src)
teleport.Grant(src)
ai_controller.blackboard[BB_CARP_RIFT] = WEAKREF(teleport)
ai_controller.blackboard[BB_OBSTACLE_TARGETTING_WHITELIST] = allowed_obstacle_targets
ai_controller.set_blackboard_key(BB_CARP_RIFT, teleport)
ai_controller.set_blackboard_key(BB_OBSTACLE_TARGETTING_WHITELIST, allowed_obstacle_targets)
/mob/living/basic/carp/Destroy()
QDEL_NULL(teleport)
@@ -129,7 +130,7 @@
/mob/living/basic/carp/proc/setup_eating()
AddElement(/datum/element/basic_eating, 10, 0, null, desired_food)
AddElement(/datum/element/basic_eating, 0, 10, BRUTE, desired_trash) // We are killing our planet
ai_controller.blackboard[BB_BASIC_FOODS] = desired_food + desired_trash
ai_controller.set_blackboard_key(BB_BASIC_FOODS, desired_food + desired_trash)
/// Set a random colour on the carp, override to do something else
/mob/living/basic/carp/proc/apply_colour()
@@ -151,9 +152,16 @@
/mob/living/basic/carp/ranged_secondary_attack(atom/atom_target, modifiers)
teleport.Trigger(target = atom_target)
/// Gives the carp a list of destinations to try and travel between when it has nothing better to do
/mob/living/basic/carp/proc/migrate_to(list/migration_points)
ai_controller.blackboard[BB_CARP_MIGRATION_PATH] = migration_points
/// Gives the carp a list of weakrefs of destinations to try and travel between when it has nothing better to do
/mob/living/basic/carp/proc/migrate_to(list/datum/weakref/migration_points)
var/list/actual_points = list()
for(var/datum/weakref/point_ref as anything in migration_points)
var/turf/point_resolved = point_ref.resolve()
if(QDELETED(point_resolved))
return // invalid list, we can't migrate to this
actual_points += point_resolved
ai_controller.set_blackboard_key(BB_CARP_MIGRATION_PATH, actual_points)
/**
* Holographic carp from the holodeck
@@ -42,8 +42,7 @@
/datum/ai_planning_subtree/find_nearest_magicarp_spell_target
/datum/ai_planning_subtree/find_nearest_magicarp_spell_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/datum/weakref/weak_action = controller.blackboard[BB_MAGICARP_SPELL]
var/datum/action/cooldown/using_action = weak_action?.resolve()
var/datum/action/cooldown/using_action = controller.blackboard[BB_MAGICARP_SPELL]
if (QDELETED(using_action))
return
if (!using_action.IsAvailable())
@@ -22,8 +22,7 @@
if (!length(migration_points))
return
var/datum/weakref/weak_target = controller.blackboard[BB_CARP_MIGRATION_TARGET]
var/turf/moving_to = weak_target?.resolve()
var/turf/moving_to = controller.blackboard[BB_CARP_MIGRATION_TARGET]
// If we don't have a target or are close enough to it, pick a new one
if (isnull(moving_to) || get_dist(controller.pawn, moving_to) <= CARP_DESTINATION_SEARCH_RANGE)
@@ -48,13 +47,12 @@
var/list/blackboard_points = controller.blackboard[path_key]
var/list/potential_migration_points = blackboard_points.Copy()
while (length(potential_migration_points))
var/datum/weakref/weak_destination = popleft(potential_migration_points)
var/turf/potential_destination = weak_destination.resolve()
var/turf/potential_destination = popleft(potential_migration_points)
if (!isnull(potential_destination) && get_dist(controller.pawn, potential_destination) > CARP_DESTINATION_SEARCH_RANGE)
controller.blackboard[target_key] = weak_destination
controller.set_blackboard_key(target_key, potential_destination)
finish_action(controller, succeeded = TRUE)
return
controller.blackboard[path_key] = potential_migration_points.Copy()
controller.set_blackboard_key(path_key, potential_migration_points.Copy())
finish_action(controller, succeeded = FALSE)
@@ -12,14 +12,9 @@
if (!rift_behaviour)
CRASH("Forgot to specify rift behaviour for [src]")
var/datum/weakref/weak_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
var/mob/living/target = weak_target?.resolve()
if (!target)
return
var/datum/weakref/weak_action = controller.blackboard[BB_CARP_RIFT]
var/datum/action/cooldown/using_action = weak_action?.resolve()
if (isnull(using_action) || !using_action.IsAvailable())
var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
var/datum/action/cooldown/using_action = controller.blackboard[BB_CARP_RIFT]
if (QDELETED(target) || QDELETED(using_action) || !using_action.IsAvailable())
return
controller.queue_behavior(rift_behaviour, BB_CARP_RIFT, BB_BASIC_MOB_CURRENT_TARGET)
@@ -53,20 +48,12 @@
/datum/ai_behavior/make_carp_rift
/datum/ai_behavior/make_carp_rift/setup(datum/ai_controller/controller, ability_key, target_key)
var/datum/weakref/weak_action = controller.blackboard[ability_key]
var/datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability = weak_action?.resolve()
if (!ability)
return FALSE
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
return target
return controller.blackboard[ability_key] && controller.blackboard[target_key]
/datum/ai_behavior/make_carp_rift/perform(seconds_per_tick, datum/ai_controller/controller, ability_key, target_key)
. = ..()
var/datum/weakref/weak_action = controller.blackboard[ability_key]
var/datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability = weak_action?.resolve()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
var/datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability = controller.blackboard[ability_key]
var/atom/target = controller.blackboard[target_key]
if (!validate_target(controller, target, ability))
finish_action(controller, FALSE, ability_key, target_key)
@@ -187,9 +174,8 @@
var/minimum_distance = 2
/datum/ai_planning_subtree/shortcut_to_target_through_carp_rift/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/datum/weakref/weak_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
var/mob/living/target = weak_target?.resolve()
if (isnull(target))
var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]
if (QDELETED(target))
return
var/distance_to_target = get_dist(controller.pawn, target)
@@ -88,7 +88,7 @@ GLOBAL_LIST_INIT(magicarp_spell_colours, list(
spell.projectile_type = spell_type
spell.button_icon_state = initial(spell_type.icon_state)
spell.Grant(src)
ai_controller.blackboard[BB_MAGICARP_SPELL] = WEAKREF(spell)
ai_controller.set_blackboard_key(BB_MAGICARP_SPELL, spell)
assign_spell_ai(spell_type)
/// If you have certain spells, use a different targetting datum
@@ -99,7 +99,7 @@ GLOBAL_LIST_INIT(magicarp_spell_colours, list(
/obj/projectile/magic/resurrection = MAGICARP_SPELL_CORPSES,
)
ai_controller.blackboard[BB_MAGICARP_SPELL_SPECIAL_TARGETTING] = spell_special_targetting[spell_type]
ai_controller.set_blackboard_key(BB_MAGICARP_SPELL_SPECIAL_TARGETTING, spell_special_targetting[spell_type])
/// Shoot when you click away from you
/mob/living/basic/carp/magic/RangedAttack(atom/atom_target, modifiers)
@@ -123,7 +123,7 @@ GLOBAL_LIST_INIT(magicarp_spell_colours, list(
chaos_bolt.permitted_projectiles = allowed_projectile_types
chaos_bolt.Grant(src)
spell = chaos_bolt
ai_controller.blackboard[BB_MAGICARP_SPELL] = spell
ai_controller.set_blackboard_key(BB_MAGICARP_SPELL, spell)
RegisterSignal(spell, COMSIG_ACTION_TRIGGER, PROC_REF(apply_colour))
/// Has a more limited spell pool but can appear from gold slime cores
@@ -67,8 +67,7 @@
/datum/ai_behavior/basic_melee_attack/faithless/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key)
. = ..()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/target = weak_target?.resolve()
var/atom/target = controller.blackboard[target_key]
var/mob/living/living_pawn = controller.pawn
if(!isliving(target))
@@ -119,13 +119,8 @@
/mob/living/basic/garden_gnome/proc/ai_retaliate_behaviour(mob/living/attacker)
if (!istype(attacker))
return
var/list/enemy_refs
for (var/mob/living/basic/garden_gnome/potential_gnome in oview(src, 7))
enemy_refs = potential_gnome.ai_controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST]
if (!enemy_refs)
enemy_refs = list()
enemy_refs |= WEAKREF(attacker)
potential_gnome.ai_controller.blackboard[BB_BASIC_MOB_RETALIATE_LIST] = enemy_refs
potential_gnome.ai_controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, attacker)
/datum/ai_controller/basic_controller/garden_gnome
blackboard = list(
@@ -73,7 +73,7 @@
var/datum/action/cooldown/lay_web/webbing = new web_type(src)
webbing.webbing_time *= web_speed
webbing.Grant(src)
ai_controller.blackboard[BB_SPIDER_WEB_ACTION] = WEAKREF(webbing)
ai_controller.set_blackboard_key(BB_SPIDER_WEB_ACTION, webbing)
/mob/living/basic/giant_spider/Login()
. = ..()
@@ -15,16 +15,15 @@
/datum/ai_behavior/find_unwebbed_turf/perform(seconds_per_tick, datum/ai_controller/controller)
. = ..()
var/mob/living/spider = controller.pawn
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/current_target = weak_target?.resolve()
var/atom/current_target = controller.blackboard[target_key]
if (current_target && !(locate(/obj/structure/spider/stickyweb) in current_target))
finish_action(controller, succeeded = FALSE) // Already got a target
return
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
var/turf/our_turf = get_turf(spider)
if (is_valid_web_turf(our_turf))
controller.blackboard[target_key] = WEAKREF(our_turf)
controller.set_blackboard_key(target_key, our_turf)
finish_action(controller, succeeded = TRUE)
return
@@ -38,7 +37,7 @@
finish_action(controller, succeeded = FALSE)
return
controller.blackboard[target_key] = WEAKREF(get_closest_atom(/turf/, potential_turfs, our_turf))
controller.set_blackboard_key(target_key, get_closest_atom(/turf/, potential_turfs, our_turf))
finish_action(controller, succeeded = TRUE)
/datum/ai_behavior/find_unwebbed_turf/proc/is_valid_web_turf(turf/target_turf, mob/living/spider)
@@ -54,11 +53,9 @@
var/target_key = BB_SPIDER_WEB_TARGET
/datum/ai_planning_subtree/spin_web/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/datum/weakref/weak_action = controller.blackboard[action_key]
var/datum/action/cooldown/using_action = weak_action?.resolve()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/turf/target_turf = weak_target?.resolve()
if (!using_action || !target_turf)
var/datum/action/cooldown/using_action = controller.blackboard[action_key]
var/turf/target_turf = controller.blackboard[target_key]
if (QDELETED(using_action) || QDELETED(target_turf))
return
controller.queue_behavior(/datum/ai_behavior/spin_web, action_key, target_key)
return SUBTREE_RETURN_FINISH_PLANNING
@@ -70,10 +67,8 @@
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
/datum/ai_behavior/spin_web/setup(datum/ai_controller/controller, action_key, target_key)
var/datum/weakref/weak_action = controller.blackboard[action_key]
var/datum/action/cooldown/web_action = weak_action?.resolve()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/turf/target_turf = weak_target?.resolve()
var/datum/action/cooldown/web_action = controller.blackboard[action_key]
var/turf/target_turf = controller.blackboard[target_key]
if (!web_action || !target_turf)
return FALSE
@@ -82,10 +77,9 @@
/datum/ai_behavior/spin_web/perform(seconds_per_tick, datum/ai_controller/controller, action_key, target_key)
. = ..()
var/datum/weakref/weak_action = controller.blackboard[action_key]
var/datum/action/cooldown/web_action = weak_action?.resolve()
var/datum/action/cooldown/web_action = controller.blackboard[action_key]
finish_action(controller, succeeded = web_action?.Trigger(), action_key = action_key, target_key = target_key)
/datum/ai_behavior/spin_web/finish_action(datum/ai_controller/controller, succeeded, action_key, target_key)
controller.blackboard[target_key] = null
controller.clear_blackboard_key(target_key)
return ..()
@@ -40,11 +40,11 @@
spikes = new(src)
spikes.Grant(src)
ai_controller.blackboard[BB_METEOR_HEART_GROUND_SPIKES] = WEAKREF(spikes)
ai_controller.set_blackboard_key(BB_METEOR_HEART_GROUND_SPIKES, spikes)
traps = new(src)
traps.Grant(src)
ai_controller.blackboard[BB_METEOR_HEART_SPINE_TRAPS] = WEAKREF(traps)
ai_controller.set_blackboard_key(BB_METEOR_HEART_SPINE_TRAPS, traps)
ai_controller.set_ai_status(AI_STATUS_OFF)
@@ -65,7 +65,7 @@
fugu.melee_damage_upper = 20
fugu.status_flags |= GODMODE
fugu.obj_damage = 60
fugu.ai_controller.blackboard[BB_BASIC_MOB_FLEEING] = FALSE
fugu.ai_controller.set_blackboard_key(BB_BASIC_MOB_FLEEING, FALSE)
fugu.ai_controller.CancelActions()
/datum/status_effect/inflated/on_remove()
@@ -84,7 +84,7 @@
if (fugu.stat != DEAD)
fugu.icon_state = "Fugu0"
fugu.obj_damage = 0
fugu.ai_controller.blackboard[BB_BASIC_MOB_FLEEING] = TRUE
fugu.ai_controller.set_blackboard_key(BB_BASIC_MOB_FLEEING, TRUE)
fugu.ai_controller.CancelActions()
/// Remove status effect if we die
@@ -53,7 +53,7 @@
add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE), ROUNDSTART_TRAIT)
expand = new(src)
expand.Grant(src)
ai_controller.blackboard[BB_FUGU_INFLATE] = WEAKREF(expand)
ai_controller.set_blackboard_key(BB_FUGU_INFLATE, expand)
/mob/living/basic/wumborian_fugu/Destroy()
QDEL_NULL(expand)
@@ -379,8 +379,7 @@
/datum/ai_planning_subtree/flee_target/mouse
/datum/ai_planning_subtree/flee_target/mouse/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/datum/weakref/hunting_weakref = controller.blackboard[BB_CURRENT_HUNTING_TARGET]
var/atom/hunted_cheese = hunting_weakref?.resolve()
var/atom/hunted_cheese = controller.blackboard[BB_CURRENT_HUNTING_TARGET]
if (!isnull(hunted_cheese))
return // We see some cheese, which is more important than our life
return ..()
+4 -12
View File
@@ -2454,12 +2454,8 @@ GLOBAL_LIST_EMPTY(fire_appearances)
if (faction.Find(friend_ref))
return FALSE
faction |= friend_ref
if (ai_controller)
var/list/friends = ai_controller.blackboard[BB_FRIENDS_LIST]
if (!friends)
friends = list()
friends[WEAKREF(new_friend)] = TRUE
ai_controller.blackboard[BB_FRIENDS_LIST] = friends
ai_controller?.insert_blackboard_key_lazylist(BB_FRIENDS_LIST, new_friend)
SEND_SIGNAL(src, COMSIG_LIVING_BEFRIENDED, new_friend)
return TRUE
@@ -2470,12 +2466,8 @@ GLOBAL_LIST_EMPTY(fire_appearances)
if (!faction.Find(friend_ref))
return FALSE
faction -= friend_ref
if (ai_controller)
var/list/friends = ai_controller.blackboard[BB_FRIENDS_LIST]
if (!friends)
return
friends[WEAKREF(old_friend)] = FALSE
ai_controller.blackboard[BB_FRIENDS_LIST] = friends
ai_controller?.remove_thing_from_blackboard_key(BB_FRIENDS_LIST, old_friend)
SEND_SIGNAL(src, COMSIG_LIVING_UNFRIENDED, old_friend)
return TRUE
@@ -29,9 +29,9 @@
clothes_set = pick(customer_info.clothing_sets)
ai_controller = customer_info.ai_controller_used
. = ..()
ai_controller.blackboard[BB_CUSTOMER_CUSTOMERINFO] = customer_info
ai_controller.blackboard[BB_CUSTOMER_ATTENDING_VENUE] = attending_venue
ai_controller.blackboard[BB_CUSTOMER_PATIENCE] = customer_info.total_patience
ai_controller.set_blackboard_key(BB_CUSTOMER_CUSTOMERINFO, customer_info)
ai_controller.set_blackboard_key(BB_CUSTOMER_ATTENDING_VENUE, attending_venue)
ai_controller.set_blackboard_key(BB_CUSTOMER_PATIENCE, customer_info.total_patience)
icon = customer_info.base_icon
icon_state = customer_info.base_icon_state
name = "[pick(customer_info.name_prefixes)]-bot"
@@ -41,9 +41,8 @@
///Clean up on the mobs seat etc when its deleted (Either by murder or because it left)
/mob/living/simple_animal/robot_customer/Destroy()
var/datum/venue/attending_venue = ai_controller.blackboard[BB_CUSTOMER_ATTENDING_VENUE]
var/obj/structure/holosign/robot_seat/our_seat = ai_controller.blackboard[BB_CUSTOMER_MY_SEAT]
attending_venue.current_visitors -= src
var/datum/weakref/seat_ref = ai_controller.blackboard[BB_CUSTOMER_MY_SEAT]
var/obj/structure/holosign/robot_seat/our_seat = seat_ref?.resolve()
if(attending_venue.linked_seats[our_seat])
attending_venue.linked_seats[our_seat] = null
QDEL_NULL(hud_to_show_on_hover)
@@ -90,6 +89,7 @@
/mob/living/simple_animal/robot_customer/examine(mob/user)
. = ..()
// this should be handled by the ai controller
if(ai_controller.blackboard[BB_CUSTOMER_CURRENT_ORDER])
var/datum/venue/attending_venue = ai_controller.blackboard[BB_CUSTOMER_ATTENDING_VENUE]
var/wanted_item = ai_controller.blackboard[BB_CUSTOMER_CURRENT_ORDER]
@@ -110,8 +110,8 @@
var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(module.mod)
module.mod.ai_controller = mod_ai
mod_ai.set_movement_target(type, imp_in)
mod_ai.blackboard[BB_MOD_TARGET] = imp_in
mod_ai.blackboard[BB_MOD_IMPLANT] = src
mod_ai.set_blackboard_key(BB_MOD_TARGET, imp_in)
mod_ai.set_blackboard_key(BB_MOD_IMPLANT, src)
module.mod.interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
module.mod.AddElement(/datum/element/movetype_handler)
ADD_TRAIT(module.mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
@@ -44,6 +44,7 @@
desc = "These cybernetic eye implants will display a security HUD over everything you see."
HUD_type = DATA_HUD_SECURITY_ADVANCED
HUD_trait = TRAIT_SECURITY_HUD
organ_flags = ALL
/obj/item/organ/internal/cyberimp/eyes/hud/diagnostic
name = "Diagnostic HUD implant"
+1 -1
View File
@@ -22,5 +22,5 @@
new /datum/ai_controller/monkey/angry(monkey)
else
new /datum/ai_controller/monkey(monkey)
monkey.ai_controller.blackboard[BB_MONKEY_TARGET_MONKEYS] = TRUE
monkey.ai_controller.set_blackboard_key(BB_MONKEY_TARGET_MONKEYS, TRUE)
sleep(monkey_timer)
+1 -1
View File
@@ -24,7 +24,7 @@
// Process behavior - this will execute the "locate the cable" behavior
biter.ai_controller.process(fake_dt)
// Check that the cable was found
TEST_ASSERT(biter.ai_controller.blackboard[BB_LOW_PRIORITY_HUNTING_TARGET] == WEAKREF(wire), "Mouse, after executing find, did not set the cable as a target.")
TEST_ASSERT(biter.ai_controller.blackboard[BB_LOW_PRIORITY_HUNTING_TARGET] == wire, "Mouse, after executing find, did not set the cable as a target.")
// Select behavior - this will queue hunting
biter.ai_controller.SelectBehaviors(fake_dt)
// Process behavior - this will execute the hunt for the cable and cause a bite (as we're in the min range)