From 24afc641c508669dfc92b5defd901b4bd951f8d1 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sun, 31 Dec 2023 15:45:33 +0100 Subject: [PATCH] [MIRROR] hygeienbots basic bots [MDB IGNORE] (#25923) * 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 :cl: 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 /:cl: * hygeienbots basic bots --------- Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> --- _maps/shuttles/emergency_tranquility.dmm | 2 +- _maps/shuttles/ruin_cyborg_mothership.dmm | 36 +-- code/__DEFINES/ai/bot_keys.dm | 16 ++ code/datums/components/crafting/robot.dm | 2 +- code/modules/mob/living/basic/bots/_bots.dm | 5 + code/modules/mob/living/basic/bots/bot_ai.dm | 15 +- code/modules/mob/living/basic/bots/bot_hud.dm | 7 +- .../living/basic/bots/cleanbot/cleanbot.dm | 2 +- .../living/basic/bots/cleanbot/cleanbot_ai.dm | 6 +- .../basic/bots/hygienebot/hygienebot.dm | 132 +++++++++ .../basic/bots/hygienebot/hygienebot_ai.dm | 142 ++++++++++ .../mob/living/basic/bots/medbot/medbot_ai.dm | 3 +- .../living/simple_animal/bot/construction.dm | 4 +- .../living/simple_animal/bot/hygienebot.dm | 253 ------------------ .../unit_tests/simple_animal_freeze.dm | 1 - tgstation.dme | 3 +- 16 files changed, 344 insertions(+), 285 deletions(-) create mode 100644 code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm create mode 100644 code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm delete mode 100644 code/modules/mob/living/simple_animal/bot/hygienebot.dm diff --git a/_maps/shuttles/emergency_tranquility.dmm b/_maps/shuttles/emergency_tranquility.dmm index b2a15c5df11..5458e59c843 100644 --- a/_maps/shuttles/emergency_tranquility.dmm +++ b/_maps/shuttles/emergency_tranquility.dmm @@ -716,7 +716,7 @@ /turf/open/floor/grass, /area/shuttle/escape) "nQ" = ( -/mob/living/simple_animal/bot/hygienebot, +/mob/living/basic/bot/hygienebot, /turf/open/floor/wood, /area/shuttle/escape) "nV" = ( diff --git a/_maps/shuttles/ruin_cyborg_mothership.dmm b/_maps/shuttles/ruin_cyborg_mothership.dmm index 7188b8bd3c0..5093dfcc7db 100644 --- a/_maps/shuttles/ruin_cyborg_mothership.dmm +++ b/_maps/shuttles/ruin_cyborg_mothership.dmm @@ -215,17 +215,6 @@ /obj/structure/lattice/catwalk, /turf/open/space/basic, /area/shuttle/ruin/cyborg_mothership) -"mG" = ( -/obj/structure/table, -/obj/item/surgery_tray/full, -/obj/effect/turf_decal/bot, -/obj/structure/sink/directional/east, -/obj/item/toy/figure/borg{ - pixel_x = 7; - pixel_y = 12 - }, -/turf/open/floor/iron/dark, -/area/shuttle/ruin/cyborg_mothership) "mN" = ( /obj/machinery/conveyor{ dir = 4; @@ -584,11 +573,6 @@ /obj/structure/cable, /turf/open/floor/iron/showroomfloor, /area/shuttle/ruin/cyborg_mothership) -"Fe" = ( -/mob/living/simple_animal/bot/hygienebot, -/obj/machinery/camera/directional/south, -/turf/open/floor/iron/showroomfloor, -/area/shuttle/ruin/cyborg_mothership) "FQ" = ( /obj/structure/lattice, /mob/living/basic/hivebot/range, @@ -654,6 +638,11 @@ /mob/living/basic/hivebot/mechanic, /turf/template_noop, /area/shuttle/ruin/cyborg_mothership) +"JS" = ( +/mob/living/basic/bot/hygienebot, +/obj/machinery/camera/directional/south, +/turf/open/floor/iron/showroomfloor, +/area/shuttle/ruin/cyborg_mothership) "Ks" = ( /obj/structure/cable, /obj/machinery/conveyor/inverted{ @@ -722,6 +711,17 @@ /obj/structure/cable, /turf/open/floor/circuit/airless, /area/shuttle/ruin/cyborg_mothership) +"Oq" = ( +/obj/structure/table, +/obj/item/surgery_tray/full, +/obj/effect/turf_decal/bot, +/obj/structure/sink/directional/east, +/obj/item/toy/figure/borg{ + pixel_x = 7; + pixel_y = 12 + }, +/turf/open/floor/iron/dark, +/area/shuttle/ruin/cyborg_mothership) "Ou" = ( /obj/machinery/camera/directional/west, /obj/structure/cable, @@ -1161,7 +1161,7 @@ zZ zZ mN iN -mG +Oq zZ kz kz @@ -1186,7 +1186,7 @@ fB zZ Ey Sd -Fe +JS yF HM yF diff --git a/code/__DEFINES/ai/bot_keys.dm b/code/__DEFINES/ai/bot_keys.dm index 25467e45485..5cf2e4263d4 100644 --- a/code/__DEFINES/ai/bot_keys.dm +++ b/code/__DEFINES/ai/bot_keys.dm @@ -15,6 +15,8 @@ #define BB_RADIO_CHANNEL "radio_channel" ///list of unreachable things we will temporarily ignore #define BB_TEMPORARY_IGNORE_LIST "temporary_ignore_list" +///Last thing we attempted to reach +#define BB_LAST_ATTEMPTED_PATHING "last_attempted_pathing" // medbot keys ///the patient we must heal @@ -55,3 +57,17 @@ #define BB_ACID_SPRAY_TARGET "acid_spray_target" ///key that holds trash we will burn #define BB_HUNTABLE_TRASH "huntable_trash" + +//hygienebots +///key that holds our threats +#define BB_WASH_THREATS "wash_threats" +///key that holds speech when we find our target +#define BB_WASH_FOUND "wash_found" +///key that holds speech when we cleaned our target +#define BB_WASH_DONE "wash_done" +///key that holds target we will wash +#define BB_WASH_TARGET "wash_target" +///key that holds how frustrated we are when target is running away +#define BB_WASH_FRUSTRATION "wash_frustration" +///key that holds cooldown after we finish cleaning something, so we dont immediately run off to patrol +#define BB_POST_CLEAN_COOLDOWN "post_clean_cooldown" diff --git a/code/datums/components/crafting/robot.dm b/code/datums/components/crafting/robot.dm index e0c6b4ecd3a..326c58d50c4 100644 --- a/code/datums/components/crafting/robot.dm +++ b/code/datums/components/crafting/robot.dm @@ -131,7 +131,7 @@ /datum/crafting_recipe/hygienebot name = "Hygienebot" - result = /mob/living/simple_animal/bot/hygienebot + result = /mob/living/basic/bot/hygienebot reqs = list( /obj/item/bot_assembly/hygienebot = 1, /obj/item/stack/ducts = 1, diff --git a/code/modules/mob/living/basic/bots/_bots.dm b/code/modules/mob/living/basic/bots/_bots.dm index 5151ec116fc..f8b273804ea 100644 --- a/code/modules/mob/living/basic/bots/_bots.dm +++ b/code/modules/mob/living/basic/bots/_bots.dm @@ -819,8 +819,13 @@ GLOBAL_LIST_INIT(command_strings, list( diag_hud_set_botmode() /mob/living/basic/bot/proc/after_attacked(datum/source, atom/attacker, attack_flags) + SIGNAL_HANDLER + if(attack_flags & ATTACKER_DAMAGING_ATTACK) do_sparks(number = 5, cardinal_only = TRUE, source = src) /mob/living/basic/bot/spawn_gibs(drop_bitflags = NONE) new /obj/effect/gibspawner/robot(drop_location(), src) + +/mob/living/basic/bot/proc/on_bot_movement(atom/movable/source, atom/oldloc, dir, forced) + return diff --git a/code/modules/mob/living/basic/bots/bot_ai.dm b/code/modules/mob/living/basic/bots/bot_ai.dm index bb8d77eb64b..802b66ef48d 100644 --- a/code/modules/mob/living/basic/bots/bot_ai.dm +++ b/code/modules/mob/living/basic/bots/bot_ai.dm @@ -23,6 +23,10 @@ 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) . = ..() @@ -70,8 +74,15 @@ var/datum/target = blackboard[key] if(QDELETED(target)) return FALSE - if(!can_reach_target(target, distance)) + 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 @@ -84,7 +95,7 @@ /datum/ai_behavior/manage_unreachable_list behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION - action_cooldown = 15 SECONDS + action_cooldown = 45 SECONDS /datum/ai_behavior/manage_unreachable_list/perform(seconds_per_tick, datum/ai_controller/controller, list_key) . = ..() diff --git a/code/modules/mob/living/basic/bots/bot_hud.dm b/code/modules/mob/living/basic/bots/bot_hud.dm index 4ed97fe32f7..c4001473f94 100644 --- a/code/modules/mob/living/basic/bots/bot_hud.dm +++ b/code/modules/mob/living/basic/bots/bot_hud.dm @@ -90,9 +90,13 @@ hud.add_atom_to_hud(src) ///proc that handles moving along the bot's drawn path -/mob/living/basic/bot/proc/handle_loop_movement(atom/movable/source) +/mob/living/basic/bot/proc/handle_loop_movement(atom/movable/source, atom/oldloc, dir, forced) SIGNAL_HANDLER + handle_hud_path() + on_bot_movement(source, oldloc, dir, forced) + +/mob/living/basic/bot/proc/handle_hud_path() if(client || !length(current_pathed_turfs) || isnull(ai_controller)) return @@ -106,7 +110,6 @@ if(target_image) animate(target_image, alpha = 0, time = 0.3 SECONDS) current_pathed_turfs -= our_turf - return ///proc that handles deleting the bot's drawn path when needed /mob/living/basic/bot/proc/clear_path_hud() diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm index c19ba69c5ce..8a8050b9afc 100644 --- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm @@ -94,7 +94,6 @@ /obj/effect/decal/cleanable/glass, /obj/effect/decal/cleanable/vomit, /obj/effect/decal/cleanable/wrapping, - /obj/effect/decal/remains, )) ///blood we can clean var/static/list/cleanable_blood = typecacheof(list( @@ -111,6 +110,7 @@ var/static/list/huntable_trash = typecacheof(list( /obj/item/trash, /obj/item/food/deadmouse, + /obj/effect/decal/remains, )) ///drawings we hunt var/static/list/cleanable_drawings = typecacheof(list(/obj/effect/decal/cleanable/crayon)) 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 8007f56f789..5a992c1cc75 100644 --- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm @@ -1,4 +1,5 @@ #define BOT_CLEAN_PATH_LIMIT 15 +#define POST_CLEAN_COOLDOWN 5 SECONDS /datum/ai_controller/basic_controller/bot/cleanbot blackboard = list( @@ -51,7 +52,6 @@ /datum/ai_planning_subtree/cleaning_subtree/SelectBehaviors(datum/ai_controller/basic_controller/bot/cleanbot/controller, seconds_per_tick) if(controller.reachable_key(BB_CLEAN_TARGET, BOT_CLEAN_PATH_LIMIT)) - controller.clear_blackboard_key(BB_BEACON_TARGET) controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_CLEAN_TARGET) return SUBTREE_RETURN_FINISH_PLANNING @@ -128,6 +128,7 @@ /datum/ai_behavior/execute_clean/finish_action(datum/ai_controller/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(QDELETED(target) || is_type_in_typecache(target, controller.blackboard[BB_HUNTABLE_TRASH])) return @@ -186,7 +187,7 @@ /datum/ai_planning_subtree/find_patrol_beacon/cleanbot /datum/ai_planning_subtree/find_patrol_beacon/cleanbot/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) - if(controller.blackboard_key_exists(BB_CLEAN_TARGET)) + if(controller.blackboard[BB_POST_CLEAN_COOLDOWN] >= world.time) return return ..() @@ -214,3 +215,4 @@ controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) #undef BOT_CLEAN_PATH_LIMIT +#undef POST_CLEAN_COOLDOWN diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm new file mode 100644 index 00000000000..1d802ca9358 --- /dev/null +++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm @@ -0,0 +1,132 @@ +#define WASH_PERIOD 3 SECONDS + +/mob/living/basic/bot/hygienebot + name = "\improper Hygienebot" + desc = "A flying cleaning robot, he'll chase down people who can't shower properly!" + icon = 'icons/mob/silicon/aibots.dmi' + icon_state = "hygienebot" + base_icon_state = "hygienebot" + pass_flags = PASSMOB | PASSFLAPS | PASSTABLE + layer = MOB_UPPER_LAYER + density = FALSE + anchored = FALSE + health = 100 + maxHealth = 100 + path_image_color = "#80dae7" + maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR) + radio_key = /obj/item/encryptionkey/headset_service + radio_channel = RADIO_CHANNEL_SERVICE + bot_type = HYGIENE_BOT + additional_access = /datum/id_trim/job/janitor + hackables = "cleaning service protocols" + ai_controller = /datum/ai_controller/basic_controller/bot/hygienebot + + ///are we currently washing someone? + var/washing = FALSE + ///Visual overlay of the bot spraying water. + var/static/mutable_appearance/water_overlay = mutable_appearance('icons/mob/silicon/aibots.dmi', "hygienebot-water") + ///Visual overlay of the bot commiting warcrimes. + var/static/mutable_appearance/fire_overlay = mutable_appearance('icons/mob/silicon/aibots.dmi', "hygienebot-fire") + ///announcements we say when we find a target + var/static/list/found_announcements = list( + HYGIENEBOT_VOICED_UNHYGIENIC = 'sound/voice/hygienebot/unhygienicclient.ogg', + ) + ///announcements we say when the target keeps moving away + var/static/list/threat_announcements = list( + HYGIENEBOT_VOICED_THREAT_AIRLOCK = 'sound/voice/hygienebot/dragyouout.ogg', + HYGIENEBOT_VOICED_FOUL_SMELL = 'sound/voice/hygienebot/foulsmelling.ogg', + HYGIENEBOT_VOICED_TROGLODYTE = 'sound/voice/hygienebot/troglodyte.ogg', + HYGIENEBOT_VOICED_GREEN_CLOUD = 'sound/voice/hygienebot/greencloud.ogg', + HYGIENEBOT_VOICED_ARSEHOLE = 'sound/voice/hygienebot/letmeclean.ogg', + HYGIENEBOT_VOICED_THREAT_ARTERIES = 'sound/voice/hygienebot/cutarteries.ogg', + HYGIENEBOT_VOICED_STOP_RUNNING = 'sound/voice/hygienebot/stoprunning.ogg', + ) + ///announcements we say after we have cleaned our target + var/static/list/cleaned_announcements = list( + HYGIENEBOT_VOICED_FUCKING_FINALLY = 'sound/voice/hygienebot/finally.ogg', + HYGIENEBOT_VOICED_THANK_GOD = 'sound/voice/hygienebot/thankgod.ogg', + HYGIENEBOT_VOICED_DEGENERATE = 'sound/voice/hygienebot/degenerate.ogg', + ) + +/mob/living/basic/bot/hygienebot/Initialize(mapload) + . = ..() + update_appearance(UPDATE_ICON) + + generate_ai_speech() + + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + var/static/list/hat_offsets = list(1, 1) + AddElement(/datum/element/hat_wearer, offsets = hat_offsets) + + ADD_TRAIT(src, TRAIT_SPRAY_PAINTABLE, INNATE_TRAIT) + RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_attack)) + +/mob/living/basic/bot/hygienebot/explode() + var/datum/effect_system/fluid_spread/foam/foam = new + foam.set_up(2, holder = src, location = loc) + foam.start() + return ..() + +/mob/living/basic/bot/hygienebot/generate_speak_list() + var/static/list/finalized_speak_list = (found_announcements + threat_announcements + cleaned_announcements) + return finalized_speak_list + +/mob/living/basic/bot/hygienebot/update_icon_state() + . = ..() + icon_state = "[base_icon_state][bot_mode_flags & BOT_MODE_ON ? "-on" : ""]" + + +/mob/living/basic/bot/hygienebot/update_overlays() + . = ..() + if(bot_mode_flags & BOT_MODE_ON) + . += mutable_appearance(icon, "hygienebot-flame") + + if(!washing) + return + + . += (bot_access_flags & BOT_COVER_EMAGGED) ? fire_overlay : water_overlay + +/mob/living/basic/bot/hygienebot/proc/on_entered(datum/source, atom/movable/movable) + SIGNAL_HANDLER + if(!washing) + return + commence_wash(movable) + +/mob/living/basic/bot/hygienebot/proc/on_attack(datum/source, atom/target) + SIGNAL_HANDLER + . = COMPONENT_HOSTILE_NO_ATTACK + if(washing) + return + set_washing_mode(new_mode = TRUE) + for(var/atom/to_wash in loc) + commence_wash(to_wash) + addtimer(CALLBACK(src, PROC_REF(set_washing_mode), FALSE), WASH_PERIOD) + +/mob/living/basic/bot/hygienebot/proc/set_washing_mode(new_mode) + washing = new_mode + update_appearance(UPDATE_OVERLAYS) + +/mob/living/basic/bot/hygienebot/proc/commence_wash(atom/target) + if(bot_access_flags & BOT_COVER_EMAGGED) + target.fire_act() + return + target.wash(CLEAN_WASH) + +/mob/living/basic/bot/hygienebot/on_bot_movement(atom/movable/source, atom/oldloc, dir, forced) + + if(!washing || !isturf(loc)) + return + + for(var/mob/living/carbon/human in loc) + commence_wash(human) + + +/mob/living/basic/bot/hygienebot/proc/generate_ai_speech() + ai_controller.set_blackboard_key(BB_WASH_FOUND, found_announcements) + ai_controller.set_blackboard_key(BB_WASH_THREATS, threat_announcements) + ai_controller.set_blackboard_key(BB_WASH_DONE, cleaned_announcements) + +#undef WASH_PERIOD diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm new file mode 100644 index 00000000000..2d2bc27079f --- /dev/null +++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm @@ -0,0 +1,142 @@ +#define BOT_FRUSTRATION_LIMIT 8 + +/datum/ai_controller/basic_controller/bot/hygienebot + blackboard = list( + BB_SALUTE_MESSAGES = list( + "salutes", + "nods in appreciation towards", + ), + 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/wash_people, + /datum/ai_planning_subtree/salute_authority, + /datum/ai_planning_subtree/find_patrol_beacon, + ) + reset_keys = list( + BB_WASH_TARGET, + BB_BEACON_TARGET, + BB_PREVIOUS_BEACON_TARGET, + BB_BOT_SUMMON_TARGET, + ) + +/datum/ai_controller/basic_controller/bot/hygienebot/TryPossessPawn(atom/new_pawn) + . = ..() + if(. & AI_CONTROLLER_INCOMPATIBLE) + return + RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_WASH_TARGET), PROC_REF(reset_anger)) + +/datum/ai_controller/basic_controller/bot/hygienebot/proc/reset_anger() + SIGNAL_HANDLER + + set_blackboard_key(BB_WASH_FRUSTRATION, 0) + + +/datum/ai_planning_subtree/wash_people + +/datum/ai_planning_subtree/wash_people/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/bot_pawn = controller.pawn + + var/atom/wash_target = controller.blackboard[BB_WASH_TARGET] + if(QDELETED(wash_target)) + controller.queue_behavior(/datum/ai_behavior/find_valid_wash_targets, BB_WASH_TARGET, bot_pawn.bot_access_flags) + return + + if(get_dist(bot_pawn, wash_target) < 9) + controller.queue_behavior(/datum/ai_behavior/wash_target, BB_WASH_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + + controller.clear_blackboard_key(BB_WASH_TARGET) //delete if too far + +/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) + . = ..() + var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] + var/atom/found_target + for(var/mob/living/carbon/human/wash_potential in oview(5, controller.pawn)) + + if(found_target) + break + + if(isnull(wash_potential.mind) || wash_potential.stat != CONSCIOUS) + continue + + if(LAZYACCESS(ignore_list, wash_potential)) + continue + + if(our_access_flags & BOT_COVER_EMAGGED) + controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, wash_potential, TRUE) + found_target = wash_potential + break + + for(var/atom/clothing in wash_potential.get_equipped_items()) + if(GET_ATOM_BLOOD_DNA_LENGTH(clothing)) + found_target = wash_potential + break + + if(isnull(found_target)) + finish_action(controller, succeeded = FALSE) + return + + controller.set_blackboard_key(target_key, found_target) + finish_action(controller, succeeded = TRUE) + + + +/datum/ai_behavior/find_valid_wash_targets/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + if(!succeeded) + return + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + announcement.announce(pick(controller.blackboard[BB_WASH_FOUND])) + +/datum/ai_behavior/wash_target + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_MOVE_AND_PERFORM + required_distance = 0 + action_cooldown = 1 SECONDS + +/datum/ai_behavior/wash_target/setup(datum/ai_controller/controller, target_key) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + set_movement_target(controller, target) + +/datum/ai_behavior/wash_target/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key) + . = ..() + var/mob/living/carbon/human/unclean_target = controller.blackboard[target_key] + var/mob/living/basic/living_pawn = controller.pawn + if(QDELETED(unclean_target)) + finish_action(controller, FALSE, target_key) + return + + if(living_pawn.loc == get_turf(unclean_target)) + living_pawn.melee_attack(unclean_target) + finish_action(controller, TRUE, target_key) + return + + var/frustration_count = controller.blackboard[BB_WASH_FRUSTRATION] + controller.set_blackboard_key(BB_WASH_FRUSTRATION, frustration_count + 1) + finish_action(controller, FALSE, target_key) + +/datum/ai_behavior/wash_target/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + + if(succeeded) + if(controller.blackboard[BB_WASH_FRUSTRATION] > 0) + announcement.announce(pick(controller.blackboard[BB_WASH_DONE])) + controller.clear_blackboard_key(target_key) + return + + if(controller.blackboard[BB_WASH_FRUSTRATION] < BOT_FRUSTRATION_LIMIT) + return + + announcement.announce(pick(controller.blackboard[BB_WASH_THREATS])) + controller.set_blackboard_key(BB_WASH_FRUSTRATION, 0) + +#undef BOT_FRUSTRATION_LIMIT 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 8d8e3efbfe3..ceb333b1755 100644 --- a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm +++ b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm @@ -16,6 +16,7 @@ BB_PREVIOUS_BEACON_TARGET, BB_BOT_SUMMON_TARGET, ) + ai_traits = PAUSE_DURING_DO_AFTER /datum/ai_movement/jps/bot/medbot @@ -185,7 +186,7 @@ return can_see(source, patient, radius) /datum/ai_behavior/announce_patient - action_cooldown = 30 SECONDS + action_cooldown = 3 MINUTES behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION /datum/ai_behavior/announce_patient/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key) diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm index 7ef55e8357c..9591aaed69c 100644 --- a/code/modules/mob/living/simple_animal/bot/construction.dm +++ b/code/modules/mob/living/simple_animal/bot/construction.dm @@ -552,8 +552,8 @@ to_chat(user, span_notice("You start to pipe up [src]...")) if(do_after(user, 40, target = src) && D.use(1)) to_chat(user, span_notice("You pipe up [src].")) - var/mob/living/simple_animal/bot/hygienebot/H = new(drop_location()) - H.name = created_name + var/mob/living/basic/bot/hygienebot/new_bot = new(drop_location()) + new_bot.name = created_name qdel(src) if(I.tool_behaviour == TOOL_SCREWDRIVER) //deconstruct new /obj/item/assembly/prox_sensor(Tsec) diff --git a/code/modules/mob/living/simple_animal/bot/hygienebot.dm b/code/modules/mob/living/simple_animal/bot/hygienebot.dm deleted file mode 100644 index 0db59e2518c..00000000000 --- a/code/modules/mob/living/simple_animal/bot/hygienebot.dm +++ /dev/null @@ -1,253 +0,0 @@ -//Cleanbot -/mob/living/simple_animal/bot/hygienebot - name = "\improper Hygienebot" - desc = "A flying cleaning robot, he'll chase down people who can't shower properly!" - icon = 'icons/mob/silicon/aibots.dmi' - icon_state = "hygienebot" - base_icon_state = "hygienebot" - pass_flags = PASSMOB | PASSFLAPS | PASSTABLE - layer = MOB_UPPER_LAYER - density = FALSE - anchored = FALSE - health = 100 - maxHealth = 100 - - maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR) - radio_key = /obj/item/encryptionkey/headset_service - radio_channel = RADIO_CHANNEL_SERVICE //Service - bot_mode_flags = ~BOT_MODE_CAN_BE_SAPIENT - bot_type = HYGIENE_BOT - hackables = "cleaning service protocols" - path_image_color = "#993299" - - automated_announcements = list( - HYGIENEBOT_VOICED_UNHYGIENIC = 'sound/voice/hygienebot/unhygienicclient.ogg', - HYGIENEBOT_VOICED_ENJOY_DAY = 'sound/voice/hygienebot/cleanandtidy.ogg', - HYGIENEBOT_VOICED_THREAT_AIRLOCK = 'sound/voice/hygienebot/dragyouout.ogg', - HYGIENEBOT_VOICED_FOUL_SMELL = 'sound/voice/hygienebot/foulsmelling.ogg', - HYGIENEBOT_VOICED_TROGLODYTE = 'sound/voice/hygienebot/troglodyte.ogg', - HYGIENEBOT_VOICED_GREEN_CLOUD = 'sound/voice/hygienebot/greencloud.ogg', - HYGIENEBOT_VOICED_ARSEHOLE = 'sound/voice/hygienebot/letmeclean.ogg', - HYGIENEBOT_VOICED_THREAT_ARTERIES = 'sound/voice/hygienebot/cutarteries.ogg', - HYGIENEBOT_VOICED_STOP_RUNNING = 'sound/voice/hygienebot/stoprunning.ogg', - HYGIENEBOT_VOICED_FUCKING_FINALLY = 'sound/voice/hygienebot/finally.ogg', - HYGIENEBOT_VOICED_THANK_GOD = 'sound/voice/hygienebot/thankgod.ogg', - HYGIENEBOT_VOICED_DEGENERATE = 'sound/voice/hygienebot/degenerate.ogg', - ) - - ///The human target the bot is trying to wash. - var/mob/living/carbon/human/target - ///The mob's current speed, which varies based on how long the bot chases it's target. - var/currentspeed = 5 - ///Is the bot currently washing it's target/everything else that crosses it? - var/washing = FALSE - ///Have the target evaded the bot for long enough that it will swear at it like kirk did to kahn? - var/mad = FALSE - ///The last time that the previous/current target was found. - var/last_found - ///Name of the previous target the bot was pursuing. - var/oldtarget_name - ///Visual overlay of the bot spraying water. - var/mutable_appearance/water_overlay - ///Visual overlay of the bot commiting warcrimes. - var/mutable_appearance/fire_overlay - -/mob/living/simple_animal/bot/hygienebot/Initialize(mapload) - . = ..() - update_appearance(UPDATE_ICON) - - // Doing this hurts my soul, but simplebot access reworks are for another day. - var/datum/id_trim/job/jani_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/janitor] - access_card.add_access(jani_trim.access + jani_trim.wildcard_access) - prev_access = access_card.access.Copy() - var/static/list/loc_connections = list( - COMSIG_ATOM_ENTERED = PROC_REF(on_entered), - ) - AddElement(/datum/element/connect_loc, loc_connections) - - ADD_TRAIT(src, TRAIT_SPRAY_PAINTABLE, INNATE_TRAIT) - -/mob/living/simple_animal/bot/hygienebot/explode() - var/datum/effect_system/fluid_spread/foam/foam = new - foam.set_up(2, holder = src, location = loc) - foam.start() - - return ..() - -/mob/living/simple_animal/bot/hygienebot/proc/on_entered(datum/source, atom/movable/AM) - SIGNAL_HANDLER - if(washing) - do_wash(AM) - -/mob/living/simple_animal/bot/hygienebot/update_icon_state() - . = ..() - icon_state = "[base_icon_state][bot_mode_flags & BOT_MODE_ON ? "-on" : null]" - - -/mob/living/simple_animal/bot/hygienebot/update_overlays() - . = ..() - if(bot_mode_flags & BOT_MODE_ON) - . += mutable_appearance(icon, "hygienebot-flame") - - if(washing) - . += mutable_appearance(icon, bot_cover_flags & BOT_COVER_EMAGGED ? "hygienebot-fire" : "hygienebot-water") - - -/mob/living/simple_animal/bot/hygienebot/turn_off() - ..() - mode = BOT_IDLE - -/mob/living/simple_animal/bot/hygienebot/bot_reset() - ..() - target = null - oldtarget_name = null - SSmove_manager.stop_looping(src) - last_found = world.time - -/mob/living/simple_animal/bot/hygienebot/handle_automated_action() - if(!..()) - return - - if(washing) - do_wash(loc) - for(var/AM in loc) - if (AM == src) - continue - do_wash(AM) - if(isopenturf(loc) && !(bot_cover_flags & BOT_COVER_EMAGGED)) - var/turf/open/tile = loc - tile.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS) - - switch(mode) - if(BOT_IDLE) // idle - SSmove_manager.stop_looping(src) - look_for_lowhygiene() // see if any disgusting fucks are in range - if(!mode && bot_mode_flags & BOT_MODE_AUTOPATROL) // still idle, and set to patrol - mode = BOT_START_PATROL // switch to patrol mode - - if(BOT_HUNT) // hunting for stinkman - if(bot_cover_flags & BOT_COVER_EMAGGED) //lol fuck em up - currentspeed = 3.5 - start_washing() - mad = TRUE - else - switch(frustration) - if(0 to 4) - currentspeed = 5 - mad = FALSE - if(5 to INFINITY) - currentspeed = 2.5 - mad = TRUE - if(target && !check_purity(target)) - if(target.loc == loc && isturf(target.loc)) //LADIES AND GENTLEMAN WE GOTEM PREPARE TO DUMP - start_washing() - if(mad) - var/static/list/relief = list( - HYGIENEBOT_VOICED_FUCKING_FINALLY, - HYGIENEBOT_VOICED_THANK_GOD, - HYGIENEBOT_VOICED_DEGENERATE, - ) - speak(pick(relief)) - playsound(loc, 'sound/effects/hygienebot_angry.ogg', 60, 1) //i think it should still make robot noises too - mad = FALSE - mode = BOT_SHOWERSTANCE - else - stop_washing() - var/olddist = get_dist(src, target) - if(olddist > 20 || frustration > 100) // Focus on something else - back_to_idle() - return - SSmove_manager.move_to(src, target, 0, currentspeed) - if(mad && prob(min(frustration * 2, 60))) - var/static/list/threats = list( - HYGIENEBOT_VOICED_THREAT_AIRLOCK, - HYGIENEBOT_VOICED_FOUL_SMELL, - HYGIENEBOT_VOICED_TROGLODYTE, - HYGIENEBOT_VOICED_GREEN_CLOUD, - HYGIENEBOT_VOICED_ARSEHOLE, - HYGIENEBOT_VOICED_THREAT_ARTERIES, - HYGIENEBOT_VOICED_STOP_RUNNING, - ) - speak(pick(threats)) - playsound(loc, 'sound/effects/hygienebot_angry.ogg', 60, 1) - if((get_dist(src, target)) >= olddist) - frustration++ - else - frustration = 0 - else - back_to_idle() - - if(BOT_SHOWERSTANCE) - if(check_purity(target)) - speak(HYGIENEBOT_VOICED_ENJOY_DAY) - playsound(loc, 'sound/effects/hygienebot_happy.ogg', 60, 1) - back_to_idle() - return - if(!target) - last_found = world.time - if(target.loc != loc || !isturf(target.loc)) - back_to_hunt() - - if(BOT_START_PATROL) - look_for_lowhygiene() - start_patrol() - - if(BOT_PATROL) - look_for_lowhygiene() - bot_patrol() - -/mob/living/simple_animal/bot/hygienebot/proc/back_to_idle() - mode = BOT_IDLE - SSmove_manager.stop_looping(src) - target = null - frustration = 0 - last_found = world.time - stop_washing() - INVOKE_ASYNC(src, PROC_REF(handle_automated_action)) - -/mob/living/simple_animal/bot/hygienebot/proc/back_to_hunt() - frustration = 0 - mode = BOT_HUNT - stop_washing() - INVOKE_ASYNC(src, PROC_REF(handle_automated_action)) - -/mob/living/simple_animal/bot/hygienebot/proc/look_for_lowhygiene() - for (var/mob/living/carbon/human/H in view(7,src)) //Find the NEET - if((H.name == oldtarget_name) && (world.time < last_found + 100)) - continue - if(!check_purity(H)) //Theyre impure - target = H - oldtarget_name = H.name - speak(HYGIENEBOT_VOICED_UNHYGIENIC) - playsound(loc, 'sound/effects/hygienebot_happy.ogg', 60, 1) - visible_message("[src] points at [H.name]!") - mode = BOT_HUNT - INVOKE_ASYNC(src, PROC_REF(handle_automated_action)) - break - else - continue - -/mob/living/simple_animal/bot/hygienebot/proc/start_washing() - washing = TRUE - update_appearance() - -/mob/living/simple_animal/bot/hygienebot/proc/stop_washing() - washing = FALSE - update_appearance() - -/mob/living/simple_animal/bot/hygienebot/proc/check_purity(mob/living/L) - if((bot_cover_flags & BOT_COVER_EMAGGED) && L.stat != DEAD) - return FALSE - - for(var/X in list(ITEM_SLOT_HEAD, ITEM_SLOT_MASK, ITEM_SLOT_ICLOTHING, ITEM_SLOT_OCLOTHING, ITEM_SLOT_FEET)) - - var/obj/item/I = L.get_item_by_slot(X) - if(I && GET_ATOM_BLOOD_DNA_LENGTH(I)) - return FALSE - return TRUE - -/mob/living/simple_animal/bot/hygienebot/proc/do_wash(atom/A) - if(bot_cover_flags & BOT_COVER_EMAGGED) - A.fire_act() //lol pranked no cleaning besides that - else - A.wash(CLEAN_WASH) diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 5a090a59a93..f47fc72fbf3 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -8,7 +8,6 @@ /mob/living/simple_animal/bot, /mob/living/simple_animal/bot/firebot, /mob/living/simple_animal/bot/floorbot, - /mob/living/simple_animal/bot/hygienebot, /mob/living/simple_animal/bot/mulebot, /mob/living/simple_animal/bot/mulebot/paranormal, /mob/living/simple_animal/bot/secbot, diff --git a/tgstation.dme b/tgstation.dme index b4b43389d62..b05d7d677cf 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4577,6 +4577,8 @@ #include "code\modules\mob\living\basic\bots\cleanbot\cleanbot.dm" #include "code\modules\mob\living\basic\bots\cleanbot\cleanbot_abilities.dm" #include "code\modules\mob\living\basic\bots\cleanbot\cleanbot_ai.dm" +#include "code\modules\mob\living\basic\bots\hygienebot\hygienebot.dm" +#include "code\modules\mob\living\basic\bots\hygienebot\hygienebot_ai.dm" #include "code\modules\mob\living\basic\bots\medbot\medbot.dm" #include "code\modules\mob\living\basic\bots\medbot\medbot_ai.dm" #include "code\modules\mob\living\basic\clown\clown.dm" @@ -4994,7 +4996,6 @@ #include "code\modules\mob\living\simple_animal\bot\firebot.dm" #include "code\modules\mob\living\simple_animal\bot\floorbot.dm" #include "code\modules\mob\living\simple_animal\bot\honkbot.dm" -#include "code\modules\mob\living\simple_animal\bot\hygienebot.dm" #include "code\modules\mob\living\simple_animal\bot\mulebot.dm" #include "code\modules\mob\living\simple_animal\bot\secbot.dm" #include "code\modules\mob\living\simple_animal\bot\SuperBeepsky.dm"