diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index 24a2db67d73..5d576959fb7 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -354,12 +354,12 @@ var/muleData[0] muleData["name"] = M.suffix muleData["location"] = get_area(M) - muleData["mode"] = M.mode + muleData["paused"] = M.paused muleData["home"] = M.homeName muleData["target"] = M.targetName muleData["ref"] = "\ref[M]" muleData["load"] = M.load ? M.load.name : "Nothing" - + mulebotsData[++mulebotsData.len] = muleData.Copy() values["mulebotcount"] = count diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index cd2dbb8642d..5679cc1a461 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -6,6 +6,7 @@ layer = MOB_LAYER universal_speak = 1 density = 0 + var/obj/item/weapon/card/id/botcard = null var/list/botcard_access = list() var/on = 1 @@ -13,11 +14,30 @@ var/locked = 1 var/emagged = 0 var/light_strength = 3 + var/busy = 0 var/obj/access_scanner = null var/list/req_access = list() var/list/req_one_access = list() + var/atom/target = null + var/list/ignore_list = list() + var/list/patrol_path = list() + var/list/target_path = list() + var/turf/obstacle = null + + var/wait_if_pulled = 0 // Only applies to moving to the target + var/will_patrol = 0 // Not a setting - whether or no this type of bots patrols at all + var/patrol_speed = 1 // How many times per tick we move when patrolling + var/target_speed = 2 // Ditto for chasing the target + var/min_target_dist = 1 // How close we try to get to the target + var/max_target_dist = 50 // How far we are willing to go + var/max_patrol_dist = 250 + + var/target_patience = 5 + var/frustration = 0 + var/max_frustration = 0 + /mob/living/bot/New() ..() update_icons() @@ -40,6 +60,9 @@ stunned = 0 paralysis = 0 + if(on && !client && !busy) + handleAI() + /mob/living/bot/updatehealth() if(status_flags & GODMODE) health = maxHealth @@ -109,6 +132,123 @@ /mob/living/bot/emag_act(var/remaining_charges, var/mob/user) return 0 +/mob/living/bot/proc/handleAI() + if(ignore_list.len) + for(var/atom/A in ignore_list) + if(!A || !A.loc || prob(1)) + ignore_list -= A + handleRegular() + if(target && confirmTarget(target)) + if(Adjacent(target)) + handleAdjacentTarget() + else + handleRangedTarget() + if(!wait_if_pulled || !pulledby) + for(var/i = 1 to target_speed) + stepToTarget() + if(i < target_speed) + sleep(20 / target_speed) + if(max_frustration && frustration > max_frustration * target_speed) + handleFrustrated(1) + else + resetTarget() + lookForTargets() + if(will_patrol && !pulledby && !target) + if(patrol_path.len) + for(var/i = 1 to patrol_speed) + handlePatrol() + if(i < patrol_speed) + sleep(20 / patrol_speed) + if(max_frustration && frustration > max_frustration * patrol_speed) + handleFrustrated(0) + else + startPatrol() + else + handleIdle() + +/mob/living/bot/proc/handleRegular() + return + +/mob/living/bot/proc/handleAdjacentTarget() + return + +/mob/living/bot/proc/handleRangedTarget() + return + +/mob/living/bot/proc/stepToTarget() + if(!target || !target.loc) + return + if(get_dist(src, target) > min_target_dist) + if(!target_path.len || get_turf(target) != target_path[target_path.len]) + calcTargetPath() + if(makeStep(target_path)) + frustration = 0 + else if(max_frustration) + ++frustration + return + +/mob/living/bot/proc/handleFrustrated(var/targ) + obstacle = targ ? target_path[1] : patrol_path[1] + target_path = list() + patrol_path = list() + return + +/mob/living/bot/proc/lookForTargets() + return + +/mob/living/bot/proc/confirmTarget(var/atom/A) + if(A.invisibility >= INVISIBILITY_LEVEL_ONE) + return 0 + if(A in ignore_list) + return 0 + if(!A.loc) + return 0 + return 1 + +/mob/living/bot/proc/handlePatrol() + makeStep(patrol_path) + return + +/mob/living/bot/proc/startPatrol() + var/turf/T = getPatrolTurf() + if(T) + patrol_path = AStar(get_turf(loc), T, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, max_patrol_dist, id = botcard, exclude = obstacle) + if(!patrol_path) + patrol_path = list() + obstacle = null + return + +/mob/living/bot/proc/getPatrolTurf() + return null + +/mob/living/bot/proc/handleIdle() + return + +/mob/living/bot/proc/calcTargetPath() + target_path = AStar(get_turf(loc), get_turf(target), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, max_target_dist, id = botcard, exclude = obstacle) + if(!target_path) + if(target && target.loc) + ignore_list |= target + resetTarget() + obstacle = null + return + +/mob/living/bot/proc/makeStep(var/list/path) + if(!path.len) + return 0 + var/turf/T = path[1] + if(get_turf(src) == T) + path -= T + return makeStep(path) + + return step_towards(src, T) + +/mob/living/bot/proc/resetTarget() + target = null + target_path = list() + frustration = 0 + obstacle = null + /mob/living/bot/proc/turn_on() if(stat) return 0 @@ -121,6 +261,9 @@ on = 0 set_light(0) update_icons() + resetTarget() + patrol_path = list() + ignore_list = list() /mob/living/bot/proc/explode() qdel(src) @@ -141,7 +284,7 @@ // for(var/turf/simulated/t in oview(src,1)) for(var/d in cardinal) - var/turf/simulated/T = get_step(src, d) + var/turf/T = get_step(src, d) if(istype(T) && !T.density) if(!LinkBlockedWithAccess(src, T, ID)) L.Add(T) diff --git a/code/modules/mob/living/bot/cleanbot.dm b/code/modules/mob/living/bot/cleanbot.dm index 30b1c0a47b4..60a431aad30 100644 --- a/code/modules/mob/living/bot/cleanbot.dm +++ b/code/modules/mob/living/bot/cleanbot.dm @@ -6,18 +6,8 @@ botcard_access = list(access_janitor, access_maint_tunnels) locked = 0 // Start unlocked so roboticist can set them to patrol. - - var/obj/effect/decal/cleanable/target - var/list/path = list() - var/list/patrol_path = list() - var/list/ignorelist = list() - - var/obj/cleanbot_listener/listener = null - var/beacon_freq = 1445 // navigation beacon frequency - var/signal_sent = 0 - var/closest_dist - var/next_dest - var/next_dest_loc + wait_if_pulled = 1 + min_target_dist = 0 var/cleaning = 0 var/screwloose = 0 @@ -26,48 +16,11 @@ var/blood = 1 var/list/target_types = list() - var/maximum_search_range = 7 - /mob/living/bot/cleanbot/New() ..() get_targets() - listener = new /obj/cleanbot_listener(src) - listener.cleanbot = src - - if(radio_controller) - radio_controller.add_object(listener, beacon_freq, filter = RADIO_NAVBEACONS) - -/mob/living/bot/cleanbot/proc/handle_target() - if(loc == target.loc) - if(!cleaning) - UnarmedAttack(target) - return 1 - if(!path.len) -// spawn(0) - path = AStar(loc, target.loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id = botcard) - if(!path) - custom_emote(2, "[src] can't reach the target and is giving up.") - target = null - path = list() - return - if(path.len) - step_to(src, path[1]) - path -= path[1] - return 1 - return - -/mob/living/bot/cleanbot/Life() - ..() - - if(!on) - return - - if(client) - return - if(cleaning) - return - +/mob/living/bot/cleanbot/handleIdle() if(!screwloose && !oddbutton && prob(5)) custom_emote(2, "makes an excited beeping booping sound!") @@ -79,68 +32,27 @@ if(oddbutton && prob(5)) // Make a big mess visible_message("Something flies out of [src]. He seems to be acting oddly.") var/obj/effect/decal/cleanable/blood/gibs/gib = new /obj/effect/decal/cleanable/blood/gibs(loc) - ignorelist += gib + ignore_list += gib spawn(600) - ignorelist -= gib + ignore_list -= gib - // Find a target - - if(pulledby) // Don't wiggle if someone pulls you - patrol_path = list() - return - - var/found_spot - search_loop: - for(var/i=0, i <= maximum_search_range, i++) - for(var/obj/effect/decal/cleanable/D in view(i, src)) - if(D in ignorelist) - continue - for(var/T in target_types) - if(istype(D, T)) - patrol_path = list() - target = D - found_spot = handle_target() - if (found_spot) - break search_loop - else - target = null - continue // no need to check the other types - - - if(!found_spot && !target) // No targets in range - if(!patrol_path || !patrol_path.len) - if(!signal_sent || signal_sent > world.time + 200) // Waited enough or didn't send yet - var/datum/radio_frequency/frequency = radio_controller.return_frequency(beacon_freq) - if(!frequency) - return - - closest_dist = 9999 - next_dest = null - next_dest_loc = null - - var/datum/signal/signal = new() - signal.source = src - signal.transmission_method = 1 - signal.data = list("findbeakon" = "patrol") - frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) - signal_sent = world.time - else - if(next_dest) - next_dest_loc = listener.memorized[next_dest] - if(next_dest_loc) - patrol_path = AStar(loc, next_dest_loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 120, id = botcard, exclude = null) - signal_sent = 0 - else - if(pulledby) // Don't wiggle if someone pulls you - patrol_path = list() - return - if(patrol_path[1] == loc) - patrol_path -= patrol_path[1] - var/moved = step_towards(src, patrol_path[1]) - if(moved) - patrol_path -= patrol_path[1] +/mob/living/bot/cleanbot/lookForTargets() + for(var/obj/effect/decal/cleanable/D in view(world.view, src)) // There was some odd code to make it start with nearest decals, it's unnecessary, this works + if(confirmTarget(D)) + target = D + return +/mob/living/bot/cleanbot/confirmTarget(var/obj/effect/decal/cleanable/D) + if(!..()) + return 0 + for(var/T in target_types) + if(istype(D, T)) + return 1 + return 0 +/mob/living/bot/cleanbot/handleAdjacentTarget() + if(get_turf(target) == src.loc) + UnarmedAttack(target) /mob/living/bot/cleanbot/UnarmedAttack(var/obj/effect/decal/cleanable/D, var/proximity) if(!..()) @@ -152,7 +64,7 @@ if(D.loc != loc) return - cleaning = 1 + busy = 1 custom_emote(2, "begins to clean up \the [D]") update_icons() var/cleantime = istype(D, /obj/effect/decal/cleanable/dirt) ? 10 : 50 @@ -165,7 +77,7 @@ qdel(D) if(D == target) target = null - cleaning = 0 + busy = 0 update_icons() /mob/living/bot/cleanbot/explode() @@ -185,17 +97,11 @@ return /mob/living/bot/cleanbot/update_icons() - if(cleaning) + if(busy) icon_state = "cleanbot-c" else icon_state = "cleanbot[on]" -/mob/living/bot/cleanbot/turn_off() - ..() - target = null - path = list() - patrol_path = list() - /mob/living/bot/cleanbot/attack_hand(var/mob/user) var/dat dat += "Automatic Station Cleaner v1.0

" @@ -230,10 +136,6 @@ if("patrol") should_patrol = !should_patrol patrol_path = null - if("freq") - var/freq = text2num(input("Select frequency for navigation beacons", "Frequnecy", num2text(beacon_freq / 10))) * 10 - if (freq > 0) - beacon_freq = freq if("screw") screwloose = !screwloose usr << "You twiddle the screw." @@ -264,25 +166,6 @@ if(blood) target_types += /obj/effect/decal/cleanable/blood -/* Radio object that listens to signals */ - -/obj/cleanbot_listener - var/mob/living/bot/cleanbot/cleanbot = null - var/list/memorized = list() - -/obj/cleanbot_listener/receive_signal(var/datum/signal/signal) - var/recv = signal.data["beacon"] - var/valid = signal.data["patrol"] - if(!recv || !valid || !cleanbot) - return - - var/dist = get_dist(cleanbot, signal.source.loc) - memorized[recv] = signal.source.loc - - if(dist < cleanbot.closest_dist) // We check all signals, choosing the closest beakon; then we move to the NEXT one after the closest one - cleanbot.closest_dist = dist - cleanbot.next_dest = signal.data["next_patrol"] - /* Assembly */ /obj/item/weapon/bucket_sensor diff --git a/code/modules/mob/living/bot/ed209bot.dm b/code/modules/mob/living/bot/ed209bot.dm index 507e63c4abe..3c50432025d 100644 --- a/code/modules/mob/living/bot/ed209bot.dm +++ b/code/modules/mob/living/bot/ed209bot.dm @@ -7,7 +7,6 @@ health = 100 maxHealth = 100 - bot_version = "2.5" is_ranged = 1 preparing_arrest_sounds = new() @@ -20,7 +19,7 @@ var/last_shot = 0 /mob/living/bot/secbot/ed209/update_icons() - if(on && is_attacking) + if(on && busy) icon_state = "ed209-c" else icon_state = "ed209[on]" @@ -50,6 +49,9 @@ new /obj/effect/decal/cleanable/blood/oil(Tsec) qdel(src) +/mob/living/bot/secbot/ed209/handleRangedTarget() + RangedAttack(target) + /mob/living/bot/secbot/ed209/RangedAttack(var/atom/A) if(last_shot + shot_delay > world.time) src << "You are not ready to fire yet!" diff --git a/code/modules/mob/living/bot/farmbot.dm b/code/modules/mob/living/bot/farmbot.dm index d8e0498e6eb..1187d16b5fd 100644 --- a/code/modules/mob/living/bot/farmbot.dm +++ b/code/modules/mob/living/bot/farmbot.dm @@ -22,17 +22,12 @@ var/obj/structure/reagent_dispensers/watertank/tank - var/attacking = 0 - var/list/path = list() - var/atom/target - var/frustration = 0 - -/mob/living/bot/farmbot/New() - ..() - spawn(5) - tank = locate() in contents - if(!tank) - tank = new /obj/structure/reagent_dispensers/watertank(src) +/mob/living/bot/farmbot/New(var/newloc, var/newTank) + ..(newloc) + if(!newTank) + newTank = new /obj/structure/reagent_dispensers/watertank(src) + tank = newTank + tank.forceMove(src) /mob/living/bot/farmbot/attack_hand(var/mob/user as mob) . = ..() @@ -110,63 +105,55 @@ else icon_state = "farmbot[on]" -/mob/living/bot/farmbot/Life() - ..() - if(!on) - return +/mob/living/bot/farmbot/handleRegular() if(emagged && prob(1)) flick("farmbot_broke", src) - if(client) - return - if(target) - if(Adjacent(target)) - UnarmedAttack(target) - path = list() - target = null - else - if(path.len && frustration < 5) - if(path[1] == loc) - path -= path[1] - var/t = step_towards(src, path[1]) - if(t) - path -= path[1] - else - ++frustration - else - path = list() - target = null +/mob/living/bot/farmbot/handleAdjacentTarget() + UnarmedAttack(target) + +/mob/living/bot/farmbot/lookForTargets() + if(emagged) + for(var/mob/living/carbon/human/H in view(7, src)) + target = H + return else - if(emagged) - for(var/mob/living/carbon/human/H in view(7, src)) - target = H - break - else - for(var/obj/machinery/portable_atmospherics/hydroponics/tray in view(7, src)) - if(process_tray(tray)) - target = tray - frustration = 0 - break - if(!target && refills_water && tank && tank.reagents.total_volume < tank.reagents.maximum_volume) - for(var/obj/structure/sink/source in view(7, src)) - target = source - frustration = 0 - break - if(target) - var/t = get_dir(target, src) // Turf with the tray is impassable, so a* can't navigate directly to it - path = AStar(loc, get_step(target, t), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id = botcard) - if(!path) - path = list() + for(var/obj/machinery/portable_atmospherics/hydroponics/tray in view(7, src)) + if(confirmTarget(tray)) + target = tray + return + if(!target && refills_water && tank && tank.reagents.total_volume < tank.reagents.maximum_volume) + for(var/obj/structure/sink/source in view(7, src)) + target = source + return + +/mob/living/bot/farmbot/calcTargetPath() // We need to land NEXT to the tray, because the tray itself is impassable + for(var/trayDir in list(NORTH, SOUTH, EAST, WEST)) + target_path = AStar(get_turf(loc), get_step(get_turf(target), trayDir), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, max_target_dist, id = botcard) + if(target_path) + break + if(!target_path) + ignore_list |= target + target = null + target_path = list() + return + +/mob/living/bot/farmbot/stepToTarget() // Same reason + var/turf/T = get_turf(target) + if(!target_path.len || !T.Adjacent(target_path[target_path.len])) + calcTargetPath() + makeStep(target_path) + return /mob/living/bot/farmbot/UnarmedAttack(var/atom/A, var/proximity) if(!..()) return - if(attacking) + if(busy) return if(istype(A, /obj/machinery/portable_atmospherics/hydroponics)) var/obj/machinery/portable_atmospherics/hydroponics/T = A - var/t = process_tray(T) + var/t = confirmTarget(T) switch(t) if(0) return @@ -174,7 +161,7 @@ action = "water" // Needs a better one update_icons() visible_message("[src] starts [T.dead? "removing the plant from" : "harvesting"] \the [A].") - attacking = 1 + busy = 1 if(do_after(src, 30)) visible_message("[src] [T.dead? "removes the plant from" : "harvests"] \the [A].") T.attack_hand(src) @@ -182,7 +169,7 @@ action = "water" update_icons() visible_message("[src] starts watering \the [A].") - attacking = 1 + busy = 1 if(do_after(src, 30)) playsound(loc, 'sound/effects/slosh.ogg', 25, 1) visible_message("[src] waters \the [A].") @@ -191,7 +178,7 @@ action = "hoe" update_icons() visible_message("[src] starts uprooting the weeds in \the [A].") - attacking = 1 + busy = 1 if(do_after(src, 30)) visible_message("[src] uproots the weeds in \the [A].") T.weedlevel = 0 @@ -199,11 +186,11 @@ action = "fertile" update_icons() visible_message("[src] starts fertilizing \the [A].") - attacking = 1 + busy = 1 if(do_after(src, 30)) - visible_message("[src] waters \the [A].") + visible_message("[src] fertilizes \the [A].") T.reagents.add_reagent("ammonia", 10) - attacking = 0 + busy = 0 action = "" update_icons() T.update_icon() @@ -213,20 +200,20 @@ action = "water" update_icons() visible_message("[src] starts refilling its tank from \the [A].") - attacking = 1 + busy = 1 while(do_after(src, 10) && tank.reagents.total_volume < tank.reagents.maximum_volume) tank.reagents.add_reagent("water", 10) if(prob(5)) playsound(loc, 'sound/effects/slosh.ogg', 25, 1) - attacking = 0 + busy = 0 action = "" update_icons() visible_message("[src] finishes refilling its tank.") else if(emagged && ishuman(A)) var/action = pick("weed", "water") - attacking = 1 + busy = 1 spawn(50) // Some delay - attacking = 0 + busy = 0 switch(action) if("weed") flick("farmbot_hoe", src) @@ -238,7 +225,8 @@ A.attack_generic(src, 5, t) if("water") flick("farmbot_water", src) - visible_message("[src] splashes [A] with water!") // That's it. RP effect. + visible_message("[src] splashes [A] with water!") + tank.reagents.splash(A, 100) /mob/living/bot/farmbot/explode() visible_message("[src] blows apart!") @@ -261,8 +249,22 @@ qdel(src) return -/mob/living/bot/farmbot/proc/process_tray(var/obj/machinery/portable_atmospherics/hydroponics/tray) - if(!tray || !istype(tray)) +/mob/living/bot/farmbot/confirmTarget(var/atom/targ) + if(!..()) + return 0 + + if(emagged && ishuman(targ)) + if(targ in view(world.view, src)) + return 1 + return 0 + + if(istype(targ, /obj/structure/sink)) + if(!tank || tank.reagents.total_volume >= tank.reagents.maximum_volume) + return 0 + return 1 + + var/obj/machinery/portable_atmospherics/hydroponics/tray = targ + if(!istype(tray)) return 0 if(tray.closed_system || !tray.seed) @@ -291,28 +293,28 @@ icon_state = "water_arm" var/build_step = 0 var/created_name = "Farmbot" + var/obj/tank w_class = 3.0 - New() - ..() - spawn(4) // If an admin spawned it, it won't have a watertank it, so lets make one for em! - var tank = locate(/obj/structure/reagent_dispensers/watertank) in contents - if(!tank) - new /obj/structure/reagent_dispensers/watertank(src) - +/obj/item/weapon/farmbot_arm_assembly/New(var/newloc, var/theTank) + ..(newloc) + if(!theTank) // If an admin spawned it, it won't have a watertank it, so lets make one for em! + tank = new /obj/structure/reagent_dispensers/watertank(src) + else + tank = theTank + tank.forceMove(src) /obj/structure/reagent_dispensers/watertank/attackby(var/obj/item/robot_parts/S, mob/user as mob) if ((!istype(S, /obj/item/robot_parts/l_arm)) && (!istype(S, /obj/item/robot_parts/r_arm))) ..() return - var/obj/item/weapon/farmbot_arm_assembly/A = new /obj/item/weapon/farmbot_arm_assembly(loc) - user << "You add the robot arm to [src]." - loc = A //Place the water tank into the assembly, it will be needed for the finished bot user.drop_from_inventory(S) qdel(S) + new /obj/item/weapon/farmbot_arm_assembly(loc, src) + /obj/item/weapon/farmbot_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob) ..() if((istype(W, /obj/item/device/analyzer/plant_analyzer)) && (build_step == 0)) @@ -339,10 +341,7 @@ else if((isprox(W)) && (build_step == 3)) build_step++ user << "You complete the Farmbot! Beep boop." - var/mob/living/bot/farmbot/S = new /mob/living/bot/farmbot(get_turf(src)) - for(var/obj/structure/reagent_dispensers/watertank/wTank in contents) - wTank.loc = S - S.tank = wTank + var/mob/living/bot/farmbot/S = new /mob/living/bot/farmbot(get_turf(src), tank) S.name = created_name user.remove_from_mob(W) qdel(W) diff --git a/code/modules/mob/living/bot/floorbot.dm b/code/modules/mob/living/bot/floorbot.dm index 783af91c659..cb297178868 100644 --- a/code/modules/mob/living/bot/floorbot.dm +++ b/code/modules/mob/living/bot/floorbot.dm @@ -3,22 +3,20 @@ desc = "A little floor repairing robot, he looks so excited!" icon_state = "floorbot0" req_access = list(access_construction) + wait_if_pulled = 1 + min_target_dist = 0 var/amount = 10 // 1 for tile, 2 for lattice var/maxAmount = 60 var/tilemake = 0 // When it reaches 100, bot makes a tile - var/repairing = 0 var/improvefloors = 0 var/eattiles = 0 var/maketiles = 0 var/targetdirection = null - var/list/path = list() - var/list/ignorelist = list() - var/turf/target var/floor_build_type = /decl/flooring/tiling // Basic steel floor. /mob/living/bot/floorbot/update_icons() - if(repairing) + if(busy) icon_state = "floorbot-c" else if(amount > 0) icon_state = "floorbot[on]" @@ -31,7 +29,7 @@ dat += "Automatic Station Floor Repairer v1.0

" dat += "Status: [src.on ? "On" : "Off"]
" dat += "Maintenance panel is [open ? "opened" : "closed"]
" - //dat += "Tiles left: [amount]
" + dat += "Tiles left: [amount]
" dat += "Behvaiour controls are [locked ? "locked" : "unlocked"]
" if(!locked || issilicon(user)) dat += "Improves floors: [improvefloors ? "Yes" : "No"]
" @@ -89,100 +87,90 @@ targetdirection = null attack_hand(usr) -/mob/living/bot/floorbot/turn_off() - ..() - target = null - path = list() - ignorelist = list() - -/mob/living/bot/floorbot/Life() - ..() - - if(!on) - return - +/mob/living/bot/floorbot/handleRegular() ++tilemake if(tilemake >= 100) tilemake = 0 addTiles(1) - if(client) - return - - if(prob(5)) + if(prob(1)) custom_emote(2, "makes an excited booping beeping sound!") - if(ignorelist.len) // Don't stick forever - for(var/T in ignorelist) - if(prob(1)) - ignorelist -= T - - if(amount && !emagged) - if(!target && targetdirection) // Building a bridge - var/turf/T = get_step(src, targetdirection) - while(T in range(src)) - if(istype(T, /turf/space)) - target = T - break - T = get_step(T, targetdirection) - - if(!target) // Fixing floors - for(var/turf/T in view(src)) - if(T.loc.name == "Space") - continue - if(T in ignorelist) - continue - if(istype(T, /turf/space)) - if(get_turf(T) == loc || prob(40)) // So they target the same tile all the time - target = T - if(improvefloors && istype(T, /turf/simulated/floor)) - var/turf/simulated/floor/F = T - if(!F.flooring && (get_turf(T) == loc || prob(40))) - target = T - - if(emagged) // Time to griff - for(var/turf/simulated/floor/D in view(src)) - if(D.loc.name == "Space") - continue - if(D in ignorelist) - continue - target = D - break - - if(!target && amount < maxAmount && eattiles || maketiles) // Eat tiles - if(eattiles) - for(var/obj/item/stack/tile/floor/T in view(src)) - if(T in ignorelist) - continue - target = T - break - if(maketiles && !target) - for(var/obj/item/stack/material/steel/T in view(src)) - if(T in ignorelist) - continue - target = T - break - - if(target && get_turf(target) == loc) +/mob/living/bot/floorbot/handleAdjacentTarget() + if(get_turf(target) == src.loc) UnarmedAttack(target) - if(target && get_turf(target) != loc && !path.len) - spawn(0) - path = AStar(loc, get_turf(target), /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30, id = botcard) - if(!path) - path = list() - ignorelist += target - target = null +/mob/living/bot/floorbot/lookForTargets() + if(emagged) // Time to griff + for(var/turf/simulated/floor/D in view(src)) + if(confirmTarget(D)) + target = D + return - if(path.len) - step_to(src, path[1]) - path -= path[1] + else if(amount) + if(targetdirection) // Building a bridge + var/turf/T = get_step(src, targetdirection) + while(T in range(world.view, src)) + if(confirmTarget(T)) + target = T + return + T = get_step(T, targetdirection) + + else // Fixing floors + for(var/turf/space/T in view(src)) // Breaches are of higher priority + if(confirmTarget(T)) + target = T + return + + for(var/turf/simulated/mineral/floor/T in view(src)) // Asteroids are of smaller priority + if(confirmTarget(T)) + target = T + return + + if(improvefloors) + for(var/turf/simulated/floor/T in view(src)) + if(confirmTarget(T)) + target = T + return + + if(amount < maxAmount && (eattiles || maketiles)) + for(var/obj/item/stack/S in view(src)) + if(confirmTarget(S)) + target = S + return + +/mob/living/bot/floorbot/confirmTarget(var/atom/A) // The fact that we do some checks twice may seem confusing but remember that the bot's settings may be toggled while it's moving and we want them to stop in that case + if(!..()) + return 0 + + if(istype(A, /obj/item/stack/tile/floor)) + return (amount < maxAmount && eattiles) + if(istype(A, /obj/item/stack/material/steel)) + return (amount < maxAmount && maketiles) + + if(A.loc.name == "Space") + return 0 + + if(emagged) + return (istype(A, /turf/simulated/floor)) + + if(!amount) + return 0 + + if(istype(A, /turf/space)) + return 1 + + if(istype(A, /turf/simulated/mineral/floor)) + return 1 + + var/turf/simulated/floor/T = A + return (istype(T) && improvefloors && !T.flooring && (get_turf(T) == loc || prob(40))) /mob/living/bot/floorbot/UnarmedAttack(var/atom/A, var/proximity) if(!..()) return - if(repairing) + if(busy) return if(get_turf(A) != loc) @@ -190,9 +178,9 @@ if(emagged && istype(A, /turf/simulated/floor)) var/turf/simulated/floor/F = A - repairing = 1 + busy = 1 update_icons() - if(F.is_plating()) + if(F.flooring) visible_message("[src] begins to tear the floor tile from the floor!") if(do_after(src, 50)) F.break_tile_to_plating() @@ -203,15 +191,15 @@ F.ReplaceWithLattice() addTiles(1) target = null - repairing = 0 + busy = 0 update_icons() - else if(istype(A, /turf/space)) + else if(istype(A, /turf/space) || istype(A, /turf/simulated/mineral/floor)) var/building = 2 if(locate(/obj/structure/lattice, A)) building = 1 if(amount < building) return - repairing = 1 + busy = 1 update_icons() visible_message("[src] begins to repair the hole.") if(do_after(src, 50)) @@ -223,12 +211,12 @@ I = PoolOrNew(/obj/item/stack/rods, src) A.attackby(I, src) target = null - repairing = 0 + busy = 0 update_icons() else if(istype(A, /turf/simulated/floor)) var/turf/simulated/floor/F = A if(!F.flooring && amount) - repairing = 1 + busy = 1 update_icons() visible_message("[src] begins to improve the floor.") if(do_after(src, 50)) @@ -236,12 +224,12 @@ F.set_flooring(get_flooring_data(floor_build_type)) addTiles(-1) target = null - repairing = 0 + busy = 0 update_icons() else if(istype(A, /obj/item/stack/tile/floor) && amount < maxAmount) var/obj/item/stack/tile/floor/T = A visible_message("[src] begins to collect tiles.") - repairing = 1 + busy = 1 update_icons() if(do_after(src, 20)) if(T) @@ -249,13 +237,13 @@ T.use(eaten) addTiles(eaten) target = null - repairing = 0 + busy = 0 update_icons() else if(istype(A, /obj/item/stack/material) && amount + 4 <= maxAmount) var/obj/item/stack/material/M = A if(M.get_material_name() == DEFAULT_WALL_MATERIAL) visible_message("[src] begins to make tiles.") - repairing = 1 + busy = 1 update_icons() if(do_after(50)) if(M) diff --git a/code/modules/mob/living/bot/medbot.dm b/code/modules/mob/living/bot/medbot.dm index 2fdabecbbcd..e4d39b5afe1 100644 --- a/code/modules/mob/living/bot/medbot.dm +++ b/code/modules/mob/living/bot/medbot.dm @@ -3,21 +3,16 @@ desc = "A little medical robot. He looks somewhat underwhelmed." icon_state = "medibot0" req_access = list(access_medical) - - var/skin = null //Set to "tox", "ointment" or "o2" for the other two firstaid kits. botcard_access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics) + var/skin = null //Set to "tox", "ointment" or "o2" for the other two firstaid kits. + //AI vars - var/frustration = 0 - var/list/path = list() - var/mob/living/carbon/human/patient = null - var/mob/ignored = list() // Used by emag var/last_newpatient_speak = 0 var/vocal = 1 //Healing vars var/obj/item/weapon/reagent_containers/glass/reagent_glass = null //Can be set to draw from this for reagents. - var/currently_healing = 0 var/injection_amount = 15 //How much reagent do we inject at a time? var/heal_threshold = 10 //Start healing when they have this much damage in a category var/use_beaker = 0 //Use reagents in beaker instead of default treatment agents. @@ -29,49 +24,26 @@ var/treatment_emag = "toxin" var/declare_treatment = 0 //When attempting to treat a patient, should it notify everyone wearing medhuds? -/mob/living/bot/medbot/Life() - ..() +/mob/living/bot/medbot/handleIdle() + if(vocal && prob(1)) + var/message = pick("Radar, put a mask on!", "There's always a catch, and it's the best there is.", "I knew it, I should've been a plastic surgeon.", "What kind of medbay is this? Everyone's dropping like dead flies.", "Delicious!") + say(message) - if(!on) - return +/mob/living/bot/medbot/handleAdjacentTarget() + UnarmedAttack(target) - if(!client) +/mob/living/bot/medbot/lookForTargets() + for(var/mob/living/carbon/human/H in view(7, src)) // Time to find a patient! + if(confirmTarget(H)) + target = H + if(last_newpatient_speak + 300 < world.time) + var/message = pick("Hey, [H.name]! Hold on, I'm coming.", "Wait [H.name]! I want to help!", "[H.name], you appear to be injured!") + say(message) + custom_emote(1, "points at [H.name].") + last_newpatient_speak = world.time + break - if(vocal && prob(1)) - var/message = pick("Radar, put a mask on!", "There's always a catch, and it's the best there is.", "I knew it, I should've been a plastic surgeon.", "What kind of medbay is this? Everyone's dropping like dead flies.", "Delicious!") - say(message) - - if(patient) - if(Adjacent(patient)) - if(!currently_healing) - UnarmedAttack(patient) - else - if(path.len && (get_dist(patient, path[path.len]) > 2)) // We have a path, but it's off - path = list() - if(!path.len && (get_dist(src, patient) > 1)) - spawn(0) - path = AStar(loc, get_turf(patient), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id = botcard) - if(!path) - path = list() - if(path.len) - step_to(src, path[1]) - path -= path[1] - ++frustration - if(get_dist(src, patient) > 7 || frustration > 8) - patient = null - else - for(var/mob/living/carbon/human/H in view(7, src)) // Time to find a patient! - if(valid_healing_target(H)) - patient = H - frustration = 0 - if(last_newpatient_speak + 300 < world.time) - var/message = pick("Hey, [H.name]! Hold on, I'm coming.", "Wait [H.name]! I want to help!", "[H.name], you appear to be injured!") - say(message) - custom_emote(1, "points at [H.name].") - last_newpatient_speak = world.time - break - -/mob/living/bot/medbot/UnarmedAttack(var/mob/living/carbon/human/H, var/proximity) +/mob/living/bot/medbot/UnarmedAttack(var/mob/living/carbon/human/H) if(!..()) return @@ -81,25 +53,27 @@ if(!istype(H)) return + if(busy) + return + if(H.stat == DEAD) var/death_message = pick("No! NO!", "Live, damnit! LIVE!", "I... I've never lost a patient before. Not today, I mean.") say(death_message) - patient = null + target = null return - var/t = valid_healing_target(H) + var/t = confirmTarget(H) if(!t) var/message = pick("All patched up!", "An apple a day keeps me away.", "Feel better soon!") say(message) - patient = null + target = null return - icon_state = "medibots" visible_message("[src] is trying to inject [H]!") if(declare_treatment) var/area/location = get_area(src) broadcast_medical_hud_message("[src] is treating [H] in [location]", src) - currently_healing = 1 + busy = 1 update_icons() if(do_mob(src, H, 30)) if(t == 1) @@ -107,14 +81,14 @@ else H.reagents.add_reagent(t, injection_amount) visible_message("[src] injects [H] with the syringe!") - currently_healing = 0 + busy = 0 update_icons() /mob/living/bot/medbot/update_icons() overlays.Cut() if(skin) overlays += image('icons/obj/aibots.dmi', "medskin_[skin]") - if(currently_healing) + if(busy) icon_state = "medibots" else icon_state = "medibot[on]" @@ -226,13 +200,13 @@ user << "You short out [src]'s reagent synthesis circuits." visible_message("[src] buzzes oddly!") flick("medibot_spark", src) - patient = null - currently_healing = 0 + target = null + busy = 0 emagged = 1 on = 1 update_icons() . = 1 - ignored |= user + ignore_list |= user /mob/living/bot/medbot/explode() on = 0 @@ -255,15 +229,15 @@ qdel(src) return -/mob/living/bot/medbot/proc/valid_healing_target(var/mob/living/carbon/human/H) +/mob/living/bot/medbot/confirmTarget(var/mob/living/carbon/human/H) + if(!..()) + return 0 + if(H.stat == DEAD) // He's dead, Jim - return null + return 0 if(H.suiciding) - return null - - if(H in ignored) - return null + return 0 if(emagged) return treatment_emag diff --git a/code/modules/mob/living/bot/mulebot.dm b/code/modules/mob/living/bot/mulebot.dm index a4ede33ab14..c330e0efc0e 100644 --- a/code/modules/mob/living/bot/mulebot.dm +++ b/code/modules/mob/living/bot/mulebot.dm @@ -17,23 +17,22 @@ maxHealth = 150 mob_bump_flag = HEAVY + min_target_dist = 0 + max_target_dist = 250 + target_speed = 3 + max_frustration = 5 botcard_access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station) - var/busy = 0 - var/mode = MULE_IDLE var/atom/movable/load + var/paused = 0 var/crates_only = 1 var/auto_return = 1 var/safety = 1 - var/list/path = list() - var/frustration = 0 - var/turf/target var/targetName var/turf/home var/homeName - var/turf/obstacle var/global/amount = 0 @@ -71,19 +70,6 @@ dat += "Power: [on ? "On" : "Off"]
" if(!open) - dat += "Status: " - switch(mode) - if(MULE_IDLE) - dat += "Ready" - if(MULE_MOVING, MULE_UNLOAD, MULE_PATH_DONE) - dat += "Navigating" - if(MULE_UNLOAD) - dat += "Unloading" - if(MULE_LOST) - dat += "Processing commands" - if(MULE_CALC_MIN to MULE_CALC_MAX) - dat += "Calculating navigation path" - dat += "
Current Load: [load ? load.name : "none"]
" if(locked) @@ -177,10 +163,9 @@ /mob/living/bot/mulebot/proc/obeyCommand(var/command) switch(command) if("Home") - mode = MULE_IDLE + resetTarget() target = home targetName = "Home" - mode = MULE_LOST if("SetD") var/new_dest var/list/beaconlist = new() @@ -192,13 +177,13 @@ else alert("No destination beacons available.") if(new_dest) + resetTarget() target = get_turf(beaconlist[new_dest]) targetName = new_dest if("GoTD") - if(mode == MULE_IDLE) - mode = MULE_LOST + paused = 0 if("Stop") - mode = MULE_IDLE + paused = 1 /mob/living/bot/mulebot/emag_act(var/remaining_charges, var/user) locked = !locked @@ -211,89 +196,49 @@ if(open) icon_state = "mulebot-hatch" return - if(mode == MULE_MOVING || mode == MULE_UNLOAD) + if(target_path.len && !paused) icon_state = "mulebot1" return icon_state = "mulebot0" -/mob/living/bot/mulebot/Life() - ..() - - if(busy) - return - +/mob/living/bot/mulebot/handleRegular() if(!safety && prob(1)) flick("mulebot-emagged", src) + update_icons() - switch(mode) - if(MULE_IDLE) // Idle - return - if(MULE_MOVING) // Moving to target - if(!target) // Return home - if(auto_return && home) - target = home - targetName = "Home" - mode = MULE_LOST - else - mode = MULE_IDLE - update_icons() - return - if(loc == target) // Unload or stop - custom_emote(2, "makes a chiming sound.") - playsound(loc, 'sound/machines/chime.ogg', 50, 0) - mode = MULE_UNLOAD - update_icons() - return - if(path.len) // Move - makeStep() - sleep(10) - if(path.len) - makeStep() - else - mode = MULE_LOST +/mob/living/bot/mulebot/handleFrustrated() + custom_emote(2, "makes a sighing buzz.") + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + ..() - update_icons() - return - if(MULE_UNLOAD) - unload(dir) +/mob/living/bot/mulebot/handleAdjacentTarget() + if(target == src.loc) + custom_emote(2, "makes a chiming sound.") + playsound(loc, 'sound/machines/chime.ogg', 50, 0) + UnarmedAttack(target) + resetTarget() + if(auto_return && home && (loc != home)) + target = home + targetName = "Home" - if(auto_return && home && (loc != home)) - target = home - targetName = "Home" - mode = MULE_LOST - else - mode = MULE_IDLE - update_icons() - return - if(MULE_LOST) // Lost my way - if(target) - spawn(0) - calc_path(obstacle) - mode = MULE_CALC_MIN - else - mode = MULE_IDLE - update_icons() - return - if(MULE_CALC_MIN to MULE_CALC_MAX) // Calcing path - if(path.len) - mode = MULE_PATH_DONE - update_icons() - else - ++mode - return - if(MULE_PATH_DONE) // Done with path - obstacle = null - if(path.len) - frustration = 0 - mode = MULE_MOVING - else - if(home) - target = home - targetName = "Home" - mode = MULE_LOST - else - mode = MULE_IDLE - update_icons() +/mob/living/bot/mulebot/confirmTarget() + return 1 + +/mob/living/bot/mulebot/calcTargetPath() + ..() + if(!target_path.len && target != home) // I presume that target is not null + resetTarget() + target = home + targetName = "Home" + +/mob/living/bot/mulebot/stepToTarget() + if(paused) + return + ..() + +/mob/living/bot/mulebot/UnarmedAttack(var/turf/T) + if(T == src.loc) + unload(dir) /mob/living/bot/mulebot/Bump(var/mob/living/M) if(!safety && istype(M)) @@ -302,28 +247,6 @@ M.Weaken(5) ..() -/mob/living/bot/mulebot/proc/makeStep() - var/turf/next = path[1] - if(next == loc) - path -= next - return - - var/moved = step_towards(src, next) - if(moved) - frustration = 0 - path -= next - else if(frustration < 6) - if(frustration == 3) - custom_emote(2, "makes an annoyed buzzing sound") - playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) - ++frustration - else - custom_emote(2, "makes a sighing buzz.") - playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) - obstacle = next - - mode = MULE_LOST - /mob/living/bot/mulebot/proc/runOver(var/mob/living/carbon/human/H) if(istype(H)) // No safety checks - WILL run over lying humans. Stop ERPing in the maint! visible_message("[src] drives over [H]!") @@ -362,11 +285,6 @@ new /obj/effect/decal/cleanable/blood/oil(Tsec) ..() -/mob/living/bot/mulebot/proc/calc_path(var/turf/avoid = null) - path = AStar(loc, target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 250, id = botcard, exclude = avoid) - if(!path) - path = list() - /mob/living/bot/mulebot/proc/load(var/atom/movable/C) if(busy || load || get_dist(C, src) > 1 || !isturf(C.loc)) return @@ -384,10 +302,6 @@ if(istype(crate)) crate.close() - //I'm sure someone will come along and ask why this is here... well people were dragging screen items onto the mule, and that was not cool. - //So this is a simple fix that only allows a selection of item types to be considered. Further narrowing-down is below. - //if(!istype(C,/obj/item) && !istype(C,/obj/machinery) && !istype(C,/obj/structure) && !ismob(C)) - // return busy = 1 C.loc = loc @@ -428,11 +342,3 @@ AM.layer = initial(AM.layer) AM.pixel_y = initial(AM.pixel_y) busy = 0 - -#undef MULE_IDLE -#undef MULE_MOVING -#undef MULE_UNLOAD -#undef MULE_LOST -#undef MULE_CALC_MIN -#undef MULE_CALC_MAX -#undef MULE_PATH_DONE diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index 6b580285769..ea80efca7e1 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -6,8 +6,8 @@ health = 50 req_one_access = list(access_security, access_forensics_lockers) botcard_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels) - - var/mob/target + patrol_speed = 2 + target_speed = 3 var/idcheck = 0 // If true, arrests for having weapons without authorization. var/check_records = 0 // If true, arrests people without a record. @@ -16,30 +16,9 @@ var/declare_arrests = 0 // If true, announces arrests over sechuds. var/auto_patrol = 0 // If true, patrols on its own - var/mode = 0 -#define SECBOT_IDLE 0 // idle -#define SECBOT_HUNT 1 // found target, hunting -#define SECBOT_ARREST 2 // arresting target -#define SECBOT_START_PATROL 3 // start patrol -#define SECBOT_WAIT_PATROL 4 // waiting for signals -#define SECBOT_PATROL 5 // patrolling -#define SECBOT_SUMMON 6 // summoned by PDA - var/is_attacking = 0 var/is_ranged = 0 var/awaiting_surrender = 0 - var/obj/secbot_listener/listener = null - var/beacon_freq = 1445 // Navigation beacon frequency - var/control_freq = BOT_FREQ // Bot control frequency - var/list/path = list() - var/frustration = 0 - var/turf/patrol_target = null // This is where we are headed - var/closest_dist // Used to find the closest beakon - var/destination = "__nearest__" // This is the current beacon's ID - var/next_destination = "__nearest__" // This is the next beacon's ID - var/nearest_beacon // Tag of the beakon that we assume to be the closest one - - var/bot_version = 1.3 var/list/threat_found_sounds = new('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg') var/list/preparing_arrest_sounds = new('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg') @@ -48,24 +27,8 @@ desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey." auto_patrol = 1 -/mob/living/bot/secbot/New() - ..() - listener = new /obj/secbot_listener(src) - listener.secbot = src - - spawn(5) // Since beepsky is made on the start... this delay is necessary - if(radio_controller) - radio_controller.add_object(listener, control_freq, filter = RADIO_SECBOT) - radio_controller.add_object(listener, beacon_freq, filter = RADIO_NAVBEACONS) - -/mob/living/bot/secbot/turn_off() - ..() - target = null - frustration = 0 - mode = SECBOT_IDLE - /mob/living/bot/secbot/update_icons() - if(on && is_attacking) + if(on && busy) icon_state = "secbot-c" else icon_state = "secbot[on]" @@ -78,7 +41,7 @@ /mob/living/bot/secbot/attack_hand(var/mob/user) user.set_machine(src) var/dat - dat += "Automatic Security Unit v[bot_version]

" + dat += "Automatic Security Unit

" dat += "Status: [on ? "On" : "Off"]
" dat += "Behaviour controls are [locked ? "locked" : "unlocked"]
" dat += "Maintenance panel is [open ? "opened" : "closed"]" @@ -89,7 +52,7 @@ dat += "Operating Mode: [arrest_type ? "Detain" : "Arrest"]
" dat += "Report Arrests: [declare_arrests ? "Yes" : "No"]
" dat += "Auto Patrol: [auto_patrol ? "On" : "Off"]" - user << browse("Securitron v[bot_version] controls[dat]", "window=autosec") + user << browse("Securitron controls[dat]", "window=autosec") onclose(user, "autosec") return @@ -118,7 +81,6 @@ arrest_type = !arrest_type if("patrol") auto_patrol = !auto_patrol - mode = SECBOT_IDLE if("declarearrests") declare_arrests = !declare_arrests attack_hand(usr) @@ -129,127 +91,47 @@ if(health < curhealth) target = user awaiting_surrender = 5 - mode = SECBOT_HUNT -/mob/living/bot/secbot/Life() +/mob/living/bot/secbot/startPatrol() + if(!locked) // Stop running away when we set you up + return ..() - if(!on) - return - if(client) - return - if(!target) - scan_view() +/mob/living/bot/secbot/confirmTarget(var/atom/A) + if(!..()) + return 0 - if(!locked && (mode == SECBOT_START_PATROL || mode == SECBOT_PATROL)) // Stop running away when we set you up - mode = SECBOT_IDLE + return (check_threat(A) > 3) - switch(mode) - if(SECBOT_IDLE) - if(auto_patrol && locked) - mode = SECBOT_START_PATROL +/mob/living/bot/secbot/lookForTargets() + for(var/mob/living/M in view(src)) + if(M.stat == DEAD) + continue + if(confirmTarget(M)) + var/threat = check_threat(M) + target = M + awaiting_surrender = -1 + say("Level [threat] infraction alert!") + custom_emote(1, "points at [M.name]!") return - if(SECBOT_HUNT) // Target is in the view or has been recently - chase it - if(frustration > 7) - target = null - frustration = 0 - awaiting_surrender = 0 - mode = SECBOT_IDLE - return - if(target) - var/threat = check_threat(target) - if(threat < 4) // Re-evaluate in case they dropped the weapon or something - target = null - frustration = 0 - awaiting_surrender = 0 - mode = SECBOT_IDLE - return - if(!(target in view(7, src))) - ++frustration - if(Adjacent(target)) - mode = SECBOT_ARREST - return - else - if(is_ranged) - RangedAttack(target) - else - step_towards(src, target) // Melee bots chase a bit faster - spawn(8) - if(!Adjacent(target)) - step_towards(src, target) - spawn(16) - if(!Adjacent(target)) - step_towards(src, target) +/mob/living/bot/secbot/calcTargetPath() + ..() + if(awaiting_surrender != -1) + awaiting_surrender = 5 // This implies that a) we have already approached the target and b) it has moved after the warning - if(SECBOT_ARREST) // Target is next to us - attack it - if(!target) - mode = SECBOT_IDLE - if(!Adjacent(target)) - awaiting_surrender = 5 // I'm done playing nice - mode = SECBOT_HUNT - return - var/threat = check_threat(target) - if(threat < 4) - target = null - awaiting_surrender = 0 - frustration = 0 - mode = SECBOT_IDLE - return - if(awaiting_surrender < 5 && ishuman(target) && !target.lying) - if(awaiting_surrender == 0) - say("Down on the floor, [target]! You have five seconds to comply.") - ++awaiting_surrender - else - UnarmedAttack(target) - if(ishuman(target) && declare_arrests) - var/area/location = get_area(src) - broadcast_security_hud_message("[src] is [arrest_type ? "detaining" : "arresting"] a level [check_threat(target)] suspect [target] in [location].", src) - return - - if(SECBOT_START_PATROL) - if(path.len && patrol_target) - mode = SECBOT_PATROL - return - else if(patrol_target) - spawn(0) - calc_path() - if(!path.len) - patrol_target = null - mode = SECBOT_IDLE - else - mode = SECBOT_PATROL - if(!patrol_target) - if(next_destination) - find_next_target() - else - find_patrol_target() - say("Engaging patrol mode.") - mode = SECBOT_WAIT_PATROL - return - - if(SECBOT_WAIT_PATROL) - if(patrol_target) - mode = SECBOT_START_PATROL - else - ++frustration - if(frustration > 120) - frustration = 0 - mode = SECBOT_IDLE - - if(SECBOT_PATROL) - patrol_step() - spawn(10) - patrol_step() - return - - if(SECBOT_SUMMON) - patrol_step() - spawn(8) - patrol_step() - spawn(16) - patrol_step() - return +/mob/living/bot/secbot/handleAdjacentTarget() + if(awaiting_surrender < 5 && ishuman(target) && !target:lying) + if(awaiting_surrender == -1) + say("Down on the floor, [target]! You have five seconds to comply.") + ++awaiting_surrender + else + UnarmedAttack(target) + if(ishuman(target) && declare_arrests) + var/area/location = get_area(src) + broadcast_security_hud_message("[src] is [arrest_type ? "detaining" : "arresting"] a level [check_threat(target)] suspect [target] in [location].", src) + + // say("Engaging patrol mode.") /mob/living/bot/secbot/UnarmedAttack(var/mob/M, var/proximity) if(!..()) @@ -271,31 +153,33 @@ C.stun_effect_act(0, 60, null) playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) do_attack_animation(C) - is_attacking = 1 + busy = 1 update_icons() spawn(2) - is_attacking = 0 + busy = 0 update_icons() visible_message("[C] was prodded by [src] with a stun baton!") else playsound(loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2) visible_message("[src] is trying to put handcuffs on [C]!") + busy = 1 if(do_mob(src, C, 60)) if(!C.handcuffed) C.handcuffed = new /obj/item/weapon/handcuffs(C) C.update_inv_handcuffed() if(preparing_arrest_sounds.len) playsound(loc, pick(preparing_arrest_sounds), 50, 0) + busy = 0 else if(istype(M, /mob/living/simple_animal)) var/mob/living/simple_animal/S = M S.AdjustStunned(10) S.adjustBruteLoss(15) do_attack_animation(M) playsound(loc, "swing_hit", 50, 1, -1) - is_attacking = 1 + busy = 1 update_icons() spawn(2) - is_attacking = 0 + busy = 0 update_icons() visible_message("[M] was beaten by [src] with a stun baton!") @@ -319,30 +203,8 @@ new /obj/effect/decal/cleanable/blood/oil(Tsec) qdel(src) -/mob/living/bot/secbot/proc/scan_view() - for(var/mob/living/M in view(7, src)) - if(M.invisibility >= INVISIBILITY_LEVEL_ONE) - continue - if(M.stat) - continue - - var/threat = check_threat(M) - - if(threat >= 4) - target = M - say("Level [threat] infraction alert!") - custom_emote(1, "points at [M.name]!") - mode = SECBOT_HUNT - break - return - -/mob/living/bot/secbot/proc/calc_path(var/turf/avoid = null) - path = AStar(loc, patrol_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 120, id=botcard, exclude=avoid) - if(!path) - path = list() - /mob/living/bot/secbot/proc/check_threat(var/mob/living/M) - if(!M || !istype(M) || M.stat || src == M) + if(!M || !istype(M) || M.stat == DEAD || src == M) return 0 if(emagged) @@ -350,131 +212,6 @@ return M.assess_perp(access_scanner, 0, idcheck, check_records, check_arrest) -/mob/living/bot/secbot/proc/patrol_step() - if(loc == patrol_target) - patrol_target = null - path = list() - mode = SECBOT_IDLE - return - - if(path.len && patrol_target) - var/turf/next = path[1] - if(loc == next) - path -= next - return - var/moved = step_towards(src, next) - if(moved) - path -= next - frustration = 0 - else - ++frustration - if(frustration > 5) // Make a new path - mode = SECBOT_START_PATROL - return - else - mode = SECBOT_START_PATROL - -/mob/living/bot/secbot/proc/find_patrol_target() - send_status() - nearest_beacon = null - next_destination = "__nearest__" - listener.post_signal(beacon_freq, "findbeacon", "patrol") - -/mob/living/bot/secbot/proc/find_next_target() - send_status() - nearest_beacon = null - listener.post_signal(beacon_freq, "findbeacon", "patrol") - -/mob/living/bot/secbot/proc/send_status() - var/list/kv = list( - "type" = "secbot", - "name" = name, - "loca" = get_area(loc), - "mode" = mode - ) - listener.post_signal_multiple(control_freq, kv) - -/obj/secbot_listener - var/mob/living/bot/secbot/secbot = null - -/obj/secbot_listener/proc/post_signal(var/freq, var/key, var/value) // send a radio signal with a single data key/value pair - post_signal_multiple(freq, list("[key]" = value)) - -/obj/secbot_listener/proc/post_signal_multiple(var/freq, var/list/keyval) // send a radio signal with multiple data key/values - var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) - if(!frequency) - return - - var/datum/signal/signal = new() - signal.source = secbot - signal.transmission_method = 1 - signal.data = keyval.Copy() - - if(signal.data["findbeacon"]) - frequency.post_signal(secbot, signal, filter = RADIO_NAVBEACONS) - else if(signal.data["type"] == "secbot") - frequency.post_signal(secbot, signal, filter = RADIO_SECBOT) - else - frequency.post_signal(secbot, signal) - -/obj/secbot_listener/receive_signal(datum/signal/signal) - if(!secbot || !secbot.on) - return - - var/recv = signal.data["command"] - if(recv == "bot_status") - secbot.send_status() - return - - if(signal.data["active"] == secbot) - switch(recv) - if("stop") - secbot.mode = SECBOT_IDLE - secbot.auto_patrol = 0 - return - - if("go") - secbot.mode = SECBOT_IDLE - secbot.auto_patrol = 1 - return - - if("summon") - secbot.patrol_target = signal.data["target"] - secbot.next_destination = secbot.destination - secbot.destination = null - //secbot.awaiting_beacon = 0 - secbot.mode = SECBOT_SUMMON - secbot.calc_path() - secbot.say("Responding.") - return - - recv = signal.data["beacon"] - var/valid = signal.data["patrol"] - if(!recv || !valid) - return - - if(recv == secbot.next_destination) // This beacon is our target - secbot.destination = secbot.next_destination - secbot.patrol_target = signal.source.loc - secbot.next_destination = signal.data["next_patrol"] - else if(secbot.next_destination == "__nearest__") - var/dist = get_dist(secbot, signal.source.loc) - if(dist <= 1) - return - - if(secbot.nearest_beacon) - if(dist < secbot.closest_dist) - secbot.nearest_beacon = recv - secbot.patrol_target = secbot.nearest_beacon - secbot.next_destination = signal.data["next_patrol"] - secbot.closest_dist = dist - return - else - secbot.nearest_beacon = recv - secbot.patrol_target = secbot.nearest_beacon - secbot.next_destination = signal.data["next_patrol"] - secbot.closest_dist = dist - //Secbot Construction /obj/item/clothing/head/helmet/attackby(var/obj/item/device/assembly/signaler/S, mob/user as mob) diff --git a/nano/templates/pda.tmpl b/nano/templates/pda.tmpl index 756f6edce0c..396cf8e34f4 100644 --- a/nano/templates/pda.tmpl +++ b/nano/templates/pda.tmpl @@ -775,14 +775,10 @@ Used In File(s): \code\game\objects\items\devices\PDA\PDA.dm
Status:
- {{if value.mode == 0}} - Idle - {{else value.mode == 1}} - Moving - {{else value.mode == 2}} - Unloading - {{else}} - Calculating path + {{if value.paused == 0}} + Nominal + {{else value.paused == 1}} + Paused {{/if}}