Adds the shower bot, makes sure you don't become too bloody (#49083)

* Epicgamers

* woops

* Update code/modules/mob/living/simple_animal/bot/construction.dm

Co-Authored-By: TheChosenEvilOne <34602646+TheChosenEvilOne@users.noreply.github.com>

* slip

* fix

* epic

* emag functionality

* Update code/modules/mob/living/simple_animal/bot/hygienebot.dm

* Update code/modules/mob/living/simple_animal/bot/hygienebot.dm

* step 1

* woops

* fixes

* fixes

* fixes++

* fixes a broken thing

* lol ok

* fixes

* oopsie

* oneliner

* lmao webedits

* small brain

Co-authored-by: TheChosenEvilOne <34602646+TheChosenEvilOne@users.noreply.github.com>
This commit is contained in:
Qustinnus
2020-02-20 22:31:22 +01:00
committed by GitHub
co-authored by TheChosenEvilOne
parent 4de1c3845d
commit 72fb1458a7
15 changed files with 378 additions and 96 deletions
+4 -2
View File
@@ -4,11 +4,11 @@
//Bot defines, placed here so they can be read by other things!
#define BOT_STEP_DELAY 4 //Delay between movemements
#define BOT_STEP_MAX_RETRIES 5 //Maximum times a bot will retry to step from its position
#define BOT_STEP_MAX_RETRIES 5 //Maximum times a bot will retry to step from its position
#define DEFAULT_SCAN_RANGE 7 //default view range for finding targets.
//Mode defines
//Mode defines. If you add a new one make sure you update mode_name in /mob/living/simple_animal/bot
#define BOT_IDLE 0 // idle
#define BOT_HUNT 1 // found target, hunting
#define BOT_PREP_ARREST 2 // at target, preparing to arrest
@@ -27,6 +27,7 @@
#define BOT_NAV 15 // computing navigation
#define BOT_WAIT_FOR_NAV 16 // waiting for nav computation
#define BOT_NO_ROUTE 17 // no destination beacon found (or no route)
#define BOT_SHOWERSTANCE 18 // cleaning unhygienic humans
//Bot types
#define SEC_BOT (1<<0) // Secutritrons (Beepsky) and ED-209s
@@ -36,6 +37,7 @@
#define MED_BOT (1<<4) // Medibots
#define HONK_BOT (1<<5) // Honkbots & ED-Honks
#define FIRE_BOT (1<<6) // Firebots
#define HYGIENE_BOT (1<<7) // Hygienebots
//AI notification defines
#define NEW_BORG 1
+16
View File
@@ -862,6 +862,22 @@
color = C
return
///Proc for being washed by a shower
/atom/proc/washed(var/atom/washer)
. = SEND_SIGNAL(src, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
var/datum/component/radioactive/healthy_green_glow = GetComponent(/datum/component/radioactive)
if(!healthy_green_glow || QDELETED(healthy_green_glow))
return
var/strength = healthy_green_glow.strength
if(strength <= RAD_BACKGROUND_RADIATION)
qdel(healthy_green_glow)
return
healthy_green_glow.strength -= max(0, (healthy_green_glow.strength - (RAD_BACKGROUND_RADIATION * 2)) * 0.2)
/**
* call back when a var is edited on this atom
*
@@ -100,6 +100,9 @@
S.blood_state = blood_state
update_icon()
H.update_inv_shoes()
/atom/effect/decal/cleanable/washed(atom/washer)
. = ..()
qdel(src)
/obj/effect/decal/cleanable/proc/can_bloodcrawl_in()
if((blood_state != BLOOD_STATE_OIL) && (blood_state != BLOOD_STATE_NOT_BLOODY))
+2 -92
View File
@@ -98,102 +98,12 @@
wash_atom(AM)
/obj/machinery/shower/proc/wash_atom(atom/A)
SEND_SIGNAL(A, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
A.washed(src)
reagents.reaction(A, TOUCH, reaction_volume)
if(isobj(A))
wash_obj(A)
else if(isturf(A))
wash_turf(A)
else if(isliving(A))
wash_mob(A)
if(isliving(A))
check_heat(A)
contamination_cleanse(A)
/obj/machinery/shower/proc/wash_obj(obj/O)
. = SEND_SIGNAL(O, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
O.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
/obj/machinery/shower/proc/wash_turf(turf/tile)
SEND_SIGNAL(tile, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
tile.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
for(var/obj/effect/E in tile)
if(is_cleanable(E))
qdel(E)
/obj/machinery/shower/proc/wash_mob(mob/living/L)
SEND_SIGNAL(L, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
L.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "shower", /datum/mood_event/nice_shower)
if(iscarbon(L))
var/mob/living/carbon/M = L
. = TRUE
for(var/obj/item/I in M.held_items)
wash_obj(I)
if(M.back && wash_obj(M.back))
M.update_inv_back(0)
var/list/obscured = M.check_obscured_slots()
if(M.head && wash_obj(M.head))
M.update_inv_head()
if(M.glasses && !(ITEM_SLOT_EYES in obscured) && wash_obj(M.glasses))
M.update_inv_glasses()
if(M.wear_mask && !(ITEM_SLOT_MASK in obscured) && wash_obj(M.wear_mask))
M.update_inv_wear_mask()
if(M.ears && !(HIDEEARS in obscured) && wash_obj(M.ears))
M.update_inv_ears()
if(M.wear_neck && !(ITEM_SLOT_NECK in obscured) && wash_obj(M.wear_neck))
M.update_inv_neck()
if(M.shoes && !(HIDESHOES in obscured) && wash_obj(M.shoes))
M.update_inv_shoes()
var/washgloves = FALSE
if(M.gloves && !(HIDEGLOVES in obscured))
washgloves = TRUE
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.wear_suit && wash_obj(H.wear_suit))
H.update_inv_wear_suit()
else if(H.w_uniform && wash_obj(H.w_uniform))
H.update_inv_w_uniform()
if(washgloves)
SEND_SIGNAL(H, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
if(!H.is_mouth_covered())
H.lip_style = null
H.update_body()
if(H.belt && wash_obj(H.belt))
H.update_inv_belt()
else
SEND_SIGNAL(M, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
else
SEND_SIGNAL(L, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
/obj/machinery/shower/proc/contamination_cleanse(atom/thing)
var/datum/component/radioactive/healthy_green_glow = thing.GetComponent(/datum/component/radioactive)
if(!healthy_green_glow || QDELETED(healthy_green_glow))
return
var/strength = healthy_green_glow.strength
if(strength <= RAD_BACKGROUND_RADIATION)
qdel(healthy_green_glow)
return
healthy_green_glow.strength -= max(0, (healthy_green_glow.strength - (RAD_BACKGROUND_RADIATION * 2)) * 0.2)
/obj/machinery/shower/process()
if(on)
wash_atom(loc)
+3
View File
@@ -144,6 +144,9 @@
/atom/movable/lighting_object/onTransitZ()
return
/atom/movable/lighting_object/washed(var/washer)
return
// Override here to prevent things accidentally moving around overlays.
/atom/movable/lighting_object/forceMove(atom/destination, no_tp=FALSE, harderforce = FALSE)
if(harderforce)
+30
View File
@@ -1038,3 +1038,33 @@
if(mood)
if(mood.sanity < SANITY_UNSTABLE)
return TRUE
/mob/living/carbon/washed(var/atom/washer)
. = ..()
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "shower", /datum/mood_event/nice_shower)
for(var/obj/item/I in held_items)
I.washed(washer)
if(back)
update_inv_back(0)
var/list/obscured = check_obscured_slots()
if(head && head.washed(washer))
update_inv_head()
if(glasses && !(ITEM_SLOT_EYES in obscured) && glasses.washed(washer))
update_inv_glasses()
if(wear_mask && !(ITEM_SLOT_MASK in obscured && wear_mask.washed(washer)))
update_inv_wear_mask()
if(ears && !(HIDEEARS in obscured) && ears.washed(washer))
update_inv_ears()
if(wear_neck && !(ITEM_SLOT_NECK in obscured) && wear_neck.washed(washer))
update_inv_neck()
if(shoes && !(HIDESHOES in obscured) && shoes.washed(washer))
update_inv_shoes()
@@ -1087,6 +1087,24 @@
remove_movespeed_modifier(MOVESPEED_ID_DAMAGE_SLOWDOWN_FLYING)
/mob/living/carbon/human/washed(var/atom/washer)
. = ..()
if(wear_suit)
update_inv_wear_suit()
else if(w_uniform && w_uniform.washed(washer))
update_inv_w_uniform()
if(!is_mouth_covered())
lip_style = null
update_body()
if(belt && belt.washed(washer))
update_inv_belt()
var/list/obscured = check_obscured_slots()
if(gloves && !(HIDEGLOVES in obscured) && gloves.washed(washer))
SEND_SIGNAL(src, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
/mob/living/carbon/human/adjust_nutrition(var/change) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks
if(HAS_TRAIT(src, TRAIT_NOHUNGER))
return FALSE
+5
View File
@@ -1113,6 +1113,11 @@
//Mobs on Fire end
//Washing
/mob/living/washed(var/atom/washer)
. = ..()
SEND_SIGNAL(src, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_STRENGTH_BLOOD)
// used by secbot and monkeys Crossed
/mob/living/proc/knockOver(var/mob/living/carbon/C)
if(C.key) //save us from monkey hordes
@@ -77,12 +77,12 @@
var/model = "" //The type of bot it is.
var/bot_type = 0 //The type of bot it is, for radio control.
var/data_hud_type = DATA_HUD_DIAGNOSTIC_BASIC //The type of data HUD the bot uses. Diagnostic by default.
//This holds text for what the bot is mode doing, reported on the remote bot control interface.
//This holds text for what the bot is mode doing, reported on the remote bot control interface. This is in order of the defines for the mode defines in robots.dm, in order
var/list/mode_name = list("In Pursuit","Preparing to Arrest", "Arresting", \
"Beginning Patrol", "Patrolling", "Summoned by PDA", \
"Cleaning", "Repairing", "Proceeding to work site", "Healing", \
"Proceeding to AI waypoint", "Navigating to Delivery Location", "Navigating to Home", \
"Waiting for clear path", "Calculating navigation path", "Pinging beacon network", "Unable to reach destination")
"Waiting for clear path", "Calculating navigation path", "Pinging beacon network", "Unable to reach destination", "Chasing filth")
var/datum/atom_hud/data/bot_path/path_hud = new /datum/atom_hud/data/bot_path()
var/path_image_icon = 'icons/mob/aibots.dmi'
var/path_image_icon_state = "path_indicator"
@@ -450,3 +450,43 @@
F.name = created_name
qdel(I)
qdel(src)
//Get cleaned
/obj/item/bot_assembly/hygienebot
name = "incomplete hygienebot assembly"
desc = "Clear out the swamp once and for all"
icon = 'icons/obj/watercloset.dmi'
icon_state = "drone"
created_name = "Hygienebot"
/obj/item/bot_assembly/hygienebot/attackby(obj/item/I, mob/user, params)
. = ..()
switch(build_step)
if(ASSEMBLY_FIRST_STEP)
if(I.tool_behaviour == TOOL_WELDER)
if(I.use_tool(src, user, 0, volume=40))
add_overlay("hs_hole")
to_chat(user, "<span class='notice'>You weld a water hole in [src]!</span>")
build_step++
if(ASSEMBLY_SECOND_STEP)
if(!can_finish_build(I, user))
return
if(istype(I, /obj/item/stack/ducts))
var/obj/item/stack/ducts/D = I
if(D.get_amount() < 1)
to_chat(user, "<span class='warning'>You need one fluid duct to finish [src]</span>")
return
to_chat(user, "<span class='notice'>You start to pipe up [src]...</span>")
if(do_after(user, 40, target = src))
if(D.get_amount() >= 1)
D.use(1)
to_chat(user, "<span class='notice'>You pipe up [src].</span>")
build_step++
else
return
var/mob/living/simple_animal/bot/hygienebot/H = new(drop_location())
H.name = created_name
qdel(I)
qdel(src)
return TRUE
@@ -0,0 +1,254 @@
//Cleanbot
/mob/living/simple_animal/bot/hygienebot
name = "\improper Hygienebot"
desc = "A flying cleaning robot, he'll chase down people who can't shower properly!"
icon = 'icons/obj/watercloset.dmi'
icon_state = "drone"
density = FALSE
anchored = FALSE
health = 100
maxHealth = 100
radio_key = /obj/item/encryptionkey/headset_service
radio_channel = RADIO_CHANNEL_SERVICE //Service
bot_type = HYGIENE_BOT
model = "Cleanbot"
bot_core_type = /obj/machinery/bot_core/hygienebot
window_id = "autoclean"
window_name = "Automatic Crew Cleaner X2"
pass_flags = PASSMOB
path_image_color = "#993299"
allow_pai = FALSE
layer = ABOVE_MOB_LAYER
var/mob/living/carbon/human/target
var/currentspeed = 5
var/washing = FALSE
var/mad = FALSE
var/last_found
var/oldtarget_name
var/mutable_appearance/water_overlay
var/mutable_appearance/fire_overlay
/mob/living/simple_animal/bot/hygienebot/Initialize()
. = ..()
update_icon()
var/datum/job/janitor/J = new/datum/job/janitor
access_card.access += J.get_access()
prev_access = access_card.access
/mob/living/simple_animal/bot/hygienebot/explode()
walk_to(src,0)
visible_message("<span class='boldannounce'>[src] blows apart in a foamy explosion!</span>")
do_sparks(3, TRUE, src)
on = FALSE
new /obj/effect/particle_effect/foam(loc)
..()
/mob/living/simple_animal/bot/hygienebot/Cross(atom/movable/AM)
. = ..()
if(washing)
do_wash(AM)
/mob/living/simple_animal/bot/hygienebot/Crossed(atom/movable/AM)
. = ..()
if(washing)
do_wash(AM)
/mob/living/simple_animal/bot/hygienebot/update_icon_state()
. = ..()
if(on)
icon_state = "drone-on"
else
icon_state = "drone"
/mob/living/simple_animal/bot/hygienebot/update_overlays()
. = ..()
if(on)
var/mutable_appearance/fire_overlay = mutable_appearance(icon,"flame")
. +=fire_overlay
if(washing)
var/mutable_appearance/water_overlay = mutable_appearance(icon, emagged ? "dronefire" : "dronewater")
. += water_overlay
/mob/living/simple_animal/bot/hygienebot/turn_off()
..()
mode = BOT_IDLE
/mob/living/simple_animal/bot/hygienebot/bot_reset()
..()
target = null
oldtarget_name = null
walk_to(src,0)
last_found = world.time
/mob/living/simple_animal/bot/hygienebot/Crossed(atom/movable/AM)
. = ..()
if(washing)
do_wash(AM)
/mob/living/simple_animal/bot/hygienebot/Moved()
. = ..()
if(washing && isturf(loc) && !emagged)
var/turf/open/OT = loc
OT.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
/mob/living/simple_animal/bot/hygienebot/handle_automated_action()
if(!..())
return
if(washing)
do_wash(loc)
for(var/AM in loc)
do_wash(AM)
if(isopenturf(loc) && !emagged)
var/turf/open/tile = loc
tile.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS)
switch(mode)
if(BOT_IDLE) // idle
walk_to(src,0)
look_for_lowhygiene() // see if any disgusting fucks are in range
if(!mode && auto_patrol) // still idle, and set to patrol
mode = BOT_START_PATROL // switch to patrol mode
if(BOT_HUNT) // hunting for stinkman
// if can't reach stinkman for long enough, don't give up, try harder.
if(emagged) //lol fuck em up
currentspeed = 8
start_washing()
mad = TRUE
else
switch(frustration)
if(0 to 4)
currentspeed = 5
stop_washing()
mad = FALSE
if(4 to INFINITY)
currentspeed = 2.5
start_washing()
mad = TRUE
if(target)
if(target.loc == loc && isturf(target.loc)) //LADIES AND GENTLEMAN WE GOTEM PREPARE TO DUMP
start_washing()
if(mad)
speak("Well about fucking time you degenerate", "Fucking finally", "Thank god, you finally stopped")
playsound(loc, 'sound/effects/hygienebot_angry.ogg', 60, 1)
mad = FALSE
mode = BOT_SHOWERSTANCE
else
var/turf/olddist = get_dist(src, target)
walk_to(src, target,0, currentspeed)
if(mad && prob(60))
playsound(loc, 'sound/effects/hygienebot_angry.ogg', 60, 1)
speak(pick("Get back here you foul smelling fucker.", "If you don't get back here right now I'm going to give you a fucking vasectomy.", "STOP RUNNING OR I WILL CUT YOUR ARTERIES!", "Just fucking let me clean you you arsehole!", "STOP. RUNNING.", "Either you stop running or I will fucking drag you out of an airlock.", "I just want to fucking clean you you troglodyte.", "If you don't come back here I'll put a green cloud around you cunt."))
if((get_dist(src, target)) >= (olddist))
frustration++
else
frustration = 0
else
back_to_idle()
if(BOT_SHOWERSTANCE)
if(check_purity(target))
speak("Enjoy your clean and tidy day!")
playsound(loc, 'sound/effects/hygienebot_happy.ogg', 60, 1)
back_to_idle()
return
if(!target)
last_found = world.time
if(target.loc != loc && !isturf(target.loc))
back_to_hunt()
if(BOT_START_PATROL)
look_for_lowhygiene()
start_patrol()
if(BOT_PATROL)
look_for_lowhygiene()
bot_patrol()
/mob/living/simple_animal/bot/hygienebot/proc/back_to_idle()
mode = BOT_IDLE
walk_to(src,0)
target = null
frustration = 0
last_found = world.time
stop_washing()
INVOKE_ASYNC(src, .proc/handle_automated_action)
/mob/living/simple_animal/bot/hygienebot/proc/back_to_hunt()
frustration = 0
mode = BOT_HUNT
stop_washing()
INVOKE_ASYNC(src, .proc/handle_automated_action)
/mob/living/simple_animal/bot/hygienebot/proc/look_for_lowhygiene()
for (var/mob/living/carbon/human/H in view(7,src)) //Find the NEET
if((H.name == oldtarget_name) && (world.time < last_found + 100))
continue
if(!check_purity(H)) //Theyre impure
target = H
oldtarget_name = H.name
speak("Unhygienic client found. Please stand still so I can clean you.")
playsound(loc, 'sound/effects/hygienebot_happy.ogg', 60, 1)
visible_message("<b>[src]</b> points at [H.name]!")
mode = BOT_HUNT
INVOKE_ASYNC(src, .proc/handle_automated_action)
break
else
continue
/mob/living/simple_animal/bot/hygienebot/proc/start_washing()
washing = TRUE
update_icon()
/mob/living/simple_animal/bot/hygienebot/proc/stop_washing()
washing = FALSE
update_icon()
/mob/living/simple_animal/bot/hygienebot/get_controls(mob/user)
var/list/dat = list()
dat += hack(user)
dat += showpai(user)
dat += {"
<TT><B>Hygienebot X2 controls</B></TT><BR><BR>
Status: ["<A href='?src=[REF(src)];power=[TRUE]'>[on ? "On" : "Off"]</A>"]<BR>
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
Maintenance panel is [open ? "opened" : "closed"]"}
if(!locked || issilicon(user) || IsAdminGhost(user))
dat += {"<BR> Auto Patrol: ["<A href='?src=[REF(src)];operation=patrol'>[auto_patrol ? "On" : "Off"]</A>"]"}
return dat.Join("")
/mob/living/simple_animal/bot/hygienebot/proc/check_purity(mob/living/L)
if(emagged && L.stat != DEAD)
return FALSE
for(var/X in list(ITEM_SLOT_HEAD, ITEM_SLOT_MASK, ITEM_SLOT_ICLOTHING, ITEM_SLOT_OCLOTHING, ITEM_SLOT_FEET))
var/obj/item/I = L.get_item_by_slot(X)
if(I && HAS_BLOOD_DNA(I))
return FALSE
return TRUE
/mob/living/simple_animal/bot/hygienebot/proc/do_wash(atom/A)
if(emagged)
A.fire_act()
return //lol pranked no cleaning besides that
else
A.washed(src)
/obj/machinery/bot_core/hygienebot
req_one_access = list(ACCESS_JANITOR, ACCESS_ROBOTICS)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.
Binary file not shown.
+1
View File
@@ -2235,6 +2235,7 @@
#include "code\modules\mob\living\simple_animal\bot\firebot.dm"
#include "code\modules\mob\living\simple_animal\bot\floorbot.dm"
#include "code\modules\mob\living\simple_animal\bot\honkbot.dm"
#include "code\modules\mob\living\simple_animal\bot\hygienebot.dm"
#include "code\modules\mob\living\simple_animal\bot\medbot.dm"
#include "code\modules\mob\living\simple_animal\bot\mulebot.dm"
#include "code\modules\mob\living\simple_animal\bot\secbot.dm"