mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-27 07:08:00 +01:00
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:
@@ -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)
|
||||
|
||||
@@ -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 +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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user