From e8df17ccadedf9376024557eea9dc356852b534a Mon Sep 17 00:00:00 2001 From: Kelenius Date: Tue, 8 Mar 2016 11:13:34 +0300 Subject: [PATCH 1/2] WIP - cleans up bots Fixes farmbot getting stuck Floorbots fix asteroid --- code/modules/mob/living/bot/bot.dm | 123 ++++++++- code/modules/mob/living/bot/cleanbot.dm | 163 ++--------- code/modules/mob/living/bot/ed209bot.dm | 6 +- code/modules/mob/living/bot/farmbot.dm | 165 ++++++----- code/modules/mob/living/bot/floorbot.dm | 180 ++++++------ code/modules/mob/living/bot/medbot.dm | 100 +++---- code/modules/mob/living/bot/mulebot.dm | 65 ++--- code/modules/mob/living/bot/secbot.dm | 353 +++--------------------- 8 files changed, 414 insertions(+), 741 deletions(-) diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index cd2dbb8642d..9f7a42f9fc0 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,28 @@ 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/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 + /mob/living/bot/New() ..() update_icons() @@ -40,6 +58,9 @@ stunned = 0 paralysis = 0 + if(on && !client && !busy) + handleAI() + /mob/living/bot/updatehealth() if(status_flags & GODMODE) health = maxHealth @@ -109,6 +130,102 @@ /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.loc || prob(1)) + ignore_list -= A + handleRegular() + if(target && confirmTarget(target)) + if(Adjacent(target)) + handleAdjacentTarget() + else + handleRangedTarget() + if(get_dist(src, target) > min_target_dist && (!wait_if_pulled || !pulledby)) + for(var/i = 1 to target_speed) + stepToTarget() + sleep(20 / target_speed) + else + target = null + lookForTargets() + if(will_patrol && !pulledby && !target) + if(patrol_path.len) + for(var/i = 1 to patrol_speed) + handlePatrol() + if(i < patrol_speed) + sleep(20 / target_speed) + 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(!target_path.len || get_turf(target) != target_path[target_path.len]) + calcTargetPath() + makeStep(target_path) + 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) + if(!patrol_path) + patrol_path = list() + 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) + if(!target_path) + if(target && target.loc) + ignore_list |= target + target = null + target_path = list() + return + +/mob/living/bot/proc/makeStep(var/list/path) + if(!path.len) + return + var/turf/T = path[1] + if(get_turf(src) == T) + path -= T + makeStep(path) + return + + step_towards(src, T) + /mob/living/bot/proc/turn_on() if(stat) return 0 @@ -121,6 +238,10 @@ on = 0 set_light(0) update_icons() + target = null + target_path = list() + patrol_path = list() + ignore_list = list() /mob/living/bot/proc/explode() qdel(src) @@ -141,7 +262,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 30e137c21a5..18a3710dbb1 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..dbc42f82821 100644 --- a/code/modules/mob/living/bot/mulebot.dm +++ b/code/modules/mob/living/bot/mulebot.dm @@ -18,18 +18,14 @@ mob_bump_flag = HEAVY botcard_access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station) + max_target_dist = 250 - var/busy = 0 - var/mode = MULE_IDLE var/atom/movable/load 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 @@ -71,19 +67,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 +160,8 @@ /mob/living/bot/mulebot/proc/obeyCommand(var/command) switch(command) if("Home") - mode = MULE_IDLE target = home targetName = "Home" - mode = MULE_LOST if("SetD") var/new_dest var/list/beaconlist = new() @@ -195,10 +176,9 @@ target = get_turf(beaconlist[new_dest]) targetName = new_dest if("GoTD") - if(mode == MULE_IDLE) - mode = MULE_LOST + if("Stop") - mode = MULE_IDLE + /mob/living/bot/mulebot/emag_act(var/remaining_charges, var/user) locked = !locked @@ -216,18 +196,21 @@ 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) +/mob/living/bot/mulebot/handleAdjacentTarget() + if(get_turf(target) == src.loc) + UnarmedAttack(target) + +/mob/living/bot/mulebot/UnarmedAttack(var/turf/T) + unload(dir) + +/mob/living/bot/mulebot/Life() + ..() + switch(mode) - if(MULE_IDLE) // Idle - return if(MULE_MOVING) // Moving to target if(!target) // Return home if(auto_return && home) @@ -255,7 +238,7 @@ update_icons() return if(MULE_UNLOAD) - unload(dir) + if(auto_return && home && (loc != home)) target = home @@ -265,22 +248,6 @@ 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) @@ -302,6 +269,7 @@ M.Weaken(5) ..() + /* /mob/living/bot/mulebot/proc/makeStep() var/turf/next = path[1] if(next == loc) @@ -323,6 +291,7 @@ 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! 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) From 0b71767b0f2c58904926e9757b3b622bcd7a0855 Mon Sep 17 00:00:00 2001 From: Kelenius Date: Tue, 8 Mar 2016 12:55:42 +0300 Subject: [PATCH 2/2] Finalizes bot updates --- code/game/objects/items/devices/PDA/cart.dm | 4 +- code/modules/mob/living/bot/bot.dm | 58 +++++--- code/modules/mob/living/bot/mulebot.dm | 143 ++++++-------------- nano/templates/pda.tmpl | 12 +- 4 files changed, 86 insertions(+), 131 deletions(-) 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 9f7a42f9fc0..5679cc1a461 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -24,6 +24,7 @@ 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 @@ -35,6 +36,7 @@ var/target_patience = 5 var/frustration = 0 + var/max_frustration = 0 /mob/living/bot/New() ..() @@ -133,7 +135,7 @@ /mob/living/bot/proc/handleAI() if(ignore_list.len) for(var/atom/A in ignore_list) - if(!A.loc || prob(1)) + if(!A || !A.loc || prob(1)) ignore_list -= A handleRegular() if(target && confirmTarget(target)) @@ -141,19 +143,24 @@ handleAdjacentTarget() else handleRangedTarget() - if(get_dist(src, target) > min_target_dist && (!wait_if_pulled || !pulledby)) + if(!wait_if_pulled || !pulledby) for(var/i = 1 to target_speed) stepToTarget() - sleep(20 / target_speed) + if(i < target_speed) + sleep(20 / target_speed) + if(max_frustration && frustration > max_frustration * target_speed) + handleFrustrated(1) else - target = null + 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 / target_speed) + sleep(20 / patrol_speed) + if(max_frustration && frustration > max_frustration * patrol_speed) + handleFrustrated(0) else startPatrol() else @@ -171,9 +178,19 @@ /mob/living/bot/proc/stepToTarget() if(!target || !target.loc) return - if(!target_path.len || get_turf(target) != target_path[target_path.len]) - calcTargetPath() - makeStep(target_path) + 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() @@ -195,9 +212,10 @@ /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) + 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() @@ -207,24 +225,29 @@ 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) + 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 - target = null - target_path = list() + resetTarget() + obstacle = null return /mob/living/bot/proc/makeStep(var/list/path) if(!path.len) - return + return 0 var/turf/T = path[1] if(get_turf(src) == T) path -= T - makeStep(path) - return + return makeStep(path) - step_towards(src, T) + 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) @@ -238,8 +261,7 @@ on = 0 set_light(0) update_icons() - target = null - target_path = list() + resetTarget() patrol_path = list() ignore_list = list() diff --git a/code/modules/mob/living/bot/mulebot.dm b/code/modules/mob/living/bot/mulebot.dm index dbc42f82821..c330e0efc0e 100644 --- a/code/modules/mob/living/bot/mulebot.dm +++ b/code/modules/mob/living/bot/mulebot.dm @@ -17,11 +17,15 @@ maxHealth = 150 mob_bump_flag = HEAVY - botcard_access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station) + 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/atom/movable/load + var/paused = 0 var/crates_only = 1 var/auto_return = 1 var/safety = 1 @@ -29,7 +33,6 @@ var/targetName var/turf/home var/homeName - var/turf/obstacle var/global/amount = 0 @@ -160,6 +163,7 @@ /mob/living/bot/mulebot/proc/obeyCommand(var/command) switch(command) if("Home") + resetTarget() target = home targetName = "Home" if("SetD") @@ -173,12 +177,13 @@ else alert("No destination beacons available.") if(new_dest) + resetTarget() target = get_turf(beaconlist[new_dest]) targetName = new_dest if("GoTD") - + paused = 0 if("Stop") - + paused = 1 /mob/living/bot/mulebot/emag_act(var/remaining_charges, var/user) locked = !locked @@ -191,7 +196,7 @@ 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" @@ -199,68 +204,41 @@ /mob/living/bot/mulebot/handleRegular() if(!safety && prob(1)) flick("mulebot-emagged", src) + update_icons() -/mob/living/bot/mulebot/handleAdjacentTarget() - if(get_turf(target) == src.loc) - UnarmedAttack(target) - -/mob/living/bot/mulebot/UnarmedAttack(var/turf/T) - unload(dir) - -/mob/living/bot/mulebot/Life() +/mob/living/bot/mulebot/handleFrustrated() + custom_emote(2, "makes a sighing buzz.") + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) ..() - switch(mode) - 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/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" - update_icons() - return - if(MULE_UNLOAD) - +/mob/living/bot/mulebot/confirmTarget() + return 1 - if(auto_return && home && (loc != home)) - target = home - targetName = "Home" - mode = MULE_LOST - else - mode = MULE_IDLE - update_icons() - 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/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)) @@ -269,30 +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]!") @@ -331,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 @@ -353,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 @@ -397,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/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}}