mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-26 14:39:58 +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:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user