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