diff --git a/baystation12.dme b/baystation12.dme
index 9a80618226..97c9f24ce3 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -366,12 +366,7 @@
#include "code\game\machinery\atmoalter\pump.dm"
#include "code\game\machinery\atmoalter\scrubber.dm"
#include "code\game\machinery\bots\bots.dm"
-#include "code\game\machinery\bots\cleanbot.dm"
-#include "code\game\machinery\bots\ed209bot.dm"
-#include "code\game\machinery\bots\floorbot.dm"
-#include "code\game\machinery\bots\medbot.dm"
#include "code\game\machinery\bots\mulebot.dm"
-#include "code\game\machinery\bots\secbot.dm"
#include "code\game\machinery\camera\camera.dm"
#include "code\game\machinery\camera\camera_assembly.dm"
#include "code\game\machinery\camera\motion.dm"
@@ -1073,6 +1068,13 @@
#include "code\modules\mob\living\logout.dm"
#include "code\modules\mob\living\say.dm"
#include "code\modules\mob\living\blob\blob.dm"
+#include "code\modules\mob\living\bot\bot.dm"
+#include "code\modules\mob\living\bot\cleanbot.dm"
+#include "code\modules\mob\living\bot\ed209bot.dm"
+#include "code\modules\mob\living\bot\farmbot.dm"
+#include "code\modules\mob\living\bot\floorbot.dm"
+#include "code\modules\mob\living\bot\medbot.dm"
+#include "code\modules\mob\living\bot\secbot.dm"
#include "code\modules\mob\living\carbon\breathe.dm"
#include "code\modules\mob\living\carbon\carbon.dm"
#include "code\modules\mob\living\carbon\carbon_defines.dm"
diff --git a/code/game/machinery/bots/cleanbot.dm b/code/game/machinery/bots/cleanbot.dm
deleted file mode 100644
index 4dcb838152..0000000000
--- a/code/game/machinery/bots/cleanbot.dm
+++ /dev/null
@@ -1,363 +0,0 @@
-//Cleanbot assembly
-/obj/item/weapon/bucket_sensor
- desc = "It's a bucket. With a sensor attached."
- name = "proxy bucket"
- icon = 'icons/obj/aibots.dmi'
- icon_state = "bucket_proxy"
- force = 3.0
- throwforce = 10.0
- throw_speed = 2
- throw_range = 5
- w_class = 3.0
- var/created_name = "Cleanbot"
-
-
-//Cleanbot
-/obj/machinery/bot/cleanbot
- name = "Cleanbot"
- desc = "A little cleaning robot, he looks so excited!"
- icon = 'icons/obj/aibots.dmi'
- icon_state = "cleanbot0"
- layer = 5.0
- density = 0
- anchored = 0
- //weight = 1.0E7
- health = 25
- maxhealth = 25
- var/cleaning = 0
- var/screwloose = 0
- var/oddbutton = 0
- var/blood = 1
- var/list/target_types = list()
- var/obj/effect/decal/cleanable/target
- var/obj/effect/decal/cleanable/oldtarget
- var/oldloc = null
- req_access = list(access_janitor)
- var/path[] = new()
- var/patrol_path[] = null
- var/beacon_freq = 1445 // navigation beacon frequency
- var/closest_dist
- var/closest_loc
- var/failed_steps
- var/should_patrol
- var/next_dest
- var/next_dest_loc
-
-/obj/machinery/bot/cleanbot/New()
- ..()
- src.get_targets()
- src.icon_state = "cleanbot[src.on]"
-
- should_patrol = 1
-
- src.botcard = new /obj/item/weapon/card/id(src)
- src.botcard.access = list(access_janitor, access_maint_tunnels)
-
- src.locked = 0 // Start unlocked so roboticist can set them to patrol.
-
- if(radio_controller)
- radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS)
-
-
-/obj/machinery/bot/cleanbot/turn_on()
- . = ..()
- src.icon_state = "cleanbot[src.on]"
- src.updateUsrDialog()
-
-/obj/machinery/bot/cleanbot/turn_off()
- ..()
- if(!isnull(src.target))
- target.targeted_by = null
- src.target = null
- src.oldtarget = null
- src.oldloc = null
- src.icon_state = "cleanbot[src.on]"
- src.path = new()
- src.updateUsrDialog()
-
-/obj/machinery/bot/cleanbot/attack_hand(mob/user as mob)
- . = ..()
- if (.)
- return
- usr.set_machine(src)
- interact(user)
-
-/obj/machinery/bot/cleanbot/interact(mob/user as mob)
- var/dat
- dat += text({"
-Automatic Station Cleaner v1.0
-Status: []
-Behaviour controls are [src.locked ? "locked" : "unlocked"]
-Maintenance panel is [src.open ? "opened" : "closed"]"},
-text("[src.on ? "On" : "Off"]"))
- if(!src.locked || issilicon(user))
- dat += text({"
Cleans Blood: []
"}, text("[src.blood ? "Yes" : "No"]"))
- dat += text({"
Patrol station: []
"}, text("[src.should_patrol ? "Yes" : "No"]"))
- // dat += text({"
Beacon frequency: []
"}, text("[src.beacon_freq]"))
- if(src.open && !src.locked)
- dat += text({"
-Odd looking screw twiddled: []
-Weird button pressed: []"},
-text("[src.screwloose ? "Yes" : "No"]"),
-text("[src.oddbutton ? "Yes" : "No"]"))
-
- user << browse("
Cleaner v1.0 controls[dat]", "window=autocleaner")
- onclose(user, "autocleaner")
- return
-
-/obj/machinery/bot/cleanbot/Topic(href, href_list)
- if(..())
- return
- usr.set_machine(src)
- src.add_fingerprint(usr)
- switch(href_list["operation"])
- if("start")
- if (src.on)
- turn_off()
- else
- turn_on()
- if("blood")
- src.blood =!src.blood
- src.get_targets()
- src.updateUsrDialog()
- if("patrol")
- src.should_patrol =!src.should_patrol
- src.patrol_path = null
- src.updateUsrDialog()
- if("freq")
- var/freq = text2num(input("Select frequency for navigation beacons", "Frequnecy", num2text(beacon_freq / 10))) * 10
- if (freq > 0)
- src.beacon_freq = freq
- src.updateUsrDialog()
- if("screw")
- src.screwloose = !src.screwloose
- usr << "You press the weird button."
- src.updateUsrDialog()
-
-/obj/machinery/bot/cleanbot/attackby(obj/item/weapon/W, mob/user as mob)
- if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
- if(src.allowed(usr) && !open && !emagged)
- src.locked = !src.locked
- user << "You [ src.locked ? "lock" : "unlock"] the [src] behaviour controls."
- else
- if(emagged)
- user << "ERROR"
- if(open)
- user << "Please close the access panel before locking it."
- else
- user << "This [src] doesn't seem to respect your authority."
- else
- return ..()
-
-/obj/machinery/bot/cleanbot/Emag(mob/user as mob)
- ..()
- if(open && !locked)
- if(user) user << "The [src] buzzes and beeps."
- src.oddbutton = 1
- src.screwloose = 1
-
-/obj/machinery/bot/cleanbot/process()
- set background = 1
-
- if(!src.on)
- return
- if(src.cleaning)
- return
-
- if(!src.screwloose && !src.oddbutton && prob(5))
- visible_message("[src] makes an excited beeping booping sound!")
-
- if(src.screwloose && prob(5))
- if(istype(loc,/turf/simulated))
- var/turf/simulated/T = src.loc
- if(T.wet < 1)
- T.wet = 1
- if(T.wet_overlay)
- T.overlays -= T.wet_overlay
- T.wet_overlay = null
- T.wet_overlay = image('icons/effects/water.dmi',T,"wet_floor")
- T.overlays += T.wet_overlay
- spawn(800)
- if (istype(T) && T.wet < 2)
- T.wet = 0
- if(T.wet_overlay)
- T.overlays -= T.wet_overlay
- T.wet_overlay = null
- if(src.oddbutton && prob(5))
- visible_message("Something flies out of [src]. He seems to be acting oddly.")
- var/obj/effect/decal/cleanable/blood/gibs/gib = new /obj/effect/decal/cleanable/blood/gibs(src.loc)
- //gib.streak(list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST))
- src.oldtarget = gib
- if(!src.target || src.target == null)
- for (var/obj/effect/decal/cleanable/D in view(7,src))
- for(var/T in src.target_types)
- if(isnull(D.targeted_by) && istype(D, T) && D != src.oldtarget) // If the mess isn't targeted (D.type == T || D.parent_type == T)
- src.oldtarget = D // or if it is but the bot is gone.
- src.target = D // and it's stuff we clean? Clean it.
- D.targeted_by = src // Claim the mess we are targeting.
- return
-
- if(!src.target || src.target == null)
- if(src.loc != src.oldloc)
- src.oldtarget = null
-
- if (!should_patrol)
- return
-
- if (!patrol_path || patrol_path.len < 1)
- var/datum/radio_frequency/frequency = radio_controller.return_frequency(beacon_freq)
-
- if(!frequency) return
-
- closest_dist = 9999
- closest_loc = null
- next_dest_loc = null
-
- var/datum/signal/signal = new()
- signal.source = src
- signal.transmission_method = 1
- signal.data = list("findbeacon" = "patrol")
- frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS)
- spawn(5)
- if (!next_dest_loc)
- next_dest_loc = closest_loc
- if (next_dest_loc)
- src.patrol_path = AStar(src.loc, next_dest_loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 120, id=botcard, exclude=null)
- else
- patrol_move()
-
- return
-
- if(target && path.len == 0)
- spawn(0)
- if(!src || !target) return
- src.path = AStar(src.loc, src.target.loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id=botcard)
- if (!path) path = list()
- if(src.path.len == 0)
- src.oldtarget = src.target
- target.targeted_by = null
- src.target = null
- return
- if(src.path.len > 0 && src.target && (src.target != null))
- step_to(src, src.path[1])
- src.path -= src.path[1]
- else if(src.path.len == 1)
- step_to(src, target)
-
- if(src.target && (src.target != null))
- patrol_path = null
- if(src.loc == src.target.loc)
- clean(src.target)
- src.path = new()
- src.target = null
- return
-
- src.oldloc = src.loc
-
-/obj/machinery/bot/cleanbot/proc/patrol_move()
- if (src.patrol_path.len <= 0)
- return
-
- var/next = src.patrol_path[1]
- src.patrol_path -= next
- if (next == src.loc)
- return
-
- var/moved = step_towards(src, next)
- if (!moved)
- failed_steps++
- if (failed_steps > 4)
- patrol_path = null
- next_dest = null
- failed_steps = 0
- else
- failed_steps = 0
-
-/obj/machinery/bot/cleanbot/receive_signal(datum/signal/signal)
- var/recv = signal.data["beacon"]
- var/valid = signal.data["patrol"]
- if(!recv || !valid)
- return
-
- var/dist = get_dist(src, signal.source.loc)
- if (dist < closest_dist && signal.source.loc != src.loc)
- closest_dist = dist
- closest_loc = signal.source.loc
- next_dest = signal.data["next_patrol"]
-
- if (recv == next_dest)
- next_dest_loc = signal.source.loc
- next_dest = signal.data["next_patrol"]
-
-/obj/machinery/bot/cleanbot/proc/get_targets()
- src.target_types = new/list()
-
- target_types += /obj/effect/decal/cleanable/blood/oil
- target_types += /obj/effect/decal/cleanable/vomit
- target_types += /obj/effect/decal/cleanable/crayon
- target_types += /obj/effect/decal/cleanable/liquid_fuel
- target_types += /obj/effect/decal/cleanable/mucus
- target_types += /obj/effect/decal/cleanable/dirt
-
- if(src.blood)
- target_types += /obj/effect/decal/cleanable/blood/
-
-/obj/machinery/bot/cleanbot/proc/clean(var/obj/effect/decal/cleanable/target)
- anchored = 1
- icon_state = "cleanbot-c"
- visible_message("\red [src] begins to clean up the [target]")
- cleaning = 1
- var/cleantime = 50
- if(istype(target,/obj/effect/decal/cleanable/dirt)) // Clean Dirt much faster
- cleantime = 10
- spawn(cleantime)
- if(istype(loc,/turf/simulated))
- var/turf/simulated/f = loc
- f.dirt = 0
- cleaning = 0
- del(target)
- icon_state = "cleanbot[on]"
- anchored = 0
- target = null
-
-/obj/machinery/bot/cleanbot/explode()
- src.on = 0
- src.visible_message("\red [src] blows apart!", 1)
- var/turf/Tsec = get_turf(src)
-
- new /obj/item/weapon/reagent_containers/glass/bucket(Tsec)
-
- new /obj/item/device/assembly/prox_sensor(Tsec)
-
- if (prob(50))
- new /obj/item/robot_parts/l_arm(Tsec)
-
- var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
- s.set_up(3, 1, src)
- s.start()
- del(src)
- return
-
-/obj/item/weapon/bucket_sensor/attackby(var/obj/item/W, mob/user as mob)
- ..()
- if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm))
- user.drop_item()
- del(W)
- var/turf/T = get_turf(src.loc)
- var/obj/machinery/bot/cleanbot/A = new /obj/machinery/bot/cleanbot(T)
- A.name = src.created_name
- user << "You add the robot arm to the bucket and sensor assembly. Beep boop!"
- user.drop_from_inventory(src)
- del(src)
-
- else if (istype(W, /obj/item/weapon/pen))
- var/t = sanitizeSafe(input(user, "Enter new robot name", src.name, src.created_name), MAX_NAME_LEN)
- if (!t)
- return
- if (!in_range(src, usr) && src.loc != usr)
- return
- src.created_name = t
diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm
deleted file mode 100644
index a955efaa1f..0000000000
--- a/code/game/machinery/bots/ed209bot.dm
+++ /dev/null
@@ -1,210 +0,0 @@
-/obj/machinery/bot/secbot/ed209
- name = "ED-209 Security Robot"
- desc = "A security robot. He looks less than thrilled."
- icon = 'icons/obj/aibots.dmi'
- icon_state = "ed2090"
- density = 1
- health = 100
- maxhealth = 100
-
- bot_version = "2.5"
- search_range = 12
- has_laser = 1
-
- preparing_arrest_sounds = new()
- secbot_assembly = /obj/item/weapon/secbot_assembly/ed209_assembly
-
-/obj/item/weapon/secbot_assembly/ed209_assembly
- name = "ED-209 assembly"
- desc = "Some sort of bizarre assembly."
- icon = 'icons/obj/aibots.dmi'
- icon_state = "ed209_frame"
- item_state = "ed209_frame"
- created_name = "ED-209 Security Robot" //To preserve the name if it's a unique securitron I guess
- var/lasercolor = ""
-
-/obj/machinery/bot/secbot/ed209/update_icon()
- if(on && is_attacking)
- src.icon_state = "[lasercolor]ed209-c"
- else
- src.icon_state = "[lasercolor]ed209[src.on]"
-
-/obj/machinery/bot/secbot/ed209/on_explosion(var/turf/Tsec)
- if(!lasercolor)
- var/obj/item/weapon/gun/energy/taser/G = new /obj/item/weapon/gun/energy/taser(Tsec)
- G.power_supply.charge = 0
- else if(lasercolor == "b")
- var/obj/item/weapon/gun/energy/lasertag/blue/G = new (Tsec)
- G.power_supply.charge = 0
- else if(lasercolor == "r")
- var/obj/item/weapon/gun/energy/lasertag/red/G = new (Tsec)
- G.power_supply.charge = 0
- if (prob(50))
- new /obj/item/robot_parts/l_leg(Tsec)
- if (prob(25))
- new /obj/item/robot_parts/r_leg(Tsec)
- if (prob(25))//50% chance for a helmet OR vest
- if (prob(50))
- new /obj/item/clothing/head/helmet(Tsec)
- else
- if(!lasercolor)
- new /obj/item/clothing/suit/armor/vest(Tsec)
- if(lasercolor == "b")
- new /obj/item/clothing/suit/bluetag(Tsec)
- if(lasercolor == "r")
- new /obj/item/clothing/suit/redtag(Tsec)
-
-/obj/item/weapon/secbot_assembly/ed209_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob)
- ..()
-
- if(istype(W, /obj/item/weapon/pen))
- var/t = sanitizeSafe(input(user, "Enter new robot name", src.name, src.created_name), MAX_NAME_LEN)
- if(!t) return
- if(!in_range(src, usr) && src.loc != usr) return
- created_name = t
- return
-
- switch(build_step)
- if(0,1)
- if( istype(W, /obj/item/robot_parts/l_leg) || istype(W, /obj/item/robot_parts/r_leg) )
- user.drop_item()
- del(W)
- build_step++
- user << "You add the robot leg to [src]."
- name = "legs/frame assembly"
- if(build_step == 1)
- item_state = "ed209_leg"
- icon_state = "ed209_leg"
- else
- item_state = "ed209_legs"
- icon_state = "ed209_legs"
-
- if(2)
- if( istype(W, /obj/item/clothing/suit/redtag) )
- lasercolor = "r"
- else if( istype(W, /obj/item/clothing/suit/bluetag) )
- lasercolor = "b"
- if( lasercolor || istype(W, /obj/item/clothing/suit/storage/vest) )
- user.drop_item()
- del(W)
- build_step++
- user << "You add the armor to [src]."
- name = "vest/legs/frame assembly"
- item_state = "[lasercolor]ed209_shell"
- icon_state = "[lasercolor]ed209_shell"
-
- if(3)
- if( istype(W, /obj/item/weapon/weldingtool) )
- var/obj/item/weapon/weldingtool/WT = W
- if(WT.remove_fuel(0,user))
- build_step++
- name = "shielded frame assembly"
- user << "You welded the vest to [src]."
- if(4)
- if( istype(W, /obj/item/clothing/head/helmet) )
- user.drop_item()
- del(W)
- build_step++
- user << "You add the helmet to [src]."
- name = "covered and shielded frame assembly"
- item_state = "[lasercolor]ed209_hat"
- icon_state = "[lasercolor]ed209_hat"
-
- if(5)
- if( isprox(W) )
- user.drop_item()
- del(W)
- build_step++
- user << "You add the prox sensor to [src]."
- name = "covered, shielded and sensored frame assembly"
- item_state = "[lasercolor]ed209_prox"
- icon_state = "[lasercolor]ed209_prox"
-
- if(6)
- if(istype(W, /obj/item/stack/cable_coil))
- var/obj/item/stack/cable_coil/C = W
- if (C.get_amount() < 1)
- user << "You need one coil of wire to do wire [src]."
- return
- user << "You start to wire [src]."
- if (do_after(user, 40) && build_step == 6)
- if (C.use(1))
- build_step++
- user << "You wire the ED-209 assembly."
- name = "wired ED-209 assembly"
- return
-
- if(7)
- switch(lasercolor)
- if("b")
- if( !istype(W, /obj/item/weapon/gun/energy/lasertag/blue) )
- return
- name = "bluetag ED-209 assembly"
- if("r")
- if( !istype(W, /obj/item/weapon/gun/energy/lasertag/red) )
- return
- name = "redtag ED-209 assembly"
- if("")
- if( !istype(W, /obj/item/weapon/gun/energy/taser) )
- return
- name = "taser ED-209 assembly"
- else
- return
- build_step++
- user << "You add [W] to [src]."
- src.item_state = "[lasercolor]ed209_taser"
- src.icon_state = "[lasercolor]ed209_taser"
- user.drop_item()
- del(W)
-
- if(8)
- if( istype(W, /obj/item/weapon/screwdriver) )
- playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1)
- var/turf/T = get_turf(user)
- user << "Now attaching the gun to the frame..."
- sleep(40)
- if(get_turf(user) == T && build_step == 8)
- build_step++
- name = "armed [name]"
- user << "Taser gun attached."
-
- if(9)
- if( istype(W, /obj/item/weapon/cell) )
- build_step++
- user << "You complete the ED-209."
- var/turf/T = get_turf(src)
- new /obj/machinery/bot/secbot/ed209(T,created_name,lasercolor)
- user.drop_item()
- del(W)
- user.drop_from_inventory(src)
- del(src)
-
-
-/obj/machinery/bot/secbot/ed209/bullet_act(var/obj/item/projectile/Proj)
- if((src.lasercolor == "b") && (src.disabled == 0))
- if(istype(Proj, /obj/item/projectile/beam/lastertag/red))
- src.disabled = 1
- del (Proj)
- sleep(100)
- src.disabled = 0
- else
- ..()
- else if((src.lasercolor == "r") && (src.disabled == 0))
- if(istype(Proj, /obj/item/projectile/beam/lastertag/blue))
- src.disabled = 1
- del (Proj)
- sleep(100)
- src.disabled = 0
- else
- ..()
- else
- ..()
-
-/obj/machinery/bot/secbot/ed209/bluetag/New()//If desired, you spawn red and bluetag bots easily
- new /obj/machinery/bot/secbot/ed209(get_turf(src),null,"b")
- del(src)
-
-
-/obj/machinery/bot/secbot/ed209/redtag/New()
- new /obj/machinery/bot/secbot/ed209(get_turf(src),null,"r")
- del(src)
diff --git a/code/game/machinery/bots/farmbot.dm b/code/game/machinery/bots/farmbot.dm
deleted file mode 100644
index ca32e937ed..0000000000
--- a/code/game/machinery/bots/farmbot.dm
+++ /dev/null
@@ -1,595 +0,0 @@
-//Farmbots by GauHelldragon - 12/30/2012
-// A new type of buildable aiBot that helps out in hydroponics
-
-// Made by using a robot arm on a water tank and then adding:
-// A plant analyzer, a bucket, a mini-hoe and then a proximity sensor (in that order)
-
-// Will water, weed and fertilize plants that need it
-// When emagged, it will "water", "weed" and "fertilize" humans instead
-// Holds up to 10 fertilizers (only the type dispensed by the machines, not chemistry bottles)
-// It will fill up it's water tank at a sink when low.
-
-// The behavior panel can be unlocked with hydroponics access and be modified to disable certain behaviors
-// By default, it will ignore weeds and mushrooms, but can be set to tend to these types of plants as well.
-
-
-#define FARMBOT_MODE_WATER 1
-#define FARMBOT_MODE_FERTILIZE 2
-#define FARMBOT_MODE_WEED 3
-#define FARMBOT_MODE_REFILL 4
-#define FARMBOT_MODE_WAITING 5
-
-#define FARMBOT_ANIMATION_TIME 25 //How long it takes to use one of the action animations
-#define FARMBOT_EMAG_DELAY 60 //How long of a delay after doing one of the emagged attack actions
-#define FARMBOT_ACTION_DELAY 35 //How long of a delay after doing one of the normal actions
-
-/obj/machinery/bot/farmbot
- name = "Farmbot"
- desc = "The botanist's best friend."
- icon = 'icons/obj/aibots.dmi'
- icon_state = "farmbot0"
- layer = 5.0
- density = 1
- anchored = 0
- health = 50
- maxhealth = 50
- req_access =list(access_hydroponics)
-
- var/Max_Fertilizers = 10
-
- var/setting_water = 1
- var/setting_refill = 1
- var/setting_fertilize = 1
- var/setting_weed = 1
- var/setting_ignoreWeeds = 1
- var/setting_ignoreMushrooms = 1
-
- var/atom/target //Current target, can be a human, a hydroponics tray, or a sink
- var/mode //Which mode is being used, 0 means it is looking for work
-
- var/obj/structure/reagent_dispensers/watertank/tank // the water tank that was used to make it, remains inside the bot.
-
- var/path[] = new() // used for pathing
- var/frustration
-
-/obj/machinery/bot/farmbot/New()
- ..()
- src.icon_state = "farmbot[src.on]"
- spawn (4)
- src.botcard = new /obj/item/weapon/card/id(src)
- src.botcard.access = req_access
-
- if ( !tank ) //Should be set as part of making it... but lets check anyway
- tank = locate(/obj/structure/reagent_dispensers/watertank/) in contents
- if ( !tank ) //An admin must have spawned the farmbot! Better give it a tank.
- tank = new /obj/structure/reagent_dispensers/watertank(src)
-
-/obj/machinery/bot/farmbot/Bump(M as mob|obj) //Leave no door unopened!
- spawn(0)
- if ((istype(M, /obj/machinery/door)) && (!isnull(src.botcard)))
- var/obj/machinery/door/D = M
- if (!istype(D, /obj/machinery/door/firedoor) && D.check_access(src.botcard))
- D.open()
- src.frustration = 0
- return
- return
-
-/obj/machinery/bot/farmbot/turn_on()
- . = ..()
- src.icon_state = "farmbot[src.on]"
- src.updateUsrDialog()
-
-/obj/machinery/bot/farmbot/turn_off()
- ..()
- src.path = new()
- src.icon_state = "farmbot[src.on]"
- src.updateUsrDialog()
-
-/obj/machinery/bot/farmbot/attack_paw(mob/user as mob)
- return attack_hand(user)
-
-
-/obj/machinery/bot/farmbot/proc/get_total_ferts()
- var total_fert = 0
- for (var/obj/item/nutrient/fert in contents)
- total_fert++
- return total_fert
-
-/obj/machinery/bot/farmbot/attack_hand(mob/user as mob)
- . = ..()
- if (.)
- return
- var/dat
- dat += "Automatic Hyrdoponic Assisting Unit v1.0
"
- dat += "Status: [src.on ? "On" : "Off"]
"
-
- dat += "Water Tank: "
- if ( tank )
- dat += "\[[tank.reagents.total_volume]/[tank.reagents.maximum_volume]\]"
- else
- dat += "Error: Water Tank not Found"
-
- dat += "
Fertilizer Storage: \[[get_total_ferts()]/[Max_Fertilizers]\]"
-
- dat += "
Behaviour controls are [src.locked ? "locked" : "unlocked"]
"
- if(!src.locked)
- dat += "Watering Controls:
"
- dat += " Water Plants : [src.setting_water ? "Yes" : "No"]
"
- dat += " Refill Watertank : [src.setting_refill ? "Yes" : "No"]
"
- dat += "
Fertilizer Controls:
"
- dat += " Fertilize Plants : [src.setting_fertilize ? "Yes" : "No"]
"
- dat += "
Weeding Controls:
"
- dat += " Weed Plants : [src.setting_weed ? "Yes" : "No"]
"
- dat += "
Ignore Weeds : [src.setting_ignoreWeeds ? "Yes" : "No"]
"
- dat += "Ignore Mushrooms : [src.setting_ignoreMushrooms ? "Yes" : "No"]
"
- dat += ""
-
- user << browse("Farmbot v1.0 controls[dat]", "window=autofarm")
- onclose(user, "autofarm")
- return
-
-/obj/machinery/bot/farmbot/Topic(href, href_list)
- if(..())
- return
- usr.machine = src
- src.add_fingerprint(usr)
- if ((href_list["power"]) && (src.allowed(usr)))
- if (src.on)
- turn_off()
- else
- turn_on()
-
- else if((href_list["water"]) && (!src.locked))
- setting_water = !setting_water
- else if((href_list["refill"]) && (!src.locked))
- setting_refill = !setting_refill
- else if((href_list["fertilize"]) && (!src.locked))
- setting_fertilize = !setting_fertilize
- else if((href_list["weed"]) && (!src.locked))
- setting_weed = !setting_weed
- else if((href_list["ignoreWeed"]) && (!src.locked))
- setting_ignoreWeeds = !setting_ignoreWeeds
- else if((href_list["ignoreMush"]) && (!src.locked))
- setting_ignoreMushrooms = !setting_ignoreMushrooms
- else if (href_list["eject"] )
- flick("farmbot_hatch",src)
- for (var/obj/item/nutrient/fert in contents)
- fert.loc = get_turf(src)
-
- src.updateUsrDialog()
- return
-
-/obj/machinery/bot/farmbot/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
- if (src.allowed(user))
- src.locked = !src.locked
- user << "Controls are now [src.locked ? "locked." : "unlocked."]"
- src.updateUsrDialog()
- else
- user << "\red Access denied."
-
- else if (istype(W, /obj/item/nutrient))
- if ( get_total_ferts() >= Max_Fertilizers )
- user << "The fertilizer storage is full!"
- return
- user.drop_item()
- W.loc = src
- user << "You insert [W]."
- flick("farmbot_hatch",src)
- src.updateUsrDialog()
- return
-
- else
- ..()
-
-/obj/machinery/bot/farmbot/Emag(mob/user as mob)
- ..()
- if(user) user << "\red You short out [src]'s plant identifier circuits."
- spawn(0)
- for(var/mob/O in hearers(src, null))
- O.show_message("\red [src] buzzes oddly!", 1)
- flick("farmbot_broke", src)
- src.emagged = 1
- src.on = 1
- src.icon_state = "farmbot[src.on]"
- target = null
- mode = FARMBOT_MODE_WAITING //Give the emagger a chance to get away! 15 seconds should be good.
- spawn(150)
- mode = 0
-
-/obj/machinery/bot/farmbot/explode()
- src.on = 0
- visible_message("\red [src] blows apart!", 1)
- var/turf/Tsec = get_turf(src)
-
- new /obj/item/weapon/minihoe(Tsec)
- new /obj/item/weapon/reagent_containers/glass/bucket(Tsec)
- new /obj/item/device/assembly/prox_sensor(Tsec)
- new /obj/item/device/analyzer/plant_analyzer(Tsec)
-
- if ( tank )
- tank.loc = Tsec
-
- for ( var/obj/item/nutrient/fert in contents )
- if ( prob(50) )
- fert.loc = Tsec
-
- if (prob(50))
- new /obj/item/robot_parts/l_arm(Tsec)
-
- var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
- s.set_up(3, 1, src)
- s.start()
- del(src)
- return
-
-/obj/machinery/bot/farmbot/process()
- set background = 1
-
- if(!src.on)
- return
-
- if ( emagged && prob(1) )
- flick("farmbot_broke", src)
-
- if ( mode == FARMBOT_MODE_WAITING )
- return
-
- if ( !mode || !target || !(target in view(7,src)) ) //Don't bother chasing down targets out of view
-
- mode = 0
- target = null
- if ( !find_target() )
- // Couldn't find a target, wait a while before trying again.
- mode = FARMBOT_MODE_WAITING
- spawn(100)
- mode = 0
- return
-
- if ( mode && target )
- if ( get_dist(target,src) <= 1 || ( emagged && mode == FARMBOT_MODE_FERTILIZE ) )
- // If we are in emagged fertilize mode, we throw the fertilizer, so distance doesn't matter
- frustration = 0
- use_farmbot_item()
- else
- move_to_target()
- return
-
-/obj/machinery/bot/farmbot/proc/use_farmbot_item()
- if ( !target )
- mode = 0
- return 0
-
- if ( emagged && !ismob(target) ) // Humans are plants!
- mode = 0
- target = null
- return 0
-
- if ( !emagged && !istype(target,/obj/machinery/hydroponics) && !istype(target,/obj/structure/sink) ) // Humans are not plants!
- mode = 0
- target = null
- return 0
-
- if ( mode == FARMBOT_MODE_FERTILIZE )
- //Find which fertilizer to use
- var/obj/item/nutrient/fert
- for ( var/obj/item/nutrient/nut in contents )
- fert = nut
- break
- if ( !fert )
- target = null
- mode = 0
- return
- fertilize(fert)
-
- if ( mode == FARMBOT_MODE_WEED )
- weed()
-
- if ( mode == FARMBOT_MODE_WATER )
- water()
-
- if ( mode == FARMBOT_MODE_REFILL )
- refill()
-
-
-
-
-/obj/machinery/bot/farmbot/proc/find_target()
- if ( emagged ) //Find a human and help them!
- for ( var/mob/living/carbon/human/human in view(7,src) )
- if (human.stat == 2)
- continue
-
- var list/options = list(FARMBOT_MODE_WEED)
- if ( get_total_ferts() )
- options.Add(FARMBOT_MODE_FERTILIZE)
- if ( tank && tank.reagents.total_volume >= 1 )
- options.Add(FARMBOT_MODE_WATER)
- mode = pick(options)
- target = human
- return mode
- return 0
- else
- if ( setting_refill && tank && tank.reagents.total_volume < 100 )
- for ( var/obj/structure/sink/source in view(7,src) )
- target = source
- mode = FARMBOT_MODE_REFILL
- return 1
- for ( var/obj/machinery/hydroponics/tray in view(7,src) )
- var newMode = GetNeededMode(tray)
- if ( newMode )
- mode = newMode
- target = tray
- return 1
- return 0
-
-/obj/machinery/bot/farmbot/proc/GetNeededMode(obj/machinery/hydroponics/tray)
- if ( !tray.planted || tray.dead )
- return 0
- if ( tray.myseed.plant_type == 1 && setting_ignoreWeeds )
- return 0
- if ( tray.myseed.plant_type == 2 && setting_ignoreMushrooms )
- return 0
-
- if ( setting_water && tray.waterlevel <= 10 && tank && tank.reagents.total_volume >= 1 )
- return FARMBOT_MODE_WATER
-
- if ( setting_weed && tray.weedlevel >= 5 )
- return FARMBOT_MODE_WEED
-
- if ( setting_fertilize && tray.nutrilevel <= 2 && get_total_ferts() )
- return FARMBOT_MODE_FERTILIZE
-
- return 0
-
-/obj/machinery/bot/farmbot/proc/move_to_target()
- //Mostly copied from medibot code.
-
- if(src.frustration > 8)
- target = null
- mode = 0
- frustration = 0
- src.path = new()
- if(src.target && (src.path.len) && (get_dist(src.target,src.path[src.path.len]) > 2))
- src.path = new()
- if(src.target && src.path.len == 0 && (get_dist(src,src.target) > 1))
- spawn(0)
- var/turf/dest = get_step_towards(target,src) //Can't pathfind to a tray, as it is dense, so pathfind to the spot next to the tray
-
- src.path = AStar(src.loc, dest, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30,id=botcard)
- if(src.path.len == 0)
- for ( var/turf/spot in orange(1,target) ) //The closest one is unpathable, try the other spots
- if ( spot == dest ) //We already tried this spot
- continue
- if ( spot.density )
- continue
- src.path = AStar(src.loc, spot, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30,id=botcard)
- src.path = reverselist(src.path)
- if ( src.path.len > 0 )
- break
-
- if ( src.path.len == 0 )
- target = null
- mode = 0
- return
-
- if(src.path.len > 0 && src.target)
- step_to(src, src.path[1])
- src.path -= src.path[1]
- spawn(3)
- if(src.path.len)
- step_to(src, src.path[1])
- src.path -= src.path[1]
-
- if(src.path.len > 8 && src.target)
- src.frustration++
-
-
-/obj/machinery/bot/farmbot/proc/fertilize(obj/item/nutrient/fert)
- if ( !fert )
- target = null
- mode = 0
- return 0
-
- if ( emagged ) // Warning, hungry humans detected: throw fertilizer at them
- spawn(0)
- fert.loc = src.loc
- fert.throw_at(target, 16, 3, src)
- src.visible_message("\red [src] launches [fert.name] at [target.name]!")
- flick("farmbot_broke", src)
- spawn (FARMBOT_EMAG_DELAY)
- mode = 0
- target = null
- return 1
-
- else // feed them plants~
- var /obj/machinery/hydroponics/tray = target
- tray.nutrilevel = 10
- tray.yieldmod = fert.yieldmod
- tray.mutmod = fert.mutmod
- del fert
- tray.updateicon()
- icon_state = "farmbot_fertile"
- mode = FARMBOT_MODE_WAITING
-
- spawn (FARMBOT_ACTION_DELAY)
- mode = 0
- target = null
- spawn (FARMBOT_ANIMATION_TIME)
- icon_state = "farmbot[src.on]"
- return 1
-
-/obj/machinery/bot/farmbot/proc/weed()
- icon_state = "farmbot_hoe"
- spawn(FARMBOT_ANIMATION_TIME)
- icon_state = "farmbot[src.on]"
-
- if ( emagged ) // Warning, humans infested with weeds!
- mode = FARMBOT_MODE_WAITING
- spawn(FARMBOT_EMAG_DELAY)
- mode = 0
-
- if ( prob(50) ) // better luck next time little guy
- src.visible_message("\red [src] swings wildly at [target] with a minihoe, missing completely!")
-
- else // yayyy take that weeds~
- var/attackVerb = pick("slashed", "sliced", "cut", "clawed")
- var /mob/living/carbon/human/human = target
-
- src.visible_message("\red [src] [attackVerb] [human]!")
- var/damage = 5
- var/dam_zone = pick("chest", "l_hand", "r_hand", "l_leg", "r_leg")
- var/datum/organ/external/affecting = human.get_organ(ran_zone(dam_zone))
- var/armor = human.run_armor_check(affecting, "melee")
- human.apply_damage(damage,BRUTE,affecting,armor,sharp=1,edge=1)
-
- else // warning, plants infested with weeds!
- mode = FARMBOT_MODE_WAITING
- spawn(FARMBOT_ACTION_DELAY)
- mode = 0
-
- var /obj/machinery/hydroponics/tray = target
- tray.weedlevel = 0
- tray.updateicon()
-
-/obj/machinery/bot/farmbot/proc/water()
- if ( !tank || tank.reagents.total_volume < 1 )
- mode = 0
- target = null
- return 0
-
- icon_state = "farmbot_water"
- spawn(FARMBOT_ANIMATION_TIME)
- icon_state = "farmbot[src.on]"
-
- if ( emagged ) // warning, humans are thirsty!
- var splashAmount = min(70,tank.reagents.total_volume)
- src.visible_message("\red [src] splashes [target] with a bucket of water!")
- playsound(src.loc, 'sound/effects/slosh.ogg', 25, 1)
- if ( prob(50) )
- tank.reagents.reaction(target, TOUCH) //splash the human!
- else
- tank.reagents.reaction(target.loc, TOUCH) //splash the human's roots!
- spawn(5)
- tank.reagents.remove_any(splashAmount)
-
- mode = FARMBOT_MODE_WAITING
- spawn(FARMBOT_EMAG_DELAY)
- mode = 0
- else
- var /obj/machinery/hydroponics/tray = target
- var/b_amount = tank.reagents.get_reagent_amount("water")
- if(b_amount > 0 && tray.waterlevel < 100)
- if(b_amount + tray.waterlevel > 100)
- b_amount = 100 - tray.waterlevel
- tank.reagents.remove_reagent("water", b_amount)
- tray.waterlevel += b_amount
- playsound(src.loc, 'sound/effects/slosh.ogg', 25, 1)
-
- // Toxicity dilutation code. The more water you put in, the lesser the toxin concentration.
- tray.toxic -= round(b_amount/4)
- if (tray.toxic < 0 ) // Make sure it won't go overboard
- tray.toxic = 0
-
- tray.updateicon()
- mode = FARMBOT_MODE_WAITING
- spawn(FARMBOT_ACTION_DELAY)
- mode = 0
-
-/obj/machinery/bot/farmbot/proc/refill()
- if ( !tank || !tank.reagents.total_volume > 600 || !istype(target,/obj/structure/sink) )
- mode = 0
- target = null
- return
-
- mode = FARMBOT_MODE_WAITING
- playsound(src.loc, 'sound/effects/slosh.ogg', 25, 1)
- src.visible_message("\blue [src] starts filling it's tank from [target].")
- spawn(300)
- src.visible_message("\blue [src] finishes filling it's tank.")
- src.mode = 0
- tank.reagents.add_reagent("water", tank.reagents.maximum_volume - tank.reagents.total_volume )
- playsound(src.loc, 'sound/effects/slosh.ogg', 25, 1)
-
-
-/obj/item/weapon/farmbot_arm_assembly
- name = "water tank/robot arm assembly"
- desc = "A water tank with a robot arm permanently grafted to it."
- icon = 'icons/obj/aibots.dmi'
- icon_state = "water_arm"
- var/build_step = 0
- var/created_name = "Farmbot" //To preserve the name if it's a unique farmbot I guess
- w_class = 3.0
-
- New()
- ..()
- spawn(4) // If an admin spawned it, it won't have a watertank it, so lets make one for em!
- var tank = locate(/obj/structure/reagent_dispensers/watertank) in contents
- if( !tank )
- new /obj/structure/reagent_dispensers/watertank(src)
-
-
-/obj/structure/reagent_dispensers/watertank/attackby(var/obj/item/robot_parts/S, mob/user as mob)
-
- if ((!istype(S, /obj/item/robot_parts/l_arm)) && (!istype(S, /obj/item/robot_parts/r_arm)))
- ..()
- return
-
- //Making a farmbot!
-
- var/obj/item/weapon/farmbot_arm_assembly/A = new /obj/item/weapon/farmbot_arm_assembly
-
- A.loc = src.loc
- user << "You add the robot arm to the [src]"
- src.loc = A //Place the water tank into the assembly, it will be needed for the finished bot
- user.remove_from_mob(S)
- del(S)
-
-/obj/item/weapon/farmbot_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob)
- ..()
- if((istype(W, /obj/item/device/analyzer/plant_analyzer)) && (!src.build_step))
- src.build_step++
- user << "You add the plant analyzer to [src]!"
- src.name = "farmbot assembly"
- user.remove_from_mob(W)
- del(W)
-
- else if(( istype(W, /obj/item/weapon/reagent_containers/glass/bucket)) && (src.build_step == 1))
- src.build_step++
- user << "You add a bucket to [src]!"
- src.name = "farmbot assembly with bucket"
- user.remove_from_mob(W)
- del(W)
-
- else if(( istype(W, /obj/item/weapon/minihoe)) && (src.build_step == 2))
- src.build_step++
- user << "You add a minihoe to [src]!"
- src.name = "farmbot assembly with bucket and minihoe"
- user.remove_from_mob(W)
- del(W)
-
- else if((isprox(W)) && (src.build_step == 3))
- src.build_step++
- user << "You complete the Farmbot! Beep boop."
- var/obj/machinery/bot/farmbot/S = new /obj/machinery/bot/farmbot
- for ( var/obj/structure/reagent_dispensers/watertank/wTank in src.contents )
- wTank.loc = S
- S.tank = wTank
- S.loc = get_turf(src)
- S.name = src.created_name
- user.remove_from_mob(W)
- del(W)
- del(src)
-
- else if(istype(W, /obj/item/weapon/pen))
- var/t = input(user, "Enter new robot name", src.name, src.created_name) as text
- t = sanitize(t, MAX_NAME_LEN)
- if (!t)
- return
- if (!in_range(src, usr) && src.loc != usr)
- return
-
- src.created_name = t
-
-/obj/item/weapon/farmbot_arm_assembly/attack_hand(mob/user as mob)
- return //it's a converted watertank, no you cannot pick it up and put it in your backpack
\ No newline at end of file
diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm
deleted file mode 100644
index b3c1c0376f..0000000000
--- a/code/game/machinery/bots/floorbot.dm
+++ /dev/null
@@ -1,449 +0,0 @@
-//Floorbot assemblies
-/obj/item/weapon/toolbox_tiles
- desc = "It's a toolbox with tiles sticking out the top"
- name = "tiles and toolbox"
- icon = 'icons/obj/aibots.dmi'
- icon_state = "toolbox_tiles"
- force = 3.0
- throwforce = 10.0
- throw_speed = 2
- throw_range = 5
- w_class = 3.0
- var/created_name = "Floorbot"
-
-/obj/item/weapon/toolbox_tiles_sensor
- desc = "It's a toolbox with tiles sticking out the top and a sensor attached"
- name = "tiles, toolbox and sensor arrangement"
- icon = 'icons/obj/aibots.dmi'
- icon_state = "toolbox_tiles_sensor"
- force = 3.0
- throwforce = 10.0
- throw_speed = 2
- throw_range = 5
- w_class = 3.0
- var/created_name = "Floorbot"
-
-//Floorbot
-/obj/machinery/bot/floorbot
- name = "Floorbot"
- desc = "A little floor repairing robot, he looks so excited!"
- icon = 'icons/obj/aibots.dmi'
- icon_state = "floorbot0"
- layer = 5.0
- density = 0
- anchored = 0
- health = 25
- maxhealth = 25
- //weight = 1.0E7
- var/amount = 10
- var/repairing = 0
- var/improvefloors = 0
- var/eattiles = 0
- var/maketiles = 0
- var/turf/target
- var/turf/oldtarget
- var/oldloc = null
- req_access = list(access_construction)
- var/path[] = new()
- var/targetdirection
-
-
-/obj/machinery/bot/floorbot/New()
- ..()
- src.updateicon()
-
-/obj/machinery/bot/floorbot/turn_on()
- . = ..()
- src.updateicon()
- src.updateUsrDialog()
-
-/obj/machinery/bot/floorbot/turn_off()
- ..()
- src.target = null
- src.oldtarget = null
- src.oldloc = null
- src.updateicon()
- src.path = new()
- src.updateUsrDialog()
-
-/obj/machinery/bot/floorbot/attack_hand(mob/user as mob)
- . = ..()
- if (.)
- return
- usr.set_machine(src)
- interact(user)
-
-/obj/machinery/bot/floorbot/interact(mob/user as mob)
- var/dat
- dat += "Automatic Station Floor Repairer v1.0
"
- dat += "Status: [src.on ? "On" : "Off"]
"
- dat += "Maintenance panel is [src.open ? "opened" : "closed"]
"
- dat += "Tiles left: [src.amount]
"
- dat += "Behvaiour controls are [src.locked ? "locked" : "unlocked"]
"
- if(!src.locked || issilicon(user))
- dat += "Improves floors: [src.improvefloors ? "Yes" : "No"]
"
- dat += "Finds tiles: [src.eattiles ? "Yes" : "No"]
"
- dat += "Make singles pieces of metal into tiles when empty: [src.maketiles ? "Yes" : "No"]
"
- var/bmode
- if (src.targetdirection)
- bmode = dir2text(src.targetdirection)
- else
- bmode = "Disabled"
- dat += "
Bridge Mode : [bmode]
"
-
- user << browse("Repairbot v1.0 controls[dat]", "window=autorepair")
- onclose(user, "autorepair")
- return
-
-
-/obj/machinery/bot/floorbot/attackby(var/obj/item/W , mob/user as mob)
- if(istype(W, /obj/item/stack/tile/plasteel))
- var/obj/item/stack/tile/plasteel/T = W
- if(src.amount >= 50)
- return
- var/loaded = min(50-src.amount, T.get_amount())
- T.use(loaded)
- src.amount += loaded
- user << "You load [loaded] tiles into the floorbot. He now contains [src.amount] tiles."
- src.updateicon()
- else if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
- if(src.allowed(usr) && !open && !emagged)
- src.locked = !src.locked
- user << "You [src.locked ? "lock" : "unlock"] the [src] behaviour controls."
- else
- if(emagged)
- user << "ERROR"
- if(open)
- user << "Please close the access panel before locking it."
- else
- user << "Access denied."
- src.updateUsrDialog()
- else
- ..()
-
-/obj/machinery/bot/floorbot/Emag(mob/user as mob)
- ..()
- if(open && !locked)
- if(user) user << "The [src] buzzes and beeps."
-
-/obj/machinery/bot/floorbot/Topic(href, href_list)
- if(..())
- return
- usr.set_machine(src)
- src.add_fingerprint(usr)
- switch(href_list["operation"])
- if("start")
- if (src.on)
- turn_off()
- else
- turn_on()
- if("improve")
- src.improvefloors = !src.improvefloors
- src.updateUsrDialog()
- if("tiles")
- src.eattiles = !src.eattiles
- src.updateUsrDialog()
- if("make")
- src.maketiles = !src.maketiles
- src.updateUsrDialog()
- if("bridgemode")
- switch(src.targetdirection)
- if(null)
- targetdirection = 1
- if(1)
- targetdirection = 2
- if(2)
- targetdirection = 4
- if(4)
- targetdirection = 8
- if(8)
- targetdirection = null
- else
- targetdirection = null
- src.updateUsrDialog()
-
-/obj/machinery/bot/floorbot/process()
- set background = 1
-
- if(!src.on)
- return
- if(src.repairing)
- return
- var/list/floorbottargets = list()
- if(src.amount <= 0 && ((src.target == null) || !src.target))
- if(src.eattiles)
- for(var/obj/item/stack/tile/plasteel/T in view(7, src))
- if(T != src.oldtarget && !(target in floorbottargets))
- src.oldtarget = T
- src.target = T
- break
- if(src.target == null || !src.target)
- if(src.maketiles)
- if(src.target == null || !src.target)
- for(var/obj/item/stack/sheet/metal/M in view(7, src))
- if(!(M in floorbottargets) && M != src.oldtarget && M.amount == 1 && !(istype(M.loc, /turf/simulated/wall)))
- src.oldtarget = M
- src.target = M
- break
- else
- return
- if(prob(5))
- visible_message("[src] makes an excited booping beeping sound!")
-
- if((!src.target || src.target == null) && emagged < 2)
- if(targetdirection != null)
- /*
- for (var/turf/space/D in view(7,src))
- if(!(D in floorbottargets) && D != src.oldtarget) // Added for bridging mode -- TLE
- if(get_dir(src, D) == targetdirection)
- src.oldtarget = D
- src.target = D
- break
- */
- var/turf/T = get_step(src, targetdirection)
- if(istype(T, /turf/space))
- src.oldtarget = T
- src.target = T
- if(!src.target || src.target == null)
- for (var/turf/space/D in view(7,src))
- if(!(D in floorbottargets) && D != src.oldtarget && (D.loc.name != "Space"))
- src.oldtarget = D
- src.target = D
- break
- if((!src.target || src.target == null ) && src.improvefloors)
- for (var/turf/simulated/floor/F in view(7,src))
- if(!(F in floorbottargets) && F != src.oldtarget && F.icon_state == "Floor1" && !(istype(F, /turf/simulated/floor/plating)))
- src.oldtarget = F
- src.target = F
- break
- if((!src.target || src.target == null) && src.eattiles)
- for(var/obj/item/stack/tile/plasteel/T in view(7, src))
- if(!(T in floorbottargets) && T != src.oldtarget)
- src.oldtarget = T
- src.target = T
- break
-
- if((!src.target || src.target == null) && emagged == 2)
- if(!src.target || src.target == null)
- for (var/turf/simulated/floor/D in view(7,src))
- if(!(D in floorbottargets) && D != src.oldtarget && D.floor_type)
- src.oldtarget = D
- src.target = D
- break
-
- if(!src.target || src.target == null)
- if(src.loc != src.oldloc)
- src.oldtarget = null
- return
-
- if(src.target && (src.target != null) && src.path.len == 0)
- spawn(0)
- if(!istype(src.target, /turf/))
- src.path = AStar(src.loc, src.target.loc, /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30, id=botcard)
- else
- src.path = AStar(src.loc, src.target, /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30, id=botcard)
- if (!src.path) src.path = list()
- if(src.path.len == 0)
- src.oldtarget = src.target
- src.target = null
- return
- if(src.path.len > 0 && src.target && (src.target != null))
- step_to(src, src.path[1])
- src.path -= src.path[1]
- else if(src.path.len == 1)
- step_to(src, target)
- src.path = new()
-
- if(src.loc == src.target || src.loc == src.target.loc)
- if(istype(src.target, /obj/item/stack/tile/plasteel))
- src.eattile(src.target)
- else if(istype(src.target, /obj/item/stack/sheet/metal))
- src.maketile(src.target)
- else if(istype(src.target, /turf/) && emagged < 2)
- repair(src.target)
- else if(emagged == 2 && istype(src.target,/turf/simulated/floor))
- var/turf/simulated/floor/F = src.target
- src.anchored = 1
- src.repairing = 1
- if(prob(90))
- F.break_tile_to_plating()
- else
- F.ReplaceWithLattice()
- visible_message("\red [src] makes an excited booping sound.")
- spawn(50)
- src.amount ++
- src.anchored = 0
- src.repairing = 0
- src.target = null
- src.path = new()
- return
-
- src.oldloc = src.loc
-
-
-/obj/machinery/bot/floorbot/proc/repair(var/turf/target)
- if(istype(target, /turf/space/))
- if(target.loc.name == "Space")
- return
- else if(!istype(target, /turf/simulated/floor))
- return
- if(src.amount <= 0)
- return
- src.anchored = 1
- src.icon_state = "floorbot-c"
- if(istype(target, /turf/space/))
- visible_message("\red [src] begins to repair the hole")
- var/obj/item/stack/tile/plasteel/T = new /obj/item/stack/tile/plasteel
- src.repairing = 1
- spawn(50)
- T.build(src.loc)
- src.repairing = 0
- src.amount -= 1
- src.updateicon()
- src.anchored = 0
- src.target = null
- else
- visible_message("\red [src] begins to improve the floor.")
- src.repairing = 1
- spawn(50)
- src.loc.icon_state = "floor"
- src.repairing = 0
- src.amount -= 1
- src.updateicon()
- src.anchored = 0
- src.target = null
-
-/obj/machinery/bot/floorbot/proc/eattile(var/obj/item/stack/tile/plasteel/T)
- if(!istype(T, /obj/item/stack/tile/plasteel))
- return
- visible_message("\red [src] begins to collect tiles.")
- src.repairing = 1
- spawn(20)
- if(isnull(T))
- src.target = null
- src.repairing = 0
- return
- if(src.amount + T.get_amount() > 50)
- var/i = 50 - src.amount
- src.amount += i
- T.use(i)
- else
- src.amount += T.get_amount()
- del(T)
- src.updateicon()
- src.target = null
- src.repairing = 0
-
-/obj/machinery/bot/floorbot/proc/maketile(var/obj/item/stack/sheet/metal/M)
- if(!istype(M, /obj/item/stack/sheet/metal))
- return
- if(M.get_amount() > 1)
- return
- visible_message("\red [src] begins to create tiles.")
- src.repairing = 1
- spawn(20)
- if(isnull(M))
- src.target = null
- src.repairing = 0
- return
- var/obj/item/stack/tile/plasteel/T = new /obj/item/stack/tile/plasteel
- T.amount = 4
- T.loc = M.loc
- del(M)
- src.target = null
- src.repairing = 0
-
-/obj/machinery/bot/floorbot/proc/updateicon()
- if(src.amount > 0)
- src.icon_state = "floorbot[src.on]"
- else
- src.icon_state = "floorbot[src.on]e"
-
-/obj/machinery/bot/floorbot/explode()
- src.on = 0
- src.visible_message("\red [src] blows apart!", 1)
- var/turf/Tsec = get_turf(src)
-
- var/obj/item/weapon/storage/toolbox/mechanical/N = new /obj/item/weapon/storage/toolbox/mechanical(Tsec)
- N.contents = list()
-
- new /obj/item/device/assembly/prox_sensor(Tsec)
-
- if (prob(50))
- new /obj/item/robot_parts/l_arm(Tsec)
-
- while (amount)//Dumps the tiles into the appropriate sized stacks
- if(amount >= 16)
- var/obj/item/stack/tile/plasteel/T = new (Tsec)
- T.amount = 16
- amount -= 16
- else
- var/obj/item/stack/tile/plasteel/T = new (Tsec)
- T.amount = src.amount
- amount = 0
-
- var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
- s.set_up(3, 1, src)
- s.start()
- del(src)
- return
-
-
-/obj/item/weapon/storage/toolbox/mechanical/attackby(var/obj/item/stack/tile/plasteel/T, mob/user as mob)
- if(!istype(T, /obj/item/stack/tile/plasteel))
- ..()
- return
- if(src.contents.len >= 1)
- user << "They wont fit in as there is already stuff inside."
- return
- if(user.s_active)
- user.s_active.close(user)
- if (T.use(10))
- var/obj/item/weapon/toolbox_tiles/B = new /obj/item/weapon/toolbox_tiles
- user.put_in_hands(B)
- user << "You add the tiles into the empty toolbox. They protrude from the top."
- user.drop_from_inventory(src)
- del(src)
- else
- user << "You need 10 floortiles for a floorbot."
- return
-
-/obj/item/weapon/toolbox_tiles/attackby(var/obj/item/W, mob/user as mob)
- ..()
- if(isprox(W))
- del(W)
- var/obj/item/weapon/toolbox_tiles_sensor/B = new /obj/item/weapon/toolbox_tiles_sensor()
- B.created_name = src.created_name
- user.put_in_hands(B)
- user << "You add the sensor to the toolbox and tiles!"
- user.drop_from_inventory(src)
- del(src)
-
- else if (istype(W, /obj/item/weapon/pen))
- var/t = sanitizeSafe(input(user, "Enter new robot name", src.name, src.created_name), MAX_NAME_LEN)
- if (!t)
- return
- if (!in_range(src, usr) && src.loc != usr)
- return
-
- src.created_name = t
-
-/obj/item/weapon/toolbox_tiles_sensor/attackby(var/obj/item/W, mob/user as mob)
- ..()
- if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm))
- del(W)
- var/turf/T = get_turf(user.loc)
- var/obj/machinery/bot/floorbot/A = new /obj/machinery/bot/floorbot(T)
- A.name = src.created_name
- user << "You add the robot arm to the odd looking toolbox assembly! Boop beep!"
- user.drop_from_inventory(src)
- del(src)
- else if (istype(W, /obj/item/weapon/pen))
- var/t = sanitizeSafe(input(user, "Enter new robot name", src.name, src.created_name), MAX_NAME_LEN)
-
- if (!t)
- return
- if (!in_range(src, usr) && src.loc != usr)
- return
-
- src.created_name = t
diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm
deleted file mode 100644
index 8ce3522707..0000000000
--- a/code/game/machinery/bots/medbot.dm
+++ /dev/null
@@ -1,596 +0,0 @@
-//MEDBOT
-//MEDBOT PATHFINDING
-//MEDBOT ASSEMBLY
-
-
-/obj/machinery/bot/medbot
- name = "Medibot"
- desc = "A little medical robot. He looks somewhat underwhelmed."
- icon = 'icons/obj/aibots.dmi'
- icon_state = "medibot0"
- layer = 5.0
- density = 0
- anchored = 0
- health = 20
- maxhealth = 20
- req_access =list(access_medical)
- var/stunned = 0 //It can be stunned by tasers. Delicate circuits.
-//var/emagged = 0
- var/list/botcard_access = list(access_medical)
- var/obj/item/weapon/reagent_containers/glass/reagent_glass = null //Can be set to draw from this for reagents.
- var/skin = null //Set to "tox", "ointment" or "o2" for the other two firstaid kits.
- var/frustration = 0
- var/path[] = new()
- var/mob/living/carbon/patient = null
- var/mob/living/carbon/oldpatient = null
- var/oldloc = null
- var/last_found = 0
- var/last_newpatient_speak = 0 //Don't spam the "HEY I'M COMING" messages
- var/currently_healing = 0
- var/injection_amount = 15 //How much reagent do we inject at a time?
- var/heal_threshold = 10 //Start healing when they have this much damage in a category
- var/use_beaker = 0 //Use reagents in beaker instead of default treatment agents.
- //Setting which reagents to use to treat what by default. By id.
- var/treatment_brute = "tricordrazine"
- var/treatment_oxy = "tricordrazine"
- var/treatment_fire = "tricordrazine"
- var/treatment_tox = "tricordrazine"
- var/treatment_virus = "spaceacillin"
- var/declare_treatment = 0 //When attempting to treat a patient, should it notify everyone wearing medhuds?
- var/shut_up = 0 //self explanatory :)
-
-/obj/machinery/bot/medbot/mysterious
- name = "Mysterious Medibot"
- desc = "International Medibot of mystery."
- skin = "bezerk"
- treatment_oxy = "dexalinp"
- treatment_brute = "bicaridine"
- treatment_fire = "kelotane"
- treatment_tox = "anti_toxin"
-
-/obj/item/weapon/firstaid_arm_assembly
- name = "first aid/robot arm assembly"
- desc = "A first aid kit with a robot arm permanently grafted to it."
- icon = 'icons/obj/aibots.dmi'
- icon_state = "firstaid_arm"
- var/build_step = 0
- var/created_name = "Medibot" //To preserve the name if it's a unique medbot I guess
- var/skin = null //Same as medbot, set to tox or ointment for the respective kits.
- w_class = 3.0
-
- New()
- ..()
- spawn(5)
- if(src.skin)
- src.overlays += image('icons/obj/aibots.dmi', "kit_skin_[src.skin]")
-
-
-/obj/machinery/bot/medbot/New()
- ..()
- src.icon_state = "medibot[src.on]"
-
- spawn(4)
- if(src.skin)
- src.overlays += image('icons/obj/aibots.dmi', "medskin_[src.skin]")
-
- src.botcard = new /obj/item/weapon/card/id(src)
- if(isnull(src.botcard_access) || (src.botcard_access.len < 1))
- src.botcard.access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics)
- else
- src.botcard.access = src.botcard_access
-
-/obj/machinery/bot/medbot/turn_on()
- . = ..()
- src.icon_state = "medibot[src.on]"
- src.updateUsrDialog()
-
-/obj/machinery/bot/medbot/turn_off()
- ..()
- src.patient = null
- src.oldpatient = null
- src.oldloc = null
- src.path = new()
- src.currently_healing = 0
- src.last_found = world.time
- src.icon_state = "medibot[src.on]"
- src.updateUsrDialog()
-
-/obj/machinery/bot/medbot/attack_hand(mob/user as mob)
- . = ..()
- if (.)
- return
- var/dat
- dat += "Automatic Medical Unit v1.0
"
- dat += "Status: [src.on ? "On" : "Off"]
"
- dat += "Maintenance panel is [src.open ? "opened" : "closed"]
"
- dat += "Beaker: "
- if (src.reagent_glass)
- dat += "Loaded \[[src.reagent_glass.reagents.total_volume]/[src.reagent_glass.reagents.maximum_volume]\]"
- else
- dat += "None Loaded"
- dat += "
Behaviour controls are [src.locked ? "locked" : "unlocked"]
"
- if(!src.locked || issilicon(user))
- dat += "Healing Threshold: "
- dat += "-- "
- dat += "- "
- dat += "[src.heal_threshold] "
- dat += "+ "
- dat += "++"
- dat += "
"
-
- dat += "Injection Level: "
- dat += "- "
- dat += "[src.injection_amount] "
- dat += "+ "
- dat += "
"
-
- dat += "Reagent Source: "
- dat += "[src.use_beaker ? "Loaded Beaker (When available)" : "Internal Synthesizer"]
"
-
- dat += "Treatment report is [src.declare_treatment ? "on" : "off"]. Toggle
"
-
- dat += "The speaker switch is [src.shut_up ? "off" : "on"]. Toggle
"
-
- user << browse("Medibot v1.0 controls[dat]", "window=automed")
- onclose(user, "automed")
- return
-
-/obj/machinery/bot/medbot/Topic(href, href_list)
- if(..())
- return
- usr.set_machine(src)
- src.add_fingerprint(usr)
- if ((href_list["power"]) && (src.allowed(usr)))
- if (src.on)
- turn_off()
- else
- turn_on()
-
- else if((href_list["adj_threshold"]) && (!src.locked || issilicon(usr)))
- var/adjust_num = text2num(href_list["adj_threshold"])
- src.heal_threshold += adjust_num
- if(src.heal_threshold < 5)
- src.heal_threshold = 5
- if(src.heal_threshold > 75)
- src.heal_threshold = 75
-
- else if((href_list["adj_inject"]) && (!src.locked || issilicon(usr)))
- var/adjust_num = text2num(href_list["adj_inject"])
- src.injection_amount += adjust_num
- if(src.injection_amount < 5)
- src.injection_amount = 5
- if(src.injection_amount > 15)
- src.injection_amount = 15
-
- else if((href_list["use_beaker"]) && (!src.locked || issilicon(usr)))
- src.use_beaker = !src.use_beaker
-
- else if (href_list["eject"] && (!isnull(src.reagent_glass)))
- if(!src.locked)
- src.reagent_glass.loc = get_turf(src)
- src.reagent_glass = null
- else
- usr << "You cannot eject the beaker because the panel is locked."
-
- else if ((href_list["togglevoice"]) && (!src.locked || issilicon(usr)))
- src.shut_up = !src.shut_up
-
- else if ((href_list["declaretreatment"]) && (!src.locked || issilicon(usr)))
- src.declare_treatment = !src.declare_treatment
-
- src.updateUsrDialog()
- return
-
-/obj/machinery/bot/medbot/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
- if (src.allowed(user) && !open && !emagged)
- src.locked = !src.locked
- user << "Controls are now [src.locked ? "locked." : "unlocked."]"
- src.updateUsrDialog()
- else
- if(emagged)
- user << "ERROR"
- if(open)
- user << "Please close the access panel before locking it."
- else
- user << "Access denied."
-
- else if (istype(W, /obj/item/weapon/reagent_containers/glass))
- if(src.locked)
- user << "You cannot insert a beaker because the panel is locked."
- return
- if(!isnull(src.reagent_glass))
- user << "There is already a beaker loaded."
- return
-
- user.drop_item()
- W.loc = src
- src.reagent_glass = W
- user << "You insert [W]."
- src.updateUsrDialog()
- return
-
- else
- ..()
- if (health < maxhealth && !istype(W, /obj/item/weapon/screwdriver) && W.force)
- step_to(src, (get_step_away(src,user)))
-
-/obj/machinery/bot/medbot/Emag(mob/user as mob)
- ..()
- if(open && !locked)
- if(user) user << "You short out [src]'s reagent synthesis circuits."
- spawn(0)
- for(var/mob/O in hearers(src, null))
- O.show_message("\red [src] buzzes oddly!", 1)
- flick("medibot_spark", src)
- src.patient = null
- if(user) src.oldpatient = user
- src.currently_healing = 0
- src.last_found = world.time
- src.anchored = 0
- src.emagged = 2
- src.on = 1
- src.icon_state = "medibot[src.on]"
-
-/obj/machinery/bot/medbot/process()
- set background = 1
-
- if(!src.on)
- src.stunned = 0
- return
-
- if(src.stunned)
- src.icon_state = "medibota"
- src.stunned--
-
- src.oldpatient = src.patient
- src.patient = null
- src.currently_healing = 0
-
- if(src.stunned <= 0)
- src.icon_state = "medibot[src.on]"
- src.stunned = 0
- return
-
- if(src.frustration > 8)
- src.oldpatient = src.patient
- src.patient = null
- src.currently_healing = 0
- src.last_found = world.time
- src.path = new()
-
- if(!src.patient)
- if(!src.shut_up && prob(1))
- var/message = pick("Radar, put a mask on!","There's always a catch, and it's the best there is.","I knew it, I should've been a plastic surgeon.","What kind of medbay is this? Everyone's dropping like dead flies.","Delicious!")
- src.speak(message)
-
- for (var/mob/living/carbon/C in view(7,src)) //Time to find a patient!
- if ((C.stat == 2) || !istype(C, /mob/living/carbon/human))
- continue
-
- if ((C == src.oldpatient) && (world.time < src.last_found + 100))
- continue
-
- if(src.assess_patient(C))
- src.patient = C
- src.oldpatient = C
- src.last_found = world.time
- if((src.last_newpatient_speak + 300) < world.time) //Don't spam these messages!
- var/message = pick("Hey, [C.name]! Hold on, I'm coming.","Wait [C.name]! I want to help!","[C.name], you appear to be injured!")
- src.speak(message)
- src.visible_message("[src] points at [C.name]!")
- src.last_newpatient_speak = world.time
- break
- else
- continue
-
-
- if(src.patient && Adjacent(patient))
- if(!src.currently_healing)
- src.currently_healing = 1
- src.frustration = 0
- src.medicate_patient(src.patient)
- return
-
- else if(src.patient && (src.path.len) && (get_dist(src.patient,src.path[src.path.len]) > 2))
- src.path = new()
- src.currently_healing = 0
- src.last_found = world.time
-
- if(src.patient && src.path.len == 0 && (get_dist(src,src.patient) > 1))
- spawn(0)
- src.path = AStar(src.loc, get_turf(src.patient), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30,id=botcard)
- if (!path) path = list()
- if(src.path.len == 0)
- src.oldpatient = src.patient
- src.patient = null
- src.currently_healing = 0
- src.last_found = world.time
- return
-
- if(src.path.len > 0 && src.patient)
- step_to(src, src.path[1])
- src.path -= src.path[1]
- spawn(3)
- if(src.path.len)
- step_to(src, src.path[1])
- src.path -= src.path[1]
-
- if(src.path.len > 8 && src.patient)
- src.frustration++
-
- return
-
-/obj/machinery/bot/medbot/proc/assess_patient(mob/living/carbon/C as mob)
- //Time to see if they need medical help!
- if(C.stat == 2)
- return 0 //welp too late for them!
-
- if(C.suiciding)
- return 0 //Kevorkian school of robotic medical assistants.
-
- if(src.emagged == 2) //Everyone needs our medicine. (Our medicine is toxins)
- return 1
-
- //If they're injured, we're using a beaker, and don't have one of our WONDERCHEMS.
- if((src.reagent_glass) && (src.use_beaker) && ((C.getBruteLoss() >= heal_threshold) || (C.getToxLoss() >= heal_threshold) || (C.getToxLoss() >= heal_threshold) || (C.getOxyLoss() >= (heal_threshold + 15))))
- for(var/datum/reagent/R in src.reagent_glass.reagents.reagent_list)
- if(!C.reagents.has_reagent(R))
- return 1
- continue
-
- //They're injured enough for it!
- if((C.getBruteLoss() >= heal_threshold) && (!C.reagents.has_reagent(src.treatment_brute)))
- return 1 //If they're already medicated don't bother!
-
- if((C.getOxyLoss() >= (15 + heal_threshold)) && (!C.reagents.has_reagent(src.treatment_oxy)))
- return 1
-
- if((C.getFireLoss() >= heal_threshold) && (!C.reagents.has_reagent(src.treatment_fire)))
- return 1
-
- if((C.getToxLoss() >= heal_threshold) && (!C.reagents.has_reagent(src.treatment_tox)))
- return 1
-
-
- for(var/datum/disease/D in C.viruses)
- if((D.stage > 1) || (D.spread_type == AIRBORNE))
-
- if (!C.reagents.has_reagent(src.treatment_virus))
- return 1 //STOP DISEASE FOREVER
-
- return 0
-
-/obj/machinery/bot/medbot/proc/medicate_patient(mob/living/carbon/C as mob)
- if(!src.on)
- return
-
- if(!istype(C))
- src.oldpatient = src.patient
- src.patient = null
- src.currently_healing = 0
- src.last_found = world.time
- return
-
- if(C.stat == 2)
- var/death_message = pick("No! NO!","Live, damnit! LIVE!","I...I've never lost a patient before. Not today, I mean.")
- src.speak(death_message)
- src.oldpatient = src.patient
- src.patient = null
- src.currently_healing = 0
- src.last_found = world.time
- return
-
- var/reagent_id = null
-
- //Use whatever is inside the loaded beaker. If there is one.
- if((src.use_beaker) && (src.reagent_glass) && (src.reagent_glass.reagents.total_volume))
- reagent_id = "internal_beaker"
-
- if(src.emagged == 2) //Emagged! Time to poison everybody.
- reagent_id = "toxin"
-
- var/virus = 0
- for(var/datum/disease/D in C.viruses)
- virus = 1
-
- if (!reagent_id && (virus))
- if(!C.reagents.has_reagent(src.treatment_virus))
- reagent_id = src.treatment_virus
-
- if (!reagent_id && (C.getBruteLoss() >= heal_threshold))
- if(!C.reagents.has_reagent(src.treatment_brute))
- reagent_id = src.treatment_brute
-
- if (!reagent_id && (C.getOxyLoss() >= (15 + heal_threshold)))
- if(!C.reagents.has_reagent(src.treatment_oxy))
- reagent_id = src.treatment_oxy
-
- if (!reagent_id && (C.getFireLoss() >= heal_threshold))
- if(!C.reagents.has_reagent(src.treatment_fire))
- reagent_id = src.treatment_fire
-
- if (!reagent_id && (C.getToxLoss() >= heal_threshold))
- if(!C.reagents.has_reagent(src.treatment_tox))
- reagent_id = src.treatment_tox
-
- if(!reagent_id) //If they don't need any of that they're probably cured!
- src.oldpatient = src.patient
- src.patient = null
- src.currently_healing = 0
- src.last_found = world.time
- var/message = pick("All patched up!","An apple a day keeps me away.","Feel better soon!")
- src.speak(message)
- return
- else
- src.icon_state = "medibots"
- visible_message("\red [src] is trying to inject [src.patient]!")
- spawn(30)
- if ((get_dist(src, src.patient) <= 1) && (src.on))
- if((reagent_id == "internal_beaker") && (src.reagent_glass) && (src.reagent_glass.reagents.total_volume))
- src.reagent_glass.reagents.trans_to(src.patient,src.injection_amount) //Inject from beaker instead.
- src.reagent_glass.reagents.reaction(src.patient, 2)
- else
- src.patient.reagents.add_reagent(reagent_id,src.injection_amount)
- visible_message("\red [src] injects [src.patient] with the syringe!")
-
- if(declare_treatment)
- var/area/location = get_area(src)
- broadcast_medical_hud_message("[src.name] is treating [C] in [location]", src)
-
- src.icon_state = "medibot[src.on]"
- src.currently_healing = 0
- return
-
-// src.speak(reagent_id)
- reagent_id = null
- return
-
-
-/obj/machinery/bot/medbot/proc/speak(var/message)
- if((!src.on) || (!message))
- return
- visible_message("[src] beeps, \"[message]\"")
- return
-
-/obj/machinery/bot/medbot/bullet_act(var/obj/item/projectile/Proj)
- if(Proj.taser_effect)
- src.stunned = min(stunned+10,20)
- ..()
-
-/obj/machinery/bot/medbot/explode()
- src.on = 0
- visible_message("\red [src] blows apart!", 1)
- var/turf/Tsec = get_turf(src)
-
- new /obj/item/weapon/storage/firstaid(Tsec)
-
- new /obj/item/device/assembly/prox_sensor(Tsec)
-
- new /obj/item/device/healthanalyzer(Tsec)
-
- if(src.reagent_glass)
- src.reagent_glass.loc = Tsec
- src.reagent_glass = null
-
- if (prob(50))
- new /obj/item/robot_parts/l_arm(Tsec)
-
- var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
- s.set_up(3, 1, src)
- s.start()
- del(src)
- return
-
-/obj/machinery/bot/medbot/Bump(M as mob|obj) //Leave no door unopened!
- if ((istype(M, /obj/machinery/door)) && (!isnull(src.botcard)))
- var/obj/machinery/door/D = M
- if (!istype(D, /obj/machinery/door/firedoor) && D.check_access(src.botcard) && !istype(D,/obj/machinery/door/blast))
- D.open()
- src.frustration = 0
- else if ((istype(M, /mob/living/)) && (!src.anchored))
- src.loc = M:loc
- src.frustration = 0
- return
-
-/* terrible
-/obj/machinery/bot/medbot/Bumped(atom/movable/M as mob|obj)
- spawn(0)
- if (M)
- var/turf/T = get_turf(src)
- M:loc = T
-*/
-
-/*
- * Pathfinding procs, allow the medibot to path through doors it has access to.
- */
-
-//Pretty ugh
-/*
-/turf/proc/AdjacentTurfsAllowMedAccess()
- var/L[] = new()
- for(var/turf/t in oview(src,1))
- if(!t.density)
- if(!LinkBlocked(src, t) && !TurfBlockedNonWindowNonDoor(t,get_access("Medical Doctor")))
- L.Add(t)
- return L
-
-
-//It isn't blocked if we can open it, man.
-/proc/TurfBlockedNonWindowNonDoor(turf/loc, var/list/access)
- for(var/obj/O in loc)
- if(O.density && !istype(O, /obj/structure/window) && !istype(O, /obj/machinery/door))
- return 1
-
- if (O.density && (istype(O, /obj/machinery/door)) && (access.len))
- var/obj/machinery/door/D = O
- for(var/req in D.req_access)
- if(!(req in access)) //doesn't have this access
- return 1
-
- return 0
-*/
-
-/*
- * Medbot Assembly -- Can be made out of all three medkits.
- */
-
-/obj/item/weapon/storage/firstaid/attackby(var/obj/item/robot_parts/S, mob/user as mob)
-
- if ((!istype(S, /obj/item/robot_parts/l_arm)) && (!istype(S, /obj/item/robot_parts/r_arm)))
- ..()
- return
-
- //Making a medibot!
- if(src.contents.len >= 1)
- user << "You need to empty [src] out first."
- return
-
- var/obj/item/weapon/firstaid_arm_assembly/A = new /obj/item/weapon/firstaid_arm_assembly
- if(istype(src,/obj/item/weapon/storage/firstaid/fire))
- A.skin = "ointment"
- else if(istype(src,/obj/item/weapon/storage/firstaid/toxin))
- A.skin = "tox"
- else if(istype(src,/obj/item/weapon/storage/firstaid/o2))
- A.skin = "o2"
-
- del(S)
- user.put_in_hands(A)
- user << "You add the robot arm to the first aid kit."
- user.drop_from_inventory(src)
- del(src)
-
-
-/obj/item/weapon/firstaid_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob)
- ..()
- if(istype(W, /obj/item/weapon/pen))
- var/t = sanitizeSafe(input(user, "Enter new robot name", src.name, src.created_name), MAX_NAME_LEN)
- if (!t)
- return
- if (!in_range(src, usr) && src.loc != usr)
- return
- src.created_name = t
- else
- switch(build_step)
- if(0)
- if(istype(W, /obj/item/device/healthanalyzer))
- user.drop_item()
- del(W)
- src.build_step++
- user << "You add the health sensor to [src]."
- src.name = "First aid/robot arm/health analyzer assembly"
- src.overlays += image('icons/obj/aibots.dmi', "na_scanner")
-
- if(1)
- if(isprox(W))
- user.drop_item()
- del(W)
- src.build_step++
- user << "You complete the Medibot! Beep boop."
- var/turf/T = get_turf(src)
- var/obj/machinery/bot/medbot/S = new /obj/machinery/bot/medbot(T)
- S.skin = src.skin
- S.name = src.created_name
- user.drop_from_inventory(src)
- del(src)
-
diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm
deleted file mode 100644
index 92b7297c42..0000000000
--- a/code/game/machinery/bots/secbot.dm
+++ /dev/null
@@ -1,924 +0,0 @@
-/obj/machinery/bot/secbot
- name = "Securitron"
- desc = "A little security robot. He looks less than thrilled."
- icon = 'icons/obj/aibots.dmi'
- icon_state = "secbot0"
- layer = 5.0
- density = 0
- anchored = 0
- health = 25
- maxhealth = 25
- fire_dam_coeff = 0.7
- brute_dam_coeff = 0.5
- req_one_access = list(access_security, access_forensics_lockers)
-
- var/mob/target
- var/oldtarget_name
- var/threatlevel = 0
- var/target_lastloc //Loc of target when arrested.
- var/last_found //There's a delay
- var/frustration = 0
-
- var/idcheck = 0 //If false, all station IDs are authorized for weapons.
- var/check_records = 0 //Does it check security records?
- var/check_arrest = 1 //Does it check arrest status?
- var/arrest_type = 0 //If true, don't handcuff
- var/declare_arrests = 0 //When making an arrest, should it notify everyone wearing sechuds?
-
- var/has_laser = 0
- var/next_harm_time = 0
- var/lastfired = 0
- var/shot_delay = 3 //.3 seconds between shots
- var/lasercolor = ""
- var/projectile = null//Holder for projectile type, to avoid so many else if chains
- var/disabled = 0//A holder for if it needs to be disabled, if true it will not seach for targets, shoot at targets, or move, currently only used for lasertag
-
- var/mode = 0
-#define SECBOT_IDLE 0 // idle
-#define SECBOT_HUNT 1 // found target, hunting
-#define SECBOT_PREP_ARREST 2 // at target, preparing to arrest
-#define SECBOT_ARREST 3 // arresting target
-#define SECBOT_START_PATROL 4 // start patrol
-#define SECBOT_PATROL 5 // patrolling
-#define SECBOT_SUMMON 6 // summoned by PDA
-
- var/auto_patrol = 0 // set to make bot automatically patrol
-
- var/beacon_freq = 1445 // navigation beacon frequency
- var/control_freq = AI_FREQ // bot control frequency
-
-
- var/turf/patrol_target // this is turf to navigate to (location of beacon)
- var/new_destination // pending new destination (waiting for beacon response)
- var/destination // destination description tag
- var/next_destination // the next destination in the patrol route
- var/list/path = new // list of path turfs
-
- var/blockcount = 0 //number of times retried a blocked path
- var/awaiting_beacon = 0 // count of pticks awaiting a beacon response
-
- var/nearest_beacon // the nearest beacon's tag
- var/turf/nearest_beacon_loc // the nearest beacon's location
-
- var/bot_version = "1.3"
- var/search_range = 7
- var/is_attacking = 0
-
- var/obj/item/weapon/secbot_assembly = /obj/item/weapon/secbot_assembly
-
- var/list/threat_found_sounds = new('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg')
- var/list/preparing_arrest_sounds = new('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg')
-
-/obj/machinery/bot/secbot/beepsky
- name = "Officer Beep O'sky"
- desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey."
- idcheck = 0
- auto_patrol = 1
-
-/obj/item/weapon/secbot_assembly
- name = "helmet/signaler assembly"
- desc = "Some sort of bizarre assembly."
- icon = 'icons/obj/aibots.dmi'
- icon_state = "helmet_signaler"
- item_state = "helmet"
- var/build_step = 0
- var/created_name = "Securitron" //To preserve the name if it's a unique securitron I guess
-
-/obj/machinery/bot/secbot/New(loc, created_name, created_lasercolor)
- ..()
- if(created_name) name = created_name
- if(created_lasercolor) lasercolor = created_lasercolor
- update_icon()
- spawn(3)
- src.botcard = new /obj/item/weapon/card/id(src)
- src.botcard.access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court)
- if(radio_controller)
- radio_controller.add_object(src, control_freq, filter = RADIO_SECBOT)
- radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS)
- if(lasercolor)
- shot_delay = 6 //Longer shot delay because JESUS CHRIST
- check_arrest = 0
- check_records = 0 //Don't actively target people set to arrest
- arrest_type = 1 //Don't even try to cuff
- req_access = list(access_maint_tunnels)
- arrest_type = 1
- if((lasercolor == "b") && (name == created_name))//Picks a name if there isn't already a custome one
- name = pick("BLUE BALLER","SANIC","BLUE KILLDEATH MURDERBOT")
- if((lasercolor == "r") && (name == created_name))
- name = pick("RED RAMPAGE","RED ROVER","RED KILLDEATH MURDERBOT")
-
-
-/obj/machinery/bot/secbot/update_icon()
- if(on && is_attacking)
- src.icon_state = "secbot-c"
- else
- src.icon_state = "secbot[src.on]"
-
-/obj/machinery/bot/secbot/turn_on()
- ..()
- update_icon()
- src.updateUsrDialog()
-
-/obj/machinery/bot/secbot/turn_off()
- ..()
- src.target = null
- src.oldtarget_name = null
- src.anchored = 0
- src.mode = SECBOT_IDLE
- walk_to(src,0)
- update_icon()
- src.updateUsrDialog()
-
-/obj/machinery/bot/secbot/attack_hand(mob/user as mob)
- . = ..()
- if(.)
- return
- usr.set_machine(src)
- interact(user)
-
-/obj/machinery/bot/secbot/interact(mob/user as mob)
- var/dat
-
- dat += text({"
-Automatic Security Unit v[bot_version]
-Status: []
-Behaviour controls are [src.locked ? "locked" : "unlocked"]
-Maintenance panel is [src.open ? "opened" : "closed"]"},
-
-"[src.on ? "On" : "Off"]" )
-
- if(!src.locked || issilicon(user))
- dat += text({"
-Check for Weapon Authorization: []
-Check Security Records: []
-Check Arrest Status: []
-Operating Mode: []
-Report Arrests: []
-Auto Patrol: []"},
-
-"[src.idcheck ? "Yes" : "No"]",
-"[src.check_records ? "Yes" : "No"]",
-"[src.check_arrest ? "Yes" : "No"]",
-"[src.arrest_type ? "Detain" : "Arrest"]",
-"[src.declare_arrests ? "Yes" : "No"]",
-"[auto_patrol ? "On" : "Off"]" )
-
-
- user << browse("Securitron v[bot_version] controls[dat]", "window=autosec")
- onclose(user, "autosec")
- return
-
-/obj/machinery/bot/secbot/Topic(href, href_list)
- if(..())
- return
- usr.set_machine(src)
- src.add_fingerprint(usr)
- if(lasercolor && (istype(usr,/mob/living/carbon/human)))
- var/mob/living/carbon/human/H = usr
- if((lasercolor == "b") && (istype(H.wear_suit, /obj/item/clothing/suit/redtag)))//Opposing team cannot operate it
- return
- else if((lasercolor == "r") && (istype(H.wear_suit, /obj/item/clothing/suit/bluetag)))
- return
- if((href_list["power"]) && (src.allowed(usr)))
- if(src.on)
- turn_off()
- else
- turn_on()
- src.updateUsrDialog()
- return
-
- switch(href_list["operation"])
- if("idcheck")
- src.idcheck = !src.idcheck
- if("ignorerec")
- src.check_records = !src.check_records
- if("ignorearr")
- src.check_arrest = !src.check_arrest
- if("switchmode")
- src.arrest_type = !src.arrest_type
- if("patrol")
- auto_patrol = !auto_patrol
- mode = SECBOT_IDLE
- if("declarearrests")
- src.declare_arrests = !src.declare_arrests
- src.updateUsrDialog()
-
-/obj/machinery/bot/secbot/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
- if(src.allowed(user) && !open && !emagged)
- src.locked = !src.locked
- user << "Controls are now [src.locked ? "locked" : "unlocked"]."
- else
- if(emagged)
- user << "ERROR"
- if(open)
- user << "Please close the access panel before locking it."
- else
- user << "Access denied."
- else
- ..()
- if(!istype(W, /obj/item/weapon/screwdriver) && W.force && !src.target)
- src.target = user
- if(lasercolor)//To make up for the fact that lasertag bots don't hunt
- src.shootAt(user)
- src.mode = SECBOT_HUNT
-
-/obj/machinery/bot/secbot/Emag(mob/user as mob)
- ..()
- if(open && !locked)
- if(user) user << "You short out [src]'s target assessment circuits."
- spawn(0)
- for(var/mob/O in hearers(src, null))
- O.show_message("\red [src] buzzes oddly!", 1)
- src.target = null
- if(user) src.oldtarget_name = user.name
- src.last_found = world.time
- src.anchored = 0
- src.emagged = 2
- src.on = 1
- update_icon()
- src.projectile = null
- mode = SECBOT_IDLE
-
-/obj/machinery/bot/secbot/process()
- set background = 1
-
- if(!src.on)
- return
-
- switch(mode)
-
- if(SECBOT_IDLE) // idle
- walk_to(src,0)
- look_for_perp() // see if any criminals are in range
- if(!mode && auto_patrol) // still idle, and set to patrol
- mode = SECBOT_START_PATROL // switch to patrol mode
-
- if(SECBOT_HUNT) // hunting for perp
- // if can't reach perp for long enough, go idle
- if(src.frustration >= 8)
- // for(var/mob/O in hearers(src, null))
- // O << "[src] beeps, \"Backup requested! Suspect has evaded arrest.\""
- src.target = null
- src.last_found = world.time
- src.frustration = 0
- src.mode = 0
- walk_to(src,0)
-
- if(target) // make sure target exists
- // We re-assess human targets, before bashing their head in, in case their credentials change
- if(istype(target, /mob/living/carbon/human))
- var/threat = src.assess_perp(target, idcheck, check_records, check_arrest)
- if(threat < 4)
- frustration = 8
- return
-
- // The target must remain in view to complete the desire to bash its head in
- if(!(target in view(search_range,src)))
- frustration++
- return
-
- if(!lasercolor && Adjacent(target)) // If right next to perp. Lasertag bots do not arrest anyone, just patrol and shoot and whatnot
- if(istype(src.target,/mob/living/carbon))
- playsound(src.loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
- is_attacking = 1
- update_icon()
- spawn(2)
- is_attacking = 0
- update_icon()
- var/mob/living/carbon/M = src.target
- var/maxstuns = 4
- if(istype(M, /mob/living/carbon/human))
- if(M.stuttering < 10 && (!(HULK in M.mutations)))
- M.stuttering = 10
- M.Stun(10)
- M.Weaken(10)
- else
- M.Weaken(10)
- M.stuttering = 10
- M.Stun(10)
- maxstuns--
- if(maxstuns <= 0)
- target = null
-
- if(declare_arrests)
- var/area/location = get_area(src)
- broadcast_security_hud_message("[src.name] is [arrest_type ? "detaining" : "arresting"] level [threatlevel] suspect [target] in [location]", src)
- visible_message("\red [src.target] has been stunned by [src]!")
-
- mode = SECBOT_PREP_ARREST
- src.anchored = 1
- src.target_lastloc = M.loc
- return
- else if(istype(src.target,/mob/living/simple_animal))
- //just harmbaton them until dead
- if(world.time > next_harm_time)
- next_harm_time = world.time + 15
- playsound(src.loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
- visible_message("\red [src] beats [src.target] with the stun baton!")
- update_icon()
- spawn(2)
- is_attacking = 0
- update_icon()
-
- var/mob/living/simple_animal/S = src.target
- S.AdjustStunned(10)
- S.adjustBruteLoss(15)
- if(S.stat)
- src.frustration = 8
- if(preparing_arrest_sounds.len > 0)
- playsound(src.loc, pick(preparing_arrest_sounds), 50, 0)
- else // not next to perp
- var/turf/olddist = get_dist(src, src.target)
- walk_to(src, target,1,4)
- shootAt(target)
- if((get_dist(src, src.target)) >= (olddist))
- src.frustration++
- else
- src.frustration = 0
- else
- src.frustration = 8
-
- if(SECBOT_PREP_ARREST) // preparing to arrest target
- if(src.lasercolor)
- mode = SECBOT_IDLE
- return
- if(!target)
- mode = SECBOT_IDLE
- src.anchored = 0
- return
- // see if he got away
- if((get_dist(src, src.target) > 1) || ((src.target.loc != src.target_lastloc) && src.target.weakened < 2))
- src.anchored = 0
- mode = SECBOT_HUNT
- return
-
- if(istype(src.target,/mob/living/carbon))
- var/mob/living/carbon/C = target
- var/wearing_hardsuit
- if(istype(C,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = C
- if(istype(H.back, /obj/item/weapon/rig) && istype(H.gloves,/obj/item/clothing/gloves/rig))
- wearing_hardsuit = 1
- if(!wearing_hardsuit && !C.handcuffed && !src.arrest_type)
- playsound(src.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2)
- mode = SECBOT_ARREST
- visible_message("\red [src] is trying to put handcuffs on [src.target]!")
-
- spawn(60)
- if(get_dist(src, src.target) <= 1)
- /*if(src.target.handcuffed)
- return*/
-
- if(istype(src.target,/mob/living/carbon))
- C = target
- if(!C.handcuffed)
- C.handcuffed = new /obj/item/weapon/handcuffs(target)
- C.update_inv_handcuffed() //update the handcuffs overlay
-
- mode = SECBOT_IDLE
- src.target = null
- src.anchored = 0
- src.last_found = world.time
- src.frustration = 0
-
- if(preparing_arrest_sounds.len > 0)
- playsound(src.loc, pick(preparing_arrest_sounds), 50, 0)
- // var/arrest_message = pick("Have a secure day!","I AM THE LAW.", "God made tomorrow for the crooks we don't catch today.","You can't outrun a radio.")
- // src.speak(arrest_message)
- else
- mode = SECBOT_IDLE
- src.target = null
- src.anchored = 0
- src.last_found = world.time
- src.frustration = 0
-
- if(SECBOT_ARREST) // arresting
- if(src.lasercolor)
- mode = SECBOT_IDLE
- return
- if(!target || !istype(target, /mob/living/carbon))
- src.anchored = 0
- mode = SECBOT_IDLE
- return
- else
- var/mob/living/carbon/C = target
- if(!C.handcuffed)
- src.anchored = 0
- mode = SECBOT_IDLE
- return
-
-
- if(SECBOT_START_PATROL) // start a patrol
-
- if(path.len > 0 && patrol_target) // have a valid path, so just resume
- mode = SECBOT_PATROL
- return
-
- else if(patrol_target) // has patrol target already
- spawn(0)
- calc_path() // so just find a route to it
- if(path.len == 0)
- patrol_target = 0
- return
- mode = SECBOT_PATROL
-
-
- else // no patrol target, so need a new one
- find_patrol_target()
- speak("Engaging patrol mode.")
-
-
- if(SECBOT_PATROL) // patrol mode
- patrol_step()
- spawn(5)
- if(mode == SECBOT_PATROL)
- patrol_step()
-
- if(SECBOT_SUMMON) // summoned to PDA
- patrol_step()
- spawn(4)
- if(mode == SECBOT_SUMMON)
- patrol_step()
- sleep(4)
- patrol_step()
-
- return
-
-
-// perform a single patrol step
-/obj/machinery/bot/secbot/proc/patrol_step()
- if(loc == patrol_target) // reached target
- at_patrol_target()
- return
- else if(path.len > 0 && patrol_target) // valid path
- var/turf/next = path[1]
- if(next == loc)
- path -= next
- return
-
- if(istype( next, /turf/simulated))
- var/moved = step_towards(src, next) // attempt to move
- if(moved) // successful move
- blockcount = 0
- path -= loc
-
- look_for_perp()
- if(lasercolor)
- sleep(20)
- else // failed to move
- blockcount++
- if(blockcount > 5) // attempt 5 times before recomputing
- // find new path excluding blocked turf
-
- spawn(2)
- calc_path(next)
- if(path.len == 0)
- find_patrol_target()
- else
- blockcount = 0
- return
- return
- else // not a valid turf
- mode = SECBOT_IDLE
- return
- else // no path, so calculate new one
- mode = SECBOT_START_PATROL
-
-// finds a new patrol target
-/obj/machinery/bot/secbot/proc/find_patrol_target()
- send_status()
- if(awaiting_beacon) // awaiting beacon response
- awaiting_beacon++
- if(awaiting_beacon > 5) // wait 5 secs for beacon response
- find_nearest_beacon() // then go to nearest instead
- return
-
- if(next_destination)
- set_destination(next_destination)
- else
- find_nearest_beacon()
- return
-
-// finds the nearest beacon to self
-// signals all beacons matching the patrol code
-/obj/machinery/bot/secbot/proc/find_nearest_beacon()
- nearest_beacon = null
- new_destination = "__nearest__"
- post_signal(beacon_freq, "findbeacon", "patrol")
- awaiting_beacon = 1
- spawn(10)
- awaiting_beacon = 0
- if(nearest_beacon)
- set_destination(nearest_beacon)
- else
- auto_patrol = 0
- mode = SECBOT_IDLE
- speak("Disengaging patrol mode.")
- send_status()
-
-/obj/machinery/bot/secbot/proc/at_patrol_target()
- find_patrol_target()
- return
-
-// sets the current destination
-// signals all beacons matching the patrol code
-// beacons will return a signal giving their locations
-/obj/machinery/bot/secbot/proc/set_destination(var/new_dest)
- new_destination = new_dest
- post_signal(beacon_freq, "findbeacon", "patrol")
- awaiting_beacon = 1
-
-
-// receive a radio signal
-// used for beacon reception
-/obj/machinery/bot/secbot/receive_signal(datum/signal/signal)
- //log_admin("DEBUG \[[world.timeofday]\]: /obj/machinery/bot/secbot/receive_signal([signal.debug_print()])")
- if(!on)
- return
-
- /*
- world << "rec signal: [signal.source]"
- for(var/x in signal.data)
- world << "* [x] = [signal.data[x]]"
- */
-
- var/recv = signal.data["command"]
- // process all-bot input
- if(recv=="bot_status")
- send_status()
-
- // check to see if we are the commanded bot
- if(signal.data["active"] == src)
- // process control input
- switch(recv)
- if("stop")
- mode = SECBOT_IDLE
- auto_patrol = 0
- return
-
- if("go")
- mode = SECBOT_IDLE
- auto_patrol = 1
- return
-
- if("summon")
- patrol_target = signal.data["target"]
- next_destination = destination
- destination = null
- awaiting_beacon = 0
- mode = SECBOT_SUMMON
- calc_path()
- speak("Responding.")
-
- return
-
-
-
- // receive response from beacon
- recv = signal.data["beacon"]
- var/valid = signal.data["patrol"]
- if(!recv || !valid)
- return
-
- if(recv == new_destination) // if the recvd beacon location matches the set destination
- // the we will navigate there
- destination = new_destination
- patrol_target = signal.source.loc
- next_destination = signal.data["next_patrol"]
- awaiting_beacon = 0
-
- // if looking for nearest beacon
- else if(new_destination == "__nearest__")
- var/dist = get_dist(src,signal.source.loc)
- if(nearest_beacon)
-
- // note we ignore the beacon we are located at
- if(dist>1 && dist 1)
- nearest_beacon = recv
- nearest_beacon_loc = signal.source.loc
- return
-
-
-// send a radio signal with a single data key/value pair
-/obj/machinery/bot/secbot/proc/post_signal(var/freq, var/key, var/value)
- post_signal_multiple(freq, list("[key]" = value) )
-
-// send a radio signal with multiple data key/values
-/obj/machinery/bot/secbot/proc/post_signal_multiple(var/freq, var/list/keyval)
-
- var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq)
-
- if(!frequency) return
-
- var/datum/signal/signal = new()
- signal.source = src
- signal.transmission_method = 1
- //for(var/key in keyval)
- // signal.data[key] = keyval[key]
- signal.data = keyval
- //world << "sent [key],[keyval[key]] on [freq]"
- if(signal.data["findbeacon"])
- frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS)
- else if(signal.data["type"] == "secbot")
- frequency.post_signal(src, signal, filter = RADIO_SECBOT)
- else
- frequency.post_signal(src, signal)
-
-// signals bot status etc. to controller
-/obj/machinery/bot/secbot/proc/send_status()
- var/list/kv = list(
- "type" = "secbot",
- "name" = name,
- "loca" = loc.loc, // area
- "mode" = mode
- )
- post_signal_multiple(control_freq, kv)
-
-// calculates a path to the current destination
-// given an optional turf to avoid
-/obj/machinery/bot/secbot/proc/calc_path(var/turf/avoid = null)
- src.path = AStar(src.loc, patrol_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 120, id=botcard, exclude=avoid)
- if(!path) path = list()
-
-// look for a criminal in view of the bot
-/obj/machinery/bot/secbot/proc/look_for_perp()
- if(src.disabled)
- return
- src.anchored = 0
- for(var/mob/living/M in view(search_range,src)) //Let's find us a criminal
- if(M.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var
- continue
-
- if(istype(M, /mob/living/carbon))
- var/mob/living/carbon/C = M
- if(C.stat || C.handcuffed)
- continue
-
- if(src.lasercolor && C.lying)
- continue//Does not shoot at people lying down when in lasertag mode, because it's just annoying, and they can fire once they get up.
-
- if(C.name == src.oldtarget_name && world.time < src.last_found + 100)
- continue
-
- if(istype(C, /mob/living/carbon/human))
- src.threatlevel = src.assess_perp(C, idcheck, check_records, check_arrest)
-
- else if(istype(M, /mob/living/simple_animal/hostile))
- if(M.stat == DEAD)
- continue
- else
- src.threatlevel = 4
-
- if(!src.threatlevel)
- continue
-
- else if(M.stat != DEAD && src.threatlevel >= 4)
- src.target = M
- src.oldtarget_name = M.name
- src.speak("Level [src.threatlevel] infraction alert!")
- if(!src.lasercolor && threat_found_sounds.len > 0)
- playsound(src.loc, pick(threat_found_sounds), 50, 0)
- src.visible_message("[src] points at [M.name]!")
-
- mode = SECBOT_HUNT
- spawn(0)
- process() // ensure bot quickly responds to a perp
- break
- else
- continue
-
-/obj/machinery/bot/secbot/on_assess_perp(mob/living/carbon/human/perp)
- if(lasercolor)
- return laser_check(perp, lasercolor)
-
- var/threat = 0
- threat -= laser_check(perp, "b")
- threat -= laser_check(perp, "r")
-
- return threat
-
-/obj/machinery/bot/secbot/proc/laser_check(mob/living/carbon/human/perp, var/lasercolor)
- var/target_suit
- var/target_weapon
- var/threat = 0
- //Lasertag turrets target the opposing team, how great is that? -Sieve
- switch(lasercolor)
- if("b")
- target_suit = /obj/item/clothing/suit/redtag
- target_weapon = /obj/item/weapon/gun/energy/lasertag/red
- if("r")
- target_suit = /obj/item/clothing/suit/bluetag
- target_weapon = /obj/item/weapon/gun/energy/lasertag/blue
-
- if((istype(perp.r_hand, target_weapon)) || (istype(perp.l_hand, target_weapon)))
- threat += 4
-
- if(istype(perp, /mob/living/carbon/human))
- if(istype(perp.wear_suit, target_suit))
- threat += 4
- if(istype(perp.belt, target_weapon))
- threat += 2
-
- return threat
-
-/obj/machinery/bot/secbot/is_assess_emagged()
- return emagged == 2
-
-/obj/machinery/bot/secbot/Bump(M as mob|obj) //Leave no door unopened!
- if((istype(M, /obj/machinery/door)) && !isnull(src.botcard))
- var/obj/machinery/door/D = M
- if(!istype(D, /obj/machinery/door/firedoor) && D.check_access(src.botcard) && !istype(D,/obj/machinery/door/blast))
- D.open()
- src.frustration = 0
- else if(!src.anchored)
- if((istype(M, /mob/living/)))
- var/mob/living/O = M
- src.loc = O.loc
- src.frustration = 0
- else if(istype(M, /obj/machinery/bot))
- var/obj/machinery/bot/B = M
- if(B.dir != src.dir) // Avoids issues if two bots are currently patrolling in the same direction
- src.loc = B.loc
- src.frustration = 0
- return
-
-/obj/machinery/bot/secbot/proc/speak(var/message)
- for(var/mob/O in hearers(src, null))
- O.show_message("[src] beeps, \"[message]\"",2)
- return
-
-/obj/machinery/bot/secbot/explode()
- walk_to(src,0)
- src.visible_message("\red [src] blows apart!", 1)
- var/turf/Tsec = get_turf(src)
-
- var/obj/item/weapon/secbot_assembly/Sa = new secbot_assembly(Tsec)
- Sa.build_step = 1
- Sa.overlays += image('icons/obj/aibots.dmi', "hs_hole")
- Sa.created_name = src.name
- new /obj/item/device/assembly/prox_sensor(Tsec)
- new /obj/item/weapon/melee/baton(Tsec)
-
- on_explosion()
-
- var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
- s.set_up(3, 1, src)
- s.start()
-
- new /obj/effect/decal/cleanable/blood/oil(src.loc)
- del(src)
-
-
-/obj/machinery/bot/secbot/proc/on_explosion(var/turf/Tsec)
- new /obj/item/weapon/melee/baton(Tsec)
- if(prob(50))
- new /obj/item/robot_parts/l_arm(Tsec)
-
-//Secbot Construction
-
-/obj/item/clothing/head/helmet/attackby(var/obj/item/device/assembly/signaler/S, mob/user as mob)
- ..()
- if(!issignaler(S))
- ..()
- return
-
- if(src.type != /obj/item/clothing/head/helmet) //Eh, but we don't want people making secbots out of space helmets.
- return
-
- if(S.secured)
- del(S)
- var/obj/item/weapon/secbot_assembly/A = new /obj/item/weapon/secbot_assembly
- user.put_in_hands(A)
- user << "You add the signaler to the helmet."
- user.drop_from_inventory(src)
- del(src)
- else
- return
-
-/obj/item/weapon/secbot_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob)
- ..()
- if((istype(W, /obj/item/weapon/weldingtool)) && (!src.build_step))
- var/obj/item/weapon/weldingtool/WT = W
- if(WT.remove_fuel(0,user))
- src.build_step++
- src.overlays += image('icons/obj/aibots.dmi', "hs_hole")
- user << "You weld a hole in [src]!"
-
- else if(isprox(W) && (src.build_step == 1))
- user.drop_item()
- src.build_step++
- user << "You add the prox sensor to [src]!"
- src.overlays += image('icons/obj/aibots.dmi', "hs_eye")
- src.name = "helmet/signaler/prox sensor assembly"
- del(W)
-
- else if(((istype(W, /obj/item/robot_parts/l_arm)) || (istype(W, /obj/item/robot_parts/r_arm))) && (src.build_step == 2))
- user.drop_item()
- src.build_step++
- user << "You add the robot arm to [src]!"
- src.name = "helmet/signaler/prox sensor/robot arm assembly"
- src.overlays += image('icons/obj/aibots.dmi', "hs_arm")
- del(W)
-
- else if((istype(W, /obj/item/weapon/melee/baton)) && (src.build_step >= 3))
- user.drop_item()
- src.build_step++
- user << "You complete the Securitron! Beep boop."
- var/obj/machinery/bot/secbot/S = new /obj/machinery/bot/secbot
- S.loc = get_turf(src)
- S.name = src.created_name
- del(W)
- del(src)
-
- else if(istype(W, /obj/item/weapon/pen))
- var/t = sanitizeSafe(input(user, "Enter new robot name", src.name, src.created_name), MAX_NAME_LEN)
- if(!t)
- return
- if(!in_range(src, usr) && src.loc != usr)
- return
- src.created_name = t
-
-/obj/machinery/bot/secbot/proc/shootAt(var/mob/target)
- if(!has_laser || (lastfired && world.time - lastfired < shot_delay))
- return
- lastfired = world.time
- var/turf/T = loc
- var/atom/U = (istype(target, /atom/movable) ? target.loc : target)
- if((!( U ) || !( T )))
- return
- while(!( istype(U, /turf) ))
- U = U.loc
- if(!( istype(T, /turf) ))
- return
-
- if(!projectile)
- if(!lasercolor)
- if(src.emagged == 2)
- projectile = /obj/item/projectile/beam
- else
- projectile = /obj/item/projectile/beam/stun
- else if(lasercolor == "b")
- if(src.emagged == 2)
- projectile = /obj/item/projectile/beam/lastertag/omni
- else
- projectile = /obj/item/projectile/beam/lastertag/blue
- else if(lasercolor == "r")
- if(src.emagged == 2)
- projectile = /obj/item/projectile/beam/lastertag/omni
- else
- projectile = /obj/item/projectile/beam/lastertag/red
-
- if(!( istype(U, /turf) ))
- return
-
- playsound(src.loc, src.emagged == 2 ? 'sound/weapons/Laser.ogg' : 'sound/weapons/Taser.ogg', 50, 1)
- var/obj/item/projectile/A = new projectile (loc)
- A.current = U
- A.yo = U.y - T.y
- A.xo = U.x - T.x
- spawn( 0 )
- A.process()
- return
- return
-
-/obj/machinery/bot/secbot/emp_act(severity)
- if(severity==2 && prob(70))
- ..(severity-1)
- else
- var/obj/effect/overlay/pulse2 = new/obj/effect/overlay ( src.loc )
- pulse2.icon = 'icons/effects/effects.dmi'
- pulse2.icon_state = "empdisable"
- pulse2.name = "emp sparks"
- pulse2.anchored = 1
- pulse2.set_dir(pick(cardinal))
- spawn(10)
- pulse2.delete()
- var/list/mob/living/carbon/targets = new
- for(var/mob/living/carbon/C in view(12,src))
- if(C.stat==2)
- continue
- targets += C
- if(targets.len)
- if(prob(50))
- var/mob/toshoot = pick(targets)
- if(toshoot)
- targets-=toshoot
- if(prob(50) && emagged < 2)
- emagged = 2
- shootAt(toshoot)
- emagged = 0
- else
- shootAt(toshoot)
- else if(prob(50))
- if(targets.len)
- var/mob/toarrest = pick(targets)
- if(toarrest)
- src.target = toarrest
- src.mode = SECBOT_HUNT
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index c6a30995d2..e49711c257 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -124,7 +124,7 @@
dat += "Back"
dat += "
Medical Robots:"
var/bdat = null
- for(var/obj/machinery/bot/medbot/M in world)
+ for(var/mob/living/bot/medbot/M in world)
if(M.z != src.z) continue //only find medibots on the same z-level as the computer
var/turf/bl = get_turf(M)
diff --git a/code/game/machinery/computer3/computers/medical.dm b/code/game/machinery/computer3/computers/medical.dm
index adb7ff9079..a0ab72d16b 100644
--- a/code/game/machinery/computer3/computers/medical.dm
+++ b/code/game/machinery/computer3/computers/medical.dm
@@ -136,7 +136,7 @@
dat += "Back"
dat += "
Medical Robots:"
var/bdat = null
- for(var/obj/machinery/bot/medbot/M in world)
+ for(var/mob/living/bot/medbot/M in world)
if(M.z != computer.z) continue //only find medibots on the same z-level as the computer
var/turf/bl = get_turf(M)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index f7058921da..0af0f49032 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -103,8 +103,8 @@
bumpopen(M)
return
- if(istype(AM, /obj/machinery/bot))
- var/obj/machinery/bot/bot = AM
+ if(istype(AM, /mob/living/bot))
+ var/mob/living/bot/bot = AM
if(src.check_access(bot.botcard))
if(density)
open()
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 362753cac8..de96052c41 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -58,7 +58,7 @@
/obj/machinery/door/window/Bumped(atom/movable/AM as mob|obj)
if (!( ismob(AM) ))
- var/obj/machinery/bot/bot = AM
+ var/mob/living/bot/bot = AM
if(istype(bot))
if(density && src.check_access(bot.botcard))
open()
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index c573229a10..0a612903d4 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -333,51 +333,3 @@ Class Procs:
I.loc = loc
del(src)
return 1
-
-/obj/machinery/proc/on_assess_perp(mob/living/carbon/human/perp)
- return 0
-
-/obj/machinery/proc/is_assess_emagged()
- return emagged
-
-/obj/machinery/proc/assess_perp(mob/living/carbon/human/perp, var/auth_weapons, var/check_records, var/check_arrest)
- var/threatcount = 0 //the integer returned
-
- if(is_assess_emagged())
- return 10 //if emagged, always return 10.
-
- threatcount += on_assess_perp(perp)
- if(threatcount >= 10)
- return threatcount
-
- //Agent cards lower threatlevel.
- var/obj/item/weapon/card/id/id = GetIdCard(perp)
- if(id && istype(id, /obj/item/weapon/card/id/syndicate))
- threatcount -= 2
-
- if(auth_weapons && !src.allowed(perp))
- if(istype(perp.l_hand, /obj/item/weapon/gun) || istype(perp.l_hand, /obj/item/weapon/melee))
- threatcount += 4
-
- if(istype(perp.r_hand, /obj/item/weapon/gun) || istype(perp.r_hand, /obj/item/weapon/melee))
- threatcount += 4
-
- if(istype(perp.belt, /obj/item/weapon/gun) || istype(perp.belt, /obj/item/weapon/melee))
- threatcount += 2
-
- if(perp.species.name != "Human") //beepsky so racist.
- threatcount += 2
-
- if(check_records || check_arrest)
- var/perpname = perp.name
- if(id)
- perpname = id.registered_name
-
- var/datum/data/record/R = find_security_record("name", perpname)
- if(check_records && !R)
- threatcount += 4
-
- if(check_arrest && R && (R.fields["criminal"] == "*Arrest*"))
- threatcount += 4
-
- return threatcount
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index 5e2c4b69cb..62e6b9f9c7 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -494,7 +494,7 @@
return check_anomalies ? TURRET_PRIORITY_TARGET : TURRET_NOT_TARGET
if(ishuman(L)) //if the target is a human, analyze threat level
- if(assess_perp(L, check_weapons, check_records, check_arrest) < 4)
+ if(assess_perp(L) < 4)
return TURRET_NOT_TARGET //if threat level < 4, keep going
if(L.lying) //if the perp is lying down, it's still a target but a less-important target
@@ -502,6 +502,45 @@
return TURRET_PRIORITY_TARGET //if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee
+/obj/machinery/porta_turret/proc/assess_perp(var/mob/living/carbon/human/H)
+ if(!H || !istype(H))
+ return
+
+ if(emagged)
+ return 10
+
+ var/threatcount = 0
+ var/obj/item/weapon/card/id/id = GetIdCard(H) //Agent cards lower threatlevel.
+ if(id && istype(id, /obj/item/weapon/card/id/syndicate))
+ threatcount -= 2
+
+ if(check_weapons && !allowed(H))
+ if(istype(H.l_hand, /obj/item/weapon/gun) || istype(H.l_hand, /obj/item/weapon/melee))
+ threatcount += 4
+
+ if(istype(H.r_hand, /obj/item/weapon/gun) || istype(H.r_hand, /obj/item/weapon/melee))
+ threatcount += 4
+
+ if(istype(H.belt, /obj/item/weapon/gun) || istype(H.belt, /obj/item/weapon/melee))
+ threatcount += 2
+
+ if(H.species.name != "Human")
+ threatcount += 2
+
+ if(check_records || check_arrest)
+ var/perpname = H.name
+ if(id)
+ perpname = id.registered_name
+
+ var/datum/data/record/R = find_security_record("name", perpname)
+ if(check_records && !R)
+ threatcount += 4
+
+ if(check_arrest && R && (R.fields["criminal"] == "*Arrest*"))
+ threatcount += 4
+
+ return threatcount
+
/obj/machinery/porta_turret/proc/tryToShootAt(var/list/mob/living/targets)
if(targets.len && last_target && (last_target in targets) && target(last_target))
return 1
@@ -549,12 +588,14 @@
update_icon()
+/*
/obj/machinery/porta_turret/on_assess_perp(mob/living/carbon/human/perp)
if((check_access || attacked) && !allowed(perp))
//if the turret has been attacked or is angry, target all non-authorized personnel, see req_access
return 10
return ..()
+ */
/obj/machinery/porta_turret/proc/target(var/mob/living/target)
diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm
index 63956f5e89..dc5e6c57e8 100644
--- a/code/game/objects/effects/decals/cleanable.dm
+++ b/code/game/objects/effects/decals/cleanable.dm
@@ -1,6 +1,5 @@
/obj/effect/decal/cleanable
var/list/random_icon_states = list()
- var/targeted_by = null // Used so cleanbots can't claim a mess.
/obj/effect/decal/cleanable/New()
if (random_icon_states && length(src.random_icon_states) > 0)
diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm
index 5ab09d0853..140e39cb64 100644
--- a/code/game/objects/items/devices/PDA/cart.dm
+++ b/code/game/objects/items/devices/PDA/cart.dm
@@ -331,7 +331,7 @@
beepskyData["botstatus"] = list("loca" = null, "mode" = -1)
var/botsCount=0
if(SC.botlist && SC.botlist.len)
- for(var/obj/machinery/bot/B in SC.botlist)
+ for(var/mob/living/bot/B in SC.botlist)
botsCount++
if(B.loc)
botsData[++botsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "ref" = "\ref[B]")
@@ -465,7 +465,7 @@
BucketData[++BucketData.len] = list("x" = 0, "y" = 0, dir=null, status = null)
var/CbotData[0]
- for(var/obj/machinery/bot/cleanbot/B in world)
+ for(var/mob/living/bot/cleanbot/B in world)
var/turf/bl = get_turf(B)
if(bl)
if(bl.z != cl.z)
diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm
index 0c7105118f..19771653aa 100644
--- a/code/game/objects/items/devices/PDA/radio.dm
+++ b/code/game/objects/items/devices/PDA/radio.dm
@@ -37,7 +37,7 @@
/obj/item/radio/integrated/beepsky
var/list/botlist = null // list of bots
- var/obj/machinery/bot/secbot/active // the active bot; if null, show bot list
+ var/mob/living/bot/secbot/active // the active bot; if null, show bot list
var/list/botstatus // the status signal sent by the bot
var/control_freq = AI_FREQ
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index 9c262243c6..290cec4abb 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -128,12 +128,12 @@
if(istype(W, /obj/item/stack/sheet/metal) && !l_arm && !r_arm && !l_leg && !r_leg && !chest && !head)
var/obj/item/stack/sheet/metal/M = W
if (M.use(1))
- var/obj/item/weapon/secbot_assembly/ed209_assembly/B = new /obj/item/weapon/secbot_assembly/ed209_assembly
- B.loc = get_turf(src)
+ //var/obj/item/weapon/secbot_assembly/ed209_assembly/B = new /obj/item/weapon/secbot_assembly/ed209_assembly
+ //B.loc = get_turf(src)
user << "You armed the robot frame."
if (user.get_inactive_hand()==src)
user.remove_from_mob(src)
- user.put_in_inactive_hand(B)
+ // user.put_in_inactive_hand(B)
del(src)
else
user << "You need one sheet of metal to arm the robot frame."
diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm
index 6a1ee735f4..ad868f856e 100644
--- a/code/game/objects/items/weapons/cards_ids.dm
+++ b/code/game/objects/items/weapons/cards_ids.dm
@@ -97,7 +97,7 @@
/obj/machinery/shieldgen,
/obj/machinery/turretid,
/obj/machinery/vending,
- /obj/machinery/bot,
+ /mob/living/bot,
/obj/machinery/door,
/obj/machinery/telecomms,
/obj/machinery/mecha_part_fabricator,
diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm
new file mode 100644
index 0000000000..be02669fe9
--- /dev/null
+++ b/code/modules/mob/living/bot/bot.dm
@@ -0,0 +1,123 @@
+/mob/living/bot
+ name = "Bot"
+ health = 20
+ maxHealth = 20
+ icon = 'icons/obj/aibots.dmi'
+ layer = MOB_LAYER
+ universal_speak = 1
+ density = 0
+ var/obj/item/weapon/card/id/botcard = null
+ var/list/botcard_access = list()
+ var/on = 1
+ var/open = 0
+ var/locked = 1
+ var/emagged = 0
+ var/light_strength = 3
+
+ var/obj/access_scanner = null
+ var/list/req_access = list()
+
+/mob/living/bot/New()
+ ..()
+ update_icons()
+
+ botcard = new /obj/item/weapon/card/id(src)
+ botcard.access = botcard_access.Copy()
+
+ access_scanner = new /obj(src)
+ access_scanner.req_access = req_access.Copy()
+
+/mob/living/bot/Life()
+ ..()
+ weakened = 0
+ stunned = 0
+ paralysis = 0
+ if(health <= 0)
+ death()
+
+/mob/living/bot/updatehealth()
+ if(status_flags & GODMODE)
+ health = maxHealth
+ stat = CONSCIOUS
+ else
+ health = maxHealth - getFireLoss() - getBruteLoss()
+ oxyloss = 0
+ toxloss = 0
+ cloneloss = 0
+ halloss = 0
+
+/mob/living/bot/death()
+ explode()
+
+/mob/living/bot/attackby(var/obj/item/O, var/mob/user)
+ if(O.GetID())
+ if(access_scanner.allowed(user) && !open && !emagged)
+ locked = !locked
+ user << "Controls are now [locked ? "locked." : "unlocked."]"
+ attack_hand(user)
+ else
+ if(emagged)
+ user << "ERROR"
+ if(open)
+ user << "Please close the access panel before locking it."
+ else
+ user << "Access denied."
+ return
+ else if(istype(O, /obj/item/weapon/screwdriver))
+ if(!locked)
+ open = !open
+ user << "Maintenance panel is now [open ? "opened" : "closed"]."
+ else
+ user << "You need to unlock the controls first."
+ return
+ else if(istype(O, /obj/item/weapon/weldingtool))
+ if(health < maxHealth)
+ if(open)
+ health = min(maxHealth, health + 10)
+ user.visible_message("[user] repairs [src].","You repair [src].")
+ else
+ user << "Unable to repair with the maintenance panel closed."
+ else
+ user << "[src] does not need a repair."
+ return
+ else if (istype(O, /obj/item/weapon/card/emag) && !emagged)
+ Emag(user)
+ return
+ else
+ ..()
+
+/mob/living/bot/attack_ai(var/mob/user)
+ return attack_hand(user)
+
+/mob/living/bot/say(var/message)
+ var/verb = "beeps"
+
+ message = sanitize(message)
+
+ ..(message, null, verb)
+
+/mob/living/bot/Bump(var/atom/A)
+ if(istype(A, /obj/machinery/door) && botcard)
+ var/obj/machinery/door/D = A
+ if(!istype(D, /obj/machinery/door/firedoor) && !istype(D, /obj/machinery/door/blast) && D.check_access(botcard))
+ D.open()
+
+/mob/living/bot/proc/Emag(var/mob/user)
+ log_and_message_admins("emagged [src]")
+ return
+
+/mob/living/bot/proc/turn_on()
+ if(stat)
+ return 0
+ on = 1
+ SetLuminosity(light_strength)
+ update_icons()
+ return 1
+
+/mob/living/bot/proc/turn_off()
+ on = 0
+ SetLuminosity(0)
+ update_icons()
+
+/mob/living/bot/proc/explode()
+ del(src)
\ No newline at end of file
diff --git a/code/modules/mob/living/bot/cleanbot.dm b/code/modules/mob/living/bot/cleanbot.dm
new file mode 100644
index 0000000000..87de386357
--- /dev/null
+++ b/code/modules/mob/living/bot/cleanbot.dm
@@ -0,0 +1,306 @@
+/mob/living/bot/cleanbot
+ name = "Cleanbot"
+ desc = "A little cleaning robot, he looks so excited!"
+ icon_state = "cleanbot0"
+ req_access = list(access_janitor)
+ botcard_access = list(access_janitor, access_maint_tunnels)
+
+ locked = 0 // Start unlocked so roboticist can set them to patrol.
+
+ var/obj/effect/decal/cleanable/target
+ var/list/path = list()
+ var/list/patrol_path = list()
+ var/list/ignorelist = list()
+
+ var/obj/cleanbot_listener/listener = null
+ var/beacon_freq = 1445 // navigation beacon frequency
+ var/signal_sent = 0
+ var/closest_dist
+ var/next_dest
+ var/next_dest_loc
+
+ var/cleaning = 0
+ var/screwloose = 0
+ var/oddbutton = 0
+ var/should_patrol = 0
+ var/blood = 1
+ var/list/target_types = list()
+
+/mob/living/bot/cleanbot/New()
+ ..()
+ get_targets()
+
+ listener = new /obj/cleanbot_listener(src)
+ listener.cleanbot = src
+
+ if(radio_controller)
+ radio_controller.add_object(listener, beacon_freq, filter = RADIO_NAVBEACONS)
+
+/mob/living/bot/cleanbot/Life()
+ ..()
+
+ if(!on)
+ return
+
+ if(client)
+ return
+ if(cleaning)
+ return
+
+ if(!screwloose && !oddbutton && prob(5))
+ custom_emote(2, "makes an excited beeping booping sound!")
+
+ if(screwloose && prob(5)) // Make a mess
+ if(istype(loc, /turf/simulated))
+ var/turf/simulated/T = loc
+ if(T.wet < 1)
+ T.wet = 1
+ if(T.wet_overlay)
+ T.overlays -= T.wet_overlay
+ T.wet_overlay = null
+ T.wet_overlay = image('icons/effects/water.dmi', T, "wet_floor")
+ T.overlays += T.wet_overlay
+ spawn(800)
+ if(istype(T) && T.wet < 2)
+ T.wet = 0
+ if(T.wet_overlay)
+ T.overlays -= T.wet_overlay
+ T.wet_overlay = null
+
+ if(oddbutton && prob(5)) // Make a big mess
+ visible_message("Something flies out of [src]. He seems to be acting oddly.")
+ var/obj/effect/decal/cleanable/blood/gibs/gib = new /obj/effect/decal/cleanable/blood/gibs(loc)
+ ignorelist += gib
+ spawn(600)
+ ignorelist -= gib
+
+ if(!target) // Find a target
+ for(var/obj/effect/decal/cleanable/D in view(7, src))
+ if(D in ignorelist)
+ continue
+ for(var/T in target_types)
+ if(istype(D, T))
+ target = D
+ patrol_path = list()
+
+ if(!target) // No targets in range
+ if(!should_patrol)
+ return
+
+ if(!patrol_path || !patrol_path.len)
+ if(!signal_sent || signal_sent > world.time + 200) // Waited enough or didn't send yet
+ var/datum/radio_frequency/frequency = radio_controller.return_frequency(beacon_freq)
+ if(!frequency)
+ return
+
+ closest_dist = 9999
+ next_dest = null
+ next_dest_loc = null
+
+ var/datum/signal/signal = new()
+ signal.source = src
+ signal.transmission_method = 1
+ signal.data = list("findbeakon" = "patrol")
+ frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS)
+ signal_sent = world.time
+ else
+ if(next_dest)
+ next_dest_loc = listener.memorized[next_dest]
+ if(next_dest_loc)
+ patrol_path = AStar(loc, next_dest_loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 120, id = botcard, exclude = null)
+ signal_sent = 0
+ else
+ if(pulledby) // Don't wiggle if someone pulls you
+ patrol_path = list()
+ return
+ if(patrol_path[1] == loc)
+ patrol_path -= patrol_path[1]
+ var/moved = step_towards(src, patrol_path[1])
+ if(moved)
+ patrol_path -= patrol_path[1]
+ if(target)
+ if(loc == target.loc)
+ if(!cleaning)
+ UnarmedAttack(target)
+ return
+ if(!path.len)
+ spawn(0)
+ path = AStar(loc, target.loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id = botcard)
+ if(!path)
+ path = list()
+ return
+ if(path.len)
+ step_to(src, path[1])
+ path -= path[1]
+ return
+
+/mob/living/bot/cleanbot/UnarmedAttack(var/obj/effect/decal/cleanable/D, var/proximity)
+ if(!..())
+ return
+
+ if(!istype(D))
+ return
+
+ if(D.loc != loc)
+ return
+
+ cleaning = 1
+ custom_emote(2, "begins to clean up the [D]")
+ update_icons()
+ var/cleantime = istype(D, /obj/effect/decal/cleanable/dirt) ? 10 : 50
+ if(do_after(src, cleantime))
+ if(istype(loc, /turf/simulated))
+ var/turf/simulated/f = loc
+ f.dirt = 0
+ if(!D)
+ return
+ del(D)
+ cleaning = 0
+ update_icons()
+
+/mob/living/bot/cleanbot/explode()
+ on = 0
+ visible_message("[src] blows apart!")
+ var/turf/Tsec = get_turf(src)
+
+ new /obj/item/weapon/reagent_containers/glass/bucket(Tsec)
+ new /obj/item/device/assembly/prox_sensor(Tsec)
+ if(prob(50))
+ new /obj/item/robot_parts/l_arm(Tsec)
+
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(3, 1, src)
+ s.start()
+ del(src)
+ return
+
+/mob/living/bot/cleanbot/update_icons()
+ if(cleaning)
+ icon_state = "cleanbot-c"
+ else
+ icon_state = "cleanbot[on]"
+
+/mob/living/bot/cleanbot/turn_off()
+ ..()
+ target = null
+ path = list()
+ patrol_path = list()
+
+/mob/living/bot/cleanbot/attack_hand(var/mob/user)
+ var/dat
+ dat += "Automatic Station Cleaner v1.0
"
+ dat += "Status: [on ? "On" : "Off"]
"
+ dat += "Behaviour controls are [locked ? "locked" : "unlocked"]
"
+ dat += "Maintenance panel is [open ? "opened" : "closed"]"
+ if(!locked || issilicon(user))
+ dat += "
Cleans Blood: [blood ? "Yes" : "No"]
"
+ dat += "
Patrol station: [should_patrol ? "Yes" : "No"]
"
+ if(open && !locked)
+ dat += "Odd looking screw twiddled: [screwloose ? "Yes" : "No"]
"
+ dat += "Weird button pressed: [oddbutton ? "Yes" : "No"]"
+
+ user << browse("Cleaner v1.0 controls[dat]", "window=autocleaner")
+ onclose(user, "autocleaner")
+ return
+
+/mob/living/bot/cleanbot/Topic(href, href_list)
+ if(..())
+ return
+ usr.set_machine(src)
+ add_fingerprint(usr)
+ switch(href_list["operation"])
+ if("start")
+ if(on)
+ turn_off()
+ else
+ turn_on()
+ if("blood")
+ blood = !blood
+ get_targets()
+ if("patrol")
+ should_patrol = !should_patrol
+ patrol_path = null
+ if("freq")
+ var/freq = text2num(input("Select frequency for navigation beacons", "Frequnecy", num2text(beacon_freq / 10))) * 10
+ if (freq > 0)
+ beacon_freq = freq
+ if("screw")
+ screwloose = !screwloose
+ usr << "You press the weird button."
+ attack_hand(usr)
+
+/mob/living/bot/cleanbot/Emag(var/mob/user)
+ ..()
+ if(user)
+ user << "The [src] buzzes and beeps."
+ oddbutton = 1
+ screwloose = 1
+
+/mob/living/bot/cleanbot/proc/get_targets()
+ target_types = list()
+
+ target_types += /obj/effect/decal/cleanable/blood/oil
+ target_types += /obj/effect/decal/cleanable/vomit
+ target_types += /obj/effect/decal/cleanable/crayon
+ target_types += /obj/effect/decal/cleanable/liquid_fuel
+ target_types += /obj/effect/decal/cleanable/mucus
+ target_types += /obj/effect/decal/cleanable/dirt
+
+ if(blood)
+ target_types += /obj/effect/decal/cleanable/blood
+
+/* Radio object that listens to signals */
+
+/obj/cleanbot_listener
+ var/mob/living/bot/cleanbot/cleanbot = null
+ var/list/memorized = list()
+
+/obj/cleanbot_listener/receive_signal(var/datum/signal/signal)
+ var/recv = signal.data["beacon"]
+ var/valid = signal.data["patrol"]
+ if(!recv || !valid || !cleanbot)
+ return
+
+ var/dist = get_dist(cleanbot, signal.source.loc)
+ memorized[recv] = signal.source.loc
+
+ if(dist < cleanbot.closest_dist) // We check all signals, choosing the closest beakon; then we move to the NEXT one after the closest one
+ cleanbot.closest_dist = dist
+ cleanbot.next_dest = signal.data["next_patrol"]
+
+/* Assembly */
+
+/obj/item/weapon/bucket_sensor
+ desc = "It's a bucket. With a sensor attached."
+ name = "proxy bucket"
+ icon = 'icons/obj/aibots.dmi'
+ icon_state = "bucket_proxy"
+ force = 3.0
+ throwforce = 10.0
+ throw_speed = 2
+ throw_range = 5
+ w_class = 3.0
+ var/created_name = "Cleanbot"
+
+/obj/item/weapon/bucket_sensor/attackby(var/obj/item/O, var/mob/user)
+ ..()
+ if(istype(O, /obj/item/robot_parts/l_arm) || istype(O, /obj/item/robot_parts/r_arm))
+ user.drop_item()
+ del(O)
+ var/turf/T = get_turf(loc)
+ var/mob/living/bot/cleanbot/A = new /mob/living/bot/cleanbot(T)
+ A.name = created_name
+ user << "You add the robot arm to the bucket and sensor assembly. Beep boop!"
+ user.drop_from_inventory(src)
+ del(src)
+
+ else if(istype(O, /obj/item/weapon/pen))
+ var/t = sanitizeSafe(input(user, "Enter new robot name", name, created_name), MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && src.loc != usr)
+ return
+ created_name = t
diff --git a/code/modules/mob/living/bot/ed209bot.dm b/code/modules/mob/living/bot/ed209bot.dm
new file mode 100644
index 0000000000..99e52b7e4a
--- /dev/null
+++ b/code/modules/mob/living/bot/ed209bot.dm
@@ -0,0 +1,192 @@
+/mob/living/bot/secbot/ed209
+ name = "ED-209 Security Robot"
+ desc = "A security robot. He looks less than thrilled."
+ icon = 'icons/obj/aibots.dmi'
+ icon_state = "ed2090"
+ density = 1
+ health = 100
+ maxHealth = 100
+
+ bot_version = "2.5"
+ is_ranged = 1
+ preparing_arrest_sounds = new()
+
+ var/shot_delay = 4
+ var/last_shot = 0
+
+/mob/living/bot/secbot/ed209/update_icons()
+ if(on && is_attacking)
+ icon_state = "ed209-c"
+ else
+ icon_state = "ed209[on]"
+
+/mob/living/bot/secbot/ed209/explode()
+ visible_message("[src] blows apart!")
+ var/turf/Tsec = get_turf(src)
+
+ new /obj/item/weapon/secbot_assembly/ed209_assembly(Tsec)
+
+ var/obj/item/weapon/gun/energy/taser/G = new /obj/item/weapon/gun/energy/taser(Tsec)
+ G.power_supply.charge = 0
+ if(prob(50))
+ new /obj/item/robot_parts/l_leg(Tsec)
+ if(prob(50))
+ new /obj/item/robot_parts/r_leg(Tsec)
+ if(prob(50))
+ if(prob(50))
+ new /obj/item/clothing/head/helmet(Tsec)
+ else
+ new /obj/item/clothing/suit/armor/vest(Tsec)
+
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(3, 1, src)
+ s.start()
+
+ new /obj/effect/decal/cleanable/blood/oil(Tsec)
+ del(src)
+
+/mob/living/bot/secbot/ed209/RangedAttack(var/atom/A)
+ if(last_shot + shot_delay > world.time)
+ src << "You are not ready to fire yet!"
+ return
+
+ last_shot = world.time
+ var/turf/T = get_turf(src)
+ var/turf/U = get_turf(A)
+
+ var/projectile = /obj/item/projectile/beam/stun
+ if(emagged)
+ projectile = /obj/item/projectile/beam
+
+ playsound(loc, emagged ? 'sound/weapons/Laser.ogg' : 'sound/weapons/Taser.ogg', 50, 1)
+ var/obj/item/projectile/P = new projectile(loc)
+
+ P.original = A
+ P.starting = T
+ P.current = T
+ P.yo = U.y - T.y
+ P.xo = U.x - T.x
+ spawn()
+ P.process()
+ return
+
+// Assembly
+
+/obj/item/weapon/secbot_assembly/ed209_assembly
+ name = "ED-209 assembly"
+ desc = "Some sort of bizarre assembly."
+ icon = 'icons/obj/aibots.dmi'
+ icon_state = "ed209_frame"
+ item_state = "ed209_frame"
+ created_name = "ED-209 Security Robot"
+ var/lasercolor = ""
+
+/obj/item/weapon/secbot_assembly/ed209_assembly/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
+ ..()
+
+ if(istype(W, /obj/item/weapon/pen))
+ var/t = sanitizeSafe(input(user, "Enter new robot name", name, created_name), MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && src.loc != usr)
+ return
+ created_name = t
+ return
+
+ switch(build_step)
+ if(0, 1)
+ if(istype(W, /obj/item/robot_parts/l_leg) || istype(W, /obj/item/robot_parts/r_leg))
+ user.drop_item()
+ del(W)
+ build_step++
+ user << "You add the robot leg to [src]."
+ name = "legs/frame assembly"
+ if(build_step == 1)
+ item_state = "ed209_leg"
+ icon_state = "ed209_leg"
+ else
+ item_state = "ed209_legs"
+ icon_state = "ed209_legs"
+
+ if(2)
+ if(istype(W, /obj/item/clothing/suit/storage/vest))
+ user.drop_item()
+ del(W)
+ build_step++
+ user << "You add the armor to [src]."
+ name = "vest/legs/frame assembly"
+ item_state = "ed209_shell"
+ icon_state = "ed209_shell"
+
+ if(3)
+ if(istype(W, /obj/item/weapon/weldingtool))
+ var/obj/item/weapon/weldingtool/WT = W
+ if(WT.remove_fuel(0, user))
+ build_step++
+ name = "shielded frame assembly"
+ user << "You welded the vest to [src]."
+ if(4)
+ if(istype(W, /obj/item/clothing/head/helmet))
+ user.drop_item()
+ del(W)
+ build_step++
+ user << "You add the helmet to [src]."
+ name = "covered and shielded frame assembly"
+ item_state = "ed209_hat"
+ icon_state = "ed209_hat"
+
+ if(5)
+ if(isprox(W))
+ user.drop_item()
+ del(W)
+ build_step++
+ user << "You add the prox sensor to [src]."
+ name = "covered, shielded and sensored frame assembly"
+ item_state = "ed209_prox"
+ icon_state = "ed209_prox"
+
+ if(6)
+ if(istype(W, /obj/item/stack/cable_coil))
+ var/obj/item/stack/cable_coil/C = W
+ if (C.get_amount() < 1)
+ user << "You need one coil of wire to wire [src]."
+ return
+ user << "You start to wire [src]."
+ if(do_after(user, 40) && build_step == 6)
+ if(C.use(1))
+ build_step++
+ user << "You wire the ED-209 assembly."
+ name = "wired ED-209 assembly"
+ return
+
+ if(7)
+ if(istype(W, /obj/item/weapon/gun/energy/taser))
+ name = "taser ED-209 assembly"
+ build_step++
+ user << "You add [W] to [src]."
+ item_state = "ed209_taser"
+ icon_state = "ed209_taser"
+ user.drop_item()
+ del(W)
+
+ if(8)
+ if(istype(W, /obj/item/weapon/screwdriver))
+ playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1)
+ var/turf/T = get_turf(user)
+ user << "Now attaching the gun to the frame..."
+ sleep(40)
+ if(get_turf(user) == T && build_step == 8)
+ build_step++
+ name = "armed [name]"
+ user << "Taser gun attached."
+
+ if(9)
+ if(istype(W, /obj/item/weapon/cell))
+ build_step++
+ user << "You complete the ED-209."
+ var/turf/T = get_turf(src)
+ new /mob/living/bot/secbot/ed209(T,created_name,lasercolor)
+ user.drop_item()
+ del(W)
+ user.drop_from_inventory(src)
+ del(src)
diff --git a/code/modules/mob/living/bot/farmbot.dm b/code/modules/mob/living/bot/farmbot.dm
new file mode 100644
index 0000000000..2c2c421dd8
--- /dev/null
+++ b/code/modules/mob/living/bot/farmbot.dm
@@ -0,0 +1,359 @@
+#define FARMBOT_COLLECT 1
+#define FARMBOT_WATER 2
+#define FARMBOT_UPROOT 3
+#define FARMBOT_NUTRIMENT 4
+
+/mob/living/bot/farmbot
+ name = "Farmbot"
+ desc = "The botanist's best friend."
+ icon = 'icons/obj/aibots.dmi'
+ icon_state = "farmbot0"
+ health = 50
+ maxHealth = 50
+ req_access = list(access_hydroponics)
+
+ var/action = "" // Used to update icon
+ var/waters_trays = 1
+ var/refills_water = 1
+ var/uproots_weeds = 1
+ var/replaces_nutriment = 0
+ var/collects_produce = 0
+ var/removes_dead = 0
+
+ var/obj/structure/reagent_dispensers/watertank/tank
+
+ var/attacking = 0
+ var/list/path = list()
+ var/atom/target
+ var/frustration = 0
+
+/mob/living/bot/farmbot/New()
+ ..()
+ spawn(5)
+ tank = locate() in contents
+ if(!tank)
+ tank = new /obj/structure/reagent_dispensers/watertank(src)
+
+/mob/living/bot/farmbot/attack_hand(var/mob/user as mob)
+ . = ..()
+ if(.)
+ return
+ var/dat = ""
+ dat += "Automatic Hyrdoponic Assisting Unit v1.0
"
+ dat += "Status: [on ? "On" : "Off"]
"
+ dat += "Water Tank: "
+ if (tank)
+ dat += "[tank.reagents.total_volume]/[tank.reagents.maximum_volume]"
+ else
+ dat += "Error: Watertank not found"
+ dat += "
Behaviour controls are [locked ? "locked" : "unlocked"]
"
+ if(!locked)
+ dat += "Watering controls:
"
+ dat += "Water plants : [waters_trays ? "Yes" : "No"]
"
+ dat += "Refill watertank : [refills_water ? "Yes" : "No"]
"
+ dat += "
Weeding controls:
"
+ dat += "Weed plants: [uproots_weeds ? "Yes" : "No"]
"
+ dat += "
Nutriment controls:
"
+ dat += "Replace fertilizer: [replaces_nutriment ? "Yes" : "No"]
"
+ dat += "
Plant controls:
"
+ dat += "Collect produce: [collects_produce ? "Yes" : "No"]
"
+ dat += "Remove dead plants: [removes_dead ? "Yes" : "No"]
"
+ dat += ""
+
+ user << browse("Farmbot v1.0 controls[dat]", "window=autofarm")
+ onclose(user, "autofarm")
+ return
+
+/mob/living/bot/farmbot/Emag(var/mob/user)
+ ..()
+ if(user)
+ user << "You short out [src]'s plant identifier circuits."
+ spawn(rand(30, 50))
+ visible_message("[src] buzzes oddly.")
+ emagged = 1
+
+/mob/living/bot/farmbot/Topic(href, href_list)
+ if(..())
+ return
+ usr.machine = src
+ add_fingerprint(usr)
+ if((href_list["power"]) && (access_scanner.allowed(usr)))
+ if(on)
+ turn_off()
+ else
+ turn_on()
+
+ if(locked)
+ return
+
+ if(href_list["water"])
+ waters_trays = !waters_trays
+ else if(href_list["refill"])
+ refills_water = !refills_water
+ else if(href_list["weed"])
+ uproots_weeds = !uproots_weeds
+ else if(href_list["replacenutri"])
+ replaces_nutriment = !replaces_nutriment
+ else if(href_list["collect"])
+ collects_produce = !collects_produce
+ else if(href_list["removedead"])
+ removes_dead = !removes_dead
+
+ attack_hand(usr)
+ return
+
+/mob/living/bot/farmbot/update_icons()
+ if(on && action)
+ icon_state = "farmbot_[action]"
+ else
+ icon_state = "farmbot[on]"
+
+/mob/living/bot/farmbot/Life()
+ ..()
+ if(!on)
+ return
+ if(emagged && prob(1))
+ flick("farmbot_broke", src)
+ if(client)
+ return
+
+ if(target)
+ if(Adjacent(target))
+ UnarmedAttack(target)
+ path = list()
+ target = null
+ else
+ if(path.len && frustration < 5)
+ if(path[1] == loc)
+ path -= path[1]
+ var/t = step_towards(src, path[1])
+ if(t)
+ path -= path[1]
+ else
+ ++frustration
+ else
+ path = list()
+ target = null
+ else
+ if(emagged)
+ for(var/mob/living/carbon/human/H in view(7, src))
+ target = H
+ break
+ else
+ for(var/obj/machinery/portable_atmospherics/hydroponics/tray in view(7, src))
+ if(process_tray(tray))
+ target = tray
+ frustration = 0
+ break
+ if(!target && refills_water && tank && tank.reagents.total_volume < tank.reagents.maximum_volume)
+ for(var/obj/structure/sink/source in view(7, src))
+ target = source
+ frustration = 0
+ break
+ if(target)
+ var/t = get_dir(target, src) // Turf with the tray is impassable, so a* can't navigate directly to it
+ path = AStar(loc, get_step(target, t), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id = botcard)
+ if(!path)
+ path = list()
+
+/mob/living/bot/farmbot/UnarmedAttack(var/atom/A, var/proximity)
+ if(!..())
+ return
+ if(attacking)
+ return
+
+ if(istype(A, /obj/machinery/portable_atmospherics/hydroponics))
+ var/obj/machinery/portable_atmospherics/hydroponics/T = A
+ var/t = process_tray(T)
+ switch(t)
+ if(0)
+ return
+ if(FARMBOT_COLLECT)
+ action = "water" // Needs a better one
+ update_icons()
+ visible_message("[src] starts [T.dead? "removing the plant from" : "harvesting"] \the [A].")
+ attacking = 1
+ if(do_after(src, 30))
+ visible_message("[src] [T.dead? "removes the plant from" : "harvests"] \the [A].")
+ T.attack_hand(src)
+ if(FARMBOT_WATER)
+ action = "water"
+ update_icons()
+ visible_message("[src] starts watering \the [A].")
+ attacking = 1
+ if(do_after(src, 30))
+ playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
+ visible_message("[src] waters \the [A].")
+ tank.reagents.trans_to(T, 100 - T.waterlevel)
+ if(FARMBOT_UPROOT)
+ action = "hoe"
+ update_icons()
+ visible_message("[src] starts uprooting the weeds in \the [A].")
+ attacking = 1
+ if(do_after(src, 30))
+ visible_message("[src] uproots the weeds in \the [A].")
+ T.weedlevel = 0
+ if(FARMBOT_NUTRIMENT)
+ action = "fertile"
+ update_icons()
+ visible_message("[src] starts fertilizing \the [A].")
+ attacking = 1
+ if(do_after(src, 30))
+ visible_message("[src] waters \the [A].")
+ T.reagents.add_reagent("ammonia", 10)
+ attacking = 0
+ action = ""
+ update_icons()
+ T.update_icon()
+ else if(istype(A, /obj/structure/sink))
+ if(!tank || tank.reagents.total_volume >= tank.reagents.maximum_volume)
+ return
+ action = "water"
+ update_icons()
+ visible_message("[src] starts refilling its tank from \the [A].")
+ attacking = 1
+ while(do_after(src, 10) && tank.reagents.total_volume < tank.reagents.maximum_volume)
+ tank.reagents.add_reagent("water", 10)
+ if(prob(5))
+ playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
+ attacking = 0
+ action = ""
+ update_icons()
+ visible_message("[src] finishes refilling its tank.")
+ else if(emagged && ishuman(A))
+ var/action = pick("weed", "water")
+ attacking = 1
+ spawn(50) // Some delay
+ attacking = 0
+ switch(action)
+ if("weed")
+ flick("farmbot_hoe", src)
+ if(prob(50))
+ visible_message("[src] swings wildly at [A] with a minihoe, missing completely!")
+ return
+ var/t = pick("slashed", "sliced", "cut", "clawed")
+ A.attack_generic(src, 5, t)
+ if("water")
+ flick("farmbot_water", src)
+ visible_message("[src] splashes [A] with water!") // That's it. RP effect.
+
+/mob/living/bot/farmbot/explode()
+ visible_message("[src] blows apart!")
+ var/turf/Tsec = get_turf(src)
+
+ new /obj/item/weapon/minihoe(Tsec)
+ new /obj/item/weapon/reagent_containers/glass/bucket(Tsec)
+ new /obj/item/device/assembly/prox_sensor(Tsec)
+ new /obj/item/device/analyzer/plant_analyzer(Tsec)
+
+ if(tank)
+ tank.loc = Tsec
+
+ if(prob(50))
+ new /obj/item/robot_parts/l_arm(Tsec)
+
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(3, 1, src)
+ s.start()
+ del(src)
+ return
+
+/mob/living/bot/farmbot/proc/process_tray(var/obj/machinery/portable_atmospherics/hydroponics/tray)
+ if(!tray || !istype(tray))
+ return 0
+
+ if(tray.closed_system || !tray.seed)
+ return 0
+
+ if(tray.dead && removes_dead || tray.harvest && collects_produce)
+ return FARMBOT_COLLECT
+
+ else if(refills_water && tray.waterlevel < 40 && !tray.reagents.has_reagent("water"))
+ return FARMBOT_WATER
+
+ else if(uproots_weeds && tray.weedlevel > 3)
+ return FARMBOT_UPROOT
+
+ else if(replaces_nutriment && tray.nutrilevel < 1 && tray.reagents.total_volume < 1)
+ return FARMBOT_NUTRIMENT
+
+ return 0
+
+// Assembly
+
+/obj/item/weapon/farmbot_arm_assembly
+ name = "water tank/robot arm assembly"
+ desc = "A water tank with a robot arm permanently grafted to it."
+ icon = 'icons/obj/aibots.dmi'
+ icon_state = "water_arm"
+ var/build_step = 0
+ var/created_name = "Farmbot"
+ w_class = 3.0
+
+ New()
+ ..()
+ spawn(4) // If an admin spawned it, it won't have a watertank it, so lets make one for em!
+ var tank = locate(/obj/structure/reagent_dispensers/watertank) in contents
+ if(!tank)
+ new /obj/structure/reagent_dispensers/watertank(src)
+
+
+/obj/structure/reagent_dispensers/watertank/attackby(var/obj/item/robot_parts/S, mob/user as mob)
+ if ((!istype(S, /obj/item/robot_parts/l_arm)) && (!istype(S, /obj/item/robot_parts/r_arm)))
+ ..()
+ return
+
+ var/obj/item/weapon/farmbot_arm_assembly/A = new /obj/item/weapon/farmbot_arm_assembly(loc)
+
+ user << "You add the robot arm to [src]."
+ loc = A //Place the water tank into the assembly, it will be needed for the finished bot
+ user.drop_from_inventory(S)
+ del(S)
+
+/obj/item/weapon/farmbot_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ ..()
+ if((istype(W, /obj/item/device/analyzer/plant_analyzer)) && (build_step == 0))
+ build_step++
+ user << "You add the plant analyzer to [src]."
+ name = "farmbot assembly"
+ user.remove_from_mob(W)
+ del(W)
+
+ else if((istype(W, /obj/item/weapon/reagent_containers/glass/bucket)) && (build_step == 1))
+ build_step++
+ user << "You add a bucket to [src]."
+ name = "farmbot assembly with bucket"
+ user.remove_from_mob(W)
+ del(W)
+
+ else if((istype(W, /obj/item/weapon/minihoe)) && (build_step == 2))
+ build_step++
+ user << "You add a minihoe to [src]."
+ name = "farmbot assembly with bucket and minihoe"
+ user.remove_from_mob(W)
+ del(W)
+
+ else if((isprox(W)) && (build_step == 3))
+ build_step++
+ user << "You complete the Farmbot! Beep boop."
+ var/mob/living/bot/farmbot/S = new /mob/living/bot/farmbot(get_turf(src))
+ for(var/obj/structure/reagent_dispensers/watertank/wTank in contents)
+ wTank.loc = S
+ S.tank = wTank
+ S.name = created_name
+ user.remove_from_mob(W)
+ del(W)
+ del(src)
+
+ else if(istype(W, /obj/item/weapon/pen))
+ var/t = input(user, "Enter new robot name", name, created_name) as text
+ t = sanitize(t, MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && loc != usr)
+ return
+
+ created_name = t
+
+/obj/item/weapon/farmbot_arm_assembly/attack_hand(mob/user as mob)
+ return //it's a converted watertank, no you cannot pick it up and put it in your backpack
\ No newline at end of file
diff --git a/code/modules/mob/living/bot/floorbot.dm b/code/modules/mob/living/bot/floorbot.dm
new file mode 100644
index 0000000000..f8df2fc6c2
--- /dev/null
+++ b/code/modules/mob/living/bot/floorbot.dm
@@ -0,0 +1,365 @@
+/mob/living/bot/floorbot
+ name = "Floorbot"
+ desc = "A little floor repairing robot, he looks so excited!"
+ icon_state = "floorbot0"
+ req_access = list(access_construction)
+
+ var/amount = 10 // 1 for tile, 2 for lattice
+ var/maxAmount = 60
+ var/tilemake = 0 // When it reaches 100, bot makes a tile
+ var/repairing = 0
+ var/improvefloors = 0
+ var/eattiles = 0
+ var/maketiles = 0
+ var/targetdirection = null
+ var/list/path = list()
+ var/list/ignorelist = list()
+ var/turf/target
+
+/mob/living/bot/floorbot/update_icons()
+ if(repairing)
+ icon_state = "floorbot-c"
+ else if(amount > 0)
+ icon_state = "floorbot[on]"
+ else
+ icon_state = "floorbot[on]e"
+
+/mob/living/bot/floorbot/attack_hand(var/mob/user)
+ user.set_machine(src)
+ var/dat
+ dat += "Automatic Station Floor Repairer v1.0
"
+ dat += "Status: [src.on ? "On" : "Off"]
"
+ dat += "Maintenance panel is [open ? "opened" : "closed"]
"
+ //dat += "Tiles left: [amount]
"
+ dat += "Behvaiour controls are [locked ? "locked" : "unlocked"]
"
+ if(!locked || issilicon(user))
+ dat += "Improves floors: [improvefloors ? "Yes" : "No"]
"
+ dat += "Finds tiles: [eattiles ? "Yes" : "No"]
"
+ dat += "Make singles pieces of metal into tiles when empty: [maketiles ? "Yes" : "No"]
"
+ var/bmode
+ if(targetdirection)
+ bmode = dir2text(targetdirection)
+ else
+ bmode = "Disabled"
+ dat += "
Bridge Mode : [bmode]
"
+
+ user << browse("Repairbot v1.0 controls[dat]", "window=autorepair")
+ onclose(user, "autorepair")
+ return
+
+/mob/living/bot/floorbot/Emag(var/mob/user)
+ ..()
+ emagged = 1
+ if(user)
+ user << "The [src] buzzes and beeps."
+
+/mob/living/bot/floorbot/Topic(href, href_list)
+ if(..())
+ return
+ usr.set_machine(src)
+ add_fingerprint(usr)
+ switch(href_list["operation"])
+ if("start")
+ if (on)
+ turn_off()
+ else
+ turn_on()
+ if("improve")
+ improvefloors = !improvefloors
+ if("tiles")
+ eattiles = !eattiles
+ if("make")
+ maketiles = !maketiles
+ if("bridgemode")
+ switch(targetdirection)
+ if(null)
+ targetdirection = 1
+ if(1)
+ targetdirection = 2
+ if(2)
+ targetdirection = 4
+ if(4)
+ targetdirection = 8
+ if(8)
+ targetdirection = null
+ else
+ targetdirection = null
+ attack_hand(usr)
+
+/mob/living/bot/floorbot/turn_off()
+ ..()
+ target = null
+ path = list()
+ ignorelist = list()
+
+/mob/living/bot/floorbot/Life()
+ ..()
+
+ if(!on)
+ return
+
+ ++tilemake
+ if(tilemake >= 100)
+ tilemake = 0
+ addTiles(1)
+
+ if(client)
+ return
+
+ if(prob(5))
+ custom_emote(2, "makes an excited booping beeping sound!")
+
+ if(ignorelist.len) // Don't stick forever
+ for(var/T in ignorelist)
+ if(prob(1))
+ ignorelist -= T
+
+ if(amount && !emagged)
+ if(!target && targetdirection) // Building a bridge
+ var/turf/T = get_step(src, targetdirection)
+ while(T in range(src))
+ if(istype(T, /turf/space))
+ target = T
+ break
+ T = get_step(T, targetdirection)
+
+ if(!target) // Fixing floors
+ for(var/turf/T in view(src))
+ if(T.loc.name == "Space")
+ continue
+ if(T in ignorelist)
+ continue
+ if(istype(T, /turf/space))
+ if(get_turf(T) == loc || prob(40)) // So they target the same tile all the time
+ target = T
+ if(improvefloors && istype(T, /turf/simulated/floor))
+ var/turf/simulated/floor/F = T
+ if(!F.floor_type && (get_turf(T) == loc || prob(40)))
+ target = T
+
+ if(emagged) // Time to griff
+ for(var/turf/simulated/floor/D in view(src))
+ if(D.loc.name == "Space")
+ continue
+ if(D in ignorelist)
+ continue
+ target = D
+ break
+
+ if(!target && amount < maxAmount && eattiles || maketiles) // Eat tiles
+ if(eattiles)
+ for(var/obj/item/stack/tile/plasteel/T in view(src))
+ if(T in ignorelist)
+ continue
+ target = T
+ break
+ if(maketiles && !target)
+ for(var/obj/item/stack/sheet/metal/T in view(src))
+ if(T in ignorelist)
+ continue
+ target = T
+ break
+
+ if(target && get_turf(target) == loc)
+ UnarmedAttack(target)
+
+ if(target && get_turf(target) != loc && !path.len)
+ spawn(0)
+ path = AStar(loc, get_turf(target), /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30, id = botcard)
+ if(!path)
+ path = list()
+ ignorelist += target
+ target = null
+
+ if(path.len)
+ step_to(src, path[1])
+ path -= path[1]
+
+/mob/living/bot/floorbot/UnarmedAttack(var/atom/A, var/proximity)
+ if(!..())
+ return
+
+ if(repairing)
+ return
+
+ if(get_turf(A) != loc)
+ return
+
+ if(emagged && istype(A, /turf/simulated/floor))
+ var/turf/simulated/floor/F = A
+ repairing = 1
+ update_icons()
+ if(F.is_plating())
+ visible_message("[src] begins to tear the floor tile from the floor!")
+ if(do_after(src, 50))
+ F.break_tile_to_plating()
+ addTiles(1)
+ else
+ visible_message("[src] begins to tear through the floor!")
+ if(do_after(src, 150)) // Extra time because this can and will kill.
+ F.ReplaceWithLattice()
+ addTiles(1)
+ target = null
+ repairing = 0
+ update_icons()
+ else if(istype(A, /turf/space))
+ var/building = 2
+ if(locate(/obj/structure/lattice, A))
+ building = 1
+ if(amount < building)
+ return
+ repairing = 1
+ update_icons()
+ visible_message("[src] begins to repair the hole.")
+ if(do_after(src, 50))
+ if(A && (locate(/obj/structure/lattice, A) && building == 1 || !locate(/obj/structure/lattice, A) && building == 2)) // Make sure that it still needs repairs
+ var/obj/item/I
+ if(building == 1)
+ I = new /obj/item/stack/tile/plasteel(src)
+ else
+ I = new /obj/item/stack/rods(src)
+ A.attackby(I, src)
+ target = null
+ repairing = 0
+ update_icons()
+ else if(istype(A, /turf/simulated/floor))
+ var/turf/simulated/floor/F = A
+ if(!F.floor_type && amount)
+ repairing = 1
+ update_icons()
+ visible_message("[src] begins to improve the floor.")
+ if(do_after(src, 50))
+ if(!F.floor_type)
+ var/obj/item/stack/tile/plasteel/T = new /obj/item/stack/tile/plasteel(src)
+ F.attackby(T, src)
+ addTiles(-1)
+ target = null
+ repairing = 0
+ update_icons()
+ else if(istype(A, /obj/item/stack/tile/plasteel) && amount < maxAmount)
+ var/obj/item/stack/tile/plasteel/T = A
+ visible_message("[src] begins to collect tiles.")
+ repairing = 1
+ update_icons()
+ if(do_after(src, 20))
+ if(T)
+ var/eaten = min(maxAmount - amount, T.get_amount())
+ T.use(eaten)
+ addTiles(eaten)
+ target = null
+ repairing = 0
+ update_icons()
+ else if(istype(A, /obj/item/stack/sheet/metal) && amount + 3 < maxAmount)
+ var/obj/item/stack/sheet/metal/M = A
+ visible_message("[src] begins to make tiles.")
+ repairing = 1
+ update_icons()
+ if(do_after(50))
+ if(M)
+ M.use(1)
+ addTiles(4)
+
+/mob/living/bot/floorbot/explode()
+ turn_off()
+ visible_message("[src] blows apart!")
+ var/turf/Tsec = get_turf(src)
+
+ var/obj/item/weapon/storage/toolbox/mechanical/N = new /obj/item/weapon/storage/toolbox/mechanical(Tsec)
+ N.contents = list()
+ new /obj/item/device/assembly/prox_sensor(Tsec)
+ if(prob(50))
+ new /obj/item/robot_parts/l_arm(Tsec)
+ var/obj/item/stack/tile/plasteel/T = new /obj/item/stack/tile/plasteel(Tsec)
+ T.amount = amount
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(3, 1, src)
+ s.start()
+ del(src)
+
+/mob/living/bot/floorbot/proc/addTiles(var/am)
+ amount += am
+ if(amount < 0)
+ amount = 0
+ else if(amount > maxAmount)
+ amount = maxAmount
+
+/* Assembly */
+
+/obj/item/weapon/storage/toolbox/mechanical/attackby(var/obj/item/stack/tile/plasteel/T, mob/user as mob)
+ if(!istype(T, /obj/item/stack/tile/plasteel))
+ ..()
+ return
+ if(contents.len >= 1)
+ user << "They wont fit in as there is already stuff inside."
+ return
+ if(user.s_active)
+ user.s_active.close(user)
+ if(T.use(10))
+ var/obj/item/weapon/toolbox_tiles/B = new /obj/item/weapon/toolbox_tiles
+ user.put_in_hands(B)
+ user << "You add the tiles into the empty toolbox. They protrude from the top."
+ user.drop_from_inventory(src)
+ del(src)
+ else
+ user << "You need 10 floor tiles for a floorbot."
+ return
+
+/obj/item/weapon/toolbox_tiles
+ desc = "It's a toolbox with tiles sticking out the top"
+ name = "tiles and toolbox"
+ icon = 'icons/obj/aibots.dmi'
+ icon_state = "toolbox_tiles"
+ force = 3.0
+ throwforce = 10.0
+ throw_speed = 2
+ throw_range = 5
+ w_class = 3.0
+ var/created_name = "Floorbot"
+
+/obj/item/weapon/toolbox_tiles/attackby(var/obj/item/W, mob/user as mob)
+ ..()
+ if(isprox(W))
+ del(W)
+ var/obj/item/weapon/toolbox_tiles_sensor/B = new /obj/item/weapon/toolbox_tiles_sensor()
+ B.created_name = created_name
+ user.put_in_hands(B)
+ user << "You add the sensor to the toolbox and tiles!"
+ user.drop_from_inventory(src)
+ del(src)
+ else if (istype(W, /obj/item/weapon/pen))
+ var/t = sanitizeSafe(input(user, "Enter new robot name", name, created_name), MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, user) && loc != user)
+ return
+ created_name = t
+
+/obj/item/weapon/toolbox_tiles_sensor
+ desc = "It's a toolbox with tiles sticking out the top and a sensor attached"
+ name = "tiles, toolbox and sensor arrangement"
+ icon = 'icons/obj/aibots.dmi'
+ icon_state = "toolbox_tiles_sensor"
+ force = 3.0
+ throwforce = 10.0
+ throw_speed = 2
+ throw_range = 5
+ w_class = 3.0
+ var/created_name = "Floorbot"
+
+/obj/item/weapon/toolbox_tiles_sensor/attackby(var/obj/item/W, mob/user as mob)
+ ..()
+ if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm))
+ del(W)
+ var/turf/T = get_turf(user.loc)
+ var/mob/living/bot/floorbot/A = new /mob/living/bot/floorbot(T)
+ A.name = created_name
+ user << "You add the robot arm to the odd looking toolbox assembly! Boop beep!"
+ user.drop_from_inventory(src)
+ del(src)
+ else if(istype(W, /obj/item/weapon/pen))
+ var/t = sanitizeSafe(input(user, "Enter new robot name", name, created_name), MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, user) && loc != user)
+ return
+ created_name = t
\ No newline at end of file
diff --git a/code/modules/mob/living/bot/medbot.dm b/code/modules/mob/living/bot/medbot.dm
new file mode 100644
index 0000000000..b0e2bd5abd
--- /dev/null
+++ b/code/modules/mob/living/bot/medbot.dm
@@ -0,0 +1,365 @@
+/mob/living/bot/medbot
+ name = "Medbot"
+ desc = "A little medical robot. He looks somewhat underwhelmed."
+ icon_state = "medibot0"
+ req_access = list(access_medical)
+
+ var/skin = null //Set to "tox", "ointment" or "o2" for the other two firstaid kits.
+ botcard_access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics)
+
+ //AI vars
+ var/frustration = 0
+ var/list/path = list()
+ var/mob/living/carbon/human/patient = null
+ var/mob/ignored = list() // Used by emag
+ var/last_newpatient_speak = 0
+ var/vocal = 1
+
+ //Healing vars
+ var/obj/item/weapon/reagent_containers/glass/reagent_glass = null //Can be set to draw from this for reagents.
+ var/currently_healing = 0
+ var/injection_amount = 15 //How much reagent do we inject at a time?
+ var/heal_threshold = 10 //Start healing when they have this much damage in a category
+ var/use_beaker = 0 //Use reagents in beaker instead of default treatment agents.
+ var/treatment_brute = "tricordrazine"
+ var/treatment_oxy = "tricordrazine"
+ var/treatment_fire = "tricordrazine"
+ var/treatment_tox = "tricordrazine"
+ var/treatment_virus = "spaceacillin"
+ var/treatment_emag = "toxin"
+ var/declare_treatment = 0 //When attempting to treat a patient, should it notify everyone wearing medhuds?
+
+/mob/living/bot/medbot/Life()
+ ..()
+
+ if(!on)
+ return
+
+ if(!client)
+
+ if(vocal && prob(1))
+ var/message = pick("Radar, put a mask on!", "There's always a catch, and it's the best there is.", "I knew it, I should've been a plastic surgeon.", "What kind of medbay is this? Everyone's dropping like dead flies.", "Delicious!")
+ say(message)
+
+ if(patient)
+ if(Adjacent(patient))
+ if(!currently_healing)
+ UnarmedAttack(patient)
+ else
+ if(path.len && (get_dist(patient, path[path.len]) > 2)) // We have a path, but it's off
+ path = list()
+ if(!path.len && (get_dist(src, patient) > 1))
+ spawn(0)
+ path = AStar(loc, get_turf(patient), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id = botcard)
+ if(!path)
+ path = list()
+ if(path.len)
+ step_to(src, path[1])
+ path -= path[1]
+ ++frustration
+ if(get_dist(src, patient) > 7 || frustration > 8)
+ patient = null
+ else
+ for(var/mob/living/carbon/human/H in view(7, src)) // Time to find a patient!
+ if(valid_healing_target(H))
+ patient = H
+ frustration = 0
+ if(last_newpatient_speak + 300 < world.time)
+ var/message = pick("Hey, [H.name]! Hold on, I'm coming.", "Wait [H.name]! I want to help!", "[H.name], you appear to be injured!")
+ say(message)
+ custom_emote(1, "points at [H.name].")
+ last_newpatient_speak = world.time
+ break
+
+/mob/living/bot/medbot/UnarmedAttack(var/mob/living/carbon/human/H, var/proximity)
+ if(!..())
+ return
+
+ if(!on)
+ return
+
+ if(!istype(H))
+ return
+
+ if(H.stat == DEAD)
+ var/death_message = pick("No! NO!", "Live, damnit! LIVE!", "I... I've never lost a patient before. Not today, I mean.")
+ say(death_message)
+ patient = null
+ return
+
+ var/t = valid_healing_target(H)
+ if(!t)
+ var/message = pick("All patched up!", "An apple a day keeps me away.", "Feel better soon!")
+ say(message)
+ patient = null
+ return
+
+ icon_state = "medibots"
+ visible_message("[src] is trying to inject [H]!")
+ if(declare_treatment)
+ var/area/location = get_area(src)
+ broadcast_medical_hud_message("[src] is treating [H] in [location]", src)
+ currently_healing = 1
+ update_icons()
+ if(do_mob(src, H, 30))
+ if(t == 1)
+ reagent_glass.reagents.trans_to(H, injection_amount)
+ reagent_glass.reagents.reaction(H, 2)
+ else
+ H.reagents.add_reagent(t, injection_amount)
+ visible_message("[src] injects [H] with the syringe!")
+ currently_healing = 0
+ update_icons()
+
+/mob/living/bot/medbot/update_icons()
+ overlays.Cut()
+ if(skin)
+ overlays += image('icons/obj/aibots.dmi', "medskin_[skin]")
+ if(currently_healing)
+ icon_state = "medibots"
+ else
+ icon_state = "medibot[on]"
+
+/mob/living/bot/medbot/attack_hand(var/mob/user)
+ var/dat
+ dat += "Automatic Medical Unit v1.0
"
+ dat += "Status: [on ? "On" : "Off"]
"
+ dat += "Maintenance panel is [open ? "opened" : "closed"]
"
+ dat += "Beaker: "
+ if (reagent_glass)
+ dat += "Loaded \[[reagent_glass.reagents.total_volume]/[reagent_glass.reagents.maximum_volume]\]"
+ else
+ dat += "None Loaded"
+ dat += "
Behaviour controls are [locked ? "locked" : "unlocked"]
"
+ if(!locked || issilicon(user))
+ dat += "Healing Threshold: "
+ dat += "-- "
+ dat += "- "
+ dat += "[heal_threshold] "
+ dat += "+ "
+ dat += "++"
+ dat += "
"
+
+ dat += "Injection Level: "
+ dat += "- "
+ dat += "[injection_amount] "
+ dat += "+ "
+ dat += "
"
+
+ dat += "Reagent Source: "
+ dat += "[use_beaker ? "Loaded Beaker (When available)" : "Internal Synthesizer"]
"
+
+ dat += "Treatment report is [declare_treatment ? "on" : "off"]. Toggle
"
+
+ dat += "The speaker switch is [vocal ? "on" : "off"]. Toggle
"
+
+ user << browse("Medibot v1.0 controls[dat]", "window=automed")
+ onclose(user, "automed")
+ return
+
+/mob/living/bot/medbot/attackby(var/obj/item/O, var/mob/user)
+ if(istype(O, /obj/item/weapon/reagent_containers/glass))
+ if(locked)
+ user << "You cannot insert a beaker because the panel is locked."
+ return
+ if(!isnull(reagent_glass))
+ user << "There is already a beaker loaded."
+ return
+
+ user.drop_item()
+ O.loc = src
+ reagent_glass = O
+ user << "You insert [O]."
+ return
+ else
+ ..()
+
+/mob/living/bot/medbot/Topic(href, href_list)
+ if(..())
+ return
+ usr.set_machine(src)
+ add_fingerprint(usr)
+ if ((href_list["power"]) && access_scanner.allowed(usr))
+ if (on)
+ turn_off()
+ else
+ turn_on()
+
+ else if((href_list["adj_threshold"]) && (!locked || issilicon(usr)))
+ var/adjust_num = text2num(href_list["adj_threshold"])
+ heal_threshold += adjust_num
+ if(heal_threshold < 5)
+ heal_threshold = 5
+ if(heal_threshold > 75)
+ heal_threshold = 75
+
+ else if((href_list["adj_inject"]) && (!locked || issilicon(usr)))
+ var/adjust_num = text2num(href_list["adj_inject"])
+ injection_amount += adjust_num
+ if(injection_amount < 5)
+ injection_amount = 5
+ if(injection_amount > 15)
+ injection_amount = 15
+
+ else if((href_list["use_beaker"]) && (!locked || issilicon(usr)))
+ use_beaker = !use_beaker
+
+ else if (href_list["eject"] && (!isnull(reagent_glass)))
+ if(!locked)
+ reagent_glass.loc = get_turf(src)
+ reagent_glass = null
+ else
+ usr << "You cannot eject the beaker because the panel is locked."
+
+ else if ((href_list["togglevoice"]) && (!locked || issilicon(usr)))
+ vocal = !vocal
+
+ else if ((href_list["declaretreatment"]) && (!locked || issilicon(usr)))
+ declare_treatment = !declare_treatment
+
+ attack_hand(usr)
+ return
+
+/mob/living/bot/medbot/Emag(var/mob/user)
+ ..()
+ if(!emagged)
+ if(user)
+ user << "You short out [src]'s reagent synthesis circuits."
+ visible_message("[src] buzzes oddly!")
+ flick("medibot_spark", src)
+ patient = null
+ currently_healing = 0
+ emagged = 1
+ on = 1
+ update_icons()
+ ignored |= user
+
+/mob/living/bot/medbot/explode()
+ on = 0
+ visible_message("[src] blows apart!")
+ var/turf/Tsec = get_turf(src)
+
+ new /obj/item/weapon/storage/firstaid(Tsec)
+ new /obj/item/device/assembly/prox_sensor(Tsec)
+ new /obj/item/device/healthanalyzer(Tsec)
+ if (prob(50))
+ new /obj/item/robot_parts/l_arm(Tsec)
+
+ if(reagent_glass)
+ reagent_glass.loc = Tsec
+ reagent_glass = null
+
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(3, 1, src)
+ s.start()
+ del(src)
+ return
+
+/mob/living/bot/medbot/proc/valid_healing_target(var/mob/living/carbon/human/H)
+ if(H.stat == DEAD) // He's dead, Jim
+ return null
+
+ if(H.suiciding)
+ return null
+
+ if(H in ignored)
+ return null
+
+ if(emagged)
+ return treatment_emag
+
+ // If they're injured, we're using a beaker, and they don't have on of the chems in the beaker
+ if(reagent_glass && use_beaker && ((H.getBruteLoss() >= heal_threshold) || (H.getToxLoss() >= heal_threshold) || (H.getToxLoss() >= heal_threshold) || (H.getOxyLoss() >= (heal_threshold + 15))))
+ for(var/datum/reagent/R in reagent_glass.reagents.reagent_list)
+ if(!H.reagents.has_reagent(R))
+ return 1
+ continue
+
+ if((H.getBruteLoss() >= heal_threshold) && (!H.reagents.has_reagent(treatment_brute)))
+ return treatment_brute //If they're already medicated don't bother!
+
+ if((H.getOxyLoss() >= (15 + heal_threshold)) && (!H.reagents.has_reagent(treatment_oxy)))
+ return treatment_oxy
+
+ if((H.getFireLoss() >= heal_threshold) && (!H.reagents.has_reagent(treatment_fire)))
+ return treatment_fire
+
+ if((H.getToxLoss() >= heal_threshold) && (!H.reagents.has_reagent(treatment_tox)))
+ return treatment_tox
+
+ for(var/datum/disease/D in H.viruses)
+ if (!H.reagents.has_reagent(treatment_virus))
+ return treatment_virus // STOP DISEASE FOREVER
+
+/* Construction */
+
+/obj/item/weapon/storage/firstaid/attackby(var/obj/item/robot_parts/S, mob/user as mob)
+ if ((!istype(S, /obj/item/robot_parts/l_arm)) && (!istype(S, /obj/item/robot_parts/r_arm)))
+ ..()
+ return
+
+ if(contents.len >= 1)
+ user << "You need to empty [src] out first."
+ return
+
+ var/obj/item/weapon/firstaid_arm_assembly/A = new /obj/item/weapon/firstaid_arm_assembly
+ if(istype(src, /obj/item/weapon/storage/firstaid/fire))
+ A.skin = "ointment"
+ else if(istype(src, /obj/item/weapon/storage/firstaid/toxin))
+ A.skin = "tox"
+ else if(istype(src, /obj/item/weapon/storage/firstaid/o2))
+ A.skin = "o2"
+
+ del(S)
+ user.put_in_hands(A)
+ user << "You add the robot arm to the first aid kit."
+ user.drop_from_inventory(src)
+ del(src)
+
+/obj/item/weapon/firstaid_arm_assembly
+ name = "first aid/robot arm assembly"
+ desc = "A first aid kit with a robot arm permanently grafted to it."
+ icon = 'icons/obj/aibots.dmi'
+ icon_state = "firstaid_arm"
+ var/build_step = 0
+ var/created_name = "Medibot" //To preserve the name if it's a unique medbot I guess
+ var/skin = null //Same as medbot, set to tox or ointment for the respective kits.
+ w_class = 3.0
+
+/obj/item/weapon/firstaid_arm_assembly/New()
+ ..()
+ spawn(5) // Terrible. TODO: fix
+ if(skin)
+ overlays += image('icons/obj/aibots.dmi', "kit_skin_[src.skin]")
+
+/obj/item/weapon/firstaid_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ ..()
+ if(istype(W, /obj/item/weapon/pen))
+ var/t = sanitizeSafe(input(user, "Enter new robot name", name, created_name), MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && loc != usr)
+ return
+ created_name = t
+ else
+ switch(build_step)
+ if(0)
+ if(istype(W, /obj/item/device/healthanalyzer))
+ user.drop_item()
+ del(W)
+ build_step++
+ user << "You add the health sensor to [src]."
+ name = "First aid/robot arm/health analyzer assembly"
+ overlays += image('icons/obj/aibots.dmi', "na_scanner")
+
+ if(1)
+ if(isprox(W))
+ user.drop_item()
+ del(W)
+ user << "You complete the Medibot! Beep boop."
+ var/turf/T = get_turf(src)
+ var/mob/living/bot/medbot/S = new /mob/living/bot/medbot(T)
+ S.skin = skin
+ S.name = created_name
+ user.drop_from_inventory(src)
+ del(src)
diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm
new file mode 100644
index 0000000000..024e1f6f1c
--- /dev/null
+++ b/code/modules/mob/living/bot/secbot.dm
@@ -0,0 +1,579 @@
+/mob/living/bot/secbot
+ name = "Securitron"
+ desc = "A little security robot. He looks less than thrilled."
+ icon_state = "secbot0"
+ maxHealth = 50
+ health = 50
+ req_access = list(access_security, access_forensics_lockers)
+ botcard_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court)
+
+ var/mob/target
+
+ var/idcheck = 0 // If true, arrests for having weapons without authorization.
+ var/check_records = 0 // If true, arrests people without a record.
+ var/check_arrest = 1 // If true, arrests people who are set to arrest.
+ var/arrest_type = 0 // If true, doesn't handcuff. You monster.
+ var/declare_arrests = 0 // If true, announces arrests over sechuds.
+ var/auto_patrol = 0 // If true, patrols on its own
+
+ var/mode = 0
+#define SECBOT_IDLE 0 // idle
+#define SECBOT_HUNT 1 // found target, hunting
+#define SECBOT_ARREST 2 // arresting target
+#define SECBOT_START_PATROL 3 // start patrol
+#define SECBOT_WAIT_PATROL 4 // waiting for signals
+#define SECBOT_PATROL 5 // patrolling
+#define SECBOT_SUMMON 6 // summoned by PDA
+ var/is_attacking = 0
+ var/is_ranged = 0
+ var/awaiting_surrender = 0
+
+ var/obj/secbot_listener/listener = null
+ var/beacon_freq = 1445 // Navigation beacon frequency
+ var/control_freq = AI_FREQ // Bot control frequency
+ var/list/path = list()
+ var/frustration = 0
+ var/turf/patrol_target = null // This is where we are headed
+ var/closest_dist // Used to find the closest beakon
+ var/destination = "__nearest__" // This is the current beacon's ID
+ var/next_destination = "__nearest__" // This is the next beacon's ID
+ var/nearest_beacon // Tag of the beakon that we assume to be the closest one
+
+ var/bot_version = 1.3
+ var/list/threat_found_sounds = new('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg')
+ var/list/preparing_arrest_sounds = new('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg')
+
+/mob/living/bot/secbot/beepsky
+ name = "Officer Beepsky"
+ desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey."
+ auto_patrol = 1
+
+/mob/living/bot/secbot/New()
+ ..()
+ listener = new /obj/secbot_listener(src)
+ listener.secbot = src
+
+ spawn(5) // Since beepsky is made on the start... this delay is necessary
+ if(radio_controller)
+ radio_controller.add_object(listener, control_freq, filter = RADIO_SECBOT)
+ radio_controller.add_object(listener, beacon_freq, filter = RADIO_NAVBEACONS)
+
+/mob/living/bot/secbot/turn_off()
+ ..()
+ target = null
+ frustration = 0
+ mode = SECBOT_IDLE
+
+/mob/living/bot/secbot/update_icons()
+ if(on && is_attacking)
+ icon_state = "secbot-c"
+ else
+ icon_state = "secbot[on]"
+
+/mob/living/bot/secbot/attack_hand(var/mob/user)
+ user.set_machine(src)
+ var/dat
+ dat += "Automatic Security Unit v[bot_version]
"
+ dat += "Status: [on ? "On" : "Off"]
"
+ dat += "Behaviour controls are [locked ? "locked" : "unlocked"]
"
+ dat += "Maintenance panel is [open ? "opened" : "closed"]"
+ if(!locked || issilicon(user))
+ dat += "
Check for Weapon Authorization: [idcheck ? "Yes" : "No"]
"
+ dat += "Check Security Records: [check_records ? "Yes" : "No"]
"
+ dat += "Check Arrest Status: [check_arrest ? "Yes" : "No"]
"
+ dat += "Operating Mode: [arrest_type ? "Detain" : "Arrest"]
"
+ dat += "Report Arrests: [declare_arrests ? "Yes" : "No"]
"
+ dat += "Auto Patrol: [auto_patrol ? "On" : "Off"]"
+ user << browse("Securitron v[bot_version] controls[dat]", "window=autosec")
+ onclose(user, "autosec")
+ return
+
+/mob/living/bot/secbot/Topic(href, href_list)
+ if(..())
+ return
+
+ usr.set_machine(src)
+ add_fingerprint(usr)
+
+ if((href_list["power"]) && (access_scanner.allowed(usr)))
+ if(on)
+ turn_off()
+ else
+ turn_on()
+ return
+
+ switch(href_list["operation"])
+ if("idcheck")
+ idcheck = !idcheck
+ if("ignorerec")
+ check_records = !check_records
+ if("ignorearr")
+ check_arrest = !check_arrest
+ if("switchmode")
+ arrest_type = !arrest_type
+ if("patrol")
+ auto_patrol = !auto_patrol
+ mode = SECBOT_IDLE
+ if("declarearrests")
+ declare_arrests = !declare_arrests
+ attack_hand(usr)
+
+/mob/living/bot/secbot/attackby(var/obj/item/O, var/mob/user)
+ var/curhealth = health
+ ..()
+ if(health < curhealth)
+ target = user
+ awaiting_surrender = 5
+ mode = SECBOT_HUNT
+
+/mob/living/bot/secbot/Life()
+ ..()
+ if(!on)
+ return
+ if(client)
+ return
+
+ if(!target)
+ scan_view()
+
+ if(!locked && (mode == SECBOT_START_PATROL || mode == SECBOT_PATROL)) // Stop running away when we set you up
+ mode = SECBOT_IDLE
+
+ switch(mode)
+ if(SECBOT_IDLE)
+ if(auto_patrol && locked)
+ mode = SECBOT_START_PATROL
+ return
+
+ if(SECBOT_HUNT) // Target is in the view or has been recently - chase it
+ if(frustration > 7)
+ target = null
+ frustration = 0
+ awaiting_surrender = 0
+ mode = SECBOT_IDLE
+ return
+ if(target)
+ var/threat = check_threat(target)
+ if(threat < 4) // Re-evaluate in case they dropped the weapon or something
+ target = null
+ frustration = 0
+ awaiting_surrender = 0
+ mode = SECBOT_IDLE
+ return
+ if(!(target in view(7, src)))
+ ++frustration
+ if(Adjacent(target))
+ mode = SECBOT_ARREST
+ return
+ else
+ if(is_ranged)
+ RangedAttack(target)
+ else
+ step_towards(src, target) // Melee bots chase a bit faster
+ spawn(8)
+ if(!Adjacent(target))
+ step_towards(src, target)
+ spawn(16)
+ if(!Adjacent(target))
+ step_towards(src, target)
+
+ if(SECBOT_ARREST) // Target is next to us - attack it
+ if(!target)
+ mode = SECBOT_IDLE
+ if(!Adjacent(target))
+ awaiting_surrender = 5 // I'm done playing nice
+ mode = SECBOT_HUNT
+ var/threat = check_threat(target)
+ if(threat < 4)
+ target = null
+ awaiting_surrender = 0
+ frustration = 0
+ mode = SECBOT_IDLE
+ return
+ if(awaiting_surrender < 5 && ishuman(target) && !target.lying)
+ if(awaiting_surrender == 0)
+ say("Down on the floor, [target]! You have five seconds to comply.")
+ ++awaiting_surrender
+ else
+ UnarmedAttack(target)
+ if(ishuman(target) && declare_arrests)
+ var/area/location = get_area(src)
+ broadcast_security_hud_message("[src] is [arrest_type ? "detaining" : "arresting"] a level [check_threat(target)] suspect [target] in [location].", src)
+ return
+
+ if(SECBOT_START_PATROL)
+ if(path.len && patrol_target)
+ mode = SECBOT_PATROL
+ return
+ else if(patrol_target)
+ spawn(0)
+ calc_path()
+ if(!path.len)
+ patrol_target = null
+ mode = SECBOT_IDLE
+ else
+ mode = SECBOT_PATROL
+ if(!patrol_target)
+ if(next_destination)
+ find_next_target()
+ else
+ find_patrol_target()
+ say("Engaging patrol mode.")
+ mode = SECBOT_WAIT_PATROL
+ return
+
+ if(SECBOT_WAIT_PATROL)
+ if(patrol_target)
+ mode = SECBOT_START_PATROL
+ else
+ ++frustration
+ if(frustration > 120)
+ frustration = 0
+ mode = SECBOT_IDLE
+
+ if(SECBOT_PATROL)
+ patrol_step()
+ spawn(10)
+ patrol_step()
+ return
+
+ if(SECBOT_SUMMON)
+ patrol_step()
+ spawn(8)
+ patrol_step()
+ spawn(16)
+ patrol_step()
+ return
+
+/mob/living/bot/secbot/UnarmedAttack(var/mob/M, var/proximity)
+ if(!..())
+ return
+
+ if(!istype(M))
+ return
+
+ if(istype(M, /mob/living/carbon))
+ var/mob/living/carbon/C = M
+ var/cuff = 1
+ if(istype(C, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = C
+ if(istype(H.back, /obj/item/weapon/rig) && istype(H.gloves,/obj/item/clothing/gloves/rig))
+ cuff = 0
+ if(!C.lying || C.handcuffed || arrest_type)
+ cuff = 0
+ if(!cuff)
+ C.stun_effect_act(0, 60, null)
+ playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
+ is_attacking = 1
+ update_icons()
+ spawn(2)
+ is_attacking = 0
+ update_icons()
+ visible_message("[C] was prodded by [src] with a stun baton!")
+ else
+ playsound(loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2)
+ visible_message("[src] is trying to put handcuffs on [C]!")
+ if(do_mob(src, C, 60))
+ if(!C.handcuffed)
+ C.handcuffed = new /obj/item/weapon/handcuffs(C)
+ C.update_inv_handcuffed()
+ if(preparing_arrest_sounds.len)
+ playsound(loc, pick(preparing_arrest_sounds), 50, 0)
+ else if(istype(M, /mob/living/simple_animal))
+ var/mob/living/simple_animal/S = M
+ S.AdjustStunned(10)
+ S.adjustBruteLoss(15)
+ playsound(loc, "swing_hit", 50, 1, -1)
+ is_attacking = 1
+ update_icons()
+ spawn(2)
+ is_attacking = 0
+ update_icons()
+ visible_message("[M] was beaten by [src] with a stun baton!")
+
+/mob/living/bot/secbot/explode()
+ visible_message("[src] blows apart!")
+ var/turf/Tsec = get_turf(src)
+
+ var/obj/item/weapon/secbot_assembly/Sa = new /obj/item/weapon/secbot_assembly(Tsec)
+ Sa.build_step = 1
+ Sa.overlays += image('icons/obj/aibots.dmi', "hs_hole")
+ Sa.created_name = name
+ new /obj/item/device/assembly/prox_sensor(Tsec)
+ new /obj/item/weapon/melee/baton(Tsec)
+ if(prob(50))
+ new /obj/item/robot_parts/l_arm(Tsec)
+
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(3, 1, src)
+ s.start()
+
+ new /obj/effect/decal/cleanable/blood/oil(Tsec)
+ del(src)
+
+/mob/living/bot/secbot/proc/scan_view()
+ for(var/mob/living/M in view(7, src))
+ if(M.invisibility >= INVISIBILITY_LEVEL_ONE)
+ continue
+ if(M.stat)
+ continue
+
+ var/threat = check_threat(M)
+
+ if(threat >= 4)
+ target = M
+ say("Level [threat] infraction alert!")
+ custom_emote(1, "points at [M.name]!")
+ mode = SECBOT_HUNT
+ break
+ return
+
+/mob/living/bot/secbot/proc/calc_path(var/turf/avoid = null)
+ path = AStar(loc, patrol_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 120, id=botcard, exclude=avoid)
+ if(!path)
+ path = list()
+
+/mob/living/bot/secbot/proc/check_threat(var/mob/living/M)
+ if(M.stat == DEAD)
+ return 0
+ if(emagged)
+ return 10
+
+ var/threatcount = 0
+
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+
+ if(H.handcuffed)
+ return 0
+
+ var/obj/item/weapon/card/id/id = GetIdCard(H) //Agent cards lower threatlevel.
+ if(id && istype(id, /obj/item/weapon/card/id/syndicate))
+ threatcount -= 2
+
+ if(idcheck && !access_scanner.allowed(H))
+ if(istype(H.l_hand, /obj/item/weapon/gun) || istype(H.l_hand, /obj/item/weapon/melee))
+ threatcount += 4
+
+ if(istype(H.r_hand, /obj/item/weapon/gun) || istype(H.r_hand, /obj/item/weapon/melee))
+ threatcount += 4
+
+ if(istype(H.belt, /obj/item/weapon/gun) || istype(H.belt, /obj/item/weapon/melee))
+ threatcount += 2
+
+ if(H.species.name != "Human") //beepsky so racist.
+ threatcount += 2
+
+ if(check_records || check_arrest)
+ var/perpname = H.name
+ if(id)
+ perpname = id.registered_name
+
+ var/datum/data/record/R = find_security_record("name", perpname)
+ if(check_records && !R)
+ threatcount += 4
+
+ if(check_arrest && R && (R.fields["criminal"] == "*Arrest*"))
+ threatcount += 4
+
+ else if(isanimal(M))
+ if(istype(M, /mob/living/simple_animal/hostile) && !istype(M, /mob/living/simple_animal/hostile/retaliate/goat))
+ threatcount += 4
+
+ return threatcount
+
+/mob/living/bot/secbot/proc/patrol_step()
+ if(loc == patrol_target)
+ patrol_target = null
+ path = list()
+ mode = SECBOT_IDLE
+ return
+
+ if(path.len && patrol_target)
+ var/turf/next = path[1]
+ if(loc == next)
+ path -= next
+ return
+ var/moved = step_towards(src, next)
+ if(moved)
+ path -= next
+ frustration = 0
+ else
+ ++frustration
+ if(frustration > 5) // Make a new path
+ mode = SECBOT_START_PATROL
+ return
+ else
+ mode = SECBOT_START_PATROL
+
+/mob/living/bot/secbot/proc/find_patrol_target()
+ send_status()
+ nearest_beacon = null
+ next_destination = "__nearest__"
+ listener.post_signal(beacon_freq, "findbeacon", "patrol")
+
+/mob/living/bot/secbot/proc/find_next_target()
+ send_status()
+ nearest_beacon = null
+ listener.post_signal(beacon_freq, "findbeacon", "patrol")
+
+/mob/living/bot/secbot/proc/send_status()
+ var/list/kv = list(
+ "type" = "secbot",
+ "name" = name,
+ "loca" = get_area(loc),
+ "mode" = mode
+ )
+ listener.post_signal_multiple(control_freq, kv)
+
+/obj/secbot_listener
+ var/mob/living/bot/secbot/secbot = null
+
+/obj/secbot_listener/proc/post_signal(var/freq, var/key, var/value) // send a radio signal with a single data key/value pair
+ post_signal_multiple(freq, list("[key]" = value))
+
+/obj/secbot_listener/proc/post_signal_multiple(var/freq, var/list/keyval) // send a radio signal with multiple data key/values
+ var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq)
+ if(!frequency)
+ return
+
+ var/datum/signal/signal = new()
+ signal.source = secbot
+ signal.transmission_method = 1
+ signal.data = keyval.Copy()
+
+ if(signal.data["findbeacon"])
+ frequency.post_signal(secbot, signal, filter = RADIO_NAVBEACONS)
+ else if(signal.data["type"] == "secbot")
+ frequency.post_signal(secbot, signal, filter = RADIO_SECBOT)
+ else
+ frequency.post_signal(secbot, signal)
+
+/obj/secbot_listener/receive_signal(datum/signal/signal)
+ if(!secbot || !secbot.on)
+ return
+
+ var/recv = signal.data["command"]
+ if(recv == "bot_status")
+ secbot.send_status()
+ return
+
+ if(signal.data["active"] == secbot)
+ switch(recv)
+ if("stop")
+ secbot.mode = SECBOT_IDLE
+ secbot.auto_patrol = 0
+ return
+
+ if("go")
+ secbot.mode = SECBOT_IDLE
+ secbot.auto_patrol = 1
+ return
+
+ if("summon")
+ secbot.patrol_target = signal.data["target"]
+ secbot.next_destination = secbot.destination
+ secbot.destination = null
+ //secbot.awaiting_beacon = 0
+ secbot.mode = SECBOT_SUMMON
+ secbot.calc_path()
+ secbot.say("Responding.")
+ return
+
+ recv = signal.data["beacon"]
+ var/valid = signal.data["patrol"]
+ if(!recv || !valid)
+ return
+
+ if(recv == secbot.next_destination) // This beacon is our target
+ secbot.destination = secbot.next_destination
+ secbot.patrol_target = signal.source.loc
+ secbot.next_destination = signal.data["next_patrol"]
+ else if(secbot.next_destination == "__nearest__")
+ var/dist = get_dist(secbot, signal.source.loc)
+ if(dist <= 1)
+ return
+
+ if(secbot.nearest_beacon)
+ if(dist < secbot.closest_dist)
+ secbot.nearest_beacon = recv
+ secbot.patrol_target = secbot.nearest_beacon
+ secbot.next_destination = signal.data["next_patrol"]
+ secbot.closest_dist = dist
+ return
+ else
+ secbot.nearest_beacon = recv
+ secbot.patrol_target = secbot.nearest_beacon
+ secbot.next_destination = signal.data["next_patrol"]
+ secbot.closest_dist = dist
+
+//Secbot Construction
+
+/obj/item/clothing/head/helmet/attackby(var/obj/item/device/assembly/signaler/S, mob/user as mob)
+ ..()
+ if(!issignaler(S))
+ ..()
+ return
+
+ if(type != /obj/item/clothing/head/helmet) //Eh, but we don't want people making secbots out of space helmets.
+ return
+
+ if(S.secured)
+ del(S)
+ var/obj/item/weapon/secbot_assembly/A = new /obj/item/weapon/secbot_assembly
+ user.put_in_hands(A)
+ user << "You add the signaler to the helmet."
+ user.drop_from_inventory(src)
+ del(src)
+ else
+ return
+
+/obj/item/weapon/secbot_assembly
+ name = "helmet/signaler assembly"
+ desc = "Some sort of bizarre assembly."
+ icon = 'icons/obj/aibots.dmi'
+ icon_state = "helmet_signaler"
+ item_state = "helmet"
+ var/build_step = 0
+ var/created_name = "Securitron"
+
+/obj/item/weapon/secbot_assembly/attackby(var/obj/item/O, var/mob/user)
+ ..()
+ if(istype(O, /obj/item/weapon/weldingtool) && !build_step)
+ var/obj/item/weapon/weldingtool/WT = O
+ if(WT.remove_fuel(0, user))
+ build_step = 1
+ overlays += image('icons/obj/aibots.dmi', "hs_hole")
+ user << "You weld a hole in \the [src]."
+
+ else if(isprox(O) && (build_step == 1))
+ user.drop_item()
+ build_step = 2
+ user << "You add \the [O] to [src]."
+ overlays += image('icons/obj/aibots.dmi', "hs_eye")
+ name = "helmet/signaler/prox sensor assembly"
+ del(O)
+
+ else if((istype(O, /obj/item/robot_parts/l_arm) || istype(O, /obj/item/robot_parts/r_arm)) && build_step == 2)
+ user.drop_item()
+ build_step = 3
+ user << "You add \the [O] to [src]."
+ name = "helmet/signaler/prox sensor/robot arm assembly"
+ overlays += image('icons/obj/aibots.dmi', "hs_arm")
+ del(O)
+
+ else if(istype(O, /obj/item/weapon/melee/baton) && build_step == 3)
+ user.drop_item()
+ user << "You complete the Securitron! Beep boop."
+ var/mob/living/bot/secbot/S = new /mob/living/bot/secbot(get_turf(src))
+ S.name = created_name
+ del(O)
+ del(src)
+
+ else if(istype(O, /obj/item/weapon/pen))
+ var/t = sanitizeSafe(input(user, "Enter new robot name", name, created_name), MAX_NAME_LEN)
+ if(!t)
+ return
+ if(!in_range(src, usr) && loc != usr)
+ return
+ created_name = t
\ No newline at end of file
diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm
index 12b56021d4..f4f6f3b2ba 100644
--- a/code/modules/reagents/reagent_containers/glass.dm
+++ b/code/modules/reagents/reagent_containers/glass.dm
@@ -28,7 +28,7 @@
/obj/machinery/atmospherics/unary/cryo_cell,
/obj/machinery/dna_scannernew,
/obj/item/weapon/grenade/chem_grenade,
- /obj/machinery/bot/medbot,
+ /mob/living/bot/medbot,
/obj/machinery/computer/pandemic,
/obj/item/weapon/storage/secure/safe,
/obj/machinery/iv_drip,
diff --git a/maps/exodus-1.dmm b/maps/exodus-1.dmm
index b1b8dcb49a..e2bff065cc 100644
--- a/maps/exodus-1.dmm
+++ b/maps/exodus-1.dmm
@@ -977,7 +977,7 @@
"asO" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; pixel_y = 0},/obj/machinery/door/firedoor/border_only{dir = 2},/obj/machinery/door/airlock/highsecurity{name = "Secure Armoury Section"; req_access = list(3)},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/turf/simulated/floor,/area/security/warden)
"asP" = (/obj/structure/bed,/obj/machinery/flasher{id = "Cell 1"; pixel_x = 0; pixel_y = -28},/turf/simulated/floor{icon_state = "red"; dir = 4},/area/security/prison)
"asQ" = (/obj/structure/cable/green{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor{icon_state = "redcorner"; dir = 1},/area/hallway/primary/fore)
-"asR" = (/obj/machinery/bot/secbot/beepsky{name = "Officer Beepsky"},/obj/structure/disposalpipe/segment,/turf/simulated/floor,/area/hallway/primary/fore)
+"asR" = (/mob/living/bot/secbot/beepsky,/obj/structure/disposalpipe/segment,/turf/simulated/floor,/area/hallway/primary/fore)
"asS" = (/obj/machinery/light{icon_state = "tube1"; dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/turf/simulated/floor{icon_state = "redcorner"; dir = 4},/area/hallway/primary/fore)
"asT" = (/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 6},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 6},/obj/structure/disposalpipe/segment{dir = 4; icon_state = "pipe-c"},/obj/effect/decal/cleanable/cobweb,/turf/simulated/floor/plating,/area/maintenance/dormitory)
"asU" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/plating,/area/maintenance/dormitory)