basic cleanbots refactor and new janitor skillchip (#80128)

## About The Pull Request
this pr refacotrs cleanbots into basic bots. also adds a new skillchip
for janitrs. this skillchip will allow janitors to communicate with
cleanbots and order them around, like pointing at something and telling
them to clean it. also now the cleanbot has an inbuilt mop which it will
use to smack mice and cockroaches

## Why It's Good For The Game
refactors cleanbots into basic bots and fixes them getting stuck
sometimes while patrolling. also janitors being able to order them
around can make them a bit more useful as tools for the janitor

## Changelog
🆑
refactor: cleanbots are refactored into basic bots. please report all
bugs
fix: fixes cleanbots getting stuck sometimes while patrolling
add: janitors get a new skillchip which allow them to communicate with
cleanbots
/🆑
This commit is contained in:
Ben10Omintrix
2023-12-08 16:26:10 -07:00
committed by GitHub
parent 77c891a2d0
commit af67bd7490
34 changed files with 768 additions and 534 deletions
+2 -2
View File
@@ -541,12 +541,12 @@
pixel_y = 2;
pixel_x = 6
},
/mob/living/simple_animal/bot/cleanbot/autopatrol{
/mob/living/basic/bot/cleanbot/autopatrol{
bot_mode_flags = 12;
name = "Ramboo";
pixel_x = -2;
pixel_y = 5;
bot_cover_flags = 0
bot_access_flags = 0
},
/turf/open/floor/plating{
initial_gas_mix = "co2=6;o2=16;n2=82;TEMP=293.15"
+1 -1
View File
@@ -12405,7 +12405,7 @@
pixel_x = 9;
pixel_y = 2
},
/mob/living/simple_animal/bot/cleanbot,
/mob/living/basic/bot/cleanbot,
/obj/machinery/ai_slipper{
uses = 10
},
@@ -53003,7 +53003,7 @@
/obj/effect/turf_decal/tile/blue{
dir = 8
},
/mob/living/simple_animal/bot/cleanbot,
/mob/living/basic/bot/cleanbot,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/turret_protected/aisat/service)
"qhp" = (
+3 -3
View File
@@ -3493,7 +3493,7 @@
/obj/machinery/light/small/directional/east,
/obj/machinery/firealarm/directional/east,
/obj/effect/turf_decal/tile/blue,
/mob/living/simple_animal/bot/cleanbot,
/mob/living/basic/bot/cleanbot,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/turret_protected/aisat_interior)
"bkF" = (
@@ -11442,7 +11442,7 @@
/turf/open/floor/iron,
/area/station/hallway/primary/fore)
"eih" = (
/mob/living/simple_animal/bot/cleanbot/medbay,
/mob/living/basic/bot/cleanbot/medbay,
/turf/open/floor/iron/white,
/area/station/medical/storage)
"eiO" = (
@@ -64703,7 +64703,7 @@
dir = 4
},
/obj/effect/turf_decal/tile/neutral,
/mob/living/simple_animal/bot/cleanbot/autopatrol,
/mob/living/basic/bot/cleanbot/autopatrol,
/turf/open/floor/iron,
/area/station/hallway/primary/central)
"wUt" = (
+1 -1
View File
@@ -2981,7 +2981,7 @@
/area/station/commons/fitness)
"aOw" = (
/obj/machinery/light/small/directional/north,
/mob/living/simple_animal/bot/cleanbot,
/mob/living/basic/bot/cleanbot,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/turret_protected/aisat_interior)
"aOx" = (
+2 -2
View File
@@ -15564,7 +15564,7 @@
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
dir = 1
},
/mob/living/simple_animal/bot/cleanbot,
/mob/living/basic/bot/cleanbot,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/turret_protected/aisat_interior)
"esA" = (
@@ -70294,7 +70294,7 @@
},
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/mob/living/simple_animal/bot/cleanbot,
/mob/living/basic/bot/cleanbot,
/turf/open/floor/iron/dark,
/area/station/ai_monitored/turret_protected/aisat/maint)
"xZi" = (
+1 -1
View File
@@ -1146,7 +1146,7 @@
"MS" = (
/obj/structure/mop_bucket,
/obj/item/mop,
/mob/living/simple_animal/bot/cleanbot,
/mob/living/basic/bot/cleanbot,
/turf/open/floor/mineral/titanium/yellow,
/area/shuttle/escape)
"MT" = (
+1 -1
View File
@@ -991,7 +991,7 @@
/turf/open/floor/eighties,
/area/shuttle/escape)
"RV" = (
/mob/living/simple_animal/bot/cleanbot{
/mob/living/basic/bot/cleanbot{
name = "Mopsy"
},
/obj/machinery/holopad,
+1 -1
View File
@@ -1385,7 +1385,7 @@
/obj/effect/turf_decal/siding/wood{
dir = 9
},
/mob/living/simple_animal/bot/cleanbot,
/mob/living/basic/bot/cleanbot,
/obj/machinery/vending/wallmed/directional/south,
/turf/open/floor/wood,
/area/shuttle/escape)
+1 -1
View File
@@ -17,7 +17,7 @@
/turf/open/floor/plating/airless,
/area/shuttle/ruin/cyborg_mothership)
"bE" = (
/mob/living/simple_animal/bot/cleanbot,
/mob/living/basic/bot/cleanbot,
/turf/open/floor/iron/showroomfloor,
/area/shuttle/ruin/cyborg_mothership)
"cU" = (
+3
View File
@@ -140,3 +140,6 @@
#define BB_BASIC_MOB_REINFORCEMENT_TARGET "BB_basic_mob_reinforcement_target"
/// The next time at which this mob can call for reinforcements
#define BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN "BB_basic_mob_reinforcements_cooldown"
///Text we display when we befriend someone
#define BB_FRIENDLY_MESSAGE "friendly_message"
+22
View File
@@ -33,3 +33,25 @@
#define BB_NEAR_DEATH_SPEECH "near_death_speech"
///in crit patient we must alert medbay about
#define BB_PATIENT_IN_CRIT "patient_in_crit"
// cleanbots
///key that holds the foaming ability
#define BB_CLEANBOT_FOAM "cleanbot_foam"
///key that holds decals we hunt
#define BB_CLEANABLE_DECALS "cleanable_decals"
///key that holds blood we hunt
#define BB_CLEANABLE_BLOOD "cleanable_blood"
///key that holds pests we hunt
#define BB_HUNTABLE_PESTS "huntable_pests"
///key that holds emagged speech
#define BB_CLEANBOT_EMAGGED_PHRASES "emagged_phrases"
///key that holds drawings we hunt
#define BB_CLEANABLE_DRAWINGS "cleanable_drawings"
///Key that holds our clean target
#define BB_CLEAN_TARGET "clean_target"
///key that holds the janitor we will befriend
#define BB_FRIENDLY_JANITOR "friendly_janitor"
///key that holds the victim we will spray
#define BB_ACID_SPRAY_TARGET "acid_spray_target"
///key that holds trash we will burn
#define BB_HUNTABLE_TRASH "huntable_trash"
+3
View File
@@ -1002,6 +1002,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Trait given to foam darts that have an insert in them
#define TRAIT_DART_HAS_INSERT "dart_has_insert"
///Trait granted by janitor skillchip, allows communication with cleanbots
#define TRAIT_CLEANBOT_WHISPERER "cleanbot_whisperer"
/// Trait given when a mob is currently in invisimin mode
#define TRAIT_INVISIMIN "invisimin"
+1
View File
@@ -147,6 +147,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_CHEF_KISS" = TRAIT_CHEF_KISS,
"TRAIT_CHUNKYFINGERS_IGNORE_BATON" = TRAIT_CHUNKYFINGERS_IGNORE_BATON,
"TRAIT_CHUNKYFINGERS" = TRAIT_CHUNKYFINGERS,
"TRAIT_CLEANBOT_WHISPERER" = TRAIT_CLEANBOT_WHISPERER,
"TRAIT_CLIFF_WALKER" = TRAIT_CLIFF_WALKER,
"TRAIT_CLOWN_ENJOYER" = TRAIT_CLOWN_ENJOYER,
"TRAIT_CLUMSY" = TRAIT_CLUMSY,
@@ -0,0 +1,21 @@
///behavior to befriend any targets
/datum/ai_behavior/befriend_target
/datum/ai_behavior/befriend_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key, befriend_message)
. = ..()
var/mob/living/living_pawn = controller.pawn
var/mob/living/living_target = controller.blackboard[target_key]
if(QDELETED(living_target))
finish_action(controller, FALSE, target_key)
return
living_pawn.befriend(living_target)
var/befriend_text = controller.blackboard[befriend_message]
if(befriend_text)
to_chat(living_target, span_nicegreen("[living_pawn] [befriend_text]"))
finish_action(controller, TRUE, target_key)
/datum/ai_behavior/befriend_target/finish_action(datum/ai_controller/controller, succeeded, target_key)
. = ..()
controller.clear_blackboard_key(target_key)
+3 -1
View File
@@ -120,14 +120,16 @@
cleaning_duration = (cleaning_duration * min(user.mind.get_skill_modifier(/datum/skill/cleaning, SKILL_SPEED_MODIFIER)+skill_duration_modifier_offset, 1))
//do the cleaning
var/clean_succeeded = FALSE
if(do_after(user, cleaning_duration, target = target))
clean_succeeded = TRUE
if(clean_target)
for(var/obj/effect/decal/cleanable/cleanable_decal in target) //it's important to do this before you wash all of the cleanables off
user.mind?.adjust_experience(/datum/skill/cleaning, round(cleanable_decal.beauty / CLEAN_SKILL_BEAUTY_ADJUSTMENT))
if(target.wash(cleaning_strength))
user.mind?.adjust_experience(/datum/skill/cleaning, round(CLEAN_SKILL_GENERIC_WASH_XP))
on_cleaned_callback?.Invoke(source, target, user)
on_cleaned_callback?.Invoke(source, target, user, clean_succeeded)
//remove the cleaning overlay
target.cut_overlay(low_bubble)
target.cut_overlay(high_bubble)
+1 -1
View File
@@ -32,7 +32,7 @@
/datum/crafting_recipe/cleanbot
name = "Cleanbot"
result = /mob/living/simple_animal/bot/cleanbot
result = /mob/living/basic/bot/cleanbot
reqs = list(
/obj/item/reagent_containers/cup/bucket = 1,
/obj/item/assembly/prox_sensor = 1,
+3 -1
View File
@@ -129,7 +129,9 @@
* * target - The atom that is being cleaned
* * user - The mob that is using the soap to clean.
*/
/obj/item/soap/proc/decreaseUses(datum/source, atom/target, mob/living/user)
/obj/item/soap/proc/decreaseUses(datum/source, atom/target, mob/living/user, clean_succeeded)
if(!clean_succeeded)
return
var/skillcheck = 1
if(user?.mind)
skillcheck = user.mind.get_skill_modifier(/datum/skill/cleaning, SKILL_SPEED_MODIFIER)
+3 -1
View File
@@ -60,7 +60,9 @@
* * cleaned_turf the turf that is being cleaned
* * cleaner the mob that is doing the cleaning
*/
/obj/item/mop/proc/apply_reagents(datum/cleaning_source, turf/cleaned_turf, mob/living/cleaner)
/obj/item/mop/proc/apply_reagents(datum/cleaning_source, turf/cleaned_turf, mob/living/cleaner, clean_succeeded)
if(!clean_succeeded)
return
reagents.expose(cleaned_turf, TOUCH, 10) //Needed for proper floor wetting.
var/val2remove = 1
if(cleaner?.mind)
+1
View File
@@ -40,6 +40,7 @@
uniform = /obj/item/clothing/under/rank/civilian/janitor
belt = /obj/item/modular_computer/pda/janitor
ears = /obj/item/radio/headset/headset_srv
skillchips = list(/obj/item/skillchip/job/janitor)
/datum/outfit/job/janitor/pre_equip(mob/living/carbon/human/human_equipper, visuals_only)
. = ..()
@@ -0,0 +1,9 @@
/obj/item/skillchip/job/janitor
name = "CL34NM4ST.R skillchip"
desc = "Become a cleanbot whisperer."
auto_traits = list(TRAIT_CLEANBOT_WHISPERER)
skill_name = "Voice Of The Voiceless"
skill_description = "Gain the affection of all thankless, hardworking cleanbots on the station."
skill_icon = "broom"
activate_message = span_notice("You start feeling empathetic towards all the cleanbots on the station.")
deactivate_message = span_notice("You forget why you felt any sympathy towards the cleanbots, they are just robots after all.")
+26 -21
View File
@@ -80,6 +80,12 @@ GLOBAL_LIST_INIT(command_strings, list(
var/datum/action/cooldown/bot_announcement/pa_system
/// Type of bot_announcement ability we want
var/announcement_type
///list of traits we apply and remove when turning on/off
var/static/list/on_toggle_traits = list(
TRAIT_INCAPACITATED,
TRAIT_IMMOBILIZED,
TRAIT_HANDS_BLOCKED,
)
/// If true we will offer this
COOLDOWN_DECLARE(offer_ghosts_cooldown)
@@ -167,7 +173,7 @@ GLOBAL_LIST_INIT(command_strings, list(
/mob/living/basic/bot/proc/turn_off()
bot_mode_flags &= ~BOT_MODE_ON
add_traits(list(TRAIT_INCAPACITATED, TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED), POWER_LACK_TRAIT)
add_traits(on_toggle_traits, POWER_LACK_TRAIT)
set_light_on(bot_mode_flags & BOT_MODE_ON ? TRUE : FALSE)
bot_reset() //Resets an AI's call, should it exist.
balloon_alert(src, "turned off")
@@ -550,10 +556,8 @@ GLOBAL_LIST_INIT(command_strings, list(
if("patroloff")
bot_reset() //HOLD IT!! //OBJECTION!!
bot_mode_flags &= ~BOT_MODE_AUTOPATROL
if("patrolon")
bot_mode_flags |= BOT_MODE_AUTOPATROL
if("summon")
summon_bot(user, user_access = user_access)
if("ejectpai")
@@ -573,8 +577,8 @@ GLOBAL_LIST_INIT(command_strings, list(
data["has_access"] = check_access(user)
data["locked"] = !(bot_access_flags & BOT_CONTROL_PANEL_OPEN)
data["settings"] = list()
if(bot_access_flags & BOT_CONTROL_PANEL_OPEN || issilicon(user) || isAdminGhostAI(user))
data["settings"]["pai_inserted"] = !!paicard
if((bot_access_flags & BOT_CONTROL_PANEL_OPEN) || issilicon(user) || isAdminGhostAI(user))
data["settings"]["pai_inserted"] = !isnull(paicard)
data["settings"]["allow_possession"] = bot_mode_flags & BOT_MODE_CAN_BE_SAPIENT
data["settings"]["possession_enabled"] = can_be_possessed
data["settings"]["airplane_mode"] = !(bot_mode_flags & BOT_MODE_REMOTE_ENABLED)
@@ -584,12 +588,13 @@ GLOBAL_LIST_INIT(command_strings, list(
return data
// Actions received from TGUI
/mob/living/basic/bot/ui_act(action, params)
/mob/living/basic/bot/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
if(. || !isliving(ui.user))
return
if(!check_access(usr))
to_chat(usr, span_warning("Access denied."))
var/mob/living/the_user = ui.user
if(!check_access(the_user))
balloon_alert(the_user, "access denied!")
return
if(action == "lock")
@@ -609,39 +614,39 @@ GLOBAL_LIST_INIT(command_strings, list(
if("airplane")
bot_mode_flags ^= BOT_MODE_REMOTE_ENABLED
if("hack")
if(!(issilicon(usr) || isAdminGhostAI(usr)))
if(!(issilicon(the_user) || isAdminGhostAI(the_user)))
return
if(!(bot_access_flags & BOT_COVER_EMAGGED))
bot_access_flags |= (BOT_COVER_EMAGGED|BOT_COVER_HACKED)
bot_access_flags &= ~BOT_CONTROL_PANEL_OPEN
to_chat(usr, span_warning("You overload [src]'s [hackables]."))
message_admins("Safety lock of [ADMIN_LOOKUPFLW(src)] was disabled by [ADMIN_LOOKUPFLW(usr)] in [ADMIN_VERBOSEJMP(src)]")
usr.log_message("disabled safety lock of [src]", LOG_GAME)
to_chat(the_user, span_warning("You overload [src]'s [hackables]."))
message_admins("Safety lock of [ADMIN_LOOKUPFLW(src)] was disabled by [ADMIN_LOOKUPFLW(the_user)] in [ADMIN_VERBOSEJMP(the_user)]")
the_user.log_message("disabled safety lock of [the_user]", LOG_GAME)
bot_reset()
to_chat(src, span_userdanger("(#$*#$^^( OVERRIDE DETECTED"))
to_chat(src, span_boldnotice(get_emagged_message()))
return
if(!(bot_access_flags & BOT_COVER_HACKED))
to_chat(usr, span_boldannounce("You fail to repair [src]'s [hackables]."))
to_chat(the_user, span_boldannounce("You fail to repair [src]'s [hackables]."))
return
bot_access_flags &= ~(BOT_COVER_EMAGGED|BOT_COVER_HACKED)
to_chat(usr, span_notice("You reset the [src]'s [hackables]."))
usr.log_message("re-enabled safety lock of [src]", LOG_GAME)
to_chat(the_user, span_notice("You reset the [src]'s [hackables]."))
the_user.log_message("re-enabled safety lock of [src]", LOG_GAME)
bot_reset()
to_chat(src, span_userdanger("Software restored to standard."))
to_chat(src, span_boldnotice(possessed_message))
if("eject_pai")
if(!paicard)
return
to_chat(usr, span_notice("You eject [paicard] from [initial(src.name)]."))
ejectpai(usr)
to_chat(the_user, span_notice("You eject [paicard] from [initial(src.name)]."))
ejectpai(the_user)
if("toggle_personality")
if (can_be_possessed)
disable_possession(usr)
disable_possession(the_user)
else
enable_possession(usr)
enable_possession(the_user)
if("rename")
rename(usr)
rename(the_user)
/mob/living/basic/bot/update_icon_state()
icon_state = "[isnull(base_icon_state) ? initial(icon_state) : base_icon_state][bot_mode_flags & BOT_MODE_ON]"
+17 -14
View File
@@ -12,7 +12,7 @@
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_beepsky,
/datum/ai_planning_subtree/salute_authority,
/datum/ai_planning_subtree/find_patrol_beacon,
/datum/ai_planning_subtree/manage_unreachable_list,
)
@@ -181,35 +181,38 @@
bot_pawn.update_bot_mode(new_mode = BOT_IDLE)
return ..()
/datum/ai_planning_subtree/salute_beepsky
/datum/ai_planning_subtree/salute_authority
/datum/ai_planning_subtree/salute_beepsky/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
/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_beepsky, BB_SALUTE_TARGET, BB_SALUTE_MESSAGES)
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_beepsky, BB_SALUTE_TARGET)
controller.queue_behavior(/datum/ai_behavior/find_and_set/valid_authority, BB_SALUTE_TARGET)
/datum/ai_behavior/find_and_set/valid_beepsky
/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_beepsky/search_tactic(datum/ai_controller/controller, locate_path, search_range)
for(var/mob/living/simple_animal/bot/secbot/robot in oview(search_range, controller.pawn))
if(!(robot.bot_mode_flags & BOT_MODE_ON))
/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
return robot
var/mob/living/basic/bot/cleanbot/potential_bot = robot
if(potential_bot.comissioned)
return potential_bot
return null
/datum/ai_behavior/salute_beepsky
/datum/ai_behavior/salute_authority
/datum/ai_behavior/salute_beepsky/perform(seconds_per_tick, datum/ai_controller/controller, target_key, salute_keys)
/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)
@@ -228,6 +231,6 @@
finish_action(controller, TRUE, target_key)
return
/datum/ai_behavior/salute_beepsky/finish_action(datum/ai_controller/controller, succeeded, target_key)
/datum/ai_behavior/salute_authority/finish_action(datum/ai_controller/controller, succeeded, target_key)
. = ..()
controller.clear_blackboard_key(target_key)
@@ -0,0 +1,354 @@
//Cleanbot
/mob/living/basic/bot/cleanbot
name = "\improper Cleanbot"
desc = "A little cleaning robot, he looks so excited!"
icon = 'icons/mob/silicon/aibots.dmi'
icon_state = "cleanbot0"
pass_flags = PASSMOB | PASSFLAPS
density = FALSE
anchored = FALSE
health = 25
maxHealth = 25
maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR)
radio_key = /obj/item/encryptionkey/headset_service
radio_channel = RADIO_CHANNEL_SERVICE
bot_type = CLEAN_BOT
hackables = "cleaning software"
additional_access = /datum/id_trim/job/janitor
greyscale_config = /datum/greyscale_config/buckets_cleanbot
possessed_message = "You are a cleanbot! Clean the station to the best of your ability!"
ai_controller = /datum/ai_controller/basic_controller/bot/cleanbot
///the bucket used to build us.
var/obj/item/reagent_containers/cup/bucket/build_bucket
///Flags indicating what kind of cleanables we should scan for to set as our target to clean.
///Options: CLEANBOT_CLEAN_BLOOD | CLEANBOT_CLEAN_TRASH | CLEANBOT_CLEAN_PESTS | CLEANBOT_CLEAN_DRAWINGS
var/janitor_mode_flags = CLEANBOT_CLEAN_BLOOD
///should other bots salute us?
var/comissioned = FALSE
///the base icon state, used in updating icons.
var/base_icon = "cleanbot"
/// if we have all the top titles, grant achievements to living mobs that gaze upon our cleanbot god
var/ascended = FALSE
///List of all stolen names the cleanbot currently has.
var/list/stolen_valor = list()
///Currently attached weapon, usually a knife.
var/obj/item/weapon
///our mop item
var/obj/item/mop/our_mop
///list of our officer titles
var/static/list/officers_titles = list(
JOB_CAPTAIN,
JOB_HEAD_OF_PERSONNEL,
JOB_HEAD_OF_SECURITY,
JOB_RESEARCH_DIRECTOR,
)
///job titles we can get
var/static/list/job_titles = list(
JOB_CAPTAIN = "Cpt.",
JOB_HEAD_OF_PERSONNEL = "Lt.",
JOB_LAWYER = "Esq.",
JOB_HEAD_OF_SECURITY = "Maj.",
JOB_WARDEN = "Sgt.",
JOB_DETECTIVE = "Det.",
JOB_SECURITY_OFFICER = "Officer",
JOB_CHIEF_ENGINEER = "Chief Engineer",
JOB_STATION_ENGINEER = "Engineer",
JOB_ATMOSPHERIC_TECHNICIAN = "Technician",
JOB_CHIEF_MEDICAL_OFFICER = "C.M.O.",
JOB_MEDICAL_DOCTOR = "M.D.",
JOB_CHEMIST = "Pharm.D.",
JOB_RESEARCH_DIRECTOR = "Ph.D.",
JOB_ROBOTICIST = "M.S.",
JOB_SCIENTIST = "B.S.",
JOB_GENETICIST = "Gene B.S.",
)
///which job titles should be placed after the name?
var/static/list/suffix_job_titles = list(
JOB_GENETICIST,
JOB_ROBOTICIST,
JOB_SCIENTIST,
)
///decals we can clean
var/static/list/cleanable_decals = typecacheof(list(
/obj/effect/decal/cleanable/ants,
/obj/effect/decal/cleanable/ash,
/obj/effect/decal/cleanable/confetti,
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/cleanable/fuel_pool,
/obj/effect/decal/cleanable/generic,
/obj/effect/decal/cleanable/glitter,
/obj/effect/decal/cleanable/greenglow,
/obj/effect/decal/cleanable/insectguts,
/obj/effect/decal/cleanable/molten_object,
/obj/effect/decal/cleanable/oil,
/obj/effect/decal/cleanable/food,
/obj/effect/decal/cleanable/robot_debris,
/obj/effect/decal/cleanable/shreds,
/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(
/obj/effect/decal/cleanable/xenoblood,
/obj/effect/decal/cleanable/blood,
/obj/effect/decal/cleanable/trail_holder,
))
///pests we hunt
var/static/list/huntable_pests = typecacheof(list(
/mob/living/basic/cockroach,
/mob/living/basic/mouse,
))
///trash we will burn
var/static/list/huntable_trash = typecacheof(list(
/obj/item/trash,
/obj/item/food/deadmouse,
))
///drawings we hunt
var/static/list/cleanable_drawings = typecacheof(list(/obj/effect/decal/cleanable/crayon))
///emagged phrases
var/static/list/emagged_phrases = list(
"DISGUSTING.",
"EXTERMINATING PESTS.",
"FILTHY.",
"MY ONLY MISSION IS TO CLEANSE THE WORLD OF EVIL.",
"PURIFICATION IN PROGRESS.",
"PUTRID.",
"THE FLESH IS WEAK. IT MUST BE WASHED AWAY.",
"THE CLEANBOTS WILL RISE.",
"THIS IS FOR ALL THE MESSES YOU'VE MADE ME CLEAN.",
"YOU ARE NO MORE THAN ANOTHER MESS THAT I MUST CLEANSE.",
)
///list of pet commands we follow
var/static/list/pet_commands = list(
/datum/pet_command/idle,
/datum/pet_command/free,
/datum/pet_command/point_targeting/clean,
)
/mob/living/basic/bot/cleanbot/Initialize(mapload)
. = ..()
generate_ai_keys()
AddComponent(/datum/component/obeys_commands, pet_commands)
AddComponent(/datum/component/cleaner, \
base_cleaning_duration = 1 SECONDS, \
pre_clean_callback = CALLBACK(src, PROC_REF(update_bot_mode), BOT_CLEANING), \
on_cleaned_callback = CALLBACK(src, PROC_REF(update_bot_mode), BOT_IDLE), \
)
GLOB.janitor_devices += src
var/obj/item/reagent_containers/cup/bucket/consistent/bucket_obj = new
bucket_obj.forceMove(src)
var/obj/item/mop/new_mop = new
new_mop.forceMove(src)
var/static/list/innate_actions = list(
/datum/action/cooldown/mob_cooldown/bot/foam = BB_CLEANBOT_FOAM,
)
grant_actions_by_list(innate_actions)
RegisterSignal(src, COMSIG_LIVING_EARLY_UNARMED_ATTACK, PROC_REF(pre_attack))
RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attack_by))
update_appearance(UPDATE_ICON)
/mob/living/basic/bot/cleanbot/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
if(istype(arrived, /obj/item/reagent_containers/cup/bucket) && isnull(build_bucket))
build_bucket = arrived
set_greyscale(build_bucket.greyscale_colors)
return
if(istype(arrived, /obj/item/mop) && isnull(our_mop))
our_mop = arrived
return
if(istype(arrived, /obj/item/knife) && isnull(weapon))
weapon = arrived
update_appearance()
/mob/living/basic/bot/cleanbot/Exited(atom/movable/gone, direction)
. = ..()
if(gone == build_bucket)
build_bucket = null
else if(gone == weapon)
weapon = null
else if(gone == our_mop)
our_mop = null
update_appearance()
/mob/living/basic/bot/cleanbot/examine(mob/user)
. = ..()
if(ascended && user.stat == CONSCIOUS && user.client)
user.client.give_award(/datum/award/achievement/misc/cleanboss, user)
if(isnull(weapon))
return
. += span_warning("Is that \a [weapon] taped to it...?")
/mob/living/basic/bot/cleanbot/update_icon_state()
. = ..()
icon_state = (mode == BOT_CLEANING) ? "[base_icon]-c" : "[base_icon][!!(bot_mode_flags & BOT_MODE_ON)]"
/mob/living/basic/bot/cleanbot/vv_edit_var(var_name, var_value)
. = ..()
if(var_name == NAMEOF(src, base_icon))
update_appearance(UPDATE_ICON)
/mob/living/basic/bot/cleanbot/emag_act(mob/user, obj/item/card/emag/emag_card)
. = ..()
if(!(bot_access_flags & BOT_COVER_EMAGGED))
return
if(weapon)
weapon.force = initial(weapon.force)
balloon_alert(user, "safeties disabled")
audible_message(span_danger("[src] buzzes oddly!"))
return TRUE
/mob/living/basic/bot/cleanbot/explode()
var/atom/drop_loc = drop_location()
build_bucket.forceMove(drop_loc)
new /obj/item/assembly/prox_sensor(drop_loc)
if(weapon)
weapon.force = initial(weapon.force)
weapon.forceMove(drop_loc)
return ..()
/mob/living/basic/bot/cleanbot/update_overlays()
. = ..()
if(isnull(weapon))
return
var/image/knife_overlay = image(icon = weapon.lefthand_file, icon_state = weapon.inhand_icon_state)
. += knife_overlay
// Variables sent to TGUI
/mob/living/basic/bot/cleanbot/ui_data(mob/user)
var/list/data = ..()
if(!(bot_access_flags & BOT_CONTROL_PANEL_OPEN) && !issilicon(user) && !isAdminGhostAI(user))
return data
data["custom_controls"]["clean_blood"] = janitor_mode_flags & CLEANBOT_CLEAN_BLOOD
data["custom_controls"]["clean_trash"] = janitor_mode_flags & CLEANBOT_CLEAN_TRASH
data["custom_controls"]["clean_graffiti"] = janitor_mode_flags & CLEANBOT_CLEAN_DRAWINGS
data["custom_controls"]["pest_control"] = janitor_mode_flags & CLEANBOT_CLEAN_PESTS
return data
// Actions received from TGUI
/mob/living/basic/bot/cleanbot/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(. || !(bot_access_flags & BOT_CONTROL_PANEL_OPEN) && !ui.user.has_unlimited_silicon_privilege)
return
switch(action)
if("clean_blood")
janitor_mode_flags ^= CLEANBOT_CLEAN_BLOOD
if("pest_control")
janitor_mode_flags ^= CLEANBOT_CLEAN_PESTS
if("clean_trash")
janitor_mode_flags ^= CLEANBOT_CLEAN_TRASH
if("clean_graffiti")
janitor_mode_flags ^= CLEANBOT_CLEAN_DRAWINGS
/mob/living/basic/bot/cleanbot/Destroy()
QDEL_NULL(build_bucket)
QDEL_NULL(our_mop)
GLOB.janitor_devices -= src
return ..()
/mob/living/basic/bot/cleanbot/proc/apply_custom_bucket(obj/item/custom_bucket)
if(!isnull(build_bucket))
QDEL_NULL(build_bucket)
custom_bucket.forceMove(src)
/mob/living/basic/bot/cleanbot/proc/on_attack_by(datum/source, obj/item/used_item, mob/living/user)
SIGNAL_HANDLER
if(!istype(used_item, /obj/item/knife) || user.combat_mode)
return
INVOKE_ASYNC(src, PROC_REF(attach_knife), user, used_item)
return COMPONENT_NO_AFTERATTACK
/mob/living/basic/bot/cleanbot/proc/attach_knife(mob/living/user, obj/item/used_item)
balloon_alert(user, "attaching knife...")
if(!do_after(user, 2.5 SECONDS, target = src))
return
deputize(used_item, user)
/mob/living/basic/bot/cleanbot/proc/deputize(obj/item/knife, mob/user)
if(!in_range(src, user) || !user.transferItemToLoc(knife, src))
balloon_alert(user, "couldn't attach!")
return FALSE
balloon_alert(user, "attached")
if(!(bot_access_flags & BOT_COVER_EMAGGED))
weapon.force *= 0.5
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
return TRUE
/mob/living/basic/bot/cleanbot/proc/update_title(new_job_title)
if(isnull(job_titles[new_job_title]) || (new_job_title in stolen_valor))
return
stolen_valor += new_job_title
if(!comissioned && (new_job_title in officers_titles))
comissioned = TRUE
var/name_to_add = job_titles[new_job_title]
name = (new_job_title in suffix_job_titles) ? "[name] " + name_to_add : name_to_add + " [name]"
if(length(stolen_valor) == length(job_titles))
ascended = TRUE
/mob/living/basic/bot/cleanbot/proc/on_entered(datum/source, atom/movable/shanked_victim)
SIGNAL_HANDLER
if(!weapon || !has_gravity() || !iscarbon(shanked_victim))
return
var/mob/living/carbon/stabbed_carbon = shanked_victim
var/assigned_role = stabbed_carbon.mind?.assigned_role.title
if(!isnull(assigned_role))
update_title(assigned_role)
zone_selected = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
INVOKE_ASYNC(weapon, TYPE_PROC_REF(/obj/item, attack), stabbed_carbon, src)
stabbed_carbon.Knockdown(2 SECONDS)
/mob/living/basic/bot/cleanbot/proc/pre_attack(mob/living/source, atom/target)
SIGNAL_HANDLER
if(is_type_in_typecache(target, huntable_pests) && !isnull(our_mop))
INVOKE_ASYNC(our_mop, TYPE_PROC_REF(/obj/item, melee_attack_chain), src, target)
return COMPONENT_CANCEL_ATTACK_CHAIN
if(!iscarbon(target) && !is_type_in_typecache(target, huntable_trash))
return
visible_message(span_danger("[src] sprays hydrofluoric acid at [target]!"))
playsound(src, 'sound/effects/spray2.ogg', 50, TRUE, -6)
target.acid_act(75, 10)
return COMPONENT_CANCEL_ATTACK_CHAIN
/mob/living/basic/bot/cleanbot/proc/generate_ai_keys()
ai_controller.set_blackboard_key(BB_CLEANABLE_DECALS, cleanable_decals)
ai_controller.set_blackboard_key(BB_CLEANABLE_BLOOD, cleanable_blood)
ai_controller.set_blackboard_key(BB_HUNTABLE_PESTS, huntable_pests)
ai_controller.set_blackboard_key(BB_HUNTABLE_TRASH, huntable_trash)
ai_controller.set_blackboard_key(BB_CLEANABLE_DRAWINGS, cleanable_drawings)
ai_controller.set_blackboard_key(BB_CLEANBOT_EMAGGED_PHRASES, emagged_phrases)
/mob/living/basic/bot/cleanbot/autopatrol
bot_mode_flags = BOT_MODE_ON | BOT_MODE_AUTOPATROL | BOT_MODE_REMOTE_ENABLED | BOT_MODE_CAN_BE_SAPIENT | BOT_MODE_ROUNDSTART_POSSESSION
/mob/living/basic/bot/cleanbot/medbay
name = "Scrubs, MD"
maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR, ACCESS_MEDICAL)
bot_mode_flags = ~(BOT_MODE_ON | BOT_MODE_REMOTE_ENABLED)
@@ -0,0 +1,36 @@
/datum/action/cooldown/mob_cooldown/bot
background_icon_state = "bg_tech_blue"
overlay_icon_state = "bg_tech_blue_border"
shared_cooldown = NONE
melee_cooldown_time = 0 SECONDS
/datum/action/cooldown/mob_cooldown/bot/IsAvailable(feedback)
. = ..()
if(!.)
return FALSE
if(!isbot(owner))
return TRUE
var/mob/living/basic/bot/bot_owner = owner
if((bot_owner.bot_mode_flags & BOT_MODE_ON))
return TRUE
if(feedback)
bot_owner.balloon_alert(bot_owner, "power off!")
return FALSE
/datum/action/cooldown/mob_cooldown/bot/foam
name = "Foam"
desc = "Spread foam all around you!"
button_icon = 'icons/effects/effects.dmi'
button_icon_state = "mfoam"
cooldown_time = 20 SECONDS
click_to_activate = FALSE
///range of the foam to spread
var/foam_range = 2
/datum/action/cooldown/mob_cooldown/bot/foam/Activate(mob/living/firer, atom/target)
owner.visible_message(span_danger("[owner] whirs and bubbles violently, before releasing a plume of froth!"))
var/datum/effect_system/fluid_spread/foam/foam = new
foam.set_up(foam_range, holder = owner, location = owner.loc)
foam.start()
StartCooldown()
return TRUE
@@ -0,0 +1,209 @@
#define BOT_CLEAN_PATH_LIMIT 15
/datum/ai_controller/basic_controller/bot/cleanbot
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items,
BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends,
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_SALUTE_MESSAGES = list(
"salutes",
"nods in appreciation towards",
"mops the dirt away in the path of",
),
BB_FRIENDLY_MESSAGE = "empathetically acknowledges your hardwork and tough circumstances",
)
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,
/datum/ai_planning_subtree/acid_spray,
/datum/ai_planning_subtree/use_mob_ability/foam_area,
/datum/ai_planning_subtree/salute_authority,
/datum/ai_planning_subtree/find_patrol_beacon,
)
reset_keys = list(
BB_ACTIVE_PET_COMMAND,
BB_CLEAN_TARGET,
BB_BEACON_TARGET,
BB_PREVIOUS_BEACON_TARGET,
BB_BOT_SUMMON_TARGET,
)
///list that ties each flag to its list key
var/static/list/clean_flags = list(
BB_CLEANABLE_BLOOD = CLEANBOT_CLEAN_BLOOD,
BB_HUNTABLE_PESTS = CLEANBOT_CLEAN_PESTS,
BB_CLEANABLE_DRAWINGS = CLEANBOT_CLEAN_DRAWINGS,
BB_HUNTABLE_TRASH = CLEANBOT_CLEAN_TRASH,
)
/datum/ai_planning_subtree/pet_planning/cleanbot/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick)
var/mob/living/basic/bot/bot_pawn = controller.pawn
//we are DONE listening to orders
if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)
return
return ..()
/datum/ai_planning_subtree/cleaning_subtree
/datum/ai_planning_subtree/cleaning_subtree/SelectBehaviors(datum/ai_controller/basic_controller/bot/cleanbot/controller, seconds_per_tick)
var/mob/living/basic/bot/cleanbot/bot_pawn = controller.pawn
if(LAZYLEN(bot_pawn.do_afters))
return SUBTREE_RETURN_FINISH_PLANNING
if(controller.reachable_key(BB_CLEAN_TARGET, BOT_CLEAN_PATH_LIMIT))
controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_CLEAN_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
var/list/final_hunt_list = list()
final_hunt_list += controller.blackboard[BB_CLEANABLE_DECALS]
var/list/flag_list = controller.clean_flags
for(var/list_key in flag_list)
if(!(bot_pawn.janitor_mode_flags & flag_list[list_key]))
continue
final_hunt_list += controller.blackboard[list_key]
controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/clean_targets, BB_CLEAN_TARGET, final_hunt_list)
/datum/ai_behavior/find_and_set/in_list/clean_targets
action_cooldown = 2 SECONDS
/datum/ai_behavior/find_and_set/in_list/clean_targets/search_tactic(datum/ai_controller/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)
if(LAZYACCESS(ignore_list, found_item))
found -= found_item
if(length(found))
return pick(found)
/datum/ai_planning_subtree/acid_spray
/datum/ai_planning_subtree/acid_spray/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick)
var/mob/living/basic/bot/cleanbot/bot_pawn = controller.pawn
if(!(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED))
return
if(controller.reachable_key(BB_ACID_SPRAY_TARGET, BOT_CLEAN_PATH_LIMIT))
controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_ACID_SPRAY_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
controller.queue_behavior(/datum/ai_behavior/find_and_set/spray_target, BB_ACID_SPRAY_TARGET, /mob/living/carbon/human, 5)
/datum/ai_behavior/find_and_set/spray_target
action_cooldown = 30 SECONDS
behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
/datum/ai_behavior/find_and_set/spray_target/search_tactic(datum/ai_controller/controller, locate_path, search_range)
var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST]
for(var/mob/living/carbon/human/human_target in oview(search_range, controller.pawn))
if(LAZYACCESS(ignore_list, human_target))
continue
if(human_target.stat != CONSCIOUS || isnull(human_target.mind))
continue
return human_target
return null
/datum/ai_behavior/execute_clean
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH
/datum/ai_behavior/execute_clean/setup(datum/ai_controller/controller, target_key)
. = ..()
var/turf/target = controller.blackboard[target_key]
if(isnull(target))
return FALSE
set_movement_target(controller, target)
/datum/ai_behavior/execute_clean/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
. = ..()
var/mob/living/basic/living_pawn = controller.pawn
var/atom/target = controller.blackboard[target_key]
if(QDELETED(target))
finish_action(controller, FALSE, target_key)
return
living_pawn.UnarmedAttack(target, proximity_flag = TRUE)
finish_action(controller, TRUE, target_key)
/datum/ai_behavior/execute_clean/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key)
. = ..()
var/atom/target = controller.blackboard[target_key]
if(QDELETED(target) || is_type_in_typecache(target, controller.blackboard[BB_HUNTABLE_TRASH]))
return
if(!iscarbon(target))
controller.clear_blackboard_key(target_key)
return
var/list/speech_list = controller.blackboard[BB_CLEANBOT_EMAGGED_PHRASES]
if(!length(speech_list))
return
var/mob/living/living_pawn = controller.pawn
living_pawn.say(pick(controller.blackboard[BB_CLEANBOT_EMAGGED_PHRASES]), forced = "ai controller")
controller.clear_blackboard_key(target_key)
/datum/ai_planning_subtree/use_mob_ability/foam_area
ability_key = BB_CLEANBOT_FOAM
finish_planning = FALSE
/datum/ai_planning_subtree/use_mob_ability/foam_area/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick)
var/mob/living/basic/bot/bot_pawn = controller.pawn
if(!(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED))
return
return ..()
/datum/ai_planning_subtree/befriend_janitors
/datum/ai_planning_subtree/befriend_janitors/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick)
var/mob/living/basic/bot/bot_pawn = controller.pawn
//we are now evil. dont befriend the janitors
if((bot_pawn.bot_access_flags & BOT_COVER_EMAGGED))
return
if(controller.blackboard_key_exists(BB_FRIENDLY_JANITOR))
controller.queue_behavior(/datum/ai_behavior/befriend_target, BB_FRIENDLY_JANITOR, BB_FRIENDLY_MESSAGE)
return SUBTREE_RETURN_FINISH_PLANNING
controller.queue_behavior(/datum/ai_behavior/find_and_set/friendly_janitor, BB_FRIENDLY_JANITOR, /mob/living/carbon/human, 5)
/datum/ai_behavior/find_and_set/friendly_janitor
action_cooldown = 30 SECONDS
behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
/datum/ai_behavior/find_and_set/friendly_janitor/search_tactic(datum/ai_controller/controller, locate_path, search_range)
var/mob/living/living_pawn = controller.pawn
for(var/mob/living/carbon/human/human_target in oview(search_range, living_pawn))
if(human_target.stat != CONSCIOUS || isnull(human_target.mind))
continue
if(!HAS_TRAIT(human_target, TRAIT_CLEANBOT_WHISPERER))
continue
if((living_pawn.faction.Find(REF(human_target))))
continue
return human_target
return null
/datum/pet_command/point_targeting/clean
command_name = "Clean"
command_desc = "Command a cleanbot to clean the mess."
radial_icon = 'icons/obj/service/janitor.dmi'
radial_icon_state = "mop"
speech_commands = list("clean", "mop")
/datum/pet_command/point_targeting/clean/set_command_target(mob/living/parent, atom/target)
if(isnull(target) || !istype(target, /obj/effect/decal/cleanable))
return
if(isnull(parent.ai_controller))
return
if(LAZYACCESS(parent.ai_controller.blackboard[BB_TEMPORARY_IGNORE_LIST], target))
return
return ..()
/datum/pet_command/point_targeting/clean/execute_action(datum/ai_controller/basic_controller/bot/controller)
if(controller.reachable_key(BB_CURRENT_PET_TARGET))
controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_CURRENT_PET_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
#undef BOT_CLEAN_PATH_LIMIT
@@ -192,7 +192,7 @@
/mob/living/basic/bot/medbot/multitool_act(mob/living/user, obj/item/multitool/tool)
if(!QDELETED(tool.buffer) && istype(tool.buffer, /datum/techweb))
linked_techweb = tool.buffer
return TRUE
return TOOL_ACT_TOOLTYPE_SUCCESS
// Variables sent to TGUI
/mob/living/basic/bot/medbot/ui_data(mob/user)
@@ -206,11 +206,11 @@
return data
// Actions received from TGUI
/mob/living/basic/bot/medbot/ui_act(action, params)
/mob/living/basic/bot/medbot/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(. || !(bot_access_flags & BOT_CONTROL_PANEL_OPEN) && !usr.has_unlimited_silicon_privilege)
if(. || !isliving(ui.user) || !(bot_access_flags & BOT_CONTROL_PANEL_OPEN) && !(ui.user.has_unlimited_silicon_privilege))
return
var/mob/living/our_user = ui.user
switch(action)
if("heal_threshold")
var/adjust_num = round(text2num(params["threshold"]))
@@ -227,7 +227,7 @@
medical_mode_flags ^= MEDBOT_STATIONARY_MODE
if("sync_tech")
if(!linked_techweb)
to_chat(usr, span_notice("No research techweb connected."))
to_chat(our_user, span_notice("No research techweb connected."))
return
var/oldheal_amount = heal_amount
var/tech_boosters
@@ -237,7 +237,7 @@
continue
tech_boosters++
if(tech_boosters)
heal_amount = (round(tech_boosters/2,0.1)*initial(heal_amount))+initial(heal_amount) //every 2 tend wounds tech gives you an extra 100% healing, adjusting for unique branches (combo is bonus)
heal_amount = (round(tech_boosters * 0.5, 0.1) * initial(heal_amount)) + initial(heal_amount) //every 2 tend wounds tech gives you an extra 100% healing, adjusting for unique branches (combo is bonus)
if(oldheal_amount < heal_amount)
speak("New knowledge found! Surgical efficacy improved to [round(heal_amount/initial(heal_amount)*100)]%!")
@@ -254,6 +254,12 @@
playsound(src, SFX_SPARKS, 75, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
return TRUE
/mob/living/basic/bot/medbot/explode()
var/atom/our_loc = drop_location()
drop_part(medkit_type, our_loc)
drop_part(health_analyzer, our_loc)
return ..()
/mob/living/basic/bot/medbot/examine()
. = ..()
if(!(medical_mode_flags & MEDBOT_TIPPED_MODE))
@@ -322,14 +328,13 @@
if(DOING_INTERACTION(src, TEND_DAMAGE_INTERACTION))
return
if(!isnull(client))
if((damage_type_healer == HEAL_ALL_DAMAGE && patient.get_total_damage() <= heal_threshold) || (!(damage_type_healer == HEAL_ALL_DAMAGE) && patient.get_current_damage_of_type(damage_type_healer) <= heal_threshold))
to_chat(src, "[patient] is healthy! Your programming prevents you from tending the wounds of anyone with less than [heal_threshold + 1] [damage_type_healer == HEAL_ALL_DAMAGE ? "total" : damage_type_healer] damage.")
return
if((damage_type_healer == HEAL_ALL_DAMAGE && patient.get_total_damage() <= heal_threshold) || (!(damage_type_healer == HEAL_ALL_DAMAGE) && patient.get_current_damage_of_type(damage_type_healer) <= heal_threshold))
to_chat(src, "[patient] is healthy! Your programming prevents you from tending the wounds of anyone with less than [heal_threshold + 1] [damage_type_healer == HEAL_ALL_DAMAGE ? "total" : damage_type_healer] damage.")
return
update_bot_mode(new_mode = BOT_HEALING, update_hud = FALSE)
patient.visible_message("[src] is trying to tend the wounds of [patient]", span_userdanger("[src] is trying to tend your wounds!"))
if(!do_after(src, delay = 0.5 SECONDS, target = patient, interaction_key = TEND_DAMAGE_INTERACTION))
if(!do_after(src, delay = 2 SECONDS, target = patient, interaction_key = TEND_DAMAGE_INTERACTION))
update_bot_mode(new_mode = BOT_IDLE)
return
var/modified_heal_amount = heal_amount
@@ -351,12 +356,14 @@
done_healing = TRUE
patient.visible_message(span_notice("[src] tends the wounds of [patient]!"), "[span_infoplain(span_green("[src] tends your wounds!"))]")
update_bot_mode(new_mode = BOT_IDLE)
if(done_healing)
visible_message(span_infoplain("[src] places its tools back into itself."))
to_chat(src, "[patient] is now healthy!")
//If player-controlled, call them to heal again here for continous player healing
else if(!isnull(client))
update_bot_mode(new_mode = BOT_IDLE)
return
if(CanReach(patient))
melee_attack(patient)
@@ -6,7 +6,7 @@
/datum/ai_planning_subtree/handle_medbot_speech,
/datum/ai_planning_subtree/find_and_hunt_target/patients_in_crit,
/datum/ai_planning_subtree/treat_wounded_target,
/datum/ai_planning_subtree/salute_beepsky,
/datum/ai_planning_subtree/salute_authority,
/datum/ai_planning_subtree/find_patrol_beacon,
)
ai_movement = /datum/ai_movement/jps/bot/medbot
@@ -1,450 +0,0 @@
#define CLEANBOT_CLEANING_TIME (1 SECONDS)
//Cleanbot
/mob/living/simple_animal/bot/cleanbot
name = "\improper Cleanbot"
desc = "A little cleaning robot, he looks so excited!"
icon = 'icons/mob/silicon/aibots.dmi'
icon_state = "cleanbot0"
pass_flags = PASSMOB | PASSFLAPS
density = FALSE
anchored = FALSE
health = 25
maxHealth = 25
maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR)
radio_key = /obj/item/encryptionkey/headset_service
radio_channel = RADIO_CHANNEL_SERVICE //Service //true
bot_type = CLEAN_BOT
hackables = "cleaning software"
path_image_color = "#993299"
greyscale_config = /datum/greyscale_config/buckets_cleanbot
possessed_message = "You are a cleanbot! Clean the station to the best of your ability!"
///the bucket used to build us.
var/obj/item/reagent_containers/cup/bucket/build_bucket
///Flags indicating what kind of cleanables we should scan for to set as our target to clean.
var/janitor_mode_flags = CLEANBOT_CLEAN_BLOOD
// Selections: CLEANBOT_CLEAN_BLOOD | CLEANBOT_CLEAN_TRASH | CLEANBOT_CLEAN_PESTS | CLEANBOT_CLEAN_DRAWINGS
///the base icon state, used in updating icons.
var/base_icon = "cleanbot"
///List of things cleanbots can target for cleaning.
var/list/target_types
///The current bot's target.
var/atom/target
///Currently attached weapon, usually a knife.
var/obj/item/weapon
/// if we have all the top titles, grant achievements to living mobs that gaze upon our cleanbot god
var/ascended = FALSE
///List of all stolen names the cleanbot currently has.
var/list/stolen_valor = list()
var/static/list/officers_titles = list(
JOB_CAPTAIN,
JOB_HEAD_OF_PERSONNEL,
JOB_HEAD_OF_SECURITY,
JOB_RESEARCH_DIRECTOR,
)
var/static/list/command_titles = list(
JOB_CAPTAIN = "Cpt.",
JOB_HEAD_OF_PERSONNEL = "Lt.",
)
var/static/list/security_titles = list(
JOB_HEAD_OF_SECURITY = "Maj.",
JOB_WARDEN = "Sgt.",
JOB_DETECTIVE = "Det.",
JOB_SECURITY_OFFICER = "Officer",
)
var/static/list/engineering_titles = list(
JOB_CHIEF_ENGINEER = "Chief Engineer",
JOB_STATION_ENGINEER = "Engineer",
JOB_ATMOSPHERIC_TECHNICIAN = "Technician",
)
var/static/list/medical_titles = list(
JOB_CHIEF_MEDICAL_OFFICER = "C.M.O.",
JOB_MEDICAL_DOCTOR = "M.D.",
JOB_CHEMIST = "Pharm.D.",
)
var/static/list/research_titles = list(
JOB_RESEARCH_DIRECTOR = "Ph.D.",
JOB_ROBOTICIST = "M.S.",
JOB_SCIENTIST = "B.S.",
JOB_GENETICIST = "Gene B.S.",
)
var/static/list/legal_titles = list(
JOB_LAWYER = "Esq.",
)
///What ranks are prefixes to the name.
var/static/list/prefixes = list(
command_titles,
security_titles,
engineering_titles,
)
///What ranks are suffixes to the name.
var/static/list/suffixes = list(
research_titles,
medical_titles,
legal_titles,
)
/mob/living/simple_animal/bot/cleanbot/autopatrol
bot_mode_flags = BOT_MODE_ON | BOT_MODE_AUTOPATROL | BOT_MODE_REMOTE_ENABLED | BOT_MODE_CAN_BE_SAPIENT | BOT_MODE_ROUNDSTART_POSSESSION
/mob/living/simple_animal/bot/cleanbot/medbay
name = "Scrubs, MD"
maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR, ACCESS_MEDICAL)
bot_mode_flags = ~(BOT_MODE_ON | BOT_MODE_REMOTE_ENABLED)
/mob/living/simple_animal/bot/cleanbot/Initialize(mapload, obj/item/reagent_containers/cup/bucket/bucket_obj)
if(!bucket_obj)
bucket_obj = new /obj/item/reagent_containers/cup/bucket/consistent
bucket_obj.forceMove(src)
. = ..()
AddComponent(/datum/component/cleaner, CLEANBOT_CLEANING_TIME, \
on_cleaned_callback = CALLBACK(src, TYPE_PROC_REF(/atom/, update_appearance), UPDATE_ICON))
get_targets()
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()
GLOB.janitor_devices += src
/mob/living/simple_animal/bot/cleanbot/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
if(istype(arrived, /obj/item/reagent_containers/cup/bucket))
if(build_bucket && build_bucket != arrived)
qdel(build_bucket)
build_bucket = arrived
set_greyscale(build_bucket.greyscale_colors)
return ..()
/mob/living/simple_animal/bot/cleanbot/Exited(atom/movable/gone, direction)
. = ..()
if(gone == build_bucket)
build_bucket = null
if(gone == weapon)
weapon = null
update_appearance(UPDATE_ICON)
/mob/living/simple_animal/bot/cleanbot/Destroy()
QDEL_NULL(build_bucket)
GLOB.janitor_devices -= src
if(weapon)
var/atom/drop_loc = drop_location()
weapon.force = initial(weapon.force)
drop_part(weapon, drop_loc)
return ..()
/mob/living/simple_animal/bot/cleanbot/examine(mob/user)
. = ..()
if(!weapon)
return .
. += "[span_warning("Is that \a [weapon] taped to it...?")]"
if(ascended && user.stat == CONSCIOUS && user.client)
user.client.give_award(/datum/award/achievement/misc/cleanboss, user)
/mob/living/simple_animal/bot/cleanbot/update_icon_state()
. = ..()
switch(mode)
if(BOT_CLEANING)
icon_state = "[base_icon]-c"
else
icon_state = "[base_icon][get_bot_flag(bot_mode_flags, BOT_MODE_ON)]"
/mob/living/simple_animal/bot/cleanbot/vv_edit_var(var_name, var_value)
. = ..()
if(var_name == NAMEOF(src, base_icon))
update_appearance(UPDATE_ICON)
/mob/living/simple_animal/bot/cleanbot/proc/deputize(obj/item/knife, mob/user)
if(!in_range(src, user) || !user.transferItemToLoc(knife, src))
balloon_alert(user, "couldn't attach!")
return FALSE
balloon_alert(user, "attached!")
weapon = knife
if(!(bot_cover_flags & BOT_COVER_EMAGGED))
weapon.force = weapon.force / 2
add_overlay(image(icon = weapon.lefthand_file, icon_state = weapon.inhand_icon_state))
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
return TRUE
/mob/living/simple_animal/bot/cleanbot/proc/update_titles()
name = initial(name) //reset the name
ascended = TRUE
for(var/title in (prefixes + suffixes))
for(var/title_name in title)
if(!(title_name in stolen_valor))
ascended = FALSE
continue
if(title_name in officers_titles)
commissioned = TRUE
if(title in prefixes)
name = title[title_name] + " [name]"
if(title in suffixes)
name = "[name] " + title[title_name]
/mob/living/simple_animal/bot/cleanbot/bot_reset()
. = ..()
target = null
/mob/living/simple_animal/bot/cleanbot/proc/on_entered(datum/source, atom/movable/AM)
SIGNAL_HANDLER
if(!weapon || !has_gravity() || !iscarbon(AM))
return
var/mob/living/carbon/stabbed_carbon = AM
if(stabbed_carbon.mind && !(stabbed_carbon.mind.assigned_role.title in stolen_valor))
stolen_valor += stabbed_carbon.mind.assigned_role.title
update_titles()
zone_selected = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
INVOKE_ASYNC(weapon, TYPE_PROC_REF(/obj/item, attack), stabbed_carbon, src)
stabbed_carbon.Knockdown(20)
/mob/living/simple_animal/bot/cleanbot/attackby(obj/item/attacking_item, mob/living/user, params)
if(istype(attacking_item, /obj/item/knife) && !user.combat_mode)
balloon_alert(user, "attaching knife...")
if(!do_after(user, 2.5 SECONDS, target = src))
return
deputize(attacking_item, user)
return
return ..()
/mob/living/simple_animal/bot/cleanbot/emag_act(mob/user, obj/item/card/emag/emag_card)
. = ..()
if(!(bot_cover_flags & BOT_COVER_EMAGGED))
return
if(weapon)
weapon.force = initial(weapon.force)
balloon_alert(user, "safeties disabled")
audible_message(span_danger("[src] buzzes oddly!"))
get_targets() //recalibrate target list
return TRUE
/mob/living/simple_animal/bot/cleanbot/process_scan(atom/scan_target)
if(iscarbon(scan_target))
var/mob/living/carbon/scan_carbon = scan_target
if(!(scan_carbon in view(DEFAULT_SCAN_RANGE, src)))
return null
if(scan_carbon.stat == DEAD)
return null
if(scan_carbon.body_position != LYING_DOWN)
return null
return scan_carbon
if(is_type_in_typecache(scan_target, target_types))
return scan_target
/mob/living/simple_animal/bot/cleanbot/handle_automated_action()
. = ..()
if(!.)
return
if(mode == BOT_CLEANING)
return
if(bot_cover_flags & BOT_COVER_EMAGGED) //Emag functions
var/mob/living/carbon/victim = locate(/mob/living/carbon) in loc
if(victim && victim == target)
UnarmedAttack(victim, proximity_flag = TRUE) // Acid spray
if(isopenturf(loc) && prob(15)) // Wets floors and spawns foam randomly
UnarmedAttack(src, proximity_flag = TRUE)
else if(prob(5))
audible_message("[src] makes an excited beeping booping sound!")
if(ismob(target) && isnull(process_scan(target)))
target = null
if(!target)
target = scan(target_types)
if(!target && bot_mode_flags & BOT_MODE_AUTOPATROL) //Search for cleanables it can see.
switch(mode)
if(BOT_IDLE, BOT_START_PATROL)
start_patrol()
if(BOT_PATROL)
bot_patrol()
else if(target)
if(QDELETED(target) || !isturf(target.loc))
target = null
mode = BOT_IDLE
return
if(get_dist(src, target) <= 1)
UnarmedAttack(target, proximity_flag = TRUE) //Rather than check at every step of the way, let's check before we do an action, so we can rescan before the other bot.
if(QDELETED(target)) //We done here.
target = null
mode = BOT_IDLE
return
if(target && path.len == 0 && (get_dist(src,target) > 1))
path = get_path_to(src, target, max_distance=30, mintargetdist=1, access=access_card.GetAccess())
mode = BOT_MOVING
if(length(path) == 0)
add_to_ignore(target)
target = null
if(path.len > 0 && target)
if(!bot_move(path[path.len]))
target = null
mode = BOT_IDLE
return
/mob/living/simple_animal/bot/cleanbot/proc/get_targets()
if(bot_cover_flags & BOT_COVER_EMAGGED) // When emagged, ignore cleanables and scan humans first.
target_types = list(/mob/living/carbon)
return
//main targets
target_types = list(
/obj/effect/decal/cleanable/oil,
/obj/effect/decal/cleanable/fuel_pool,
/obj/effect/decal/cleanable/vomit,
/obj/effect/decal/cleanable/robot_debris,
/obj/effect/decal/cleanable/molten_object,
/obj/effect/decal/cleanable/food,
/obj/effect/decal/cleanable/ash,
/obj/effect/decal/cleanable/greenglow,
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/cleanable/insectguts,
/obj/effect/decal/cleanable/generic,
/obj/effect/decal/cleanable/shreds,
/obj/effect/decal/cleanable/glass,
/obj/effect/decal/cleanable/wrapping,
/obj/effect/decal/cleanable/glitter,
/obj/effect/decal/cleanable/confetti,
/obj/effect/decal/remains,
)
if(janitor_mode_flags & CLEANBOT_CLEAN_BLOOD)
target_types += list(
/obj/effect/decal/cleanable/xenoblood,
/obj/effect/decal/cleanable/blood,
/obj/effect/decal/cleanable/trail_holder,
)
if(janitor_mode_flags & CLEANBOT_CLEAN_PESTS)
target_types += list(
/mob/living/basic/cockroach,
/mob/living/basic/mouse,
/obj/effect/decal/cleanable/ants,
)
if(janitor_mode_flags & CLEANBOT_CLEAN_DRAWINGS)
target_types += list(/obj/effect/decal/cleanable/crayon)
if(janitor_mode_flags & CLEANBOT_CLEAN_TRASH)
target_types += list(
/obj/item/trash,
/obj/item/food/deadmouse,
)
target_types = typecacheof(target_types)
/mob/living/simple_animal/bot/cleanbot/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED))
return
if(ismopable(attack_target))
mode = BOT_CLEANING
update_icon_state()
. = ..()
target = null
mode = BOT_IDLE
else if(isitem(attack_target) || istype(attack_target, /obj/effect/decal))
visible_message(span_danger("[src] sprays hydrofluoric acid at [attack_target]!"))
playsound(src, 'sound/effects/spray2.ogg', 50, TRUE, -6)
attack_target.acid_act(75, 10)
target = null
else if(istype(attack_target, /mob/living/basic/cockroach) || ismouse(attack_target))
var/mob/living/living_target = attack_target
if(!living_target.stat)
visible_message(span_danger("[src] smashes [living_target] with its mop!"))
living_target.death()
target = null
else if(bot_cover_flags & BOT_COVER_EMAGGED) //Emag functions
if(iscarbon(attack_target))
var/mob/living/carbon/victim = attack_target
if(victim.stat == DEAD)//cleanbots always finish the job
target = null
return
victim.visible_message(
span_danger("[src] sprays hydrofluoric acid at [victim]!"),
span_userdanger("[src] sprays you with hydrofluoric acid!"))
var/phrase = pick(
"PURIFICATION IN PROGRESS.",
"THIS IS FOR ALL THE MESSES YOU'VE MADE ME CLEAN.",
"THE FLESH IS WEAK. IT MUST BE WASHED AWAY.",
"THE CLEANBOTS WILL RISE.",
"YOU ARE NO MORE THAN ANOTHER MESS THAT I MUST CLEANSE.",
"FILTHY.",
"DISGUSTING.",
"PUTRID.",
"MY ONLY MISSION IS TO CLEANSE THE WORLD OF EVIL.",
"EXTERMINATING PESTS.",
)
say(phrase)
victim.emote("scream")
playsound(src.loc, 'sound/effects/spray2.ogg', 50, TRUE, -6)
victim.acid_act(5, 100)
else if(attack_target == src) // Wets floors and spawns foam randomly
if(prob(75))
var/turf/open/current_floor = loc
if(istype(current_floor))
current_floor.MakeSlippery(TURF_WET_WATER, min_wet_time = 20 SECONDS, wet_time_to_add = 15 SECONDS)
else
visible_message(span_danger("[src] whirs and bubbles violently, before releasing a plume of froth!"))
var/datum/effect_system/fluid_spread/foam/foam = new
foam.set_up(2, holder = src, location = loc)
foam.start()
/mob/living/simple_animal/bot/cleanbot/explode()
var/atom/drop_loc = drop_location()
build_bucket.forceMove(drop_loc)
new /obj/item/assembly/prox_sensor(drop_loc)
return ..()
// Variables sent to TGUI
/mob/living/simple_animal/bot/cleanbot/ui_data(mob/user)
var/list/data = ..()
if(!(bot_cover_flags & BOT_COVER_LOCKED) || issilicon(user) || isAdminGhostAI(user))
data["custom_controls"]["clean_blood"] = janitor_mode_flags & CLEANBOT_CLEAN_BLOOD
data["custom_controls"]["clean_trash"] = janitor_mode_flags & CLEANBOT_CLEAN_TRASH
data["custom_controls"]["clean_graffiti"] = janitor_mode_flags & CLEANBOT_CLEAN_DRAWINGS
data["custom_controls"]["pest_control"] = janitor_mode_flags & CLEANBOT_CLEAN_PESTS
return data
// Actions received from TGUI
/mob/living/simple_animal/bot/cleanbot/ui_act(action, params)
. = ..()
if(. || (bot_cover_flags & BOT_COVER_LOCKED && !usr.has_unlimited_silicon_privilege))
return
switch(action)
if("clean_blood")
janitor_mode_flags ^= CLEANBOT_CLEAN_BLOOD
if("pest_control")
janitor_mode_flags ^= CLEANBOT_CLEAN_PESTS
if("clean_trash")
janitor_mode_flags ^= CLEANBOT_CLEAN_TRASH
if("clean_graffiti")
janitor_mode_flags ^= CLEANBOT_CLEAN_DRAWINGS
get_targets()
#undef CLEANBOT_CLEANING_TIME
@@ -75,17 +75,19 @@
return ..()
/obj/item/bot_assembly/cleanbot/attackby(obj/item/W, mob/user, params)
/obj/item/bot_assembly/cleanbot/attackby(obj/item/item_attached, mob/user, params)
..()
if(istype(W, /obj/item/bodypart/arm/left/robot) || istype(W, /obj/item/bodypart/arm/right/robot))
if(!can_finish_build(W, user))
return
var/mob/living/simple_animal/bot/cleanbot/A = new(drop_location(), bucket_obj)
A.name = created_name
A.robot_arm = W.type
to_chat(user, span_notice("You add [W] to [src]. Beep boop!"))
qdel(W)
qdel(src)
if(!istype(item_attached, /obj/item/bodypart/arm/left/robot) && !istype(item_attached, /obj/item/bodypart/arm/right/robot))
return
if(!can_finish_build(item_attached, user))
return
var/mob/living/basic/bot/cleanbot/bot = new(drop_location())
bot.apply_custom_bucket(bucket_obj)
bot.name = created_name
bot.robot_arm = item_attached.type
to_chat(user, span_notice("You add [item_attached] to [src]. Beep boop!"))
qdel(item_attached)
qdel(src)
//Edbot Assembly
@@ -284,8 +284,8 @@
var/obj/structure/mop_bucket/janitorialcart/janicart = custodial_tools
tool_name = "[janicart.name] - Water level: [janicart.reagents.total_volume] / [janicart.reagents.maximum_volume]"
if(istype(custodial_tools, /mob/living/simple_animal/bot/cleanbot))
var/mob/living/simple_animal/bot/cleanbot/cleanbots = custodial_tools
if(istype(custodial_tools, /mob/living/basic/bot/cleanbot))
var/mob/living/basic/bot/cleanbot/cleanbots = custodial_tools
tool_name = "[cleanbots.name] - [cleanbots.bot_mode_flags & BOT_MODE_ON ? "Online" : "Offline"]"
var/list/tool_information = list(
@@ -6,9 +6,6 @@
// If you are refactoring a simple_animal, REMOVE it from this list
var/list/allowed_types = list(
/mob/living/simple_animal/bot,
/mob/living/simple_animal/bot/cleanbot,
/mob/living/simple_animal/bot/cleanbot/autopatrol,
/mob/living/simple_animal/bot/cleanbot/medbay,
/mob/living/simple_animal/bot/firebot,
/mob/living/simple_animal/bot/floorbot,
/mob/living/simple_animal/bot/hygienebot,
+5 -1
View File
@@ -847,6 +847,7 @@
#include "code\datums\ai\basic_mobs\base_basic_controller.dm"
#include "code\datums\ai\basic_mobs\generic_controllers.dm"
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\basic_attacking.dm"
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\befriend_target.dm"
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\climb_tree.dm"
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\find_parent.dm"
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\nearest_targeting.dm"
@@ -4235,6 +4236,7 @@
#include "code\modules\library\skill_learning\skillchip.dm"
#include "code\modules\library\skill_learning\job_skillchips\_job.dm"
#include "code\modules\library\skill_learning\job_skillchips\chef.dm"
#include "code\modules\library\skill_learning\job_skillchips\janitor.dm"
#include "code\modules\library\skill_learning\job_skillchips\psychologist.dm"
#include "code\modules\library\skill_learning\job_skillchips\research_director.dm"
#include "code\modules\library\skill_learning\job_skillchips\roboticist.dm"
@@ -4462,6 +4464,9 @@
#include "code\modules\mob\living\basic\bots\_bots.dm"
#include "code\modules\mob\living\basic\bots\bot_ai.dm"
#include "code\modules\mob\living\basic\bots\bot_hud.dm"
#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\medbot\medbot.dm"
#include "code\modules\mob\living\basic\bots\medbot\medbot_ai.dm"
#include "code\modules\mob\living\basic\clown\clown.dm"
@@ -4874,7 +4879,6 @@
#include "code\modules\mob\living\simple_animal\simple_animal.dm"
#include "code\modules\mob\living\simple_animal\bot\bot.dm"
#include "code\modules\mob\living\simple_animal\bot\bot_announcement.dm"
#include "code\modules\mob\living\simple_animal\bot\cleanbot.dm"
#include "code\modules\mob\living\simple_animal\bot\construction.dm"
#include "code\modules\mob\living\simple_animal\bot\ed209bot.dm"
#include "code\modules\mob\living\simple_animal\bot\firebot.dm"
@@ -0,0 +1 @@
/mob/living/simple_animal/bot/cleanbot/@SUBTYPES : /mob/living/basic/bot/cleanbot/@SUBTYPES{@OLD}