mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-31 09:08:30 +01:00
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:
@@ -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"
|
||||
|
||||
@@ -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" = (
|
||||
|
||||
@@ -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" = (
|
||||
|
||||
@@ -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" = (
|
||||
|
||||
@@ -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" = (
|
||||
|
||||
@@ -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" = (
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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" = (
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.")
|
||||
@@ -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]"
|
||||
|
||||
@@ -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
@@ -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}
|
||||
Reference in New Issue
Block a user