mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-17 13:12:37 +00:00
* hygeienbots basic bots (#80435) ## About The Pull Request turns hygenic bots into basic bots. also now PAIs and people can play as hygeinebots. and they can wear hats ## Why It's Good For The Game transforms hyginebots into basic bots. their old AI used to handle all the logic. i moved some of the logic to the mob itself so players can also clean (or burn) things. also this pr will add pathing limits to bots, in the case the jps movement thinks it can reach something, but actually cant, in which case the bot will give up the chase ## Changelog 🆑 refactor: hygeinebots are now basic bots. please report all the bugs fix: fixes hygenebots not being able to patrol add: hygeinebots can now be controlled by Players /🆑 * hygeienbots basic bots --------- Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com>
252 lines
9.1 KiB
Plaintext
252 lines
9.1 KiB
Plaintext
/datum/ai_controller/basic_controller/bot
|
|
blackboard = list(
|
|
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
|
BB_SALUTE_MESSAGES = list(
|
|
"salutes",
|
|
"nods in appreciation towards",
|
|
"fist bumps",
|
|
)
|
|
)
|
|
|
|
ai_movement = /datum/ai_movement/jps/bot
|
|
idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking
|
|
planning_subtrees = list(
|
|
/datum/ai_planning_subtree/respond_to_summon,
|
|
/datum/ai_planning_subtree/salute_authority,
|
|
/datum/ai_planning_subtree/find_patrol_beacon,
|
|
/datum/ai_planning_subtree/manage_unreachable_list,
|
|
)
|
|
max_target_distance = AI_BOT_PATH_LENGTH
|
|
///keys to be reset when the bot is reseted
|
|
var/list/reset_keys = list(
|
|
BB_BEACON_TARGET,
|
|
BB_PREVIOUS_BEACON_TARGET,
|
|
BB_BOT_SUMMON_TARGET,
|
|
)
|
|
///how many times we tried to reach the target
|
|
var/current_pathing_attempts = 0
|
|
///if we cant reach it after this many attempts, add it to our ignore list
|
|
var/max_pathing_attempts = 25
|
|
|
|
/datum/ai_controller/basic_controller/bot/TryPossessPawn(atom/new_pawn)
|
|
. = ..()
|
|
if(. & AI_CONTROLLER_INCOMPATIBLE)
|
|
return
|
|
RegisterSignal(new_pawn, COMSIG_BOT_RESET, PROC_REF(reset_bot))
|
|
|
|
/datum/ai_controller/basic_controller/bot/able_to_run()
|
|
var/mob/living/basic/bot/bot_pawn = pawn
|
|
if(!(bot_pawn.bot_mode_flags & BOT_MODE_ON))
|
|
return FALSE
|
|
return ..()
|
|
|
|
/datum/ai_controller/basic_controller/bot/get_access()
|
|
var/mob/living/basic/bot/basic_bot = pawn
|
|
return basic_bot.access_card?.access
|
|
|
|
/datum/ai_controller/basic_controller/bot/proc/reset_bot()
|
|
SIGNAL_HANDLER
|
|
|
|
if(!length(reset_keys))
|
|
return
|
|
for(var/key in reset_keys)
|
|
clear_blackboard_key(key)
|
|
|
|
///set the target if we can reach them
|
|
/datum/ai_controller/basic_controller/bot/proc/set_if_can_reach(key, target, distance = 10)
|
|
if(can_reach_target(target, distance))
|
|
set_blackboard_key(key, target)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/datum/ai_controller/basic_controller/bot/proc/can_reach_target(target, distance = 10)
|
|
if(!isdatum(target)) //we dont need to check if its not a datum!
|
|
return TRUE
|
|
if(get_turf(pawn) == get_turf(target))
|
|
return TRUE
|
|
var/list/path = get_path_to(pawn, target, max_distance = distance, access = get_access())
|
|
if(!length(path))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
///check if the target is too far away, and delete them if so and add them to the unreachables list
|
|
/datum/ai_controller/basic_controller/bot/proc/reachable_key(key, distance = 10)
|
|
var/datum/target = blackboard[key]
|
|
if(QDELETED(target))
|
|
return FALSE
|
|
var/datum/last_attempt = blackboard[BB_LAST_ATTEMPTED_PATHING]
|
|
if(last_attempt != target)
|
|
current_pathing_attempts = 0
|
|
set_blackboard_key(BB_LAST_ATTEMPTED_PATHING, target)
|
|
else
|
|
current_pathing_attempts++
|
|
if(current_pathing_attempts >= max_pathing_attempts || !can_reach_target(target, distance))
|
|
clear_blackboard_key(key)
|
|
clear_blackboard_key(BB_LAST_ATTEMPTED_PATHING)
|
|
set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// subtree to manage our list of unreachables, we reset it every 15 seconds
|
|
/datum/ai_planning_subtree/manage_unreachable_list
|
|
|
|
/datum/ai_planning_subtree/manage_unreachable_list/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
|
|
controller.queue_behavior(/datum/ai_behavior/manage_unreachable_list, BB_TEMPORARY_IGNORE_LIST)
|
|
|
|
/datum/ai_behavior/manage_unreachable_list
|
|
behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
|
action_cooldown = 45 SECONDS
|
|
|
|
/datum/ai_behavior/manage_unreachable_list/perform(seconds_per_tick, datum/ai_controller/controller, list_key)
|
|
. = ..()
|
|
if(!isnull(controller.blackboard[list_key]))
|
|
controller.clear_blackboard_key(list_key)
|
|
finish_action(controller, TRUE)
|
|
|
|
|
|
/datum/ai_planning_subtree/find_patrol_beacon
|
|
|
|
/datum/ai_planning_subtree/find_patrol_beacon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
|
|
var/mob/living/basic/bot/bot_pawn = controller.pawn
|
|
if(!(bot_pawn.bot_mode_flags & BOT_MODE_AUTOPATROL) || bot_pawn.mode == BOT_SUMMON)
|
|
return
|
|
|
|
if(controller.blackboard_key_exists(BB_BEACON_TARGET))
|
|
bot_pawn.update_bot_mode(new_mode = BOT_PATROL)
|
|
controller.queue_behavior(/datum/ai_behavior/travel_towards/beacon, BB_BEACON_TARGET)
|
|
return
|
|
|
|
if(controller.blackboard_key_exists(BB_PREVIOUS_BEACON_TARGET))
|
|
controller.queue_behavior(/datum/ai_behavior/find_next_beacon_target, BB_BEACON_TARGET)
|
|
return
|
|
|
|
controller.queue_behavior(/datum/ai_behavior/find_first_beacon_target, BB_BEACON_TARGET)
|
|
|
|
/datum/ai_behavior/find_first_beacon_target
|
|
|
|
/datum/ai_behavior/find_first_beacon_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
|
. = ..()
|
|
var/closest_distance = INFINITY
|
|
var/mob/living/basic/bot/bot_pawn = controller.pawn
|
|
var/atom/final_target
|
|
var/atom/previous_target = controller.blackboard[BB_PREVIOUS_BEACON_TARGET]
|
|
for(var/obj/machinery/navbeacon/beacon as anything in GLOB.navbeacons["[bot_pawn.z]"])
|
|
if(beacon == previous_target)
|
|
continue
|
|
var/dist = get_dist(bot_pawn, beacon)
|
|
if(dist > closest_distance)
|
|
continue
|
|
closest_distance = dist
|
|
final_target = beacon
|
|
|
|
if(isnull(final_target))
|
|
finish_action(controller, FALSE)
|
|
return
|
|
controller.set_blackboard_key(BB_BEACON_TARGET, final_target)
|
|
finish_action(controller, TRUE)
|
|
|
|
/datum/ai_behavior/find_next_beacon_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
|
. = ..()
|
|
var/mob/living/basic/bot/bot_pawn = controller.pawn
|
|
var/atom/final_target
|
|
var/obj/machinery/navbeacon/prev_beacon = controller.blackboard[BB_PREVIOUS_BEACON_TARGET]
|
|
if(QDELETED(prev_beacon))
|
|
finish_action(controller, FALSE)
|
|
return
|
|
|
|
for(var/obj/machinery/navbeacon/beacon as anything in GLOB.navbeacons["[bot_pawn.z]"])
|
|
if(beacon.location == prev_beacon.codes[NAVBEACON_PATROL_NEXT])
|
|
final_target = beacon
|
|
break
|
|
|
|
if(isnull(final_target))
|
|
controller.clear_blackboard_key(BB_PREVIOUS_BEACON_TARGET)
|
|
finish_action(controller, FALSE)
|
|
|
|
controller.set_blackboard_key(BB_BEACON_TARGET, final_target)
|
|
finish_action(controller, TRUE)
|
|
|
|
|
|
/datum/ai_behavior/travel_towards/beacon
|
|
clear_target = TRUE
|
|
|
|
/datum/ai_behavior/travel_towards/beacon/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
|
var/atom/target = controller.blackboard[target_key]
|
|
controller.set_blackboard_key(BB_PREVIOUS_BEACON_TARGET, target)
|
|
return ..()
|
|
|
|
/datum/ai_planning_subtree/respond_to_summon
|
|
|
|
/datum/ai_planning_subtree/respond_to_summon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
|
|
if(!controller.blackboard_key_exists(BB_BOT_SUMMON_TARGET))
|
|
return
|
|
controller.clear_blackboard_key(BB_PREVIOUS_BEACON_TARGET)
|
|
controller.clear_blackboard_key(BB_BEACON_TARGET)
|
|
controller.queue_behavior(/datum/ai_behavior/travel_towards/bot_summon, BB_BOT_SUMMON_TARGET)
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|
|
|
|
/datum/ai_behavior/travel_towards/bot_summon
|
|
clear_target = TRUE
|
|
|
|
/datum/ai_behavior/travel_towards/bot_summon/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
|
var/mob/living/basic/bot/bot_pawn = controller.pawn
|
|
if(QDELETED(bot_pawn)) // pawn can be null at this point
|
|
return ..()
|
|
bot_pawn.calling_ai_ref = null
|
|
bot_pawn.update_bot_mode(new_mode = BOT_IDLE)
|
|
return ..()
|
|
|
|
/datum/ai_planning_subtree/salute_authority
|
|
|
|
/datum/ai_planning_subtree/salute_authority/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
|
|
var/mob/living/basic/bot/bot_pawn = controller.pawn
|
|
//we are criminals, dont salute the dirty pigs
|
|
if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)
|
|
return
|
|
if(controller.blackboard_key_exists(BB_SALUTE_TARGET))
|
|
controller.queue_behavior(/datum/ai_behavior/salute_authority, BB_SALUTE_TARGET, BB_SALUTE_MESSAGES)
|
|
return SUBTREE_RETURN_FINISH_PLANNING
|
|
|
|
controller.queue_behavior(/datum/ai_behavior/find_and_set/valid_authority, BB_SALUTE_TARGET)
|
|
|
|
|
|
/datum/ai_behavior/find_and_set/valid_authority
|
|
behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
|
action_cooldown = 30 SECONDS
|
|
|
|
/datum/ai_behavior/find_and_set/valid_authority/search_tactic(datum/ai_controller/controller, locate_path, search_range)
|
|
for(var/mob/living/robot in oview(search_range, controller.pawn))
|
|
if(istype(robot, /mob/living/simple_animal/bot/secbot))
|
|
return robot
|
|
if(!istype(robot, /mob/living/basic/bot/cleanbot))
|
|
continue
|
|
var/mob/living/basic/bot/cleanbot/potential_bot = robot
|
|
if(potential_bot.comissioned)
|
|
return potential_bot
|
|
return null
|
|
|
|
/datum/ai_behavior/salute_authority
|
|
|
|
/datum/ai_behavior/salute_authority/perform(seconds_per_tick, datum/ai_controller/controller, target_key, salute_keys)
|
|
. = ..()
|
|
if(!controller.blackboard_key_exists(target_key))
|
|
finish_action(controller, FALSE, target_key)
|
|
return
|
|
var/list/salute_list = controller.blackboard[salute_keys]
|
|
if(!length(salute_list))
|
|
finish_action(controller, FALSE, target_key)
|
|
return
|
|
var/mob/living/basic/bot/bot_pawn = controller.pawn
|
|
//special interaction if we are wearing a fedora
|
|
var/obj/item/our_hat = (locate(/obj/item/clothing/head) in bot_pawn)
|
|
if(our_hat)
|
|
salute_list += "tips [our_hat] at "
|
|
|
|
bot_pawn.manual_emote(pick(salute_list) + " [controller.blackboard[target_key]]")
|
|
finish_action(controller, TRUE, target_key)
|
|
return
|
|
|
|
/datum/ai_behavior/salute_authority/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
|
. = ..()
|
|
controller.clear_blackboard_key(target_key)
|