diff --git a/code/defines/global.dm b/code/defines/global.dm index 712d0dc9e14..ba58eb24e62 100644 --- a/code/defines/global.dm +++ b/code/defines/global.dm @@ -101,6 +101,7 @@ var viroMax = 1 wardenMax = 1 cmoMax = 1 + mimeMax = 1 list/bombers = list( ) list/admin_log = list ( ) diff --git a/code/defines/obj/radio.dm b/code/defines/obj/radio.dm index 07aeb3fd96f..32ef45e0e70 100644 --- a/code/defines/obj/radio.dm +++ b/code/defines/obj/radio.dm @@ -1,4 +1,5 @@ /obj/item/device/radio + icon = 'radio.dmi' name = "station bounced radio" suffix = "\[3\]" icon_state = "walkietalkie" @@ -116,7 +117,7 @@ name = "Captain's Headset" icon_state = "com_headset" item_state = "headset" - channels = list("Command" = 1, "Science" = 0, "Medical" = 0, "Security" = 1, "Engineering" = 0) + channels = list("Command" = 1, "Science" = 0, "Medical" = 0, "Security" = 1, "Engineering" = 0, "Mining" = 0, "Cargo" = 0) /obj/item/device/radio/headset/heads/rd // -- Bar name = "Research Director's Headset" @@ -134,10 +135,34 @@ name = "Chief Engineer's Headset" icon_state = "com_headset" item_state = "headset" - channels = list("Engineering" = 1, "Command" = 1) + channels = list("Engineering" = 1, "Command" = 1, "Mining" = 1) /obj/item/device/radio/headset/heads/cmo // -- Bar name = "Chief Medical Officer's Headset" icon_state = "com_headset" item_state = "headset" - channels = list("Medical" = 1, "Command" = 1) \ No newline at end of file + channels = list("Medical" = 1, "Command" = 1) + +/obj/item/device/radio/headset/heads/hop // -- rastaf0 + name = "Head of Personnel's Headset" + icon_state = "com_headset" + item_state = "headset" + channels = list("Command" = 1, "Security" = 1, "Cargo" = 0) + +/obj/item/device/radio/headset/headset_mine // -- rastaf0 + name = "Mining Radio Headset" + icon_state = "mine_headset" + item_state = "headset" + channels = list("Mining" = 1) + +/obj/item/device/radio/headset/headset_cargo // -- rastaf0 + name = "Cargo Radio Headset" + icon_state = "cargo_headset" + item_state = "headset" + channels = list("Cargo" = 1) + +/obj/item/device/radio/headset/heads/qm // -- rastaf0 //he is not a real head but he gives orders sometimes + name = "Quartermaster's Headset" + icon_state = "cargo_headset" + item_state = "headset" + channels = list("Cargo" = 1, "Mining" = 1) //no command frequency yet diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm index becb0ee43a3..51df169d756 100644 --- a/code/defines/obj/weapon.dm +++ b/code/defines/obj/weapon.dm @@ -376,6 +376,10 @@ item_state = "wrench" w_class = 2.0 m_amt = 50 + +/obj/item/weapon/crowbar/red + icon = 'items.dmi' + icon_state = "red_crowbar" /obj/item/weapon/disk name = "disk" @@ -991,7 +995,7 @@ Total SMES charging rate should not exceed total power generation rate, or an ov /obj/item/weapon/syndicate_uplink name = "station bounced radio" - icon = 'device.dmi' + icon = 'radio.dmi' icon_state = "radio" var/temp = null var/uses = 10.0 @@ -1007,7 +1011,7 @@ Total SMES charging rate should not exceed total power generation rate, or an ov /obj/item/weapon/SWF_uplink name = "station bounced radio" - icon = 'device.dmi' + icon = 'radio.dmi' icon_state = "radio" var/temp = null var/uses = 4.0 diff --git a/code/game/asteroid/asteroid.dm b/code/game/asteroid/asteroid.dm index 62e2d97b8c5..6d288d76643 100644 --- a/code/game/asteroid/asteroid.dm +++ b/code/game/asteroid/asteroid.dm @@ -1,5 +1,5 @@ proc/spawn_asteroid(var/atom/start_loc,var/type,var/size,var/richness)//type: 0 or null - random, 1 - nothing, 2 - iron, 3 - silicon - if(start_loc.x - size < 3 || start_loc.x + size >= world.maxx - 3 || start_loc.y - size < 3 || start_loc.y + size > world.maxy -3) + if(start_loc.x - size < 5 || start_loc.x + size >= world.maxx - 5 || start_loc.y - size < 5 || start_loc.y + size > world.maxy -5) return 0 if(!size) size = pick(100;2,50;3,35;4,25;6,10;12) diff --git a/code/game/communications.dm b/code/game/communications.dm index c2748c90a2d..763e80696e4 100644 --- a/code/game/communications.dm +++ b/code/game/communications.dm @@ -74,6 +74,8 @@ Radio: 1359 - Security 1441 - death squad 1443 - Confession Intercom +1349 - Miners +1347 - Cargo techs Devices: 1451 - tracking implant @@ -104,10 +106,12 @@ var/list/radiochannels = list( "Security" = 1359, "Deathsquad" = 1441, "Syndicate" = 1213, + "Mining" = 1349, + "Cargo" = 1347, ) //depenging helpers -var/list/DEPT_FREQS = list(1351,1355,1357,1359,1213,1441) -var/const/COMM_FREQ = 1353 +var/list/DEPT_FREQS = list(1351,1355,1357,1359,1213,1441,1349,1347) +var/const/COMM_FREQ = 1353 //command, colored gold in chat window var/const/SYND_FREQ = 1213 #define TRANSMISSION_WIRE 0 diff --git a/code/game/jobs/jobprocs.dm b/code/game/jobs/jobprocs.dm index 6293afd8da7..5eff0949634 100644 --- a/code/game/jobs/jobprocs.dm +++ b/code/game/jobs/jobprocs.dm @@ -367,6 +367,7 @@ src.equip_if_possible(new /obj/item/device/t_scanner(src), slot_r_store) if ("Shaft Miner") + src.equip_if_possible(new /obj/item/device/radio/headset/headset_mine (src), slot_ears) src.equip_if_possible(new /obj/item/clothing/under/color/white(src), slot_w_uniform) src.equip_if_possible(new /obj/item/clothing/shoes/black(src), slot_shoes) src.equip_if_possible(new /obj/item/clothing/gloves/black(src), slot_gloves) @@ -475,7 +476,7 @@ if ("Head of Personnel") - src.equip_if_possible(new /obj/item/device/radio/headset/headset_com (src), slot_ears) // -- TLE + src.equip_if_possible(new /obj/item/device/radio/headset/heads/hop (src), slot_ears) // -- TLE src.equip_if_possible(new /obj/item/weapon/storage/backpack(src), slot_back) src.equip_if_possible(new /obj/item/device/pda/heads(src), slot_belt) src.equip_if_possible(new /obj/item/clothing/under/rank/head_of_personnel(src), slot_w_uniform) @@ -546,6 +547,7 @@ src.equip_if_possible(new /obj/item/weapon/storage/briefcase(src), slot_l_hand) if ("Quartermaster") + src.equip_if_possible(new /obj/item/device/radio/headset/heads/qm (src), slot_ears) src.equip_if_possible(new /obj/item/clothing/gloves/black(src), slot_gloves) src.equip_if_possible(new /obj/item/clothing/shoes/black(src), slot_shoes) src.equip_if_possible(new /obj/item/clothing/under/rank/cargo(src), slot_w_uniform) @@ -555,6 +557,7 @@ //src.equip_if_possible(new /obj/item/clothing/suit/exo_suit(src), slot_wear_suit) if ("Cargo Technician") + src.equip_if_possible(new /obj/item/device/radio/headset/headset_cargo(src), slot_ears) src.equip_if_possible(new /obj/item/clothing/gloves/black(src), slot_gloves) src.equip_if_possible(new /obj/item/clothing/shoes/black(src), slot_shoes) src.equip_if_possible(new /obj/item/clothing/under/rank/cargo(src), slot_w_uniform) diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm index c2da21c1670..7d3b24cb6c9 100644 --- a/code/game/machinery/bots/bots.dm +++ b/code/game/machinery/bots/bots.dm @@ -4,9 +4,132 @@ icon = 'aibots.dmi' layer = MOB_LAYER var/obj/item/weapon/card/id/botcard // the ID card that the bot "holds" + var/on = 1 + var/health = 0 //do not forget to set health for your bot! + var/maxhealth = 0 + var/fire_dam_coeff = 1.0 + var/brute_dam_coeff = 1.0 + + +/obj/machinery/bot/proc/turn_on() + if (stat) + return 0 + src.on = 1 + return 1 + +/obj/machinery/bot/proc/turn_off() + src.on = 0 + +/obj/machinery/bot/proc/explode() + del(src) + +/obj/machinery/bot/proc/healthcheck() + if (src.health <= 0) + src.explode() + +/obj/machinery/bot/examine() + set src in view() + ..() + if (src.health < maxhealth) + if (src.health > maxhealth/3) + usr << text("\red [src]'s parts look loose.") + else + usr << text("\red [src]'s parts look very loose!") + return + +/obj/machinery/bot/attack_alien(var/mob/living/carbon/alien/user as mob) + /* Well, aliums dislike machines and do not want to caress them. + if (user.a_intent == "help") + for(var/mob/O in viewers(src, null)) + if ((O.client && !( O.blinded ))) + O.show_message(text("\blue [user] caresses [src.name] with its scythe like arm."), 1) + else + */ + src.health -= rand(15,30)*brute_dam_coeff + src.visible_message("\red [user] has slashed [src]!") + playsound(src.loc, 'slice.ogg', 25, 1, -1) + if(prob(10)) + new /obj/decal/cleanable/oil(src.loc) + healthcheck() +/obj/machinery/bot/attackby(obj/item/weapon/W as obj, mob/user as mob) + if (istype(W, /obj/item/weapon/screwdriver)) + if (src.health < maxhealth) + src.health = min(maxhealth, src.health+25) + user.visible_message( + "\red [user] repairs [src]!", + "\blue You repair [src]!" + ) + else + user << "\blue [src] does not need a repair!" + else + switch(W.damtype) + if("fire") + src.health -= W.force * fire_dam_coeff + if("brute") + src.health -= W.force * brute_dam_coeff + ..() + healthcheck() + +/obj/machinery/bot/bullet_act(flag, A as obj) + switch(flag) + if (PROJECTILE_BULLET) + src.health -= 20 + //if (PROJECTILE_WEAKBULLET || PROJECTILE_BEANBAG) //Detective's revolver fires marshmallows + // src.health -= 2 + if (PROJECTILE_LASER) + src.health -= 10 + if (PROJECTILE_PULSE) + src.health -= 40 + healthcheck() + +/obj/machinery/bot/meteorhit() + src.explode() + return + +/obj/machinery/bot/blob_act() + src.health -= rand(20,40)*fire_dam_coeff + healthcheck() + return + +/obj/machinery/bot/ex_act(severity) + switch(severity) + if(1.0) + src.explode() + return + if(2.0) + src.health -= rand(5,10)*fire_dam_coeff + src.health -= rand(10,20)*brute_dam_coeff + healthcheck() + return + if(3.0) + if (prob(50)) + src.health -= rand(1,5)*fire_dam_coeff + src.health -= rand(1,5)*brute_dam_coeff + healthcheck() + return + return + +/obj/machinery/bot/emp_act(severity) + var/was_on = on + stat |= EMPED + var/obj/overlay/pulse2 = new/obj/overlay ( src.loc ) + pulse2.icon = 'effects.dmi' + pulse2.icon_state = "empdisable" + pulse2.name = "emp sparks" + pulse2.anchored = 1 + pulse2.dir = pick(cardinal) + + spawn(10) + del(pulse2) + if (on) + turn_off() + spawn(severity*300) + stat &= ~EMPED + if (was_on) + turn_on() /******************************************************************/ // Navigation procs // Used for A-star pathfinding diff --git a/code/game/machinery/bots/cleanbot.dm b/code/game/machinery/bots/cleanbot.dm index 2ddb9e985a5..8afc5a5b6de 100644 --- a/code/game/machinery/bots/cleanbot.dm +++ b/code/game/machinery/bots/cleanbot.dm @@ -23,7 +23,8 @@ density = 0 anchored = 0 //weight = 1.0E7 - var/on = 1 + health = 25 + maxhealth = 25 var/cleaning = 0 var/locked = 1 var/screwloose = 0 @@ -36,15 +37,35 @@ var/oldloc = null req_access = list(access_janitor) var/path[] = new() - - + /obj/machinery/bot/cleanbot/New() ..() src.get_targets() src.icon_state = "cleanbot[src.on]" -/obj/machinery/bot/cleanbot/attack_hand(user as mob) +/obj/machinery/bot/cleanbot/turn_on() + . = ..() + src.icon_state = "cleanbot[src.on]" + src.updateUsrDialog() + +/obj/machinery/bot/cleanbot/turn_off() + ..() + 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.machine = src + interact(user) + +/obj/machinery/bot/cleanbot/proc/interact(mob/user as mob) var/dat dat += text({" Automatic Station Cleaner v1.0

@@ -73,13 +94,10 @@ text("[src.oddbutton ? "Yes" : "No" src.add_fingerprint(usr) switch(href_list["operation"]) if("start") - src.on = !src.on - src.target = null - src.oldtarget = null - src.oldloc = null - src.icon_state = "cleanbot[src.on]" - src.path = new() - src.updateUsrDialog() + if (src.on) + turn_off() + else + turn_on() if("blood") src.blood =!src.blood src.get_targets() @@ -94,12 +112,10 @@ text("[src.oddbutton ? "Yes" : "No" src.updateUsrDialog() /obj/machinery/bot/cleanbot/attack_ai() - src.on = !src.on - src.target = null - src.oldtarget = null - src.oldloc = null - src.icon_state = "cleanbot[src.on]" - src.path = new() + if (src.on) + turn_off() + else + turn_on() /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)) @@ -108,7 +124,7 @@ text("[src.oddbutton ? "Yes" : "No" user << "You [ src.locked ? "lock" : "unlock"] the [src] behaviour controls." else user << "\red This [src] doesn't seem to accept your authority." - if (istype(W, /obj/item/weapon/screwdriver)) + else if (istype(W, /obj/item/weapon/screwdriver)) if(!src.locked) src.panelopen = !src.panelopen user << "You [ src.panelopen ? "open" : "close"] the hidden panel on [src]." @@ -118,6 +134,8 @@ text("[src.oddbutton ? "Yes" : "No" src.screwloose = 1 src.panelopen = 0 src.locked = 1*/ + else + return ..() /obj/machinery/bot/cleanbot/process() set background = 1 @@ -211,6 +229,24 @@ text("[src.oddbutton ? "Yes" : "No" src.anchored = 0 src.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/prox_sensor(Tsec) + + if (prob(50)) + new /obj/item/robot_parts/l_arm(Tsec) + + var/datum/effects/system/spark_spread/s = new /datum/effects/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)) var/obj/machinery/bot/cleanbot/A = new /obj/machinery/bot/cleanbot diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm index 62cda87a4df..ed8b745127d 100644 --- a/code/game/machinery/bots/ed209bot.dm +++ b/code/game/machinery/bots/ed209bot.dm @@ -8,13 +8,16 @@ anchored = 0 // weight = 1.0E7 req_access = list(access_security) + health = 100 + maxhealth = 100 + fire_dam_coeff = 0.7 + brute_dam_coeff = 0.5 var/lastfired = 0 var/shot_delay = 3 //.3 seconds between shots - var/lasers = 0 + //var/lasers = 0 - var/on = 1 var/locked = 1 //Behavior Controls lock var/mob/living/carbon/target var/oldtarget_name @@ -23,7 +26,6 @@ var/last_found //There's a delay var/frustration = 0 var/emagged = 0 //Emagged Secbots view everyone as a criminal - var/health = 100 var/idcheck = 1 //If false, all station IDs are authorized for weapons. var/check_records = 1 //Does it check security records? var/arrest_type = 0 //If true, don't handcuff @@ -68,44 +70,50 @@ var/created_name = "ED-209 Security Robot" //To preserve the name if it's a unique securitron I guess +/obj/machinery/bot/ed209/New() + ..() + src.icon_state = "ed209[src.on]" + spawn(3) + src.botcard = new /obj/item/weapon/card/id(src) + src.botcard.access = get_access("Detective") + src.cam = new /obj/machinery/camera(src) + src.cam.c_tag = src.name + src.cam.network = "SS13" + if(radio_controller) + radio_controller.add_object(src, control_freq, filter = RADIO_SECBOT) + radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) -/obj/machinery/bot/ed209 - New() - ..() - src.icon_state = "ed209[src.on]" - spawn(3) - src.botcard = new /obj/item/weapon/card/id(src) - src.botcard.access = get_access("Detective") - src.cam = new /obj/machinery/camera(src) - src.cam.c_tag = src.name - src.cam.network = "SS13" - if(radio_controller) - radio_controller.add_object(src, control_freq, filter = RADIO_SECBOT) - radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) +/obj/machinery/bot/ed209/turn_on() + . = ..() + src.icon_state = "ed209[src.on]" + src.mode = SECBOT_IDLE + src.updateUsrDialog() - examine() - set src in view() - ..() +/obj/machinery/bot/ed209/turn_off() + ..() + src.target = null + src.oldtarget_name = null + src.anchored = 0 + src.mode = SECBOT_IDLE + walk_to(src,0) + src.icon_state = "ed209[src.on]" + src.updateUsrDialog() - if (src.health < 25) - if (src.health > 15) - usr << text("\red [src]'s parts look loose.") - else - usr << text("\red [src]'s parts look very loose!") +/obj/machinery/bot/ed209/attack_hand(mob/user as mob) + . = ..() + if (.) return + var/dat - attack_hand(user as mob) - var/dat - - dat += text({" + dat += text({" Automatic Security Unit v2.5

Status: []
Behaviour controls are [src.locked ? "locked" : "unlocked"]"}, "
[src.on ? "On" : "Off"]" ) - if(!src.locked) - dat += text({"
+ if(!src.locked) + dat += text({"
Check for Weapon Authorization: []
Check Security Records: []
Operating Mode: []
@@ -117,650 +125,669 @@ Auto Patrol: []"}, "[auto_patrol ? "On" : "Off"]" ) - user << browse("Securitron v2.5 controls[dat]", "window=autosec") - onclose(user, "autosec") + user << browse("Securitron v2.5 controls[dat]", "window=autosec") + onclose(user, "autosec") + return + +/obj/machinery/bot/ed209/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() return - Topic(href, href_list) - usr.machine = src - src.add_fingerprint(usr) - if ((href_list["power"]) && (src.allowed(usr))) - src.on = !src.on - src.target = null - src.oldtarget_name = null - src.anchored = 0 - src.mode = SECBOT_IDLE - walk_to(src,0) - src.icon_state = "ed209[src.on]" + switch(href_list["operation"]) + if ("idcheck") + src.idcheck = !src.idcheck src.updateUsrDialog() - - switch(href_list["operation"]) - if ("idcheck") - src.idcheck = !src.idcheck - src.updateUsrDialog() - if ("ignorerec") - src.check_records = !src.check_records - src.updateUsrDialog() - if ("switchmode") - src.arrest_type = !src.arrest_type - src.updateUsrDialog() - if("patrol") - auto_patrol = !auto_patrol - mode = SECBOT_IDLE - updateUsrDialog() - - attack_ai(mob/user as mob) - src.on = !src.on - src.target = null - src.oldtarget_name = null - mode = SECBOT_IDLE - src.anchored = 0 - src.icon_state = "ed209[src.on]" - walk_to(src,0) - - attackby(obj/item/weapon/W as obj, mob/user as mob) - if ((istype(W, /obj/item/weapon/card/emag)) && (!src.emagged)) - user << "\red 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 - src.oldtarget_name = user.name - src.last_found = world.time - src.anchored = 0 - src.emagged = 1 - src.on = 1 - src.icon_state = "ed209[src.on]" + if ("ignorerec") + src.check_records = !src.check_records + src.updateUsrDialog() + if ("switchmode") + src.arrest_type = !src.arrest_type + src.updateUsrDialog() + if("patrol") + auto_patrol = !auto_patrol mode = SECBOT_IDLE - else 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."]" - else - user << "\red Access denied." + updateUsrDialog() - else if (istype(W, /obj/item/weapon/screwdriver)) - if (src.health < 25) - src.health = 25 - for(var/mob/O in viewers(src, null)) - O << "\red [user] repairs [src]!" +/obj/machinery/bot/ed209/attack_ai(mob/user as mob) + if (src.on) + turn_off() + else + turn_on() + +/obj/machinery/bot/ed209/attackby(obj/item/weapon/W as obj, mob/user as mob) + if ((istype(W, /obj/item/weapon/card/emag)) && (!src.emagged)) + user << "\red 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 + src.oldtarget_name = user.name + src.last_found = world.time + src.anchored = 0 + src.emagged = 1 + src.on = 1 + src.icon_state = "ed209[src.on]" + mode = SECBOT_IDLE + else 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."]" else - switch(W.damtype) - if("fire") - src.health -= W.force * 0.75 - if("brute") - src.health -= W.force * 0.5 - else - if (src.health <= 0) - src.explode() - else if ((W.force) && (!src.target)) - src.target = user - src.mode = SECBOT_HUNT - ..() + user << "\red Access denied." + else + ..() + if (!istype(W, /obj/item/weapon/screwdriver) && (W.force) && (!src.target)) + src.target = user + src.mode = SECBOT_HUNT +/obj/machinery/bot/ed209/process() + set background = 1 + if (!src.on) + return + var/list/targets = list() + for (var/mob/living/carbon/C in view(12,src)) //Let's find us a target + var/threatlevel = 0 + if ((C.stat) || (C.lying)) + continue + if (istype(C, /mob/living/carbon/human)) + threatlevel = src.assess_perp(C) + else if ((istype(C, /mob/living/carbon/monkey)) && (C.client) && (ticker.mode.name == "monkey")) + threatlevel = 4 + //src.speak(C.real_name + text(": threat: []", threatlevel)) + if (threatlevel < 4 ) + continue - process() - set background = 1 + var/dst = get_dist(src, C) + if ( dst <= 1 || dst > 12) + continue - if (!src.on) - return - var/list/targets = list() - for (var/mob/living/carbon/C in view(12,src)) //Let's find us a target - var/threatlevel = 0 - if ((C.stat) || (C.lying)) - continue - if (istype(C, /mob/living/carbon/human)) - threatlevel = src.assess_perp(C) - else if ((istype(C, /mob/living/carbon/monkey)) && (C.client) && (ticker.mode.name == "monkey")) - threatlevel = 4 - //src.speak(C.real_name + text(": threat: []", threatlevel)) - if (threatlevel < 4 ) - continue + targets += C + if (targets.len>0) + var/mob/t = pick(targets) + if (istype(t, /mob/living)) + if (t.stat!=2) + //src.speak("selected target: " + t.real_name) + src.shootAt(t) + switch(mode) - var/dst = get_dist(src, C) - if ( dst <= 1 || dst > 12) - continue + 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 - targets += C - if (targets.len>0) - var/mob/t = pick(targets) - if (istype(t, /mob/living)) - if (t.stat!=2) - //src.speak("selected target: " + t.real_name) - src.shootAt(t) - switch(mode) + if(SECBOT_HUNT) // hunting for perp - if(SECBOT_IDLE) // idle + // 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) - 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 - if (get_dist(src, src.target) <= 1) // if right next to perp - playsound(src.loc, 'Egloves.ogg', 50, 1, -1) - src.icon_state = "ed209-c" - spawn(2) - src.icon_state = "ed209[src.on]" - var/mob/living/carbon/M = src.target - var/maxstuns = 4 - if (istype(M, /mob/living/carbon/human)) - if (M.weakened < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) - M.weakened = 10 - if (M.stuttering < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) - M.stuttering = 10 - if (M.stunned < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) - M.stunned = 10 - else + if (target) // make sure target exists + if (get_dist(src, src.target) <= 1) // if right next to perp + playsound(src.loc, 'Egloves.ogg', 50, 1, -1) + src.icon_state = "ed209-c" + spawn(2) + src.icon_state = "ed209[src.on]" + var/mob/living/carbon/M = src.target + var/maxstuns = 4 + if (istype(M, /mob/living/carbon/human)) + if (M.weakened < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) M.weakened = 10 + if (M.stuttering < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) M.stuttering = 10 + if (M.stunned < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) M.stunned = 10 - maxstuns-- - if (maxstuns <= 0) - target = null - for(var/mob/O in viewers(src, null)) - O.show_message("\red [src.target] has been stunned by [src]!", 1, "\red You hear someone fall", 2) - - mode = SECBOT_PREP_ARREST - src.anchored = 1 - src.target_lastloc = M.loc - return - - else // not next to perp - var/turf/olddist = get_dist(src, src.target) - walk_to(src, src.target,1,4) - if ((get_dist(src, src.target)) >= (olddist)) - src.frustration++ - else - src.frustration = 0 - - if(SECBOT_PREP_ARREST) // preparing to arrest target - - // 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 (!src.target.handcuffed && !src.arrest_type) - playsound(src.loc, 'handcuffs.ogg', 30, 1, -2) - mode = SECBOT_ARREST + else + M.weakened = 10 + M.stuttering = 10 + M.stunned = 10 + maxstuns-- + if (maxstuns <= 0) + target = null for(var/mob/O in viewers(src, null)) - O.show_message("\red [src] is trying to put handcuffs on [src.target]!", 1) + O.show_message("\red [src.target] has been stunned by [src]!", 1, "\red You hear someone fall", 2) - spawn(60) - if (get_dist(src, src.target) <= 1) - if (src.target.handcuffed) - return - - if(istype(src.target,/mob/living/carbon)) - src.target.handcuffed = new /obj/item/weapon/handcuffs(src.target) - - mode = SECBOT_IDLE - src.target = null - src.anchored = 0 - src.last_found = world.time - src.frustration = 0 - - // playsound(src.loc, pick('bgod.ogg', 'biamthelaw.ogg', 'bsecureday.ogg', 'bradio.ogg', 'binsult.ogg', 'bcreep.ogg'), 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) - - if(SECBOT_ARREST) // arresting - if (src.target.handcuffed) - src.anchored = 0 - mode = SECBOT_IDLE + mode = SECBOT_PREP_ARREST + src.anchored = 1 + src.target_lastloc = M.loc return + else // not next to perp + var/turf/olddist = get_dist(src, src.target) + walk_to(src, src.target,1,4) + if ((get_dist(src, src.target)) >= (olddist)) + src.frustration++ + else + src.frustration = 0 - if(SECBOT_START_PATROL) // start a patrol + if(SECBOT_PREP_ARREST) // preparing to arrest target + 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(path.len > 0 && patrol_target) // have a valid path, so just resume - mode = SECBOT_PATROL - return + if (!src.target.handcuffed && !src.arrest_type) + playsound(src.loc, 'handcuffs.ogg', 30, 1, -2) + mode = SECBOT_ARREST + for(var/mob/O in viewers(src, null)) + O.show_message("\red [src] is trying to put handcuffs on [src.target]!", 1) - 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 + spawn(60) + if (get_dist(src, src.target) <= 1) + if (src.target.handcuffed) return - mode = SECBOT_PATROL + if(istype(src.target,/mob/living/carbon)) + src.target.handcuffed = new /obj/item/weapon/handcuffs(src.target) - else // no patrol target, so need a new one - find_patrol_target() - speak("Engaging patrol mode.") + mode = SECBOT_IDLE + src.target = null + src.anchored = 0 + src.last_found = world.time + src.frustration = 0 + // playsound(src.loc, pick('bgod.ogg', 'biamthelaw.ogg', 'bsecureday.ogg', 'bradio.ogg', 'binsult.ogg', 'bcreep.ogg'), 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) - 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 - - 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 + if(SECBOT_ARREST) // arresting + if (!target || src.target.handcuffed) + src.anchored = 0 + mode = SECBOT_IDLE return - if(istype( next, /turf/simulated)) + if(SECBOT_START_PATROL) // start a patrol - var/moved = step_towards(src, next) // attempt to move - if(moved) // successful move - blockcount = 0 - path -= loc - - look_for_perp() - 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 + 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/ed209/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() + 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 - 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 - 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 + else // not a valid turf + mode = SECBOT_IDLE return - if(next_destination) - set_destination(next_destination) + else // no path, so calculate new one + mode = SECBOT_START_PATROL + + +// finds a new patrol target +/obj/machinery/bot/ed209/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/ed209/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 - find_nearest_beacon() - return - - - // finds the nearest beacon to self - // signals all beacons matching the patrol code - 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() - - - 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 - 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 - - receive_signal(datum/signal/signal) - - 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") + auto_patrol = 0 + mode = SECBOT_IDLE + speak("Disengaging patrol mode.") 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 +/obj/machinery/bot/ed209/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/ed209/proc/set_destination(var/new_dest) + new_destination = new_dest + post_signal(beacon_freq, "findbeacon", "patrol") + awaiting_beacon = 1 - // 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 +// receive a radio signal +// used for beacon reception - // if looking for nearest beacon - else if(new_destination == "__nearest__") - var/dist = get_dist(src,signal.source.loc) - if(nearest_beacon) +/obj/machinery/bot/ed209/receive_signal(datum/signal/signal) - // note we ignore the beacon we are located at - if(dist>1 && dist 1) - nearest_beacon = recv - nearest_beacon_loc = signal.source.loc + if(!on) return + /* + world << "rec signal: [signal.source]" + for(var/x in signal.data) + world << "* [x] = [signal.data[x]]" + */ - // send a radio signal with a single data key/value pair - proc/post_signal(var/freq, var/key, var/value) - post_signal_multiple(freq, list("[key]" = value) ) + var/recv = signal.data["command"] + // process all-bot input + if(recv=="bot_status") + send_status() - // send a radio signal with multiple data key/values - proc/post_signal_multiple(var/freq, var/list/keyval) + // 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 - var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) + if("go") + mode = SECBOT_IDLE + auto_patrol = 1 + return - if(!frequency) return + if("summon") + patrol_target = signal.data["target"] + next_destination = destination + destination = null + awaiting_beacon = 0 + mode = SECBOT_SUMMON + calc_path() + speak("Responding.") - var/datum/signal/signal = new() - signal.source = src - signal.transmission_method = 1 - //for(var/key in keyval) - // signal.data[key] = keyval[key] - //world << "sent [key],[keyval[key]] on [freq]" - signal.data = keyval - 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) + return - // signals bot status etc. to controller - proc/send_status() - var/list/kv = list( - "type" = "secbot", - "name" = name, - "loca" = loc.loc, // area - "mode" = mode, - ) - post_signal_multiple(control_freq, kv) + + + // 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/ed209/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/ed209/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] + //world << "sent [key],[keyval[key]] on [freq]" + signal.data = keyval + 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/ed209/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 - 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) - src.path = reverselist(src.path) +/obj/machinery/bot/ed209/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) + src.path = reverselist(src.path) // look for a criminal in view of the bot - proc/look_for_perp() - src.anchored = 0 - src.threatlevel = 0 - for (var/mob/living/carbon/C in view(12,src)) //Let's find us a criminal - if ((C.stat) || (C.handcuffed)) - continue +/obj/machinery/bot/ed209/proc/look_for_perp() + src.anchored = 0 + src.threatlevel = 0 + for (var/mob/living/carbon/C in view(12,src)) //Let's find us a criminal + if ((C.stat) || (C.handcuffed)) + continue - if ((C.name == src.oldtarget_name) && (world.time < src.last_found + 100)) - continue + 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) - else if ((istype(C, /mob/living/carbon/monkey)) && (C.client) && (ticker.mode.name == "monkey")) - src.threatlevel = 4 + if (istype(C, /mob/living/carbon/human)) + src.threatlevel = src.assess_perp(C) + else if ((istype(C, /mob/living/carbon/monkey)) && (C.client) && (ticker.mode.name == "monkey")) + src.threatlevel = 4 - if (!src.threatlevel) - continue + if (!src.threatlevel) + continue - else if (src.threatlevel >= 4) - src.target = C - src.oldtarget_name = C.name - src.speak("Level [src.threatlevel] infraction alert!") - playsound(src.loc, pick('ed209_20sec.ogg', 'ed209_15sec.ogg'), 50, 0) - src.visible_message("[src] points at [C.name]!") - mode = SECBOT_HUNT - spawn(0) - process() // ensure bot quickly responds to a perp - break - else - continue + else if (src.threatlevel >= 4) + src.target = C + src.oldtarget_name = C.name + src.speak("Level [src.threatlevel] infraction alert!") + playsound(src.loc, pick('ed209_20sec.ogg', 'ed209_15sec.ogg'), 50, 0) + src.visible_message("[src] points at [C.name]!") + mode = SECBOT_HUNT + spawn(0) + process() // ensure bot quickly responds to a perp + break + else + continue //If the security records say to arrest them, arrest them //Or if they have weapons and aren't security, arrest them. - proc/assess_perp(mob/living/carbon/human/perp as mob) - var/threatcount = 0 +/obj/machinery/bot/ed209/proc/assess_perp(mob/living/carbon/human/perp as mob) + var/threatcount = 0 - if(src.emagged) return 10 //Everyone is a criminal! + if(src.emagged) return 10 //Everyone is a criminal! - if((src.idcheck) || (isnull(perp:wear_id)) || (istype(perp:wear_id, /obj/item/weapon/card/id/syndicate))) - if(src.allowed(perp)) //Corrupt cops cannot exist beep boop - return 0 + if((src.idcheck) || (isnull(perp:wear_id)) || (istype(perp:wear_id, /obj/item/weapon/card/id/syndicate))) + if(src.allowed(perp)) //Corrupt cops cannot exist beep boop + return 0 - if((istype(perp.l_hand, /obj/item/weapon/gun) && !istype(perp.l_hand, /obj/item/weapon/gun/shotgun)) || istype(perp.l_hand, /obj/item/weapon/baton)) - threatcount += 4 + if((istype(perp.l_hand, /obj/item/weapon/gun) && !istype(perp.l_hand, /obj/item/weapon/gun/shotgun)) || istype(perp.l_hand, /obj/item/weapon/baton)) + threatcount += 4 - if((istype(perp.r_hand, /obj/item/weapon/gun) && !istype(perp.r_hand, /obj/item/weapon/gun/shotgun)) || istype(perp.r_hand, /obj/item/weapon/baton)) - threatcount += 4 + if((istype(perp.r_hand, /obj/item/weapon/gun) && !istype(perp.r_hand, /obj/item/weapon/gun/shotgun)) || istype(perp.r_hand, /obj/item/weapon/baton)) + threatcount += 4 - if(istype(perp:belt, /obj/item/weapon/gun) || istype(perp:belt, /obj/item/weapon/baton)) - threatcount += 2 + if(istype(perp:belt, /obj/item/weapon/gun) || istype(perp:belt, /obj/item/weapon/baton)) + threatcount += 2 - if(istype(perp:wear_suit, /obj/item/clothing/suit/wizrobe)) - threatcount += 2 + if(istype(perp:wear_suit, /obj/item/clothing/suit/wizrobe)) + threatcount += 2 - if(perp.mutantrace && perp.mutantrace != "none") - threatcount += 2 + if(perp.mutantrace && perp.mutantrace != "none") + threatcount += 2 - //Agent cards lower threatlevel when normal idchecking is off. - if((istype(perp:wear_id, /obj/item/weapon/card/id/syndicate)) && src.idcheck) - threatcount -= 2 +//Agent cards lower threatlevel when normal idchecking is off. + if((istype(perp:wear_id, /obj/item/weapon/card/id/syndicate)) && src.idcheck) + threatcount -= 2 - if (src.check_records) - for (var/datum/data/record/E in data_core.general) - var/perpname = perp.name - if (perp:wear_id) - var/obj/item/weapon/card/id/id = perp:wear_id - if(istype(perp:wear_id, /obj/item/device/pda)) - var/obj/item/device/pda/pda = perp:wear_id - id = pda.id - if (id) - perpname = id.registered - else - var/obj/item/device/pda/pda = perp:wear_id - perpname = pda.owner - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.security) - if ((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "*Arrest*")) - threatcount = 4 - break + if (src.check_records) + for (var/datum/data/record/E in data_core.general) + var/perpname = perp.name + if (perp:wear_id) + var/obj/item/weapon/card/id/id = perp:wear_id + if(istype(perp:wear_id, /obj/item/device/pda)) + var/obj/item/device/pda/pda = perp:wear_id + id = pda.id + if (id) + perpname = id.registered + else + var/obj/item/device/pda/pda = perp:wear_id + perpname = pda.owner + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.security) + if ((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "*Arrest*")) + threatcount = 4 + break - return threatcount + return threatcount - 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 - else if ((istype(M, /mob/living/)) && (!src.anchored)) - src.loc = M:loc +/obj/machinery/bot/ed209/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 + else if ((istype(M, /mob/living/)) && (!src.anchored)) + src.loc = M:loc + src.frustration = 0 - return + return + return + +/obj/machinery/bot/ed209/Bumped(M as mob|obj) + spawn(0) + var/turf/T = get_turf(src) + M:loc = T + +/obj/machinery/bot/ed209/proc/speak(var/message) + for(var/mob/O in hearers(src, null)) + O << "[src] beeps, \"[message]\"" + return + +/obj/machinery/bot/ed209/explode() + walk_to(src,0) + src.visible_message("\red [src] blows apart!", 1) + var/turf/Tsec = get_turf(src) + + var/obj/item/weapon/ed209_assembly/Sa = new /obj/item/weapon/ed209_assembly(Tsec) + Sa.build_step = 1 + Sa.overlays += image('aibots.dmi', "hs_hole") + Sa.created_name = src.name + new /obj/item/device/prox_sensor(Tsec) + + var/obj/item/weapon/baton/B = new /obj/item/weapon/baton(Tsec) + B.charges = 0 + + if (prob(50)) + new /obj/item/robot_parts/l_arm(Tsec) + + var/datum/effects/system/spark_spread/s = new /datum/effects/system/spark_spread + s.set_up(3, 1, src) + s.start() + + new /obj/decal/cleanable/oil(src.loc) + del(src) + + +/obj/machinery/bot/ed209/proc/shootAt(var/mob/target) + if(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 - Bumped(M as mob|obj) - spawn(0) - var/turf/T = get_turf(src) - M:loc = T + //if(lastfired && world.time - lastfired < 100) + // playsound(src.loc, 'ed209_shoot.ogg', 50, 0) - bullet_act(flag, A as obj) - switch(flag) - if (PROJECTILE_BULLET) - src.health -= 20 - //if (PROJECTILE_WEAKBULLET || PROJECTILE_BEANBAG) //Detective's revolver fires marshmallows - // src.health -= 2 - if (PROJECTILE_LASER) - src.health -= 10 - if (PROJECTILE_PULSE) - src.health -=40 - if (src.health <= 0) - src.explode() + var/obj/beam/a_laser/A + if (src.emagged) + A = new /obj/beam/a_laser( loc ) + else + A = new /obj/bullet/electrode( loc ) - proc/speak(var/message) - for(var/mob/O in hearers(src, null)) - O << "[src] beeps, \"[message]\"" + if (!( istype(U, /turf) )) + //A = null + del(A) return - - //Generally we want to explode() instead of just deleting the securitron. - ex_act(severity) - switch(severity) - if(1.0) - src.explode() - return - if(2.0) - src.health -= 15 - if (src.health <= 0) - src.explode() - return + A.current = U + A.yo = U.y - T.y + A.xo = U.x - T.x + spawn( 0 ) + A.process() return + return - meteorhit() - src.explode() - return +/obj/machinery/bot/ed209/attack_alien(var/mob/living/carbon/alien/user as mob) + ..() + if (!isalien(target)) + src.target = user + src.mode = SECBOT_HUNT - blob_act() - if(prob(50)) - src.explode() - return - - proc/explode() - - walk_to(src,0) - for(var/mob/O in hearers(src, null)) - O.show_message("\red [src] blows apart!", 1) - var/turf/Tsec = get_turf(src) - - var/obj/item/weapon/ed209_assembly/Sa = new /obj/item/weapon/ed209_assembly(Tsec) - Sa.build_step = 1 - Sa.overlays += image('aibots.dmi', "hs_hole") - Sa.created_name = src.name - new /obj/item/device/prox_sensor(Tsec) - - var/obj/item/weapon/baton/B = new /obj/item/weapon/baton(Tsec) - B.charges = 0 +/obj/machinery/bot/ed209/emp_act(severity) + if (severity > 2) + ..(severity-1) + else if (severity==2 && prob(70)) + ..(severity-1) + else + var/obj/overlay/pulse2 = new/obj/overlay ( src.loc ) + pulse2.icon = 'effects.dmi' + pulse2.icon_state = "empdisable" + pulse2.name = "emp sparks" + pulse2.anchored = 1 + pulse2.dir = pick(cardinal) + spawn(10) + del(pulse2) + 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 (prob(50)) - new /obj/item/robot_parts/l_arm(Tsec) - - var/datum/effects/system/spark_spread/s = new /datum/effects/system/spark_spread - s.set_up(3, 1, src) - s.start() - del(src) - - - - - + var/mob/toshoot = pick(targets) + if (toshoot) + targets-=toshoot + if (prob(50) && !emagged) + emagged = 1 + shootAt(toshoot) + emagged = 0 + else + shootAt(toshoot) + if (/*prob(50)*/ 1 ) + var/mob/toarrest = pick(targets) + if (toarrest) + //targets-=toarrest + src.target = toarrest + src.mode = SECBOT_HUNT /obj/item/weapon/ed209_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob) - - if (((istype(W, /obj/item/robot_parts/l_leg)) || (istype(W, /obj/item/robot_parts/r_leg))) && (src.build_step == 0 || src.build_step == 1)) src.build_step++ user << "You add the robot leg to [src]!" @@ -845,36 +872,3 @@ Auto Patrol: []"}, src.created_name = t -/obj/machinery/bot/ed209/proc/shootAt(var/mob/target) - if(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(lastfired && world.time - lastfired < 100) - // playsound(src.loc, 'ed209_shoot.ogg', 50, 0) - - var/obj/beam/a_laser/A - if (src.emagged) - A = new /obj/beam/a_laser( loc ) - else - A = new /obj/bullet/electrode( loc ) - - if (!( istype(U, /turf) )) - //A = null - del(A) - return - A.current = U - A.yo = U.y - T.y - A.xo = U.x - T.x - spawn( 0 ) - A.process() - return - return \ No newline at end of file diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm index ae279596d10..ea1711ca088 100644 --- a/code/game/machinery/bots/floorbot.dm +++ b/code/game/machinery/bots/floorbot.dm @@ -34,9 +34,10 @@ layer = 5.0 density = 0 anchored = 0 + health = 25 + maxhealth = 25 //weight = 1.0E7 var/amount = 10 - var/on = 1 var/repairing = 0 var/improvefloors = 0 var/eattiles = 0 @@ -54,7 +55,28 @@ ..() src.updateicon() -/obj/machinery/bot/floorbot/attack_hand(user as mob) +/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.machine = src + interact(user) + +/obj/machinery/bot/floorbot/proc/interact(mob/user as mob) var/dat dat += "Automatic Station Floor Repairer v1.0

" dat += "Status: [src.on ? "On" : "Off"]
" @@ -86,13 +108,15 @@ src.amount += loaded user << "\red You load [loaded] tiles into the floorbot. He now contains [src.amount] tiles!" src.updateicon() - if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) + else if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) if(src.allowed(usr)) src.locked = !src.locked user << "You [src.locked ? "lock" : "unlock"] the [src] behaviour controls." else user << "The [src] doesn't seem to accept your authority." src.updateUsrDialog() + else + ..() /obj/machinery/bot/floorbot/Topic(href, href_list) @@ -102,13 +126,10 @@ src.add_fingerprint(usr) switch(href_list["operation"]) if("start") - src.on = !src.on - src.target = null - src.oldtarget = null - src.oldloc = null - src.updateicon() - src.path = new() - src.updateUsrDialog() + if (src.on) + turn_off() + else + turn_on() if("improve") src.improvefloors = !src.improvefloors src.updateUsrDialog() @@ -136,12 +157,10 @@ /obj/machinery/bot/floorbot/attack_ai() - src.on = !src.on - src.target = null - src.oldtarget = null - src.oldloc = null - src.updateicon() - src.path = new() + if (src.on) + turn_off() + else + turn_on() /obj/machinery/bot/floorbot/process() set background = 1 @@ -327,6 +346,26 @@ 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) + + new /obj/item/weapon/storage/toolbox/mechanical(Tsec) + + new /obj/item/device/prox_sensor(Tsec) + + if (prob(50)) + new /obj/item/robot_parts/l_arm(Tsec) + + if (amount) + new /obj/item/stack/tile(Tsec) // only one tile, yes + + var/datum/effects/system/spark_spread/s = new /datum/effects/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/T, mob/user as mob) diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm index 07b38a56ed1..3ff800b2d33 100644 --- a/code/game/machinery/bots/medbot.dm +++ b/code/game/machinery/bots/medbot.dm @@ -11,9 +11,9 @@ layer = 5.0 density = 1 anchored = 0 + health = 20 + maxhealth = 20 req_access =list(access_medical) - var/on = 1 - var/health = 20 var/stunned = 0 //It can be stunned by tasers. Delicate circuits. var/locked = 1 var/emagged = 0 @@ -79,16 +79,22 @@ src.cam.c_tag = src.name src.cam.network = "SS13" -/obj/machinery/bot/medbot/examine() - set src in view() - ..() +/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() - if (src.health < 20) - if (src.health > 15) - usr << text("\red [src]'s parts look loose.") - else - usr << text("\red [src]'s parts look very loose!") - return /obj/machinery/bot/medbot/attack_ai(mob/user as mob) return toggle_power() @@ -97,6 +103,9 @@ return attack_hand(user) /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"]
" @@ -190,12 +199,6 @@ else user << "\red Access denied." - else if (istype(W, /obj/item/weapon/screwdriver)) - if (src.health < initial(src.health)) - src.health = initial(src.health) - for(var/mob/O in viewers(src, null)) - O << "\red [user] repairs [src]!" - else if (istype(W, /obj/item/weapon/reagent_containers/glass)) if(src.locked) user << "You cannot insert a beaker because the panel is locked!" @@ -212,17 +215,9 @@ return else - switch(W.damtype) - if("fire") - src.health -= W.force * 0.75 - if("brute") - src.health -= W.force * 0.5 - else - if (src.health <= 0) - src.explode() - else if (W.force) - step_to(src, (get_step_away(src,user))) ..() + if (health < maxhealth && !istype(W, /obj/item/weapon/screwdriver) && W.force) + step_to(src, (get_step_away(src,user))) /obj/machinery/bot/medbot/process() @@ -317,15 +312,10 @@ /obj/machinery/bot/medbot/proc/toggle_power() - src.on = !src.on - 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() + if (src.on) + turn_off() + else + turn_on() return /obj/machinery/bot/medbot/proc/assess_patient(mob/living/carbon/C as mob) @@ -453,43 +443,11 @@ return /obj/machinery/bot/medbot/bullet_act(flag, A as obj) - if (flag == PROJECTILE_BULLET) - src.health -= 18 + if (flag == PROJECTILE_TASER) + src.stunned = min(stunned+10,20) + ..() - else if (flag == PROJECTILE_TASER) - src.stunned += 10 - if(src.stunned > 20) - src.stunned = 20 - - else if (flag == PROJECTILE_LASER) - src.health -= 8 - - - if (src.health <= 0) - src.explode() - -/obj/machinery/bot/medbot/ex_act(severity) - switch(severity) - if(1.0) - src.explode() - return - if(2.0) - src.health -= 15 - if (src.health <= 0) - src.explode() - return - return - -/obj/machinery/bot/medbot/meteorhit() - src.explode() - return - -/obj/machinery/bot/medbot/blob_act() - if(prob(50)) - src.explode() - return - -/obj/machinery/bot/medbot/proc/explode() +/obj/machinery/bot/medbot/explode() src.on = 0 for(var/mob/O in hearers(src, null)) O.show_message("\red [src] blows apart!", 1) diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm index 890cac9d833..5461bd1bf10 100644 --- a/code/game/machinery/bots/mulebot.dm +++ b/code/game/machinery/bots/mulebot.dm @@ -11,7 +11,10 @@ density = 1 anchored = 1 animate_movement=1 - var/on = 1 + health = 150 //yeah, it's tougher than ed209 because it is a big metal box with wheels --rastaf0 + maxhealth = 150 + fire_dam_coeff = 0.7 + brute_dam_coeff = 0.5 var/locked = 1 var/atom/movable/load = null // the loaded crate (usually) @@ -69,853 +72,895 @@ var/bloodiness = 0 // count of bloodiness - New() - ..() - botcard = new(src) - botcard.access = get_access("Quartermaster") - cell = new(src) - cell.charge = 2000 - cell.maxcharge = 2000 - setup_wires() +/obj/machinery/bot/mulebot/New() + ..() + botcard = new(src) + botcard.access = get_access("Quartermaster") + cell = new(src) + cell.charge = 2000 + cell.maxcharge = 2000 + setup_wires() - spawn(5) // must wait for map loading to finish - if(radio_controller) - radio_controller.add_object(src, control_freq, filter = RADIO_MULEBOT) - radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) + spawn(5) // must wait for map loading to finish + if(radio_controller) + radio_controller.add_object(src, control_freq, filter = RADIO_MULEBOT) + radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) - var/count = 0 - for(var/obj/machinery/bot/mulebot/other in world) - count++ - if(!suffix) - suffix = "#[count]" - name = "Mulebot ([suffix])" + var/count = 0 + for(var/obj/machinery/bot/mulebot/other in world) + count++ + if(!suffix) + suffix = "#[count]" + name = "Mulebot ([suffix])" - verbs -= /atom/movable/verb/pull + verbs -= /atom/movable/verb/pull - // set up the wire colours in random order - // and the random wire display order - // needs 10 wire colours - proc/setup_wires() - var/list/colours = list("Red", "Green", "Blue", "Magenta", "Cyan", "Yellow", "Black", "White", "Orange", "Grey") - var/list/orders = list("0","1","2","3","4","5","6","7","8","9") - wire_text = list() - wire_order = list() - while(colours.len > 0) - var/colour = colours[ rand(1,colours.len) ] - wire_text += colour - colours -= colour +// set up the wire colours in random order +// and the random wire display order +// needs 10 wire colours +/obj/machinery/bot/mulebot/proc/setup_wires() + var/list/colours = list("Red", "Green", "Blue", "Magenta", "Cyan", "Yellow", "Black", "White", "Orange", "Grey") + var/list/orders = list("0","1","2","3","4","5","6","7","8","9") + wire_text = list() + wire_order = list() + while(colours.len > 0) + var/colour = colours[ rand(1,colours.len) ] + wire_text += colour + colours -= colour - var/order = orders[ rand(1,orders.len) ] - wire_order += text2num(order) - orders -= order + var/order = orders[ rand(1,orders.len) ] + wire_order += text2num(order) + orders -= order - // attack by item - // emag : lock/unlock, - // screwdriver: open/close hatch - // cell: insert it - // other: chance to knock rider off bot - attackby(var/obj/item/I, var/mob/user) - if(istype(I,/obj/item/weapon/card/emag)) - locked = !locked - user << "\blue You [locked ? "lock" : "unlock"] the mulebot's controls!" - flick("mulebot-emagged", src) - playsound(src.loc, 'sparks1.ogg', 100, 0) - else if(istype(I,/obj/item/weapon/cell) && open && !cell) - var/obj/item/weapon/cell/C = I - user.drop_item() - C.loc = src - cell = C - updateDialog() - else if(istype(I,/obj/item/weapon/screwdriver)) - if(locked) - user << "\blue The maintenance hatch cannot be opened or closed while the controls are locked." - return +// attack by item +// emag : lock/unlock, +// screwdriver: open/close hatch +// cell: insert it +// other: chance to knock rider off bot +/obj/machinery/bot/mulebot/attackby(var/obj/item/I, var/mob/user) + if(istype(I,/obj/item/weapon/card/emag)) + locked = !locked + user << "\blue You [locked ? "lock" : "unlock"] the mulebot's controls!" + flick("mulebot-emagged", src) + playsound(src.loc, 'sparks1.ogg', 100, 0) + else if(istype(I,/obj/item/weapon/cell) && open && !cell) + var/obj/item/weapon/cell/C = I + user.drop_item() + C.loc = src + cell = C + updateDialog() + else if(istype(I,/obj/item/weapon/screwdriver)) + if(locked) + user << "\blue The maintenance hatch cannot be opened or closed while the controls are locked." + return - open = !open - if(open) - src.visible_message("[user] opens the maintenance hatch of [src]", "\blue You open [src]'s maintenance hatch.") - on = 0 - icon_state="mulebot-hatch" - else - src.visible_message("[user] closes the maintenance hatch of [src]", "\blue You close [src]'s maintenance hatch.") - icon_state = "mulebot0" - - updateDialog() - else if(load && ismob(load)) // chance to knock off rider - if(prob(1+I.force * 2)) - unload(0) - user.visible_message("\red [user] knocks [load] off [src] with \the [I]!", "\red You knock [load] off [src] with \the [I]!") - else - user << "You hit [src] with \the [I] but to no effect." + open = !open + if(open) + src.visible_message("[user] opens the maintenance hatch of [src]", "\blue You open [src]'s maintenance hatch.") + on = 0 + icon_state="mulebot-hatch" else - ..() - return + src.visible_message("[user] closes the maintenance hatch of [src]", "\blue You close [src]'s maintenance hatch.") + icon_state = "mulebot0" - - ex_act(var/severity) - unload(0) - switch(severity) - if(1) - del(src) - if(2) - wires &= ~(1 << rand(0,9)) - wires &= ~(1 << rand(0,9)) - wires &= ~(1 << rand(0,9)) - if(3) - wires &= ~(1 << rand(0,9)) - - return - - bullet_act() - if(prob(50) && !isnull(load)) - load.bullet_act() + updateDialog() + else if (istype(I, /obj/item/weapon/wrench)) + if (src.health < maxhealth) + src.health = min(maxhealth, src.health+25) + user.visible_message( + "\red [user] repairs [src]!", + "\blue You repair [src]!" + ) + else + user << "\blue [src] does not need a repair!" + else if(load && ismob(load)) // chance to knock off rider + if(prob(1+I.force * 2)) unload(0) - if(prob(25)) - src.visible_message("Something shorts out inside [src]!") - var/index = 1<< (rand(0,9)) - if(wires & index) - wires &= ~index - else - wires |= index - - - attack_ai(var/mob/user) - interact(user, 1) - - attack_hand(var/mob/user) - interact(user, 0) - - proc/interact(var/mob/user, var/ai=0) - var/dat - dat += "Multiple Utility Load Effector Mk. III

" - dat += "ID: [suffix]
" - dat += "Power: [on ? "On" : "Off"]
" - - if(!open) - - dat += "Status: " - switch(mode) - if(0) - dat += "Ready" - if(1) - dat += "Loading/Unloading" - if(2) - dat += "Navigating to Delivery Location" - if(3) - dat += "Navigating to Home" - if(4) - dat += "Waiting for clear path" - if(5,6) - dat += "Calculating navigation path" - if(7) - dat += "Unable to locate destination" - - - dat += "
Current Load: [load ? load.name : "none"]
" - dat += "Destination: [!destination ? "none" : destination]
" - dat += "Power level: [cell ? cell.percent() : 0]%
" - - if(locked && !ai) - dat += "
Controls are locked (unlock)" - else - dat += "
Controls are unlocked (lock)

" - - dat += "Toggle Power
" - dat += "Stop
" - dat += "Proceed
" - dat += "Return to Home
" - dat += "Set Destination
" - dat += "Set Bot ID
" - dat += "Set Home
" - dat += "Toggle Auto Return Home ([auto_return ? "On":"Off"])
" - dat += "Toggle Auto Pickup Crate ([auto_pickup ? "On":"Off"])
" - - if(load) - dat += "Unload Now
" - dat += "
The maintenance hatch is closed.
" - + user.visible_message("\red [user] knocks [load] off [src] with \the [I]!", "\red You knock [load] off [src] with \the [I]!") else - if(!ai) - dat += "The maintenance hatch is open.

" - dat += "Power cell: " - if(cell) - dat += "Installed
" - else - dat += "Removed
" + user << "You hit [src] with \the [I] but to no effect." + else + ..() + return - dat += wires() - else - dat += "The bot is in maintenance mode and cannot be controlled.
" - user << browse("Mulebot [suffix ? "([suffix])" : ""][dat]", "window=mulebot;size=350x500") - onclose(user, "mulebot") +/obj/machinery/bot/mulebot/ex_act(var/severity) + unload(0) + switch(severity) + if(2) + wires &= ~(1 << rand(0,9)) + wires &= ~(1 << rand(0,9)) + wires &= ~(1 << rand(0,9)) + if(3) + wires &= ~(1 << rand(0,9)) + ..() + return + +/obj/machinery/bot/mulebot/bullet_act() + if(prob(50) && !isnull(load)) + load.bullet_act() + unload(0) + if(prob(25)) + src.visible_message("\red Something shorts out inside [src]!") + var/index = 1<< (rand(0,9)) + if(wires & index) + wires &= ~index + else + wires |= index + ..() + + +/obj/machinery/bot/mulebot/attack_ai(var/mob/user) + usr.machine = src + interact(user, 1) + +/obj/machinery/bot/mulebot/attack_hand(var/mob/user) + . = ..() + if (.) return + usr.machine = src + interact(user, 0) - // returns the wire panel text - proc/wires() - var/t = "" - for(var/i = 0 to 9) - var/index = 1<" + + if(!open) + + dat += "Status: " + switch(mode) + if(0) + dat += "Ready" + if(1) + dat += "Loading/Unloading" + if(2) + dat += "Navigating to Delivery Location" + if(3) + dat += "Navigating to Home" + if(4) + dat += "Waiting for clear path" + if(5,6) + dat += "Calculating navigation path" + if(7) + dat += "Unable to locate destination" + + + dat += "
Current Load: [load ? load.name : "none"]
" + dat += "Destination: [!destination ? "none" : destination]
" + dat += "Power level: [cell ? cell.percent() : 0]%
" + + if(locked && !ai) + dat += "
Controls are locked (unlock)" + else + dat += "
Controls are unlocked (lock)

" + + dat += "Toggle Power
" + dat += "Stop
" + dat += "Proceed
" + dat += "Return to Home
" + dat += "Set Destination
" + dat += "Set Bot ID
" + dat += "Set Home
" + dat += "Toggle Auto Return Home ([auto_return ? "On":"Off"])
" + dat += "Toggle Auto Pickup Crate ([auto_pickup ? "On":"Off"])
" + + if(load) + dat += "Unload Now
" + dat += "
The maintenance hatch is closed.
" + + else + if(!ai) + dat += "The maintenance hatch is open.

" + dat += "Power cell: " + if(cell) + dat += "Installed
" else - t += "(mend)
" + dat += "Removed
" - return t + dat += wires() + else + dat += "The bot is in maintenance mode and cannot be controlled.
" + + user << browse("Mulebot [suffix ? "([suffix])" : ""][dat]", "window=mulebot;size=350x500") + onclose(user, "mulebot") + return + +// returns the wire panel text +/obj/machinery/bot/mulebot/proc/wires() + var/t = "" + for(var/i = 0 to 9) + var/index = 1<=2) - mode = 0 - updateDialog() - - if("go") - if(mode == 0) - start() - updateDialog() - - if("home") - if(mode == 0 || mode == 2) - start_home() - updateDialog() - - if("destination") - refresh=0 - var/new_dest = input("Enter new destination tag", "Mulebot [suffix ? "([suffix])" : ""]", destination) as text|null - refresh=1 - if(new_dest) - set_destination(new_dest) - - - if("setid") - refresh=0 - var/new_id = input("Enter new bot ID", "Mulebot [suffix ? "([suffix])" : ""]", suffix) as text|null - refresh=1 - if(new_id) - suffix = new_id - name = "Mulebot ([suffix])" - updateDialog() - - if("sethome") - refresh=0 - var/new_home = input("Enter new home tag", "Mulebot [suffix ? "([suffix])" : ""]", home_destination) as text|null - refresh=1 - if(new_home) - home_destination = new_home - updateDialog() - - if("unload") - if(load && mode !=1) - if(loc == target) - unload(loaddir) - else - unload(0) - - if("autoret") - auto_return = !auto_return - - if("autopick") - auto_pickup = !auto_pickup - - if("close") - usr.machine = null - usr << browse(null,"window=mulebot") - - - if("wirecut") - if(istype(usr.equipped(), /obj/item/weapon/wirecutters)) - var/wirebit = text2num(href_list["wire"]) - wires &= ~wirebit - else - usr << "\blue You need wirecutters!" - if("wiremend") - if(istype(usr.equipped(), /obj/item/weapon/wirecutters)) - var/wirebit = text2num(href_list["wire"]) - wires |= wirebit - else - usr << "\blue You need wirecutters!" - - if("wirepulse") - if(istype(usr.equipped(), /obj/item/device/multitool)) - switch(href_list["wire"]) - if("1","2") - usr << "\blue \icon[src] The charge light flickers." - if("4") - usr << "\blue \icon[src] The external warning lights flash briefly." - if("8") - usr << "\blue \icon[src] The load platform clunks." - if("16", "32") - usr << "\blue \icon[src] The drive motor whines briefly." - else - usr << "\blue \icon[src] You hear a radio crackle." - else - usr << "\blue You need a multitool!" - - - - updateDialog() - //src.updateUsrDialog() - else - usr << browse(null, "window=mulebot") - usr.machine = null - return - - - - // returns true if the bot has power - proc/has_power() - return !open && cell && cell.charge>0 && (wires & wire_power1) && (wires & wire_power2) - - // mousedrop a crate to load the bot - // can load anything if emagged - - MouseDrop_T(var/atom/movable/C, mob/user) - - if(user.stat) - return - - if (!on || !istype(C)|| C.anchored || get_dist(user, src) > 1 || get_dist(src,C) > 1 ) - return - - if(load) - return - - load(C) - - - // called to load a crate - proc/load(var/atom/movable/C) - if((wires & wire_loadcheck) && !istype(C,/obj/crate)) - src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") - playsound(src.loc, 'buzz-sigh.ogg', 50, 0) - return // if not emagged, only allow crates to be loaded - - if(get_dist(C, src) > 1 || load || !on) - return - mode = 1 - - // if a create, close before loading - var/obj/crate/crate = C - if(istype(crate)) - crate.close() - - C.loc = src.loc - sleep(2) - C.loc = src - load = C - - C.pixel_y += 9 - if(C.layer < layer) - C.layer = layer + 0.1 - overlays += C - - if(ismob(C)) - var/mob/M = C - if(M.client) - M.client.perspective = EYE_PERSPECTIVE - M.client.eye = src - - mode = 0 - send_status() - - // called to unload the bot - // argument is optional direction to unload - // if zero, unload at bot's location - proc/unload(var/dirn = 0) - if(!load) - return - - mode = 1 - overlays = null - - load.loc = src.loc - load.pixel_y -= 9 - load.layer = initial(load.layer) - if(ismob(load)) - var/mob/M = load - if(M.client) - M.client.perspective = MOB_PERSPECTIVE - M.client.eye = src - - - if(dirn) - step(load, dirn) - - load = null - - // in case non-load items end up in contents, dump every else too - // this seems to happen sometimes due to race conditions - // with items dropping as mobs are loaded - - for(var/atom/movable/AM in src) - if(AM == cell || AM == botcard) continue - - AM.loc = src.loc - AM.layer = initial(AM.layer) - AM.pixel_y = initial(AM.pixel_y) - if(ismob(AM)) - var/mob/M = AM - if(M.client) - M.client.perspective = MOB_PERSPECTIVE - M.client.eye = src - mode = 0 - - - process() - if(!has_power()) - on = 0 - return - if(on) - var/speed = ((wires & wire_motor1) ? 1:0) + ((wires & wire_motor2) ? 2:0) - //world << "speed: [speed]" - switch(speed) - if(0) - // do nothing - if(1) - process_bot() - spawn(3) - process_bot() - sleep(3) - process_bot() - if(2) - process_bot() - spawn(5) - process_bot() - if(3) - process_bot() - - if(refresh) updateDialog() - - proc/process_bot() - //if(mode) world << "Mode: [mode]" - switch(mode) - if(0) // idle - icon_state = "mulebot0" - return - if(1) // loading/unloading - return - if(2,3,4) // navigating to deliver,home, or blocked - - if(loc == target) // reached target - at_target() - return - - else if(path.len > 0 && target) // valid path - - var/turf/next = path[1] - reached_target = 0 - if(next == loc) - path -= next - return - - - if(istype( next, /turf/simulated)) - //world << "at ([x],[y]) moving to ([next.x],[next.y])" - - - if(bloodiness) - var/obj/decal/cleanable/blood/tracks/B = new(loc) - var/newdir = get_dir(next, loc) - if(newdir == dir) - B.dir = newdir - else - newdir = newdir | dir - if(newdir == 3) - newdir = 1 - else if(newdir == 12) - newdir = 4 - B.dir = newdir - bloodiness-- - - - - var/moved = step_towards(src, next) // attempt to move - if(cell) cell.use(1) - if(moved) // successful move - //world << "Successful move." - blockcount = 0 - path -= loc - - - if(mode==4) - spawn(1) - send_status() - - if(destination == home_destination) - mode = 3 - else - mode = 2 - - else // failed to move - - //world << "Unable to move." - - - - blockcount++ - mode = 4 - if(blockcount == 3) - src.visible_message("[src] makes an annoyed buzzing sound", "You hear an electronic buzzing sound.") - playsound(src.loc, 'buzz-two.ogg', 50, 0) - - if(blockcount > 5) // attempt 5 times before recomputing - // find new path excluding blocked turf - src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") - playsound(src.loc, 'buzz-sigh.ogg', 50, 0) - - spawn(2) - calc_path(next) - if(path.len > 0) - src.visible_message("[src] makes a delighted ping!", "You hear a ping.") - playsound(src.loc, 'ping.ogg', 50, 0) - mode = 4 - mode =6 - return - return - else - src.visible_message("[src] makes an annoyed buzzing sound", "You hear an electronic buzzing sound.") - playsound(src.loc, 'buzz-two.ogg', 50, 0) - //world << "Bad turf." - mode = 5 - return - else - //world << "No path." - mode = 5 - return - - if(5) // calculate new path - //world << "Calc new path." - mode = 6 - spawn(0) - - calc_path() - - if(path.len > 0) - blockcount = 0 - mode = 4 - src.visible_message("[src] makes a delighted ping!", "You hear a ping.") - playsound(src.loc, 'ping.ogg', 50, 0) - - else - src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") - playsound(src.loc, 'buzz-sigh.ogg', 50, 0) - - mode = 7 - //if(6) - //world << "Pending path calc." - //if(7) - //world << "No dest / no route." - return - - - // calculates a path to the current destination - // given an optional turf to avoid - proc/calc_path(var/turf/avoid = null) - src.path = AStar(src.loc, src.target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 250, id=botcard, exclude=avoid) - src.path = reverselist(src.path) - - - // sets the current destination - // signals all beacons matching the delivery code - // beacons will return a signal giving their locations - proc/set_destination(var/new_dest) - spawn(0) - new_destination = new_dest - post_signal(beacon_freq, "findbeacon", "delivery") - updateDialog() - - // starts bot moving to current destination - proc/start() - if(destination == home_destination) - mode = 3 - else - mode = 2 - icon_state = "mulebot[(wires & wire_mobavoid) == wire_mobavoid]" - - // starts bot moving to home - // sends a beacon query to find - proc/start_home() - spawn(0) - set_destination(home_destination) - mode = 4 - icon_state = "mulebot[(wires & wire_mobavoid) == wire_mobavoid]" - - // called when bot reaches current target - proc/at_target() - if(!reached_target) - src.visible_message("[src] makes a chiming sound!", "You hear a chime.") - playsound(src.loc, 'chime.ogg', 50, 0) - reached_target = 1 - - if(load) // if loaded, unload at target - unload(loaddir) - else - // not loaded - if(auto_pickup) // find a crate - var/atom/movable/AM - if(!(wires & wire_loadcheck)) // if emagged, load first unanchored thing we find - for(var/atom/movable/A in get_step(loc, loaddir)) - if(!A.anchored) - AM = A - break - else // otherwise, look for crates only - AM = locate(/obj/crate) in get_step(loc,loaddir) - if(AM) - load(AM) - // whatever happened, check to see if we return home - - if(auto_return && destination != home_destination) - // auto return set and not at home already - start_home() - mode = 4 - else - mode = 0 // otherwise go idle - - send_status() // report status to anyone listening - - return - - // called when bot bumps into anything - Bump(var/atom/obs) - if(!(wires & wire_mobavoid)) //usually just bumps, but if avoidance disabled knock over mobs - var/mob/M = obs - if(ismob(M)) - if(istype(M,/mob/living/silicon/robot)) - src.visible_message("\red [src] bumps into [M]!") - else - src.visible_message("\red [src] knocks over [M]!") - M.pulling = null - M.stunned = 8 - M.weakened = 5 - M.lying = 1 - ..() - - alter_health() - return get_turf(src) - - - // called from mob/living/carbon/human/HasEntered() - // when mulebot is in the same loc - proc/RunOver(var/mob/living/carbon/human/H) - src.visible_message("\red [src] drives over [H]!") - playsound(src.loc, 'splat.ogg', 50, 1) - - /* - if(ismob(load)) - var/mob/M = load - if(M.reagents.has_reagent("beer")) - M.unlock_medal("DUI", 1) - if(M.reagents.has_reagent("space_drugs") && istype(H) && H.wear_id.assignment == "Security Officer") - M.unlock_medel("Ridin' Dirty",1) - */ - - var/damage = rand(5,15) - - H.TakeDamage("head", 2*damage, 0) - H.TakeDamage("chest",2*damage, 0) - H.TakeDamage("l_leg",0.5*damage, 0) - H.TakeDamage("r_leg",0.5*damage, 0) - H.TakeDamage("l_arm",0.5*damage, 0) - H.TakeDamage("r_arm",0.5*damage, 0) - - var/obj/decal/cleanable/blood/B = new(src.loc) - B.blood_DNA = H.dna.unique_enzymes - B.blood_type = H.b_type - - bloodiness += 4 - - // player on mulebot attempted to move - relaymove(var/mob/user) - if(user.stat) - return - if(load == user) - unload(0) - return - - // receive a radio signal - // used for control and beacon reception - - receive_signal(datum/signal/signal) - - 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" && (wires & wire_remote_rx)) - send_status() - - - recv = signal.data["command [suffix]"] - if(wires & wire_remote_rx) - // process control input - switch(recv) - if("stop") + if("stop") + if(mode >=2) mode = 0 - return + updateDialog() - if("go") + if("go") + if(mode == 0) start() - return + updateDialog() - if("target") - set_destination(signal.data["destination"] ) - return + if("home") + if(mode == 0 || mode == 2) + start_home() + updateDialog() - if("unload") + if("destination") + refresh=0 + var/new_dest = input("Enter new destination tag", "Mulebot [suffix ? "([suffix])" : ""]", destination) as text|null + refresh=1 + if(new_dest) + set_destination(new_dest) + + + if("setid") + refresh=0 + var/new_id = input("Enter new bot ID", "Mulebot [suffix ? "([suffix])" : ""]", suffix) as text|null + refresh=1 + if(new_id) + suffix = new_id + name = "Mulebot ([suffix])" + updateDialog() + + if("sethome") + refresh=0 + var/new_home = input("Enter new home tag", "Mulebot [suffix ? "([suffix])" : ""]", home_destination) as text|null + refresh=1 + if(new_home) + home_destination = new_home + updateDialog() + + if("unload") + if(load && mode !=1) if(loc == target) unload(loaddir) else unload(0) - return - if("home") - start_home() - return + if("autoret") + auto_return = !auto_return - if("bot_status") - send_status() - return + if("autopick") + auto_pickup = !auto_pickup - if("autoret") - auto_return = text2num(signal.data["value"]) - return + if("close") + usr.machine = null + usr << browse(null,"window=mulebot") - if("autopick") - auto_pickup = text2num(signal.data["value"]) - return - // receive response from beacon - recv = signal.data["beacon"] - if(wires & wire_beacon_rx) - if(recv == new_destination) // if the recvd beacon location matches the set destination - // the we will navigate there - destination = new_destination - target = signal.source.loc - var/direction = signal.data["dir"] // this will be the load/unload dir - if(direction) - loaddir = text2num(direction) + if("wirecut") + if(istype(usr.equipped(), /obj/item/weapon/wirecutters)) + var/wirebit = text2num(href_list["wire"]) + wires &= ~wirebit else - loaddir = 0 - icon_state = "mulebot[(wires & wire_mobavoid) == wire_mobavoid]" + usr << "\blue You need wirecutters!" + if("wiremend") + if(istype(usr.equipped(), /obj/item/weapon/wirecutters)) + var/wirebit = text2num(href_list["wire"]) + wires |= wirebit + else + usr << "\blue You need wirecutters!" + + if("wirepulse") + if(istype(usr.equipped(), /obj/item/device/multitool)) + switch(href_list["wire"]) + if("1","2") + usr << "\blue \icon[src] The charge light flickers." + if("4") + usr << "\blue \icon[src] The external warning lights flash briefly." + if("8") + usr << "\blue \icon[src] The load platform clunks." + if("16", "32") + usr << "\blue \icon[src] The drive motor whines briefly." + else + usr << "\blue \icon[src] You hear a radio crackle." + else + usr << "\blue You need a multitool!" + + + + updateDialog() + //src.updateUsrDialog() + else + usr << browse(null, "window=mulebot") + usr.machine = null + return + + + +// returns true if the bot has power +/obj/machinery/bot/mulebot/proc/has_power() + return !open && cell && cell.charge>0 && (wires & wire_power1) && (wires & wire_power2) + +// mousedrop a crate to load the bot +// can load anything if emagged + +/obj/machinery/bot/mulebot/MouseDrop_T(var/atom/movable/C, mob/user) + + if(user.stat) + return + + if (!on || !istype(C)|| C.anchored || get_dist(user, src) > 1 || get_dist(src,C) > 1 ) + return + + if(load) + return + + load(C) + + +// called to load a crate +/obj/machinery/bot/mulebot/proc/load(var/atom/movable/C) + if((wires & wire_loadcheck) && !istype(C,/obj/crate)) + src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") + playsound(src.loc, 'buzz-sigh.ogg', 50, 0) + return // if not emagged, only allow crates to be loaded + + if(get_dist(C, src) > 1 || load || !on) + return + mode = 1 + + // if a create, close before loading + var/obj/crate/crate = C + if(istype(crate)) + crate.close() + + C.loc = src.loc + sleep(2) + C.loc = src + load = C + + C.pixel_y += 9 + if(C.layer < layer) + C.layer = layer + 0.1 + overlays += C + + if(ismob(C)) + var/mob/M = C + if(M.client) + M.client.perspective = EYE_PERSPECTIVE + M.client.eye = src + + mode = 0 + send_status() + +// called to unload the bot +// argument is optional direction to unload +// if zero, unload at bot's location +/obj/machinery/bot/mulebot/proc/unload(var/dirn = 0) + if(!load) + return + + mode = 1 + overlays = null + + load.loc = src.loc + load.pixel_y -= 9 + load.layer = initial(load.layer) + if(ismob(load)) + var/mob/M = load + if(M.client) + M.client.perspective = MOB_PERSPECTIVE + M.client.eye = src + + + if(dirn) + step(load, dirn) + + load = null + + // in case non-load items end up in contents, dump every else too + // this seems to happen sometimes due to race conditions + // with items dropping as mobs are loaded + + for(var/atom/movable/AM in src) + if(AM == cell || AM == botcard) continue + + AM.loc = src.loc + AM.layer = initial(AM.layer) + AM.pixel_y = initial(AM.pixel_y) + if(ismob(AM)) + var/mob/M = AM + if(M.client) + M.client.perspective = MOB_PERSPECTIVE + M.client.eye = src + mode = 0 + + +/obj/machinery/bot/mulebot/process() + if(!has_power()) + on = 0 + return + if(on) + var/speed = ((wires & wire_motor1) ? 1:0) + ((wires & wire_motor2) ? 2:0) + //world << "speed: [speed]" + switch(speed) + if(0) + // do nothing + if(1) + process_bot() + spawn(3) + process_bot() + sleep(3) + process_bot() + if(2) + process_bot() + spawn(5) + process_bot() + if(3) + process_bot() + + if(refresh) updateDialog() + +/obj/machinery/bot/mulebot/proc/process_bot() + //if(mode) world << "Mode: [mode]" + switch(mode) + if(0) // idle + icon_state = "mulebot0" + return + if(1) // loading/unloading + return + if(2,3,4) // navigating to deliver,home, or blocked + + if(loc == target) // reached target + at_target() + return + + else if(path.len > 0 && target) // valid path + + var/turf/next = path[1] + reached_target = 0 + if(next == loc) + path -= next + return + + + if(istype( next, /turf/simulated)) + //world << "at ([x],[y]) moving to ([next.x],[next.y])" + + + if(bloodiness) + var/obj/decal/cleanable/blood/tracks/B = new(loc) + var/newdir = get_dir(next, loc) + if(newdir == dir) + B.dir = newdir + else + newdir = newdir | dir + if(newdir == 3) + newdir = 1 + else if(newdir == 12) + newdir = 4 + B.dir = newdir + bloodiness-- + + + + var/moved = step_towards(src, next) // attempt to move + if(cell) cell.use(1) + if(moved) // successful move + //world << "Successful move." + blockcount = 0 + path -= loc + + + if(mode==4) + spawn(1) + send_status() + + if(destination == home_destination) + mode = 3 + else + mode = 2 + + else // failed to move + + //world << "Unable to move." + + + + blockcount++ + mode = 4 + if(blockcount == 3) + src.visible_message("[src] makes an annoyed buzzing sound", "You hear an electronic buzzing sound.") + playsound(src.loc, 'buzz-two.ogg', 50, 0) + + if(blockcount > 5) // attempt 5 times before recomputing + // find new path excluding blocked turf + src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") + playsound(src.loc, 'buzz-sigh.ogg', 50, 0) + + spawn(2) + calc_path(next) + if(path.len > 0) + src.visible_message("[src] makes a delighted ping!", "You hear a ping.") + playsound(src.loc, 'ping.ogg', 50, 0) + mode = 4 + mode =6 + return + return + else + src.visible_message("[src] makes an annoyed buzzing sound", "You hear an electronic buzzing sound.") + playsound(src.loc, 'buzz-two.ogg', 50, 0) + //world << "Bad turf." + mode = 5 + return + else + //world << "No path." + mode = 5 + return + + if(5) // calculate new path + //world << "Calc new path." + mode = 6 + spawn(0) + calc_path() - updateDialog() - // send a radio signal with a single data key/value pair - proc/post_signal(var/freq, var/key, var/value) - post_signal_multiple(freq, list("[key]" = value) ) + if(path.len > 0) + blockcount = 0 + mode = 4 + src.visible_message("[src] makes a delighted ping!", "You hear a ping.") + playsound(src.loc, 'ping.ogg', 50, 0) - // send a radio signal with multiple data key/values - proc/post_signal_multiple(var/freq, var/list/keyval) + else + src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") + playsound(src.loc, 'buzz-sigh.ogg', 50, 0) - if(freq == beacon_freq && !(wires & wire_beacon_tx)) - return - if(freq == control_freq && !(wires & wire_remote_tx)) - return - - var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) - - if(!frequency) return + mode = 7 + //if(6) + //world << "Pending path calc." + //if(7) + //world << "No dest / no route." + return +// calculates a path to the current destination +// given an optional turf to avoid +/obj/machinery/bot/mulebot/proc/calc_path(var/turf/avoid = null) + src.path = AStar(src.loc, src.target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 250, id=botcard, exclude=avoid) + src.path = reverselist(src.path) - 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"] == "mulebot") - frequency.post_signal(src, signal, filter = RADIO_MULEBOT) + +// sets the current destination +// signals all beacons matching the delivery code +// beacons will return a signal giving their locations +/obj/machinery/bot/mulebot/proc/set_destination(var/new_dest) + spawn(0) + new_destination = new_dest + post_signal(beacon_freq, "findbeacon", "delivery") + updateDialog() + +// starts bot moving to current destination +/obj/machinery/bot/mulebot/proc/start() + if(destination == home_destination) + mode = 3 + else + mode = 2 + icon_state = "mulebot[(wires & wire_mobavoid) == wire_mobavoid]" + +// starts bot moving to home +// sends a beacon query to find +/obj/machinery/bot/mulebot/proc/start_home() + spawn(0) + set_destination(home_destination) + mode = 4 + icon_state = "mulebot[(wires & wire_mobavoid) == wire_mobavoid]" + +// called when bot reaches current target +/obj/machinery/bot/mulebot/proc/at_target() + if(!reached_target) + src.visible_message("[src] makes a chiming sound!", "You hear a chime.") + playsound(src.loc, 'chime.ogg', 50, 0) + reached_target = 1 + + if(load) // if loaded, unload at target + unload(loaddir) else - frequency.post_signal(src, signal) + // not loaded + if(auto_pickup) // find a crate + var/atom/movable/AM + if(!(wires & wire_loadcheck)) // if emagged, load first unanchored thing we find + for(var/atom/movable/A in get_step(loc, loaddir)) + if(!A.anchored) + AM = A + break + else // otherwise, look for crates only + AM = locate(/obj/crate) in get_step(loc,loaddir) + if(AM) + load(AM) + // whatever happened, check to see if we return home - // signals bot status etc. to controller - proc/send_status() - var/list/kv = list( - "type" = "mulebot", - "name" = suffix, - "loca" = loc.loc, // area - "mode" = mode, - "powr" = (cell ? cell.percent() : 0), - "dest" = destination, - "home" = home_destination, - "load" = load, - "retn" = auto_return, - "pick" = auto_pickup, - ) - post_signal_multiple(control_freq, kv) + if(auto_return && destination != home_destination) + // auto return set and not at home already + start_home() + mode = 4 + else + mode = 0 // otherwise go idle + + send_status() // report status to anyone listening + + return + +// called when bot bumps into anything +/obj/machinery/bot/mulebot/Bump(var/atom/obs) + if(!(wires & wire_mobavoid)) //usually just bumps, but if avoidance disabled knock over mobs + var/mob/M = obs + if(ismob(M)) + if(istype(M,/mob/living/silicon/robot)) + src.visible_message("\red [src] bumps into [M]!") + else + src.visible_message("\red [src] knocks over [M]!") + M.pulling = null + M.stunned = 8 + M.weakened = 5 + M.lying = 1 + ..() + +/obj/machinery/bot/mulebot/alter_health() + return get_turf(src) + + +// called from mob/living/carbon/human/HasEntered() +// when mulebot is in the same loc +/obj/machinery/bot/mulebot/proc/RunOver(var/mob/living/carbon/human/H) + src.visible_message("\red [src] drives over [H]!") + playsound(src.loc, 'splat.ogg', 50, 1) + + /* + if(ismob(load)) + var/mob/M = load + if(M.reagents.has_reagent("beer")) + M.unlock_medal("DUI", 1) + if(M.reagents.has_reagent("space_drugs") && istype(H) && H.wear_id.assignment == "Security Officer") + M.unlock_medel("Ridin' Dirty",1) + */ + + var/damage = rand(5,15) + + H.TakeDamage("head", 2*damage, 0) + H.TakeDamage("chest",2*damage, 0) + H.TakeDamage("l_leg",0.5*damage, 0) + H.TakeDamage("r_leg",0.5*damage, 0) + H.TakeDamage("l_arm",0.5*damage, 0) + H.TakeDamage("r_arm",0.5*damage, 0) + + var/obj/decal/cleanable/blood/B = new(src.loc) + B.blood_DNA = H.dna.unique_enzymes + B.blood_type = H.b_type + + bloodiness += 4 + +// player on mulebot attempted to move +/obj/machinery/bot/mulebot/relaymove(var/mob/user) + if(user.stat) + return + if(load == user) + unload(0) + return + +// receive a radio signal +// used for control and beacon reception + +/obj/machinery/bot/mulebot/receive_signal(datum/signal/signal) + + 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" && (wires & wire_remote_rx)) + send_status() + + + recv = signal.data["command [suffix]"] + if(wires & wire_remote_rx) + // process control input + switch(recv) + if("stop") + mode = 0 + return + + if("go") + start() + return + + if("target") + set_destination(signal.data["destination"] ) + return + + if("unload") + if(loc == target) + unload(loaddir) + else + unload(0) + return + + if("home") + start_home() + return + + if("bot_status") + send_status() + return + + if("autoret") + auto_return = text2num(signal.data["value"]) + return + + if("autopick") + auto_pickup = text2num(signal.data["value"]) + return + + // receive response from beacon + recv = signal.data["beacon"] + if(wires & wire_beacon_rx) + if(recv == new_destination) // if the recvd beacon location matches the set destination + // the we will navigate there + destination = new_destination + target = signal.source.loc + var/direction = signal.data["dir"] // this will be the load/unload dir + if(direction) + loaddir = text2num(direction) + else + loaddir = 0 + icon_state = "mulebot[(wires & wire_mobavoid) == wire_mobavoid]" + calc_path() + updateDialog() + +// send a radio signal with a single data key/value pair +/obj/machinery/bot/mulebot/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/mulebot/proc/post_signal_multiple(var/freq, var/list/keyval) + + if(freq == beacon_freq && !(wires & wire_beacon_tx)) + return + if(freq == control_freq && !(wires & wire_remote_tx)) + return + + 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"] == "mulebot") + frequency.post_signal(src, signal, filter = RADIO_MULEBOT) + else + frequency.post_signal(src, signal) + +// signals bot status etc. to controller +/obj/machinery/bot/mulebot/proc/send_status() + var/list/kv = list( + "type" = "mulebot", + "name" = suffix, + "loca" = loc.loc, // area + "mode" = mode, + "powr" = (cell ? cell.percent() : 0), + "dest" = destination, + "home" = home_destination, + "load" = load, + "retn" = auto_return, + "pick" = auto_pickup, + ) + post_signal_multiple(control_freq, kv) + +/obj/machinery/bot/mulebot/emp_act(severity) + if (cell) + cell.emp_act(severity) + if(load) + load.emp_act(severity) + ..() + + +/obj/machinery/bot/mulebot/explode() + src.visible_message("\red [src] blows apart!", 1) + var/turf/Tsec = get_turf(src) + + new /obj/item/device/prox_sensor(Tsec) + new /obj/item/stack/rods(Tsec) + new /obj/item/stack/rods(Tsec) + new /obj/item/weapon/cable_coil/cut(Tsec) + if (cell) + cell.loc = Tsec + cell.update_icon() + cell = null + + var/datum/effects/system/spark_spread/s = new /datum/effects/system/spark_spread + s.set_up(3, 1, src) + s.start() + + new /obj/decal/cleanable/oil(src.loc) + del(src) \ No newline at end of file diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm index 294f6d1445b..b76a5964d26 100644 --- a/code/game/machinery/bots/secbot.dm +++ b/code/game/machinery/bots/secbot.dm @@ -6,9 +6,12 @@ layer = 5.0 density = 1 anchored = 0 + health = 25 + maxhealth = 25 + fire_dam_coeff = 0.7 + brute_dam_coeff = 0.5 // weight = 1.0E7 req_access = list(access_security) - var/on = 1 var/locked = 1 //Behavior Controls lock var/mob/living/carbon/target var/oldtarget_name @@ -17,7 +20,6 @@ var/last_found //There's a delay var/frustration = 0 var/emagged = 0 //Emagged Secbots view everyone as a criminal - var/health = 25 var/idcheck = 1 //If false, all station IDs are authorized for weapons. var/check_records = 1 //Does it check security records? var/arrest_type = 0 //If true, don't handcuff @@ -83,33 +85,45 @@ radio_controller.add_object(src, control_freq, filter = RADIO_SECBOT) radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) - examine() - set src in view() - ..() - if (src.health < 25) - if (src.health > 15) - usr << text("\red [src]'s parts look loose.") - else - usr << text("\red [src]'s parts look very loose!") +/obj/machinery/bot/secbot/turn_on() + ..() + src.icon_state = "secbot[src.on]" + 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) + src.icon_state = "secbot[src.on]" + src.updateUsrDialog() + +/obj/machinery/bot/secbot/attack_hand(mob/user as mob) + . = ..() + if (.) return + usr.machine = src + interact(user) - attack_hand(user as mob) - var/dat +/obj/machinery/bot/secbot/proc/interact(mob/user as mob) + var/dat - dat += text({" + dat += text({" Automatic Security Unit v1.3

-Status: []
-Behaviour controls are [src.locked ? "locked" : "unlocked"]"}, +/obj/machinery/bot/secbot/Status: []
+/obj/machinery/bot/secbot/Behaviour controls are [src.locked ? "locked" : "unlocked"]"}, "[src.on ? "On" : "Off"]" ) - if(!src.locked) - dat += text({"
-Check for Weapon Authorization: []
-Check Security Records: []
-Operating Mode: []
-Auto Patrol: []"}, + if(!src.locked) + dat += text({"
+/obj/machinery/bot/secbot/Check for Weapon Authorization: []
+/obj/machinery/bot/secbot/Check Security Records: []
+/obj/machinery/bot/secbot/Operating Mode: []
+/obj/machinery/bot/secbot/Auto Patrol: []"}, "[src.idcheck ? "Yes" : "No"]", "[src.check_records ? "Yes" : "No"]", @@ -117,621 +131,570 @@ Auto Patrol: []"}, "[auto_patrol ? "On" : "Off"]" ) - user << browse("Securitron v1.3 controls[dat]", "window=autosec") - onclose(user, "autosec") + user << browse("Securitron v1.3 controls[dat]", "window=autosec") + onclose(user, "autosec") + return + +/obj/machinery/bot/secbot/Topic(href, href_list) + usr.machine = src + src.add_fingerprint(usr) + if ((href_list["power"]) && (src.allowed(usr))) + if (src.on) + turn_off() + else + turn_on() return - Topic(href, href_list) - usr.machine = src - src.add_fingerprint(usr) - if ((href_list["power"]) && (src.allowed(usr))) - src.on = !src.on - src.target = null - src.oldtarget_name = null - src.anchored = 0 - src.mode = SECBOT_IDLE - walk_to(src,0) - src.icon_state = "secbot[src.on]" + switch(href_list["operation"]) + if ("idcheck") + src.idcheck = !src.idcheck src.updateUsrDialog() - - switch(href_list["operation"]) - if ("idcheck") - src.idcheck = !src.idcheck - src.updateUsrDialog() - if ("ignorerec") - src.check_records = !src.check_records - src.updateUsrDialog() - if ("switchmode") - src.arrest_type = !src.arrest_type - src.updateUsrDialog() - if("patrol") - auto_patrol = !auto_patrol - mode = SECBOT_IDLE - updateUsrDialog() - - attack_ai(mob/user as mob) - src.on = !src.on - src.target = null - src.oldtarget_name = null - mode = SECBOT_IDLE - src.anchored = 0 - src.icon_state = "secbot[src.on]" - walk_to(src,0) - - attackby(obj/item/weapon/W as obj, mob/user as mob) - if ((istype(W, /obj/item/weapon/card/emag)) && (!src.emagged)) - user << "\red 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 - src.oldtarget_name = user.name - src.last_found = world.time - src.anchored = 0 - src.emagged = 1 - src.on = 1 - src.icon_state = "secbot[src.on]" + if ("ignorerec") + src.check_records = !src.check_records + src.updateUsrDialog() + if ("switchmode") + src.arrest_type = !src.arrest_type + src.updateUsrDialog() + if("patrol") + auto_patrol = !auto_patrol mode = SECBOT_IDLE - else 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."]" - else - user << "\red Access denied." + updateUsrDialog() - else if (istype(W, /obj/item/weapon/screwdriver)) - if (src.health < 25) - src.health = 25 - for(var/mob/O in viewers(src, null)) - O << "\red [user] repairs [src]!" +/obj/machinery/bot/secbot/attack_ai(mob/user as mob) + if (src.on) + turn_off() + else + turn_on() + +/obj/machinery/bot/secbot/attackby(obj/item/weapon/W as obj, mob/user as mob) + if ((istype(W, /obj/item/weapon/card/emag)) && (!src.emagged)) + user << "\red 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 + src.oldtarget_name = user.name + src.last_found = world.time + src.anchored = 0 + src.emagged = 1 + src.on = 1 + src.icon_state = "secbot[src.on]" + mode = SECBOT_IDLE + else 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."]" else - switch(W.damtype) - if("fire") - src.health -= W.force * 0.75 - if("brute") - src.health -= W.force * 0.5 - else - if (src.health <= 0) - src.explode() - else if ((W.force) && (!src.target)) - src.target = user - src.mode = SECBOT_HUNT - ..() + user << "\red Access denied." + else + ..() + if (!istype(W, /obj/item/weapon/screwdriver) && (W.force) && (!src.target)) + src.target = user + src.mode = SECBOT_HUNT - process() - set background = 1 +/obj/machinery/bot/secbot/process() + set background = 1 - if (!src.on) - return + if (!src.on) + return - switch(mode) + switch(mode) - if(SECBOT_IDLE) // idle + 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) - 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 - if (get_dist(src, src.target) <= 1) // if right next to perp - playsound(src.loc, 'Egloves.ogg', 50, 1, -1) - src.icon_state = "secbot-c" - spawn(2) - src.icon_state = "secbot[src.on]" - var/mob/living/carbon/M = src.target - var/maxstuns = 4 - if (istype(M, /mob/living/carbon/human)) - if (M.weakened < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) - M.weakened = 10 - if (M.stuttering < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) - M.stuttering = 10 - if (M.stunned < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) - M.stunned = 10 - else + if (target) // make sure target exists + if (get_dist(src, src.target) <= 1) // if right next to perp + playsound(src.loc, 'Egloves.ogg', 50, 1, -1) + src.icon_state = "secbot-c" + spawn(2) + src.icon_state = "secbot[src.on]" + var/mob/living/carbon/M = src.target + var/maxstuns = 4 + if (istype(M, /mob/living/carbon/human)) + if (M.weakened < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) M.weakened = 10 + if (M.stuttering < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) M.stuttering = 10 + if (M.stunned < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) M.stunned = 10 - maxstuns-- - if (maxstuns <= 0) - target = null - for(var/mob/O in viewers(src, null)) - O.show_message("\red [src.target] has been stunned by [src]!", 1, "\red You hear someone fall", 2) - - mode = SECBOT_PREP_ARREST - src.anchored = 1 - src.target_lastloc = M.loc - return - - else // not next to perp - var/turf/olddist = get_dist(src, src.target) - walk_to(src, src.target,1,4) - if ((get_dist(src, src.target)) >= (olddist)) - src.frustration++ - else - src.frustration = 0 - - if(SECBOT_PREP_ARREST) // preparing to arrest target - - // 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 (!src.target.handcuffed && !src.arrest_type) - playsound(src.loc, 'handcuffs.ogg', 30, 1, -2) - mode = SECBOT_ARREST + else + M.weakened = 10 + M.stuttering = 10 + M.stunned = 10 + maxstuns-- + if (maxstuns <= 0) + target = null for(var/mob/O in viewers(src, null)) - O.show_message("\red [src] is trying to put handcuffs on [src.target]!", 1) + O.show_message("\red [src.target] has been stunned by [src]!", 1, "\red You hear someone fall", 2) - spawn(60) - if (get_dist(src, src.target) <= 1) - if (src.target.handcuffed) - return - - if(istype(src.target,/mob/living/carbon)) - src.target.handcuffed = new /obj/item/weapon/handcuffs(src.target) - - mode = SECBOT_IDLE - src.target = null - src.anchored = 0 - src.last_found = world.time - src.frustration = 0 - - playsound(src.loc, pick('bgod.ogg', 'biamthelaw.ogg', 'bsecureday.ogg', 'bradio.ogg', 'binsult.ogg', 'bcreep.ogg'), 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) - - if(SECBOT_ARREST) // arresting - - if (src.target.handcuffed) - src.anchored = 0 - mode = SECBOT_IDLE + mode = SECBOT_PREP_ARREST + src.anchored = 1 + src.target_lastloc = M.loc return + else // not next to perp + var/turf/olddist = get_dist(src, src.target) + walk_to(src, src.target,1,4) + if ((get_dist(src, src.target)) >= (olddist)) + src.frustration++ + else + src.frustration = 0 - if(SECBOT_START_PATROL) // start a patrol + if(SECBOT_PREP_ARREST) // preparing to arrest target - if(path.len > 0 && patrol_target) // have a valid path, so just resume - mode = SECBOT_PATROL - 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 - 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 + if (!src.target.handcuffed && !src.arrest_type) + playsound(src.loc, 'handcuffs.ogg', 30, 1, -2) + mode = SECBOT_ARREST + for(var/mob/O in viewers(src, null)) + O.show_message("\red [src] is trying to put handcuffs on [src.target]!", 1) + + spawn(60) + if (get_dist(src, src.target) <= 1) + if (src.target.handcuffed) return - mode = SECBOT_PATROL + if(istype(src.target,/mob/living/carbon)) + src.target.handcuffed = new /obj/item/weapon/handcuffs(src.target) - else // no patrol target, so need a new one - find_patrol_target() - speak("Engaging patrol mode.") + mode = SECBOT_IDLE + src.target = null + src.anchored = 0 + src.last_found = world.time + src.frustration = 0 + playsound(src.loc, pick('bgod.ogg', 'biamthelaw.ogg', 'bsecureday.ogg', 'bradio.ogg', 'binsult.ogg', 'bcreep.ogg'), 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) - if(SECBOT_PATROL) // patrol mode + if(SECBOT_ARREST) // arresting - 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 - - 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 + if (src.target.handcuffed) + src.anchored = 0 + mode = SECBOT_IDLE return - if(istype( next, /turf/simulated)) + if(SECBOT_START_PATROL) // start a patrol - var/moved = step_towards(src, next) // attempt to move - if(moved) // successful move - blockcount = 0 - path -= loc - - look_for_perp() - 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 + 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() + 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 - 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 - 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 + else // not a valid turf + mode = SECBOT_IDLE return - if(next_destination) - set_destination(next_destination) + 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 - find_nearest_beacon() - return - - - // finds the nearest beacon to self - // signals all beacons matching the patrol code - 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() - - - 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 - 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 - - 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") + auto_patrol = 0 + mode = SECBOT_IDLE + speak("Disengaging patrol mode.") 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 +/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 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 +// receive a radio signal +// used for beacon reception - // 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 +/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]]" + */ - // send a radio signal with a single data key/value pair - proc/post_signal(var/freq, var/key, var/value) - post_signal_multiple(freq, list("[key]" = value) ) + var/recv = signal.data["command"] + // process all-bot input + if(recv=="bot_status") + send_status() - // send a radio signal with multiple data key/values - proc/post_signal_multiple(var/freq, var/list/keyval) + // 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 - var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) + if("go") + mode = SECBOT_IDLE + auto_patrol = 1 + return - if(!frequency) return + if("summon") + patrol_target = signal.data["target"] + next_destination = destination + destination = null + awaiting_beacon = 0 + mode = SECBOT_SUMMON + calc_path() + speak("Responding.") - 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) + return - // signals bot status etc. to controller - proc/send_status() - var/list/kv = new() - kv["type"] = "secbot" - kv["name"] = name - kv["loca"] = loc.loc // area - kv["mode"] = mode - post_signal_multiple(control_freq, kv) + + + // 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 - 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) - src.path = reverselist(src.path) +/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) + src.path = reverselist(src.path) // look for a criminal in view of the bot - proc/look_for_perp() - src.anchored = 0 - for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal - if ((C.stat) || (C.handcuffed)) - continue +/obj/machinery/bot/secbot/proc/look_for_perp() + src.anchored = 0 + for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal + if ((C.stat) || (C.handcuffed)) + continue - if ((C.name == src.oldtarget_name) && (world.time < src.last_found + 100)) - continue + 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) - else if ((istype(C, /mob/living/carbon/monkey)) && (C.client) && (ticker.mode.name == "monkey")) - src.threatlevel = 4 + if (istype(C, /mob/living/carbon/human)) + src.threatlevel = src.assess_perp(C) + else if ((istype(C, /mob/living/carbon/monkey)) && (C.client) && (ticker.mode.name == "monkey")) + src.threatlevel = 4 - if (!src.threatlevel) - continue + if (!src.threatlevel) + continue - else if (src.threatlevel >= 4) - src.target = C - src.oldtarget_name = C.name - src.speak("Level [src.threatlevel] infraction alert!") - playsound(src.loc, pick('bcriminal.ogg', 'bjustice.ogg', 'bfreeze.ogg'), 50, 0) - src.visible_message("[src] points at [C.name]!") - mode = SECBOT_HUNT - spawn(0) - process() // ensure bot quickly responds to a perp - break - else - continue + else if (src.threatlevel >= 4) + src.target = C + src.oldtarget_name = C.name + src.speak("Level [src.threatlevel] infraction alert!") + playsound(src.loc, pick('bcriminal.ogg', 'bjustice.ogg', 'bfreeze.ogg'), 50, 0) + src.visible_message("[src] points at [C.name]!") + mode = SECBOT_HUNT + spawn(0) + process() // ensure bot quickly responds to a perp + break + else + continue //If the security records say to arrest them, arrest them //Or if they have weapons and aren't security, arrest them. - proc/assess_perp(mob/living/carbon/human/perp as mob) - var/threatcount = 0 +/obj/machinery/bot/secbot/proc/assess_perp(mob/living/carbon/human/perp as mob) + var/threatcount = 0 - if(src.emagged) return 10 //Everyone is a criminal! + if(src.emagged) return 10 //Everyone is a criminal! - if((src.idcheck) || (isnull(perp:wear_id)) || (istype(perp:wear_id, /obj/item/weapon/card/id/syndicate))) - if(src.allowed(perp)) //Corrupt cops cannot exist beep boop - return 0 + if((src.idcheck) || (isnull(perp:wear_id)) || (istype(perp:wear_id, /obj/item/weapon/card/id/syndicate))) + if(src.allowed(perp)) //Corrupt cops cannot exist beep boop + return 0 - if((istype(perp.l_hand, /obj/item/weapon/gun) && !istype(perp.l_hand, /obj/item/weapon/gun/shotgun)) || istype(perp.l_hand, /obj/item/weapon/baton)) - threatcount += 4 + if((istype(perp.l_hand, /obj/item/weapon/gun) && !istype(perp.l_hand, /obj/item/weapon/gun/shotgun)) || istype(perp.l_hand, /obj/item/weapon/baton)) + threatcount += 4 - if((istype(perp.r_hand, /obj/item/weapon/gun) && !istype(perp.r_hand, /obj/item/weapon/gun/shotgun)) || istype(perp.r_hand, /obj/item/weapon/baton)) - threatcount += 4 + if((istype(perp.r_hand, /obj/item/weapon/gun) && !istype(perp.r_hand, /obj/item/weapon/gun/shotgun)) || istype(perp.r_hand, /obj/item/weapon/baton)) + threatcount += 4 - if(istype(perp:belt, /obj/item/weapon/gun) || istype(perp:belt, /obj/item/weapon/baton)) - threatcount += 2 + if(istype(perp:belt, /obj/item/weapon/gun) || istype(perp:belt, /obj/item/weapon/baton)) + threatcount += 2 - if(istype(perp:wear_suit, /obj/item/clothing/suit/wizrobe)) - threatcount += 2 + if(istype(perp:wear_suit, /obj/item/clothing/suit/wizrobe)) + threatcount += 2 - if(perp.mutantrace && perp.mutantrace != "none") - threatcount += 2 + if(perp.mutantrace && perp.mutantrace != "none") + threatcount += 2 - //Agent cards lower threatlevel when normal idchecking is off. - if((istype(perp:wear_id, /obj/item/weapon/card/id/syndicate)) && src.idcheck) - threatcount -= 2 +//Agent cards lower threatlevel when normal idchecking is off. + if((istype(perp:wear_id, /obj/item/weapon/card/id/syndicate)) && src.idcheck) + threatcount -= 2 - if (src.check_records) - for (var/datum/data/record/E in data_core.general) - var/perpname = perp.name - if (perp:wear_id) - var/obj/item/weapon/card/id/id = perp:wear_id - if(istype(perp:wear_id, /obj/item/device/pda)) - var/obj/item/device/pda/pda = perp:wear_id - id = pda.id - if (id) - perpname = id.registered - else - var/obj/item/device/pda/pda = perp:wear_id - perpname = pda.owner - if (E.fields["name"] == perpname) - for (var/datum/data/record/R in data_core.security) - if ((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "*Arrest*")) - threatcount = 4 - break + if (src.check_records) + for (var/datum/data/record/E in data_core.general) + var/perpname = perp.name + if (perp:wear_id) + var/obj/item/weapon/card/id/id = perp:wear_id + if(istype(perp:wear_id, /obj/item/device/pda)) + var/obj/item/device/pda/pda = perp:wear_id + id = pda.id + if (id) + perpname = id.registered + else + var/obj/item/device/pda/pda = perp:wear_id + perpname = pda.owner + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.security) + if ((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "*Arrest*")) + threatcount = 4 + break - return threatcount + return threatcount - 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 - else if ((istype(M, /mob/living/)) && (!src.anchored)) - src.loc = M:loc +/obj/machinery/bot/secbot/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 + else if ((istype(M, /mob/living/)) && (!src.anchored)) + src.loc = M:loc + src.frustration = 0 - return return + return - Bumped(M as mob|obj) - spawn(0) - var/turf/T = get_turf(src) - M:loc = T +/obj/machinery/bot/secbot/Bumped(M as mob|obj) + spawn(0) + var/turf/T = get_turf(src) + M:loc = T - bullet_act(flag, A as obj) - switch(flag) - if (PROJECTILE_BULLET) - src.health -= 20 - //if (PROJECTILE_WEAKBULLET || PROJECTILE_BEANBAG) //Detective's revolver fires marshmallows - // src.health -= 2 - if (PROJECTILE_LASER) - src.health -= 10 - if (PROJECTILE_PULSE) - src.health -=40 - if (src.health <= 0) - src.explode() - - proc/speak(var/message) - for(var/mob/O in hearers(src, null)) - O << "[src] beeps, \"[message]\"" - return - - //Generally we want to explode() instead of just deleting the securitron. - ex_act(severity) - switch(severity) - if(1.0) - src.explode() - return - if(2.0) - src.health -= 15 - if (src.health <= 0) - src.explode() - return - return - - meteorhit() - src.explode() - return - - blob_act() - if(prob(50)) - src.explode() - return - - proc/explode() - - walk_to(src,0) - for(var/mob/O in hearers(src, null)) - O.show_message("\red [src] blows apart!", 1) - 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('aibots.dmi', "hs_hole") - Sa.created_name = src.name - new /obj/item/device/prox_sensor(Tsec) - - var/obj/item/weapon/baton/B = new /obj/item/weapon/baton(Tsec) - B.charges = 0 - - if (prob(50)) - new /obj/item/robot_parts/l_arm(Tsec) - - var/datum/effects/system/spark_spread/s = new /datum/effects/system/spark_spread - s.set_up(3, 1, src) - s.start() - del(src) +/obj/machinery/bot/secbot/proc/speak(var/message) + for(var/mob/O in hearers(src, null)) + O << "[src] beeps, \"[message]\"" + 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 /obj/item/weapon/secbot_assembly(Tsec) + Sa.build_step = 1 + Sa.overlays += image('aibots.dmi', "hs_hole") + Sa.created_name = src.name + new /obj/item/device/prox_sensor(Tsec) + var/obj/item/weapon/baton/B = new /obj/item/weapon/baton(Tsec) + B.charges = 0 + + if (prob(50)) + new /obj/item/robot_parts/l_arm(Tsec) + + var/datum/effects/system/spark_spread/s = new /datum/effects/system/spark_spread + s.set_up(3, 1, src) + s.start() + + new /obj/decal/cleanable/oil(src.loc) + del(src) + +/obj/machinery/bot/secbot/attack_alien(var/mob/living/carbon/alien/user as mob) + ..() + if (!isalien(target)) + src.target = user + src.mode = SECBOT_HUNT //Secbot Construction @@ -798,4 +761,4 @@ Auto Patrol: []"}, if (!in_range(src, usr) && src.loc != usr) return - src.created_name = t \ No newline at end of file + src.created_name = t diff --git a/code/game/objects/devices/PDA/radio.dm b/code/game/objects/devices/PDA/radio.dm index de232604e5f..8cd587dac5d 100644 --- a/code/game/objects/devices/PDA/radio.dm +++ b/code/game/objects/devices/PDA/radio.dm @@ -127,7 +127,7 @@ radio_controller.add_object(src, control_freq, filter = RADIO_MULEBOT) radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) spawn(10) - post_signal(beacon_freq, "findbeacon", "delivery", filter = RADIO_NAVBEACONS) + post_signal(beacon_freq, "findbeacon", "delivery", s_filter = RADIO_NAVBEACONS) // receive radio signals // can detect bot status signals diff --git a/code/game/objects/storage/toolbox.dm b/code/game/objects/storage/toolbox.dm index 410ad545262..c91050ce95c 100644 --- a/code/game/objects/storage/toolbox.dm +++ b/code/game/objects/storage/toolbox.dm @@ -6,7 +6,7 @@ /obj/item/weapon/storage/toolbox/emergency/New() ..() - new /obj/item/weapon/crowbar(src) + new /obj/item/weapon/crowbar/red(src) new /obj/item/weapon/extinguisher(src) new /obj/item/device/flashlight(src) new /obj/item/device/radio(src) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 3621ca01c8c..47b88f6af7e 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1402,7 +1402,9 @@ src << "Here's your rightclick admin verbs back" src.verbs -= /client/proc/unstealthadmin + var/temp = src.deadchat src.update_admins(src.holder.rank) + src.deadchat = temp /client/proc/stealthadmin() set name = "Toggle admin verb visibility" diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 3d9dc9f9edf..a4aec223567 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -63,6 +63,8 @@ ":b" = "binary", ":a" = "alientalk", ":t" = "Syndicate", + ":d" = "Mining", + ":q" = "Cargo", //kinda localization -- rastaf0 //same keys as above, but on russian keyboard layout. This file uses cp1251 as encoding. @@ -79,6 +81,8 @@ ":è" = "binary", ":ô" = "alientalk", ":å" = "Syndicate", + ":â" = "Mining", + ":é" = "Cargo", ) message_mode = keys[channel_prefix] diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index fb868f79b66..1a8b86fa36a 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1222,7 +1222,8 @@ var/obj/item/weapon/grab/G = src.l_hand if (!( L.container.Find(G.affecting) )) L.container += G.affecting - G.affecting.ret_grab(L, 1) + if (G.affecting) + G.affecting.ret_grab(L, 1) if (istype(src.r_hand, /obj/item/weapon/grab)) var/obj/item/weapon/grab/G = src.r_hand if (!( L.container.Find(G.affecting) )) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index b7b66db72c8..275f637b5d2 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -232,6 +232,8 @@ mob/new_player AttemptLateSpawn("Warden", wardenMax) if ("29") AttemptLateSpawn("Shaft Miner", minerMax) + if ("30") + AttemptLateSpawn("Mime", mimeMax) if(!ready && href_list["preferences"]) preferences.process_link(src, href_list) @@ -376,6 +378,9 @@ mob/new_player if (IsJobAvailable("Clown",clownMax)) dat += "Clown
" + if (IsJobAvailable("Mime",mimeMax)) + dat += "Mime
" + if (IsJobAvailable("Chef",chefMax)) dat += "Chef
" diff --git a/code/setup.dm b/code/setup.dm index b1e928ac466..9d4f7003476 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -135,6 +135,7 @@ #define NOPOWER 2 #define POWEROFF 4 // tbd #define MAINT 8 // under maintaince +#define EMPED 16 // temporary broken by EMP pulse #define ENGINE_EJECT_Z 3 diff --git a/icons/changelog.html b/icons/changelog.html index 6647b9b4595..ba0ac8e8d06 100644 --- a/icons/changelog.html +++ b/icons/changelog.html @@ -30,7 +30,7 @@ Credits:
- Coders: TLE, NEO, Errorage, muskets, veryinky, Skie, Numbers, Agouri, Noka, Urist McDorf, Uhangi, Darem, Mport
+ Coders: TLE, NEO, Errorage, muskets, veryinky, Skie, Numbers, Agouri, Noka, Urist McDorf, Uhangi, Darem, Mport, rastaf0
Spriters: Agouri, Cheridan, Cruazy Guest, Deeaych, Deuryn, Matty406, Microwave, ShiftyEyesShady, Skie, Uhangi, Veyveyr
Sounds: Skie
Thanks to: CDK Station devs, GoonStation devs, the original SpaceStation developers and Erikat for the title image @@ -43,6 +43,11 @@
  • Rastaf0 updated:
      +
    • Added radio channels and headsets for miners (:h or :d ("diggers" lol)) and for cargo techs (:h or :q )
    • +
    • Added a personal headsets to HoP and QM.
    • +
    • Aliens now attack bots instead of opening control window.
    • +
    • All bots can be damaged and repaired.
    • +
    • All bots are effected to EMP now.
    • Atmos now starts with nitrous oxide in storage tank.
  • diff --git a/icons/obj/aibots.dmi b/icons/obj/aibots.dmi index f1cfac3305e..8651fa47b27 100644 Binary files a/icons/obj/aibots.dmi and b/icons/obj/aibots.dmi differ diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 24f78b1d334..7b5aa3b599c 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi index 2c875cd0dfb..498c0e7d151 100644 Binary files a/icons/obj/items.dmi and b/icons/obj/items.dmi differ diff --git a/icons/obj/radio.dmi b/icons/obj/radio.dmi new file mode 100644 index 00000000000..d066b144abb Binary files /dev/null and b/icons/obj/radio.dmi differ