From 173185ea9a2785bfad40a829ae9de8f5b0f97031 Mon Sep 17 00:00:00 2001 From: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:29:20 +0200 Subject: [PATCH] [no gbp] few repairbot fixes and some beacon pathing improvements. (#88144) ## About The Pull Request this fixes some few sentient repairbot-related issues that were reported a few days ago. ive also given sentient repairbots a new simple UI to manage their materials. ![image](https://github.com/user-attachments/assets/bc269769-439e-4aa8-82ca-e433c031fbec) while I was at it, i also fixed bots continuously trying to path to unreachable beacons, which can be quite costly since they're regularly very far away. ## Why It's Good For The Game closes #88067 ## Changelog :cl: fix: repairbots no longer get flashed by their own welder fix: repairbots no longer break glass tables they step on fix: repairbots can no longer flush their own welders fix: fixes some runtimes when emagged repairbots try to deconstruct things fix: fixes sentient repairbots not being able to see or remove their material counts /:cl: --- code/__DEFINES/ai/ai.dm | 2 +- code/__DEFINES/ai/bot_keys.dm | 2 + code/datums/ai/movement/ai_movement_jps.dm | 2 +- code/modules/mob/living/basic/bots/bot_ai.dm | 96 +++++++++------- .../living/basic/bots/cleanbot/cleanbot_ai.dm | 9 +- code/modules/mob/living/basic/bots/dedbot.dm | 1 - .../living/basic/bots/firebot/firebot_ai.dm | 7 +- .../living/basic/bots/honkbots/honkbot_ai.dm | 11 +- .../basic/bots/hygienebot/hygienebot_ai.dm | 5 +- .../mob/living/basic/bots/medbot/medbot_ai.dm | 11 +- .../living/basic/bots/repairbot/repairbot.dm | 42 ++++--- .../bots/repairbot/repairbot_abilities.dm | 66 +++++++++++ .../basic/bots/repairbot/repairbot_ai.dm | 24 ++-- .../living/basic/bots/vibebot/vibebot_ai.dm | 5 +- .../repairbotmanagement/repairbot_smile.dmi | Bin 0 -> 257 bytes .../tgui/interfaces/RepairbotResources.tsx | 106 ++++++++++++++++++ 16 files changed, 299 insertions(+), 90 deletions(-) create mode 100644 icons/ui/repairbotmanagement/repairbot_smile.dmi create mode 100644 tgui/packages/tgui/interfaces/RepairbotResources.tsx diff --git a/code/__DEFINES/ai/ai.dm b/code/__DEFINES/ai/ai.dm index c91f7eb0659..7cb863d07c1 100644 --- a/code/__DEFINES/ai/ai.dm +++ b/code/__DEFINES/ai/ai.dm @@ -19,7 +19,7 @@ ///For JPS pathing, the maximum length of a path we'll try to generate. Should be modularized depending on what we're doing later on #define AI_MAX_PATH_LENGTH 30 // 30 is possibly overkill since by default we lose interest after 14 tiles of distance, but this gives wiggle room for weaving around obstacles -#define AI_BOT_PATH_LENGTH 75 +#define AI_BOT_PATH_LENGTH 60 // How far should we, by default, be looking for interesting things to de-idle? #define AI_DEFAULT_INTERESTING_DIST 10 diff --git a/code/__DEFINES/ai/bot_keys.dm b/code/__DEFINES/ai/bot_keys.dm index 945102d6aa3..c12f48273ee 100644 --- a/code/__DEFINES/ai/bot_keys.dm +++ b/code/__DEFINES/ai/bot_keys.dm @@ -52,6 +52,8 @@ DEFINE_BITFIELD(repairbot_flags, list( #define BB_RADIO_CHANNEL "radio_channel" ///list of unreachable things we will temporarily ignore #define BB_TEMPORARY_IGNORE_LIST "temporary_ignore_list" +///penalty cooldown if we are unable to path to any beacons +#define BB_BOT_BEACON_COOLDOWN "bot_beacon_cooldown" // medbot keys ///the patient we must heal diff --git a/code/datums/ai/movement/ai_movement_jps.dm b/code/datums/ai/movement/ai_movement_jps.dm index ddae792b31c..3781dd7f5dc 100644 --- a/code/datums/ai/movement/ai_movement_jps.dm +++ b/code/datums/ai/movement/ai_movement_jps.dm @@ -52,7 +52,7 @@ /datum/ai_movement/jps/bot/travel_to_beacon maximum_length = AI_BOT_PATH_LENGTH - max_pathing_attempts = 20 + max_pathing_attempts = 10 /datum/ai_movement/jps/modsuit maximum_length = MOD_AI_RANGE diff --git a/code/modules/mob/living/basic/bots/bot_ai.dm b/code/modules/mob/living/basic/bots/bot_ai.dm index dfef7f1e378..f04aed05eaa 100644 --- a/code/modules/mob/living/basic/bots/bot_ai.dm +++ b/code/modules/mob/living/basic/bots/bot_ai.dm @@ -1,3 +1,5 @@ +#define BOT_NO_BEACON_PATH_PENALTY 30 SECONDS + /datum/ai_controller/basic_controller/bot blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, @@ -9,12 +11,10 @@ ) 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 can_idle = FALSE @@ -40,7 +40,7 @@ return ..() var/list/path = get_path_to(living_mob, living_target, mintargetdist = my_controller.minimum_distance, max_distance = 10, access = my_controller.get_access()) if(!length(path) || QDELETED(living_mob)) - my_controller?.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, living_target, TRUE) + my_controller?.add_to_blacklist(living_target) return FALSE return ..() @@ -57,6 +57,16 @@ if(current_movement_target == blackboard[BB_BEACON_TARGET]) source.update_bot_mode(new_mode = BOT_PATROL) +/datum/ai_controller/basic_controller/bot/proc/add_to_blacklist(atom/target, duration) + var/final_duration = duration || blackboard[BB_UNREACHABLE_LIST_COOLDOWN] + set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) + addtimer(CALLBACK(src, PROC_REF(remove_from_blacklist), target), final_duration) + +/datum/ai_controller/basic_controller/bot/proc/remove_from_blacklist(atom/target) + if(QDELETED(target)) + return + remove_from_blackboard_lazylist_key(BB_TEMPORARY_IGNORE_LIST, target) + /datum/ai_controller/basic_controller/bot/proc/clear_summon() SIGNAL_HANDLER @@ -90,12 +100,14 @@ 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) +/datum/ai_controller/basic_controller/bot/proc/set_if_can_reach(key, target, duration, 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, target, TRUE) + if(bypass_add_to_blacklist) + return FALSE + var/final_duration = duration || blackboard[BB_UNREACHABLE_LIST_COOLDOWN] + add_to_blacklist(target, final_duration) return FALSE /datum/ai_controller/basic_controller/bot/proc/can_reach_target(target, distance = 10) @@ -104,29 +116,7 @@ if(get_turf(pawn) == get_turf(target)) return TRUE var/list/path = get_path_to(pawn, target, simulated_only = !HAS_TRAIT(pawn, TRAIT_SPACEWALK), mintargetdist = minimum_distance, 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) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED - -/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) + return (!!length(path)) /datum/ai_planning_subtree/find_patrol_beacon ///travel towards beacon behavior @@ -134,6 +124,10 @@ /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(controller.blackboard[BB_BOT_BEACON_COOLDOWN] > world.time) + return + if(!(bot_pawn.bot_mode_flags & BOT_MODE_AUTOPATROL) || bot_pawn.mode == BOT_SUMMON) return @@ -168,7 +162,10 @@ controller.set_blackboard_key(BB_BEACON_TARGET, final_target) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/find_next_beacon_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) +/datum/ai_behavior/find_next_beacon_target + action_cooldown = 5 SECONDS + +/datum/ai_behavior/find_next_beacon_target/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/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] @@ -181,19 +178,37 @@ break if(isnull(final_target)) - controller.clear_blackboard_key(BB_PREVIOUS_BEACON_TARGET) + controller.clear_blackboard_key(BB_PREVIOUS_BEACON_TARGET) //failed to find the next beacon, search for a first beacon again return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - controller.set_blackboard_key(BB_BEACON_TARGET, final_target) - return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + controller.set_blackboard_key(BB_PREVIOUS_BEACON_TARGET, final_target) + controller.clear_blackboard_key(BB_BEACON_TARGET) + + if(LAZYACCESS(controller.blackboard[BB_TEMPORARY_IGNORE_LIST], final_target) || get_dist(bot_pawn, final_target) > controller.max_target_distance) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED + + if(controller.set_if_can_reach(key = BB_BEACON_TARGET, target = final_target, duration = 3 MINUTES, distance = controller.max_target_distance)) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED + + controller.set_blackboard_key(BB_BOT_BEACON_COOLDOWN, world.time + BOT_NO_BEACON_PATH_PENALTY) + return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED /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) +/datum/ai_behavior/travel_towards/beacon/setup(datum/ai_controller/controller, target_key) + var/atom/target_beacon = controller.blackboard[target_key] + if(LAZYACCESS(controller.blackboard[BB_TEMPORARY_IGNORE_LIST], target_beacon)) + return FALSE + return ..() + +/datum/ai_behavior/travel_towards/beacon/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key) var/atom/target = controller.blackboard[target_key] + if(!succeeded) + controller.set_blackboard_key(BB_BOT_BEACON_COOLDOWN, world.time + BOT_NO_BEACON_PATH_PENALTY) + controller.add_to_blacklist(target, 3 MINUTES) controller.set_blackboard_key(BB_PREVIOUS_BEACON_TARGET, target) return ..() @@ -274,7 +289,7 @@ var/mob/living/living_pawn = controller.pawn var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] - var/list/objects_to_search = turf_search ? spiral_range_turfs(radius, controller.pawn) : oview(radius, controller.pawn) //use range turfs instead of oview when we can for performance + var/list/objects_to_search = turf_search ? RANGE_TURFS(radius, controller.pawn) : oview(radius, controller.pawn) //use range turfs instead of oview when we can for performance for(var/atom/potential_target as anything in objects_to_search) if(QDELETED(living_pawn)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED @@ -284,7 +299,9 @@ continue if(!valid_target(controller, potential_target)) continue - if(controller.set_if_can_reach(target_key, potential_target, distance = pathing_distance, bypass_add_to_blacklist = bypass_add_blacklist)) + if(!can_see(controller.pawn, potential_target, radius)) + continue + if(controller.set_if_can_reach(key = target_key, target = potential_target, distance = pathing_distance, bypass_add_to_blacklist = bypass_add_blacklist)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED @@ -328,13 +345,16 @@ living_pawn.UnarmedAttack(target, proximity_flag = TRUE) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/bot_interact/finish_action(datum/ai_controller/controller, succeeded, target_key) +/datum/ai_behavior/bot_interact/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key) . = ..() var/atom/target = controller.blackboard[target_key] if(clear_target) controller.clear_blackboard_key(target_key) if(!succeeded && !isnull(target)) - controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) + controller.add_to_blacklist(target) /datum/ai_behavior/bot_interact/keep_target clear_target = FALSE + + +#undef BOT_NO_BEACON_PATH_PENALTY diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm index 1fbaa6db2a9..1b6d8400622 100644 --- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm @@ -15,7 +15,6 @@ ) planning_subtrees = list( /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/manage_unreachable_list, /datum/ai_planning_subtree/pet_planning/cleanbot, /datum/ai_planning_subtree/cleaning_subtree, /datum/ai_planning_subtree/befriend_janitors, @@ -70,7 +69,7 @@ /datum/ai_behavior/find_and_set/in_list/clean_targets action_cooldown = 3 SECONDS -/datum/ai_behavior/find_and_set/in_list/clean_targets/search_tactic(datum/ai_controller/controller, locate_paths, search_range) +/datum/ai_behavior/find_and_set/in_list/clean_targets/search_tactic(datum/ai_controller/basic_controller/bot/controller, locate_paths, search_range) var/list/found = typecache_filter_list(oview(search_range, controller.pawn), locate_paths) var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] for(var/atom/found_item in found) @@ -82,7 +81,7 @@ return found_item var/list/path = get_path_to(controller.pawn, found_item, max_distance = BOT_CLEAN_PATH_LIMIT, access = controller.get_access()) if(!length(path)) - controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, found_item, TRUE) + controller.add_to_blacklist(found_item) continue return found_item @@ -132,13 +131,13 @@ living_pawn.UnarmedAttack(target, proximity_flag = TRUE) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/execute_clean/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) +/datum/ai_behavior/execute_clean/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) . = ..() controller.set_blackboard_key(BB_POST_CLEAN_COOLDOWN, POST_CLEAN_COOLDOWN + world.time) var/atom/target = controller.blackboard[target_key] if(!succeeded && !isnull(target)) controller.clear_blackboard_key(target_key) - controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) + controller.add_to_blacklist(target) return if(QDELETED(target) || is_type_in_typecache(target, controller.blackboard[BB_HUNTABLE_TRASH])) return diff --git a/code/modules/mob/living/basic/bots/dedbot.dm b/code/modules/mob/living/basic/bots/dedbot.dm index 1b077af343f..0560f02a562 100644 --- a/code/modules/mob/living/basic/bots/dedbot.dm +++ b/code/modules/mob/living/basic/bots/dedbot.dm @@ -63,7 +63,6 @@ /datum/ai_planning_subtree/targeted_mob_ability/exenterate, /datum/ai_planning_subtree/respond_to_summon, /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 diff --git a/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm b/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm index 31127ecdfb4..fa66c6d794d 100644 --- a/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm +++ b/code/modules/mob/living/basic/bots/firebot/firebot_ai.dm @@ -3,11 +3,10 @@ /datum/ai_controller/basic_controller/bot/firebot blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_turfs, - BB_UNREACHABLE_LIST_COOLDOWN = 45 SECONDS, + BB_UNREACHABLE_LIST_COOLDOWN = 3 MINUTES, ) planning_subtrees = list( /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/manage_unreachable_list, /datum/ai_planning_subtree/extinguishing_people, /datum/ai_planning_subtree/extinguishing_turfs, /datum/ai_planning_subtree/salute_authority, @@ -99,7 +98,7 @@ continue if(LAZYACCESS(ignore_list, possible_turf)) continue - if(controller.set_if_can_reach(target_key, possible_turf, bypass_add_to_blacklist = bypass_add_blacklist)) + if(controller.set_if_can_reach(key = target_key, target = possible_turf, bypass_add_to_blacklist = bypass_add_blacklist)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED @@ -113,7 +112,7 @@ //if we couldnt path, or we successfully burnt someone, ignore them for a bit! if(!succeeded || (isliving(target) && (living_bot.bot_access_flags & BOT_COVER_EMAGGED))) - controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) + controller.add_to_blacklist(target) return ..() diff --git a/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm b/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm index f8d4f55150d..7801d64a92a 100644 --- a/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm +++ b/code/modules/mob/living/basic/bots/honkbots/honkbot_ai.dm @@ -7,7 +7,6 @@ planning_subtrees = list( /datum/ai_planning_subtree/respond_to_summon, /datum/ai_planning_subtree/use_mob_ability/random_honk, - /datum/ai_planning_subtree/manage_unreachable_list, /datum/ai_planning_subtree/find_wanted_targets, /datum/ai_planning_subtree/troll_target, /datum/ai_planning_subtree/slip_victims, @@ -41,7 +40,7 @@ return var/atom/slip_target = blackboard[BB_SLIP_TARGET] - set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, slip_target, TRUE) + add_to_blacklist(slip_target) clear_blackboard_key(BB_SLIP_TARGET) /datum/ai_planning_subtree/find_wanted_targets @@ -139,12 +138,12 @@ living_pawn.emote("beep") return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/play_with_clown/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) +/datum/ai_behavior/play_with_clown/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) . = ..() var/mob/living/living_target = controller.blackboard[target_key] if(QDELETED(living_target)) return - controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, living_target, TRUE) + controller.add_to_blacklist(living_target) controller.clear_blackboard_key(target_key) /datum/ai_planning_subtree/slip_victims/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) @@ -204,14 +203,14 @@ our_pawn.stop_pulling() return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED -/datum/ai_behavior/drag_to_slip/finish_action(datum/ai_controller/controller, success, slip_target, slippery_target) +/datum/ai_behavior/drag_to_slip/finish_action(datum/ai_controller/basic_controller/bot/controller, success, slip_target, slippery_target) . = ..() if(success) var/mob/living/living_pawn = controller.pawn living_pawn.emote("flip") var/atom/slipped_victim = controller.blackboard[slip_target] if(!isnull(slipped_victim)) - controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, slipped_victim, TRUE) + controller.add_to_blacklist(slipped_victim) controller.clear_blackboard_key(slip_target) controller.clear_blackboard_key(slippery_target) diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm index f678843c7cc..60f3c80c3ea 100644 --- a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm +++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm @@ -10,7 +10,6 @@ BB_WASH_FRUSTRATION = 0, ) planning_subtrees = list( - /datum/ai_planning_subtree/manage_unreachable_list, /datum/ai_planning_subtree/respond_to_summon, /datum/ai_planning_subtree/handle_trash_talk, /datum/ai_planning_subtree/wash_people, @@ -66,7 +65,7 @@ /datum/ai_behavior/find_valid_wash_targets action_cooldown = 5 SECONDS -/datum/ai_behavior/find_valid_wash_targets/perform(seconds_per_tick, datum/ai_controller/controller, target_key, our_access_flags) +/datum/ai_behavior/find_valid_wash_targets/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, our_access_flags) . = ..() var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] var/atom/found_target @@ -82,7 +81,7 @@ continue if(our_access_flags & BOT_COVER_EMAGGED) - controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, wash_potential, TRUE) + controller.add_to_blacklist(wash_potential) found_target = wash_potential break diff --git a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm index 2fe705433a1..b31a58f912c 100644 --- a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm +++ b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm @@ -1,7 +1,6 @@ #define BOT_PATIENT_PATH_LIMIT 20 /datum/ai_controller/basic_controller/bot/medbot planning_subtrees = list( - /datum/ai_planning_subtree/manage_unreachable_list, /datum/ai_planning_subtree/respond_to_summon, /datum/ai_planning_subtree/handle_medbot_speech, /datum/ai_planning_subtree/find_and_hunt_target/patients_in_crit, @@ -59,15 +58,15 @@ if(LAZYACCESS(ignore_keys, treatable_target) || treatable_target.stat == DEAD) continue if((access_flags & BOT_COVER_EMAGGED) && treatable_target.stat == CONSCIOUS) - controller.set_if_can_reach(BB_PATIENT_TARGET, treatable_target, distance =BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) + controller.set_if_can_reach(key = BB_PATIENT_TARGET, target = treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) break if((heal_type == HEAL_ALL_DAMAGE)) if(treatable_target.get_total_damage() > threshold) - controller.set_if_can_reach(BB_PATIENT_TARGET, treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) + controller.set_if_can_reach(key = BB_PATIENT_TARGET, target = treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) break continue if(treatable_target.get_current_damage_of_type(damagetype = heal_type) > threshold) - controller.set_if_can_reach(BB_PATIENT_TARGET, treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) + controller.set_if_can_reach(key = BB_PATIENT_TARGET, target = treatable_target, distance = BOT_PATIENT_PATH_LIMIT, bypass_add_to_blacklist = (search_range == 1)) break if(controller.blackboard_key_exists(BB_PATIENT_TARGET)) @@ -107,13 +106,13 @@ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED // only clear the target if they get healed -/datum/ai_behavior/tend_to_patient/finish_action(datum/ai_controller/controller, succeeded, target_key, threshold, damage_type_healer, access_flags, is_stationary) +/datum/ai_behavior/tend_to_patient/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, target_key, threshold, damage_type_healer, access_flags, is_stationary) . = ..() var/atom/target = controller.blackboard[target_key] if(!succeeded) if(!isnull(target) && !is_stationary) - controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) + controller.add_to_blacklist(target) controller.clear_blackboard_key(target_key) return diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot.dm index 89dad9b4f35..56ba7ef4ebe 100644 --- a/code/modules/mob/living/basic/bots/repairbot/repairbot.dm +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot.dm @@ -6,7 +6,6 @@ icon_state = "repairbot_base" base_icon_state = "repairbot_base" pass_flags = parent_type::pass_flags | PASSTABLE - density = FALSE layer = BELOW_MOB_LAYER anchored = FALSE health = 100 @@ -20,6 +19,8 @@ bot_type = REPAIR_BOT additional_access = /datum/id_trim/job/station_engineer ai_controller = /datum/ai_controller/basic_controller/bot/repairbot + mob_size = MOB_SIZE_SMALL + possessed_message = "You are a repairbot, cursed to prolong the swiss-cheesening of this death metal trap!" ///our iron stack var/obj/item/stack/sheet/iron/our_iron ///our glass stack @@ -43,8 +44,8 @@ /obj/item/stack/sheet/glass = typecacheof(list(/obj/structure/grille)), ) var/static/list/possible_tool_interactions = list( - /obj/item/weldingtool/repairbot = typecacheof(list(/obj/machinery, /obj/structure/window)), - /obj/item/crowbar = typecacheof(list(/turf/open/floor)), + /obj/item/weldingtool/repairbot = typecacheof(list(/obj/structure/window)), + /obj/item/crowbar = typecacheof(list(/obj/machinery/door, /turf/open/floor)), ) ///our neutral voicelines var/static/list/neutral_voicelines = list( @@ -81,6 +82,7 @@ ai_controller.set_blackboard_key(BB_REPAIRBOT_NORMAL_SPEECH, neutral_voicelines) var/static/list/abilities = list( /datum/action/cooldown/mob_cooldown/bot/build_girder = BB_GIRDER_BUILD_ABILITY, + /datum/action/repairbot_resources = null, ) grant_actions_by_list(abilities) add_traits(list(TRAIT_SPACEWALK, TRAIT_NEGATES_GRAVITY, TRAIT_MOB_MERGE_STACKS, TRAIT_FIREDOOR_OPENER), INNATE_TRAIT) @@ -114,6 +116,7 @@ user?.balloon_alert(user, "full!") return if(!our_sheet.can_merge(potential_stack)) + user?.balloon_alert(user, "not suitable!") return var/atom/movable/to_move = potential_stack.split_stack(user, min(our_sheet.max_amount - our_sheet.amount, potential_stack.amount)) to_move.forceMove(src) @@ -150,12 +153,12 @@ if(istype(target, /turf/open/space)) var/turf/open/space/space_target = target if(!space_target.has_valid_support() && !(locate(/obj/structure/lattice) in space_target)) - our_rods?.melee_attack_chain(src, space_target) + attempt_use_stack(our_rods ? our_rods : our_rods::name, space_target) if(istype(target, /obj/structure/grille)) var/obj/structure/grille/grille_target = target if(grille_target.broken) - our_rods?.melee_attack_chain(src, grille_target) + attempt_use_stack(our_rods ? our_rods : our_rods::name, grille_target) if(istype(target, /turf/open)) var/turf/open/open_target = target @@ -168,13 +171,11 @@ our_screwdriver?.melee_attack_chain(src, target_window) //stack interactions - for(var/type in possible_stack_interactions) - var/obj/item/target_stack = locate(type) in src - if(isnull(target_stack)) + for(var/obj/item/stack/stack_type as anything in possible_stack_interactions) + if(!is_type_in_typecache(target, possible_stack_interactions[stack_type])) continue - if(!is_type_in_typecache(target, possible_stack_interactions[type])) - continue - target_stack.melee_attack_chain(src, target) + var/obj/item/target_stack = locate(stack_type) in src + attempt_use_stack(target_stack ? target_stack : stack_type::name, target) return //tool interactions @@ -186,7 +187,7 @@ /mob/living/basic/bot/repairbot/proc/emagged_interactions(atom/target, modifiers) if(!istype(target, /mob/living/silicon/robot)) - deconstruction_device.interact_with_atom_secondary(target, src, modifiers) + deconstruction_device?.interact_with_atom_secondary(target, src, modifiers) return if(HAS_TRAIT(target, TRAIT_MOB_TIPPED)) return @@ -200,6 +201,16 @@ if(pulling) setGrabState(GRAB_AGGRESSIVE) //automatically aggro grab everything! +/mob/living/basic/bot/repairbot/proc/attempt_use_stack(obj/item/stack_to_use, atom/target) + if(!isdatum(stack_to_use)) + to_chat(src, span_warning("You do not have anymore [stack_to_use]!")) + return + stack_to_use.melee_attack_chain(src, target) + +/mob/living/basic/bot/repairbot/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, length = 25) + if(affect_silicon) + return ..() + /mob/living/basic/bot/repairbot/Destroy() . = ..() QDEL_NULL(our_iron) @@ -230,8 +241,9 @@ return ..() /mob/living/basic/bot/repairbot/process(seconds_per_tick) //generate 1 iron rod every 2 seconds - if(isnull(our_rods) || our_rods.amount < our_rods.max_amount) - new /obj/item/stack/rods(src) + if(!isnull(our_rods) && our_rods.amount >= our_rods.max_amount) + var/obj/item/stack/rods/new_rods = new() + new_rods.forceMove(src) /mob/living/basic/bot/repairbot/turn_on() . = ..() @@ -264,7 +276,7 @@ /mob/living/basic/bot/repairbot/generate_speak_list() return neutral_voicelines + emagged_voicelines -/mob/living/basic/bot/repairbot/Bumped(atom/movable/bumped_object) +/mob/living/basic/bot/repairbot/Bump(atom/movable/bumped_object) . = ..() if(istype(bumped_object, /obj/machinery/door/firedoor) && bumped_object.density) our_crowbar.melee_attack_chain(src, bumped_object) diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot_abilities.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot_abilities.dm index fa8d4970634..ff6354744ea 100644 --- a/code/modules/mob/living/basic/bots/repairbot/repairbot_abilities.dm +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot_abilities.dm @@ -3,6 +3,8 @@ /datum/action/cooldown/mob_cooldown/bot/build_girder name = "Build Girder" desc = "Use iron rods to build a girder!" + button_icon = 'icons/obj/structures.dmi' + button_icon_state = "girder" cooldown_time = 3 SECONDS click_to_activate = TRUE @@ -40,4 +42,68 @@ qdel(effect) return TRUE +/datum/action/repairbot_resources + name = "Resources" + desc = "Manage your resources." + button_icon = 'icons/obj/stack_objects.dmi' + button_icon_state = "sheet-metal_3" + background_icon_state = "bg_tech_blue" + overlay_icon_state = "bg_tech_blue_border" + ///things we arent allowed to eject + var/static/list/eject_blacklist = typecacheof(list( + /obj/item/stack/rods, + )) + +/datum/action/repairbot_resources/Trigger(trigger_flags) + . = ..() + if(!.) + return + ui_interact(owner) + +/datum/action/repairbot_resources/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "RepairbotResources") + ui.open() + +/datum/action/repairbot_resources/ui_state(mob/user) + return GLOB.always_state + +/datum/action/repairbot_resources/ui_data(mob/user) + var/list/data = list() + data["stacks"] = list() + for(var/obj/item/stack/managed_stack in user.contents) + data["stacks"] += list(list( + "stack_reference" = REF(managed_stack), + "stack_name" = managed_stack.name, + "stack_amount" = managed_stack.amount, + "stack_maximum_amount" = managed_stack.max_amount, + "stack_icon" = managed_stack.icon, + "stack_icon_state" = managed_stack.icon_state, + )) + + return data + +/datum/action/repairbot_resources/ui_static_data(mob/user) + var/list/data = list() + data["repairbot_icon"] = 'icons/ui/repairbotmanagement/repairbot_smile.dmi' + data["repairbot_icon_state"] = "repairbot_smile" + return data + + +/datum/action/repairbot_resources/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + switch(action) + if("eject") + var/atom/movable/my_sheet = locate(params["item_reference"]) in owner.contents + if(isnull(my_sheet)) + return + if(is_type_in_typecache(my_sheet, eject_blacklist)) + to_chat(owner, span_warning("You're unable to eject [my_sheet]!")) + return + + my_sheet.forceMove(owner.drop_location()) + #undef BUILDING_WALL_ABILITY diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm index 1793f0b65c2..a952130bb4f 100644 --- a/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm +++ b/code/modules/mob/living/basic/bots/repairbot/repairbot_ai.dm @@ -3,7 +3,6 @@ /datum/ai_controller/basic_controller/bot/repairbot planning_subtrees = list( /datum/ai_planning_subtree/repairbot_speech, - /datum/ai_planning_subtree/manage_unreachable_list, /datum/ai_planning_subtree/mug_robot, /datum/ai_planning_subtree/refill_materials, /datum/ai_planning_subtree/repairbot_deconstruction, @@ -82,6 +81,7 @@ return SUBTREE_RETURN_FINISH_PLANNING /datum/ai_behavior/bot_search/valid_robot + action_cooldown = 10 SECONDS /datum/ai_behavior/bot_search/valid_robot/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) return (!HAS_TRAIT(my_target, TRAIT_MOB_TIPPED)) && can_see(controller.pawn, my_target) @@ -113,6 +113,7 @@ return SUBTREE_RETURN_FINISH_PLANNING /datum/ai_behavior/bot_search/deconstructable + action_cooldown = 5 SECONDS /datum/ai_behavior/bot_search/deconstructable/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target) return (!(my_target.resistance_flags & INDESTRUCTIBLE) && !isgroundlessturf(my_target)) @@ -179,18 +180,27 @@ controller.queue_behavior(search_behavior, floor_key, type_of_turf, 5, 10, FALSE, TRUE) /datum/ai_behavior/bot_search/valid_plateless_turf + action_cooldown = 5 SECONDS /datum/ai_behavior/bot_search/valid_plateless_turf/valid_target(datum/ai_controller/basic_controller/bot/controller, turf/open/my_target) var/static/list/blacklist_objects = typecacheof(list( /obj/structure/window, /obj/structure/grille, )) - for(var/atom/possible_blacklisted as anything in my_target) + + for(var/atom/possible_blacklisted in my_target.contents) if(is_type_in_typecache(possible_blacklisted, blacklist_objects)) return FALSE + if(istype(my_target, /turf/open/floor/plating) && !can_see(controller.pawn, my_target, 5)) return FALSE - return !istype(get_area(my_target), /area/space) + + var/static/list/blacklist_areas = typecacheof(list( + /area/space, + /area/station/maintenance, + )) + var/turf_area = get_area(my_target) + return !(is_type_in_typecache(turf_area, blacklist_areas)) ///subtree to fix hull breaches /datum/ai_planning_subtree/replace_floors/breaches @@ -256,11 +266,7 @@ /datum/ai_behavior/targeted_mob_ability/build_girder/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) . = ..() - var/atom/target = controller.blackboard[target_key] controller.clear_blackboard_key(target_key) - if(!succeeded && !isnull(target)) - controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) - ///subtree to place glass on windows /datum/ai_planning_subtree/replace_window @@ -299,6 +305,9 @@ var/static/list/searchable_girder = typecacheof(list(/obj/structure/girder)) controller.queue_behavior(/datum/ai_behavior/bot_search/valid_girder, BB_GIRDER_TO_WALL_TARGET, searchable_girder) +/datum/ai_behavior/bot_search/valid_girder + action_cooldown = 5 SECONDS + /datum/ai_behavior/bot_search/valid_girder/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/my_target) return isfloorturf(my_target.loc) @@ -313,6 +322,7 @@ controller.queue_behavior(/datum/ai_behavior/bot_search/valid_window_fix, BB_WELDER_TARGET, searchable_objects) /datum/ai_behavior/bot_search/valid_window_fix + action_cooldown = 5 SECONDS /datum/ai_behavior/bot_search/valid_window_fix/valid_target(datum/ai_controller/basic_controller/bot/controller, obj/my_target) return (my_target.get_integrity() < my_target.max_integrity || !my_target.anchored) diff --git a/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm b/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm index 945b09274d7..1b458811acc 100644 --- a/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm +++ b/code/modules/mob/living/basic/bots/vibebot/vibebot_ai.dm @@ -7,7 +7,6 @@ ) planning_subtrees = list( /datum/ai_planning_subtree/respond_to_summon, - /datum/ai_planning_subtree/manage_unreachable_list, /datum/ai_planning_subtree/find_party_friends, /datum/ai_planning_subtree/find_patrol_beacon, ) @@ -69,9 +68,9 @@ return FALSE set_movement_target(controller, target) -/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) +/datum/ai_behavior/targeted_mob_ability/and_clear_target/vibebot_party/finish_action(datum/ai_controller/basic_controller/bot/controller, succeeded, ability_key, target_key) var/atom/target = controller.blackboard[target_key] - controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) + controller.add_to_blacklist(target) if(succeeded) var/mob/living/living_pawn = controller.pawn living_pawn.manual_emote("celebrates with [target]!") diff --git a/icons/ui/repairbotmanagement/repairbot_smile.dmi b/icons/ui/repairbotmanagement/repairbot_smile.dmi new file mode 100644 index 0000000000000000000000000000000000000000..6dad3497910d7494ed8b82f0b26986622508bbbd GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@&H$ef*8>L*{Qv)7P(+pW%cKiH zLB^6GzhDN3XE)M-oZ_mGh!U67;^d;tf|AVqJO+k}Il*BCMWx@r1Q&e#`b5iHSL@uF z^T8WJ4K5l#c%*aQNAn~@QBUs<3*#W;%f?;`a~@4PGATqMc%{Zx1;fo6n)_AuS(qEE zhjw-wt8QK)p^<#z=*+MwGiGxoYHRQKwX;E&fnidWoKL}@4+=o*)jeGtLnNjqCoEu3 z;N>l4FuoDCw#g{%Y?INYOBa?-OIz4^VC!Xh1_qyvTz#((9{3B?!r { + const { act, data } = useBackend(); + const { stacks, repairbot_icon, repairbot_icon_state } = data; + return ( + + +
+ } + > + + {stacks.map((stack) => ( + + + + + + + + + + + + + + + + + + ))} + +
+
+
+ ); +};