diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm
index 51742f30bca..3122f089bd6 100644
--- a/code/_onclick/ai.dm
+++ b/code/_onclick/ai.dm
@@ -64,6 +64,10 @@
aiCamera.captureimage(A, usr)
return
+ if(waypoint_mode)
+ set_waypoint(A)
+ waypoint_mode = 0
+ return
/*
AI restrained() currently does nothing
if(restrained())
diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm
index f85d57657f3..dda1b8afcd0 100644
--- a/code/game/machinery/bots/floorbot.dm
+++ b/code/game/machinery/bots/floorbot.dm
@@ -343,8 +343,9 @@ obj/machinery/bot/floorbot/process_scan(var/scan_target)
result = F
if(FIX_TILE) //Selects only damaged floors.
F = scan_target
- if(F.broken || F.burnt)
- result = F
+ if(istype(F, /turf))
+ if(F.broken || F.burnt)
+ result = F
if(TILE_EMAG) //Emag mode! Rip up the floor and cause breaches to space!
F = scan_target
if(!istype(F, /turf/simulated/floor/plating))
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 59afd4794db..bd9f66d7b35 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -55,6 +55,9 @@ var/list/ai_list = list()
var/datum/trackable/track = null
var/can_shunt = 1
var/last_announcement = ""
+ var/obj/machinery/bot/Bot
+ var/turf/waypoint //Holds the turf of the currently selected waypoint.
+ var/waypoint_mode = 0 //Waypoint mode is for selecting a turf via clicking.
/mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/device/mmi/B, var/safety = 0)
var/list/possibleNames = ai_names
@@ -100,7 +103,7 @@ var/list/ai_list = list()
if (istype(loc, /turf))
verbs.Add(/mob/living/silicon/ai/proc/ai_network_change, \
/mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \
- /mob/living/silicon/ai/proc/toggle_camera_light, /mob/living/silicon/ai/proc/control_integrated_radio)
+ /mob/living/silicon/ai/proc/toggle_camera_light, /mob/living/silicon/ai/proc/botcall, /mob/living/silicon/ai/proc/control_integrated_radio)
if(!safety)//Only used by AIize() to successfully spawn an AI.
if (!B)//If there is no player/brain inside.
@@ -442,6 +445,24 @@ var/list/ai_list = list()
if(A && target)
A.ai_actual_track(target)
return
+
+ if (href_list["callbot"]) //Command a bot to move to a selected location.
+ Bot = locate(href_list["callbot"]) in aibots
+ if(!Bot || Bot.remote_disabled || src.control_disabled)
+ return //True if there is no bot found, the bot is manually emagged, or the AI is carded with wireless off.
+ waypoint_mode = 1
+ src << "Set your waypoint by clicking on a valid location free of obstructions."
+ return
+
+ if (href_list["interface"]) //Remotely connect to a bot!
+ Bot = locate(href_list["interface"]) in aibots
+ if(!Bot || Bot.remote_disabled || src.control_disabled)
+ return
+ Bot.attack_ai(src)
+
+ if (href_list["botrefresh"]) //Refreshes the bot control panel.
+ botcall()
+ return
else if (href_list["faketrack"])
var/mob/target = locate(href_list["track"]) in mob_list
@@ -536,7 +557,61 @@ var/list/ai_list = list()
if(camera_light_on) A.SetLuminosity(AI_CAMERA_LUMINOSITY)
else A.SetLuminosity(0)
+/mob/living/silicon/ai/proc/botcall()
+ set category = "AI Commands"
+ set name = "Access Robot Control"
+ set desc = "Wirelessly control various automatic robots."
+ if(stat == 2)
+ src << "Critical error. System offline."
+ return
+ if(control_disabled)
+ src << "Wireless communication is disabled."
+ return
+ var/turf/ai_current_turf = get_turf(src)
+ var/ai_Zlevel = ai_current_turf.z
+ var/d
+ var/area/bot_area
+ d += "Query network status
"
+ d += "
Name | Status | Location | Control | |
| [Bot.hacked ? "(!) [Bot.name]" : Bot.name] | " + //If the bot is on, it will display the bot's current mode status. If the bot is not mode, it will just report "Idle". "Inactive if it is not on at all. + d += "[Bot.on ? "[Bot.mode ? "[ Bot.mode_name[Bot.mode] ]": "Idle"]" : "Inactive"] | " + d += "[bot_area.name] | " + d += "Interface | " + d += "Call | " + d += "