mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-29 16:18:01 +01:00
## About The Pull Request 1. Simple bots use Manual Emotes like Basic Bots 2. Re-ups the CD for emoting to commission bots to 60 seconds, as it was originally with Simple Bots 3. Punctuates the manual emotes 4. Deletes the fist bump message ## Why It's Good For The Game 1. Consistency. 2. These messages get a little... a lot spammy when walking around the halls, especially if Robotics has made a few additional bots. The original implementation had a CD of 60 seconds, which the AI behavior halved. Upping it back up for consistency + less spam. 3. Looks a little less shoddy. 4. It's a cute message, but it's completely out of character for what the bots are supposed to be "doing". They're "saluting" their "commissioned officer", not meeting up with the bros. Also they don't have fists! I understand saluting with their "robot appendage" but they don't punch people! ## Changelog 🆑 Melbert qol: Basic Bots will use their "salute" emotes less often. del: Basic bot fists. /🆑
240 lines
9.0 KiB
Plaintext
240 lines
9.0 KiB
Plaintext
/datum/ai_controller/basic_controller/bot
|
|
blackboard = list(
|
|
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
|
BB_SALUTE_MESSAGES = list(
|
|
"performs an elaborate salute for",
|
|
"nods in appreciation towards",
|
|
),
|
|
BB_UNREACHABLE_LIST_COOLDOWN = 45 SECONDS,
|
|
)
|
|
|
|
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, bypass_add_to_blacklist = FALSE)
|
|
if(can_reach_target(target, distance))
|
|
set_blackboard_key(key, target)
|
|
return TRUE
|
|
if(!bypass_add_to_blacklist)
|
|
set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, REF(target), 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
|
|
|
|
/// 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)
|
|
if(isnull(controller.blackboard[BB_UNREACHABLE_LIST_COOLDOWN]) || controller.blackboard[BB_CLEAR_LIST_READY] > world.time)
|
|
return
|
|
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
|
|
|
|
/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_behavior/manage_unreachable_list/finish_action(datum/ai_controller/controller, succeeded)
|
|
. = ..()
|
|
controller.set_blackboard_key(BB_CLEAR_LIST_READY, controller.blackboard[BB_UNREACHABLE_LIST_COOLDOWN] + world.time)
|
|
|
|
/datum/ai_planning_subtree/find_patrol_beacon
|
|
///travel towards beacon behavior
|
|
var/travel_behavior = /datum/ai_behavior/travel_towards/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(travel_behavior, 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]"])
|
|
var/dist = get_dist(bot_pawn, beacon)
|
|
if(beacon == previous_target || dist <= 1)
|
|
continue
|
|
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
|
|
new_movement_type = /datum/ai_movement/jps/bot/travel_to_beacon
|
|
|
|
/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
|
|
new_movement_type = /datum/ai_movement/jps/bot/travel_to_beacon
|
|
|
|
/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 = BOT_COMMISSIONED_SALUTE_DELAY
|
|
|
|
/datum/ai_behavior/find_and_set/valid_authority/search_tactic(datum/ai_controller/controller, locate_path, search_range)
|
|
for(var/mob/living/nearby_mob in oview(search_range, controller.pawn))
|
|
if(!HAS_TRAIT(nearby_mob, TRAIT_COMMISSIONED))
|
|
continue
|
|
return nearby_mob
|
|
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)
|