Files
Bubberstation/code/datums/ai/robot_customer/robot_customer_controller.dm
SkyratBot e6f66d3a4a [MIRROR] Experiment with replacing weakrefs in AI blackboard with deleting signals, ideally making it easier to work with and harder to cause hard deletes [MDB IGNORE] (#20719)
* 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
/🆑

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

---------

Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
2023-04-26 21:17:15 +01:00

120 lines
4.6 KiB
Plaintext

/datum/ai_controller/robot_customer
ai_movement = /datum/ai_movement/basic_avoidance
movement_delay = 0.8 SECONDS
blackboard = list(BB_CUSTOMER_CURRENT_ORDER = null,
BB_CUSTOMER_MY_SEAT = null,
BB_CUSTOMER_PATIENCE = 999,
BB_CUSTOMER_CUSTOMERINFO = null,
BB_CUSTOMER_EATING = FALSE,
BB_CUSTOMER_LEAVING = FALSE,
BB_CUSTOMER_ATTENDING_VENUE = null,
BB_CUSTOMER_SAID_CANT_FIND_SEAT_LINE = FALSE)
planning_subtrees = list(/datum/ai_planning_subtree/robot_customer)
/datum/ai_controller/robot_customer/Destroy()
// clear possible datum refs
clear_blackboard_key(BB_CUSTOMER_CURRENT_ORDER)
clear_blackboard_key(BB_CUSTOMER_CUSTOMERINFO)
return ..()
/datum/ai_controller/robot_customer/TryPossessPawn(atom/new_pawn)
if(!istype(new_pawn, /mob/living/simple_animal/robot_customer))
return AI_CONTROLLER_INCOMPATIBLE
new_pawn.AddElement(/datum/element/relay_attackers)
RegisterSignal(new_pawn, COMSIG_PARENT_ATTACKBY, PROC_REF(on_attackby))
RegisterSignal(new_pawn, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked))
RegisterSignal(new_pawn, COMSIG_LIVING_GET_PULLED, PROC_REF(on_get_pulled))
RegisterSignal(new_pawn, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_get_punched))
return ..() //Run parent at end
/datum/ai_controller/robot_customer/UnpossessPawn(destroy)
UnregisterSignal(pawn, list(COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_WAS_ATTACKED, COMSIG_LIVING_GET_PULLED, COMSIG_ATOM_ATTACK_HAND))
return ..() //Run parent at end
/datum/ai_controller/robot_customer/proc/on_attackby(datum/source, obj/item/I, mob/living/user)
SIGNAL_HANDLER
var/datum/venue/attending_venue = blackboard[BB_CUSTOMER_ATTENDING_VENUE]
if(attending_venue.is_correct_order(I, blackboard[BB_CUSTOMER_CURRENT_ORDER]))
to_chat(user, span_notice("You hand [I] to [pawn]."))
eat_order(I, attending_venue)
return COMPONENT_NO_AFTERATTACK
else if(!I.force)
INVOKE_ASYNC(src, PROC_REF(dont_want_that), I, user)
else
INVOKE_ASYNC(src, PROC_REF(warn_greytider), user)
/datum/ai_controller/robot_customer/proc/on_attacked(datum/source, mob/living/attacker)
SIGNAL_HANDLER
INVOKE_ASYNC(src, PROC_REF(warn_greytider), attacker)
/datum/ai_controller/robot_customer/proc/eat_order(obj/item/order_item, datum/venue/attending_venue)
if(blackboard[BB_CUSTOMER_EATING])
return
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)
clear_blackboard_key(BB_CUSTOMER_CURRENT_ORDER)
///Called when
/datum/ai_controller/robot_customer/proc/on_get_pulled(datum/source, mob/living/puller)
SIGNAL_HANDLER
INVOKE_ASYNC(src, PROC_REF(async_on_get_pulled), source, puller)
/datum/ai_controller/robot_customer/proc/async_on_get_pulled(datum/source, mob/living/puller)
var/mob/living/simple_animal/robot_customer/customer = pawn
var/datum/customer_data/customer_data = blackboard[BB_CUSTOMER_CUSTOMERINFO]
var/datum/venue/attending_venue = blackboard[BB_CUSTOMER_ATTENDING_VENUE]
var/obj/item/card/id/used_id = puller.get_idcard(TRUE)
if(used_id && (attending_venue.req_access in used_id?.GetAccess()))
customer.say(customer_data.friendly_pull_line)
return
warn_greytider(puller)
customer.resist()
/datum/ai_controller/robot_customer/proc/dont_want_that(mob/living/chef, obj/item/thing)
var/mob/living/simple_animal/robot_customer/customer = pawn
var/datum/customer_data/customer_data = blackboard[BB_CUSTOMER_CUSTOMERINFO]
customer.say(customer_data.wrong_item_line)
/datum/ai_controller/robot_customer/proc/warn_greytider(mob/living/greytider)
var/mob/living/simple_animal/robot_customer/customer = pawn
var/datum/venue/attending_venue = blackboard[BB_CUSTOMER_ATTENDING_VENUE]
var/datum/customer_data/customer_data = blackboard[BB_CUSTOMER_CUSTOMERINFO]
//Living mobs are tagged, so these will always be valid
attending_venue.mob_blacklist[REF(greytider)] += 1
switch(attending_venue.mob_blacklist[REF(greytider)])
if(1)
customer.say(customer_data.first_warning_line)
return
if(2)
customer.say(customer_data.second_warning_line)
return
if(3)
customer.say(customer_data.self_defense_line)
set_blackboard_key(BB_CUSTOMER_CURRENT_TARGET, greytider)
CancelActions()
/datum/ai_controller/robot_customer/proc/on_get_punched(datum/source, mob/living/living_hitter)
SIGNAL_HANDLER
var/datum/venue/attending_venue = blackboard[BB_CUSTOMER_ATTENDING_VENUE]
var/obj/item/card/id/used_id = living_hitter.get_idcard(hand_first = TRUE)
if(used_id && (attending_venue.req_access in used_id?.GetAccess()))
return
if(living_hitter.combat_mode)
INVOKE_ASYNC(src, PROC_REF(warn_greytider), living_hitter)