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)