diff --git a/beestation.dme b/beestation.dme
index c1184eb49f1..93b3f970d3b 100644
--- a/beestation.dme
+++ b/beestation.dme
@@ -34,6 +34,11 @@
#include "code\__DEFINES\atom_hud.dm"
#include "code\__DEFINES\balloon_alert.dm"
#include "code\__DEFINES\bitfields.dm"
+<<<<<<< HEAD
+=======
+#include "code\__DEFINES\bodyparts.dm"
+#include "code\__DEFINES\bot_defines.dm"
+>>>>>>> 197e63bb5e... [PORT] Bot Multi-Z Movement (#7587)
#include "code\__DEFINES\callbacks.dm"
#include "code\__DEFINES\cargo.dm"
#include "code\__DEFINES\chat.dm"
@@ -1196,6 +1201,7 @@
#include "code\game\objects\structures\artstuff.dm"
#include "code\game\objects\structures\barsigns.dm"
#include "code\game\objects\structures\bedsheet_bin.dm"
+#include "code\game\objects\structures\bot_elevator.dm"
#include "code\game\objects\structures\catwalk.dm"
#include "code\game\objects\structures\destructible_structures.dm"
#include "code\game\objects\structures\displaycase.dm"
diff --git a/code/__DEFINES/bot_defines.dm b/code/__DEFINES/bot_defines.dm
new file mode 100644
index 00000000000..81b89275ae2
--- /dev/null
+++ b/code/__DEFINES/bot_defines.dm
@@ -0,0 +1,6 @@
+#define BOT_Z_MODE_AI_CALLED 10
+#define BOT_Z_MODE_PATROLLING 20
+#define BOT_Z_MODE_SUMMONED 30
+
+#define MULEBOT_Z_MODE_ACTIVE 10
+
diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index 0c878226656..c0bf3f1ef79 100644
--- a/code/_globalvars/lists/objects.dm
+++ b/code/_globalvars/lists/objects.dm
@@ -29,6 +29,7 @@ GLOBAL_LIST_EMPTY(zombie_infection_list) // A list of all zombie_infection org
GLOBAL_LIST_EMPTY(meteor_list) // List of all meteors.
GLOBAL_LIST_EMPTY(active_jammers) // List of active radio jammers
GLOBAL_LIST_EMPTY(ladders)
+GLOBAL_LIST_EMPTY(bot_elevator)
GLOBAL_LIST_EMPTY(trophy_cases)
GLOBAL_LIST_EMPTY(wire_color_directory)
diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm
index 2de3276a7cf..d7aff8b5014 100644
--- a/code/game/objects/items/devices/PDA/cart.dm
+++ b/code/game/objects/items/devices/PDA/cart.dm
@@ -665,10 +665,16 @@ Code:
var/botcount = 0
for(var/B in GLOB.bots_list) //Git da botz
var/mob/living/simple_animal/bot/Bot = B
- if(!Bot.on || Bot.get_virtual_z_level() != zlevel || Bot.remote_disabled || !(bot_access_flags & Bot.bot_type)) //Only non-emagged bots on the same Z-level are detected!
+ if(!Bot.on || Bot.remote_disabled || !(bot_access_flags & Bot.bot_type)) //Only non-emagged bots are detected!
continue //Also, the PDA must have access to the bot type.
- menu += "[Bot.name] ([Bot.get_mode()])
"
- botcount++
+ if(Bot.get_virtual_z_level() in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ if(zlevel in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ menu += "[Bot.name] ([Bot.get_mode()])
"
+ botcount++
+ else if (Bot.get_virtual_z_level() == zlevel)
+ if(!(zlevel in SSmapping.levels_by_trait(ZTRAIT_STATION)))
+ menu += "[Bot.name] ([Bot.get_mode()])
"
+ botcount++
if(!botcount) //No bots at all? Lame.
menu += "No bots found.
"
return
diff --git a/code/game/objects/structures/bot_elevator.dm b/code/game/objects/structures/bot_elevator.dm
new file mode 100644
index 00000000000..06c9f22f298
--- /dev/null
+++ b/code/game/objects/structures/bot_elevator.dm
@@ -0,0 +1,62 @@
+/obj/structure/bot_elevator
+ name = "bot elevator"
+ desc = "A basic service elevator built specifically for the various bots onboard the vessel."
+ icon = 'icons/obj/structures/bot_elevator.dmi'
+ icon_state = "elevator1"
+ anchored = TRUE
+ var/obj/structure/bot_elevator/down
+ var/obj/structure/bot_elevator/up
+ max_integrity = 100
+
+/obj/structure/bot_elevator/Initialize(mapload, obj/structure/bot_elevator/up, obj/structure/bot_elevator/down)
+ ..()
+ GLOB.bot_elevator += src
+ if (up)
+ src.up = up
+ up.down = src
+ if (down)
+ src.down = down
+ down.up = src
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/structure/bot_elevator/Destroy(force)
+ if ((resistance_flags & INDESTRUCTIBLE) && !force)
+ return QDEL_HINT_LETMELIVE
+ GLOB.bot_elevator -= src
+ disconnect()
+ return ..()
+
+/obj/structure/bot_elevator/proc/disconnect()
+ if(up && up.down == src)
+ up.down = null
+ if(down && down.up == src)
+ down.up = null
+ up = down = null
+
+/obj/structure/bot_elevator/LateInitialize()
+ // By default, discover bot elevators above and below us vertically
+ var/turf/T = get_turf(src)
+ var/obj/structure/bot_elevator/Elevator
+
+ if (!down)
+ Elevator = locate() in SSmapping.get_turf_below(T)
+ if (Elevator)
+ down = Elevator
+ Elevator.up = src // Don't waste effort looping the other way
+ if (!up)
+ Elevator = locate() in SSmapping.get_turf_above(T)
+ if (Elevator)
+ up = Elevator
+ Elevator.down = src // Don't waste effort looping the other way
+
+
+
+/obj/structure/bot_elevator/proc/travel(going_up, mob/user, is_ghost, obj/structure/bot_elevator/elevator, needs_do_after=TRUE)
+ var/turf/T = get_turf(elevator)
+ if(!is_ghost && isbot(user))
+ user.say("Weeeeeee!")
+ if(needs_do_after)
+ if(!do_after(user, 1 SECONDS, target=src))
+ return FALSE
+ user.forceMove(T)
+
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index c4f10f30a00..95785c2aa6b 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -74,6 +74,11 @@
var/blockcount = 0 //number of times retried a blocked path
var/awaiting_beacon = 0 // count of pticks awaiting a beacon response
+ var/turf/last_waypoint
+ var/bot_z_mode //SETTINGS: 10 = AI CALLED. 20 = PATROLLING. 30 = SOMEONE CALLED.
+ var/turf/original_patrol
+ var/turf/last_summon
+
var/nearest_beacon // the nearest beacon's tag
var/turf/nearest_beacon_loc // the nearest beacon's location
@@ -535,6 +540,9 @@ Pass a positive integer as an argument to override a bot's default speed.
return FALSE
else if(path.len == 1)
step_to(src, dest)
+ if(last_waypoint != null)
+ if(z != last_waypoint.z)
+ bot_z_movement()
set_path(null)
return TRUE
@@ -551,9 +559,17 @@ Pass a positive integer as an argument to override a bot's default speed.
var/datum/job/captain/All = new/datum/job/captain
all_access.access = All.get_access()
- set_path(get_path_to(src, waypoint, 200, id=all_access))
calling_ai = caller //Link the AI to the bot!
ai_waypoint = waypoint
+ last_waypoint = ai_waypoint
+
+ if(!is_reserved_level(z))
+ if(z != waypoint.z)
+ call_bot_z_move(caller, waypoint)
+ return
+
+
+ set_path(get_path_to(src, waypoint, 200, id=all_access))
if(path && path.len) //Ensures that a valid path is calculated!
var/end_area = get_area_name(waypoint)
@@ -647,6 +663,10 @@ Pass a positive integer as an argument to override a bot's default speed.
return
if(loc == patrol_target) // reached target
+ if(original_patrol != null)
+ if(z != original_patrol.z)
+ bot_z_movement()
+ return
//Find the next beacon matching the target.
if(!get_next_patrol_target())
find_patrol_target() //If it fails, look for the nearest one instead.
@@ -684,27 +704,30 @@ Pass a positive integer as an argument to override a bot's default speed.
/mob/living/simple_animal/bot/proc/get_next_patrol_target()
// search the beacon list for the next target in the list.
- for(var/obj/machinery/navbeacon/NB in GLOB.navbeacons["[z]"])
- if(NB.location == next_destination) //Does the Beacon location text match the destination?
- destination = new_destination //We now know the name of where we want to go.
- patrol_target = NB.loc //Get its location and set it as the target.
- next_destination = NB.codes["next_patrol"] //Also get the name of the next beacon in line.
- return TRUE
+ for(var/Zlevel in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ for(var/obj/machinery/navbeacon/NB in GLOB.navbeacons["[Zlevel]"])
+ if(NB.location == next_destination) //Does the Beacon location text match the destination?
+ destination = new_destination //We now know the name of where we want to go.
+ patrol_target = NB.loc //Get its location and set it as the target.
+ original_patrol = NB.loc // Original Patrol Destination
+ next_destination = NB.codes["next_patrol"] //Also get the name of the next beacon in line.
+ return TRUE
/mob/living/simple_animal/bot/proc/find_nearest_beacon()
- for(var/obj/machinery/navbeacon/NB in GLOB.navbeacons["[z]"])
- var/dist = get_dist(src, NB)
- if(nearest_beacon) //Loop though the beacon net to find the true closest beacon.
- //Ignore the beacon if were are located on it.
- if(dist>1 && dist1 && dist 1) //Begin the search, save this one for comparison on the next loop.
nearest_beacon = NB.location
nearest_beacon_loc = NB.loc
- next_destination = NB.codes["next_patrol"]
- else
- continue
- else if(dist > 1) //Begin the search, save this one for comparison on the next loop.
- nearest_beacon = NB.location
- nearest_beacon_loc = NB.loc
patrol_target = nearest_beacon_loc
destination = nearest_beacon
@@ -765,11 +788,27 @@ Pass a positive integer as an argument to override a bot's default speed.
// given an optional turf to avoid
/mob/living/simple_animal/bot/proc/calc_path(turf/avoid)
check_bot_access()
+ if(!is_reserved_level(z))
+ if(patrol_target != null)
+ if(z > patrol_target.z)
+ go_up_or_down(DOWN)
+ return
+ if(z < patrol_target.z)
+ go_up_or_down(UP)
+ return
set_path(get_path_to(src, patrol_target, 120, id=access_card, exclude=avoid))
/mob/living/simple_animal/bot/proc/calc_summon_path(turf/avoid)
check_bot_access()
spawn()
+ if(!is_reserved_level(z))
+ if(summon_target != null)
+ if(z > summon_target.z)
+ summon_up_or_down(DOWN)
+ return
+ if(z < summon_target.z)
+ summon_up_or_down(UP)
+ return
set_path(get_path_to(src, summon_target, 150, id=access_card, exclude=avoid))
if(!path.len) //Cannot reach target. Give up and announce the issue.
speak("Summon command failed, destination unreachable.",radio_channel)
@@ -781,6 +820,10 @@ Pass a positive integer as an argument to override a bot's default speed.
return
if(loc == summon_target) // Arrived to summon location.
+ if(last_summon != null)
+ if(z != last_summon.z)
+ bot_z_movement()
+ return
bot_reset()
return
@@ -796,6 +839,9 @@ Pass a positive integer as an argument to override a bot's default speed.
tries = 0
else // no path, so calculate new one
+ if(summon_target != null)
+ if(z != summon_target.z)
+ last_summon = summon_target
calc_summon_path()
/mob/living/simple_animal/bot/Bump(M as mob|obj) //Leave no door unopened!
@@ -1019,7 +1065,7 @@ Pass a positive integer as an argument to override a bot's default speed.
var/turf/T = newpath[i]
if(T == loc) //don't bother putting an image if it's where we already exist.
continue
- var/direction = NORTH
+ var/direction = get_dir(src, T)
if(i > 1)
var/turf/prevT = path[i - 1]
var/image/prevI = path[prevT]
@@ -1042,7 +1088,7 @@ Pass a positive integer as an argument to override a bot's default speed.
MA.icon = path_image_icon
MA.icon_state = path_image_icon_state
MA.layer = ABOVE_OPEN_TURF_LAYER
- MA.plane = 0
+ MA.plane = GAME_PLANE
MA.appearance_flags = RESET_COLOR|RESET_TRANSFORM
MA.color = path_image_color
MA.dir = direction
@@ -1066,3 +1112,150 @@ Pass a positive integer as an argument to override a bot's default speed.
/mob/living/simple_animal/bot/rust_heretic_act()
adjustBruteLoss(400)
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//Multi-Z Related section
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+/**
+ * Finds nearest bot elevator.
+ *
+ * Arguments:
+ * * direciton - UP or DOWN.
+ */
+/mob/living/simple_animal/bot/proc/find_nearest_bot_elevator(direction)
+ if(!direction)
+ return
+ if(direction != UP && direction != DOWN)
+ return
+
+ var/target
+ for(var/obj/structure/bot_elevator/elevat in GLOB.bot_elevator)
+ if(elevat.z != z)
+ continue
+ if(direction == UP && !elevat.up)
+ continue
+ if(direction == DOWN && !elevat.down)
+ continue
+ if(!target)
+ target = elevat
+ continue
+ if(get_dist_euclidian(elevat, src) > get_dist_euclidian(target, src))
+ continue
+ target = elevat
+ return target
+
+/**
+ *
+ * Makes the bot move up or down a Z-level depending on the bot_z_mode
+ * and the original destination
+ */
+/mob/living/simple_animal/bot/proc/bot_z_movement()
+ var/obj/structure/bot_elevator/E = locate(/obj/structure/bot_elevator) in get_turf(src)
+ if(bot_z_mode == BOT_Z_MODE_AI_CALLED)
+ if(E)
+ if(z > last_waypoint.z)
+ E.travel(FALSE, src, FALSE, E.down, FALSE)
+ ai_waypoint = last_waypoint
+ call_bot(calling_ai, ai_waypoint)
+ else
+ E.travel(TRUE, src, FALSE, E.up, FALSE)
+ ai_waypoint = last_waypoint
+ call_bot(calling_ai, ai_waypoint)
+
+ if(bot_z_mode == BOT_Z_MODE_PATROLLING)
+ if(E)
+ if(z > original_patrol.z)
+ E.travel(FALSE, src, FALSE, E.down, FALSE)
+ patrol_target = original_patrol
+ calc_path()
+ else
+ E.travel(TRUE, src, FALSE, E.up, FALSE)
+ patrol_target = original_patrol
+ calc_path()
+ if(bot_z_mode == BOT_Z_MODE_SUMMONED)
+ if(E)
+ if(z > last_summon.z)
+ E.travel(FALSE, src, FALSE, E.down, FALSE)
+ summon_target = last_summon
+ calc_summon_path()
+ else if(z < last_summon.z)
+ E.travel(TRUE, src, FALSE, E.up, FALSE)
+ summon_target = last_summon
+ calc_summon_path()
+
+//BOT MULTI-Z MOVEMENT
+/mob/living/simple_animal/bot/proc/call_bot_z_move(caller, turf/ori_dest, message=TRUE)
+ //For giving the bot temporary all-access.
+ var/obj/item/card/id/all_access = new /obj/item/card/id
+ var/datum/job/captain/all = new/datum/job/captain
+ all_access.access = all.get_access()
+ bot_z_mode = BOT_Z_MODE_AI_CALLED
+
+ var/target
+ var/turf/destination
+ if(!is_reserved_level(z))
+ if(z > ori_dest.z)
+ target = DOWN
+ if(z < ori_dest.z)
+ target = UP
+
+ if(target == UP || target == DOWN)
+ var/new_target = find_nearest_bot_elevator(target)
+
+ if(!new_target)
+ return
+
+ destination = get_turf(new_target)
+
+ set_path(get_path_to(src, destination, 200, id=all_access))
+ ai_waypoint = destination
+
+ if(path && path.len) //Ensures that a valid path is calculated!
+ var/end_area = get_area_name(destination)
+ if(!on)
+ turn_on() //Saves the AI the hassle of having to activate a bot manually.
+ access_card = all_access //Give the bot all-access while under the AI's command.
+ if(client)
+ reset_access_timer_id = addtimer(CALLBACK (src, .proc/bot_reset), 600, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE) //if the bot is player controlled, they get the extra access for a limited time
+ to_chat(src, "Priority waypoint set by [icon2html(calling_ai, src)] [caller]. Proceed to [end_area].
[path.len-1] meters to destination. You have been granted additional door access for 60 seconds.")
+ pathset = 1
+ mode = BOT_RESPONDING
+ tries = 0
+ else
+ if(message)
+ to_chat(calling_ai, "Failed to calculate a valid route. Ensure destination is clear of obstructions and within range.")
+ calling_ai = null
+ set_path(null)
+
+//PATROL SECTION
+/mob/living/simple_animal/bot/proc/go_up_or_down(direction)
+ //For giving the bot temporary all-access.
+ var/obj/item/card/id/all_access = new /obj/item/card/id
+ var/datum/job/captain/all = new/datum/job/captain
+ all_access.access = all.get_access()
+ bot_z_mode = BOT_Z_MODE_PATROLLING
+
+ if(!is_reserved_level(z) && is_station_level(z))
+ var/new_target = find_nearest_bot_elevator(direction)
+
+ if(!new_target)
+ return
+ patrol_target = get_turf(new_target)
+ set_path(get_path_to(src, patrol_target, 200, id=all_access))
+
+/mob/living/simple_animal/bot/proc/summon_up_or_down(direction)
+ bot_z_mode = BOT_Z_MODE_SUMMONED
+
+ if(!is_reserved_level(z) && is_station_level(z))
+ var/new_target = find_nearest_bot_elevator(direction)
+
+ var/target
+ if(!new_target)
+ return
+ target = get_turf(new_target)
+ last_summon = summon_target
+ summon_target = target
+ set_path(get_path_to(src, summon_target, 200, id=access_card))
+
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 4af0591aa84..ec09d52ae5f 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -45,9 +45,17 @@
var/reached_target = 1 //true if already reached the target
+<<<<<<< HEAD
var/auto_return = 1 // true if auto return to home beacon after unload
var/auto_pickup = 1 // true if auto-pickup at beacon
var/report_delivery = 1 // true if bot will announce an arrival to a location.
+=======
+ var/auto_return = TRUE /// true if auto return to home beacon after unload
+ var/auto_pickup = TRUE /// true if auto-pickup at beacon
+ var/report_delivery = TRUE /// true if bot will announce an arrival to a location.
+ var/turf/last_target
+ var/mulebot_z_mode
+>>>>>>> 197e63bb5e... [PORT] Bot Multi-Z Movement (#7587)
var/obj/item/stock_parts/cell/cell
var/bloodiness = 0
@@ -81,7 +89,7 @@
id = new_id
if(paicard)
bot_name = "\improper MULEbot ([new_id])"
- else
+ else if(name == "\improper MULEbot")
name = "\improper MULEbot ([new_id])"
/mob/living/simple_animal/bot/mulebot/bot_reset()
@@ -491,6 +499,10 @@
if(BOT_DELIVER, BOT_GO_HOME, BOT_BLOCKED) // navigating to deliver,home, or blocked
if(loc == target) // reached target
+ if(last_target != null)
+ if(z != last_target.z)
+ mulebot_z_movement()
+ return
at_target()
return
@@ -579,8 +591,42 @@
// calculates a path to the current destination
// given an optional turf to avoid
/mob/living/simple_animal/bot/mulebot/calc_path(turf/avoid = null)
+ if(!is_reserved_level(z) && is_station_level(z))
+ if(target != null)
+ if(z > target.z)
+ mule_up_or_down(DOWN)
+ return
+ if(z < target.z)
+ mule_up_or_down(UP)
+ return
path = get_path_to(src, target, 250, id=access_card, exclude=avoid)
+/mob/living/simple_animal/bot/mulebot/proc/mule_up_or_down(direction)
+ if(!is_reserved_level(z) && is_station_level(z))
+ var/new_target = find_nearest_bot_elevator(direction)
+
+ var/go_here
+ mulebot_z_mode = MULEBOT_Z_MODE_ACTIVE
+ if(!new_target)
+ return
+ go_here = get_turf(new_target)
+ last_target = target
+ target = go_here
+ path = get_path_to(src, target, 250, id=access_card)
+
+/mob/living/simple_animal/bot/mulebot/proc/mulebot_z_movement()
+ var/obj/structure/bot_elevator/E = locate(/obj/structure/bot_elevator) in get_turf(src)
+ if(mulebot_z_mode == MULEBOT_Z_MODE_ACTIVE)
+ if(E)
+ if(z > last_target.z)
+ E.travel(FALSE, src, FALSE, E.down, FALSE)
+ target = last_target
+ calc_path()
+ else
+ E.travel(TRUE, src, FALSE, E.up, FALSE)
+ target = last_target
+ calc_path()
+
// sets the current destination
// signals all beacons matching the delivery code
// beacons will return a signal giving their locations
diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm
index 2413ccb62c5..a68f67db611 100644
--- a/code/modules/modular_computers/file_system/programs/robocontrol.dm
+++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm
@@ -39,6 +39,7 @@
for(var/B in GLOB.bots_list)
var/mob/living/simple_animal/bot/Bot = B
if(!Bot.on || Bot.get_virtual_z_level() != zlevel || Bot.remote_disabled) //Only non-emagged bots on the same Z-level are detected!
+<<<<<<< HEAD
continue //Also, the PDA must have access to the bot type.
var/list/newbot = list("name" = Bot.name, "mode" = Bot.get_mode_ui(), "model" = Bot.model, "locat" = get_area(Bot), "bot_ref" = REF(Bot), "mule_check" = FALSE)
if(Bot.bot_type == MULE_BOT)
@@ -48,6 +49,35 @@
data["load"] = MULE.load.name
newbot["mule_check"] = TRUE
botlist += list(newbot)
+=======
+ continue
+ else if(computer) //Also, the inserted ID must have access to the bot type
+ var/obj/item/card/id/id_card = card_slot ? card_slot.stored_card : null
+ if(!id_card && !Bot.bot_core.allowed(current_user))
+ continue
+ else if(id_card && !Bot.bot_core.check_access(id_card))
+ continue
+ if(Bot.get_virtual_z_level() in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ if(zlevel in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ var/list/newbot = list("name" = Bot.name, "mode" = Bot.get_mode_ui(), "model" = Bot.model, "locat" = get_area(Bot), "bot_ref" = REF(Bot), "mule_check" = FALSE)
+ if(Bot.bot_type == MULE_BOT)
+ var/mob/living/simple_animal/bot/mulebot/MULE = Bot
+ mulelist += list(list("name" = MULE.name, "dest" = MULE.destination, "power" = MULE.cell ? MULE.cell.percent() : 0, "home" = MULE.home_destination, "autoReturn" = MULE.auto_return, "autoPickup" = MULE.auto_pickup, "reportDelivery" = MULE.report_delivery, "mule_ref" = REF(MULE)))
+ if(MULE.load)
+ data["load"] = MULE.load.name
+ newbot["mule_check"] = TRUE
+ botlist += list(newbot)
+ else if (Bot.get_virtual_z_level() == zlevel)
+ if(!(zlevel in SSmapping.levels_by_trait(ZTRAIT_STATION)))
+ var/list/newbot = list("name" = Bot.name, "mode" = Bot.get_mode_ui(), "model" = Bot.model, "locat" = get_area(Bot), "bot_ref" = REF(Bot), "mule_check" = FALSE)
+ if(Bot.bot_type == MULE_BOT)
+ var/mob/living/simple_animal/bot/mulebot/MULE = Bot
+ mulelist += list(list("name" = MULE.name, "dest" = MULE.destination, "power" = MULE.cell ? MULE.cell.percent() : 0, "home" = MULE.home_destination, "autoReturn" = MULE.auto_return, "autoPickup" = MULE.auto_pickup, "reportDelivery" = MULE.report_delivery, "mule_ref" = REF(MULE)))
+ if(MULE.load)
+ data["load"] = MULE.load.name
+ newbot["mule_check"] = TRUE
+ botlist += list(newbot)
+>>>>>>> 197e63bb5e... [PORT] Bot Multi-Z Movement (#7587)
data["bots"] = botlist
data["mules"] = mulelist
diff --git a/icons/obj/structures/bot_elevator.dmi b/icons/obj/structures/bot_elevator.dmi
new file mode 100644
index 00000000000..d06040dd808
Binary files /dev/null and b/icons/obj/structures/bot_elevator.dmi differ